From 72b051147e341e3bbdcfbc426ab3d4c40dee4760 Mon Sep 17 00:00:00 2001 From: Shriti Priya Date: Tue, 9 Jun 2026 14:11:05 -0400 Subject: [PATCH 1/7] feat: add cpex-wasm-plugin and cpex-wasm-host crates Introduces the WASM plugin sandbox system: cpex-wasm-plugin (guest-side cdylib targeting wasm32-wasip2) and cpex-wasm-host (host-side runtime using wasmtime with sandbox policy enforcement, resource limits, and network filtering). Signed-off-by: Shriti Priya --- crates/cpex-wasm-host/Cargo.toml | 17 + crates/cpex-wasm-host/Makefile | 17 + crates/cpex-wasm-host/README.md | 245 +++++ crates/cpex-wasm-host/config/config.yaml | 43 + .../examples/wasm_plugin_demo.rs | 196 ++++ crates/cpex-wasm-host/src/conversions.rs | 483 +++++++++ crates/cpex-wasm-host/src/factory.rs | 200 ++++ crates/cpex-wasm-host/src/lib.rs | 9 + crates/cpex-wasm-host/src/policy_loader.rs | 201 ++++ crates/cpex-wasm-host/src/sandbox_manager.rs | 253 +++++ .../tests/test_policy_loader.rs | 107 ++ crates/cpex-wasm-host/wasm/plugin.wasm | Bin 0 -> 604424 bytes crates/cpex-wasm-host/wit/deps/cli.wit | 261 +++++ crates/cpex-wasm-host/wit/deps/clocks.wit | 157 +++ crates/cpex-wasm-host/wit/deps/filesystem.wit | 587 +++++++++++ crates/cpex-wasm-host/wit/deps/http.wit | 733 ++++++++++++++ crates/cpex-wasm-host/wit/deps/io.wit | 331 ++++++ crates/cpex-wasm-host/wit/deps/random.wit | 92 ++ crates/cpex-wasm-host/wit/deps/sockets.wit | 949 ++++++++++++++++++ crates/cpex-wasm-host/wit/world.wit | 231 +++++ crates/cpex-wasm-plugin/Cargo.lock | 787 +++++++++++++++ crates/cpex-wasm-plugin/Cargo.toml | 17 + crates/cpex-wasm-plugin/Makefile | 21 + crates/cpex-wasm-plugin/README.md | 215 ++++ crates/cpex-wasm-plugin/plugin.wasm | Bin 0 -> 604424 bytes crates/cpex-wasm-plugin/src/conversions.rs | 597 +++++++++++ crates/cpex-wasm-plugin/src/lib.rs | 78 ++ crates/cpex-wasm-plugin/wit/deps/cli.wit | 261 +++++ crates/cpex-wasm-plugin/wit/deps/clocks.wit | 157 +++ .../cpex-wasm-plugin/wit/deps/filesystem.wit | 587 +++++++++++ crates/cpex-wasm-plugin/wit/deps/http.wit | 733 ++++++++++++++ crates/cpex-wasm-plugin/wit/deps/io.wit | 331 ++++++ crates/cpex-wasm-plugin/wit/deps/random.wit | 92 ++ crates/cpex-wasm-plugin/wit/deps/sockets.wit | 949 ++++++++++++++++++ crates/cpex-wasm-plugin/wit/world.wit | 231 +++++ 35 files changed, 10168 insertions(+) create mode 100644 crates/cpex-wasm-host/Cargo.toml create mode 100644 crates/cpex-wasm-host/Makefile create mode 100644 crates/cpex-wasm-host/README.md create mode 100644 crates/cpex-wasm-host/config/config.yaml create mode 100644 crates/cpex-wasm-host/examples/wasm_plugin_demo.rs create mode 100644 crates/cpex-wasm-host/src/conversions.rs create mode 100644 crates/cpex-wasm-host/src/factory.rs create mode 100644 crates/cpex-wasm-host/src/lib.rs create mode 100644 crates/cpex-wasm-host/src/policy_loader.rs create mode 100644 crates/cpex-wasm-host/src/sandbox_manager.rs create mode 100644 crates/cpex-wasm-host/tests/test_policy_loader.rs create mode 100644 crates/cpex-wasm-host/wasm/plugin.wasm create mode 100644 crates/cpex-wasm-host/wit/deps/cli.wit create mode 100644 crates/cpex-wasm-host/wit/deps/clocks.wit create mode 100644 crates/cpex-wasm-host/wit/deps/filesystem.wit create mode 100644 crates/cpex-wasm-host/wit/deps/http.wit create mode 100644 crates/cpex-wasm-host/wit/deps/io.wit create mode 100644 crates/cpex-wasm-host/wit/deps/random.wit create mode 100644 crates/cpex-wasm-host/wit/deps/sockets.wit create mode 100644 crates/cpex-wasm-host/wit/world.wit create mode 100644 crates/cpex-wasm-plugin/Cargo.lock create mode 100644 crates/cpex-wasm-plugin/Cargo.toml create mode 100644 crates/cpex-wasm-plugin/Makefile create mode 100644 crates/cpex-wasm-plugin/README.md create mode 100644 crates/cpex-wasm-plugin/plugin.wasm create mode 100644 crates/cpex-wasm-plugin/src/conversions.rs create mode 100644 crates/cpex-wasm-plugin/src/lib.rs create mode 100644 crates/cpex-wasm-plugin/wit/deps/cli.wit create mode 100644 crates/cpex-wasm-plugin/wit/deps/clocks.wit create mode 100644 crates/cpex-wasm-plugin/wit/deps/filesystem.wit create mode 100644 crates/cpex-wasm-plugin/wit/deps/http.wit create mode 100644 crates/cpex-wasm-plugin/wit/deps/io.wit create mode 100644 crates/cpex-wasm-plugin/wit/deps/random.wit create mode 100644 crates/cpex-wasm-plugin/wit/deps/sockets.wit create mode 100644 crates/cpex-wasm-plugin/wit/world.wit diff --git a/crates/cpex-wasm-host/Cargo.toml b/crates/cpex-wasm-host/Cargo.toml new file mode 100644 index 00000000..376d998f --- /dev/null +++ b/crates/cpex-wasm-host/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "cpex-wasm-host" +version = "0.1.0" +edition = "2021" + +[dependencies] +tokio = { version = "1", features = ["sync", "macros", "io-util", "rt", "rt-multi-thread", "time", "signal", "net"] } +wasmtime = { version = "45.0", features = ["component-model", "async"] } +wasmtime-wasi = "45.0" +wasmtime-wasi-http = { version = "45.0", features = ["default-send-request"] } +hyper = "1" +anyhow = "1.0" +serde = { version = "1", features = ["derive"] } +serde_yaml = "0.9" +serde_json = { workspace = true } +cpex-core = { path = "../cpex-core" } +async-trait = "0.1" diff --git a/crates/cpex-wasm-host/Makefile b/crates/cpex-wasm-host/Makefile new file mode 100644 index 00000000..7c4a1c83 --- /dev/null +++ b/crates/cpex-wasm-host/Makefile @@ -0,0 +1,17 @@ +.PHONY: build build-plugin run clean + +PLUGIN_DIR = ../cpex-wasm-plugin +WASM_DIR = wasm + +build-plugin: + $(MAKE) -C $(PLUGIN_DIR) build + +build: build-plugin + cargo build --release + +run: build + cargo run --release + +clean: + cargo clean + rm -f $(WASM_DIR)/plugin.wasm diff --git a/crates/cpex-wasm-host/README.md b/crates/cpex-wasm-host/README.md new file mode 100644 index 00000000..9753358d --- /dev/null +++ b/crates/cpex-wasm-host/README.md @@ -0,0 +1,245 @@ +# cpex-wasm-host + +Loads and executes a WASM plugin inside a sandboxed wasmtime environment. Enforces resource limits (fuel, memory, execution time) and network/filesystem policies. Provides a bridge to cpex-core's `PluginManager` for integration into the hook pipeline. + +## How It Works + +``` +┌──────────────────────────────────────────────────────────┐ +│ PluginManager (cpex-core) │ +│ invoke_named::("cmf.tool_pre_invoke", ...) │ +└──────────────────────┬───────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────┐ +│ WasmBridgeHandler (factory.rs) │ +│ native MessagePayload → WIT MessagePayload │ +│ native Extensions → WIT Extensions │ +│ native PluginContext → WIT PluginContext │ +└──────────────────────┬───────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────┐ +│ SandboxManager (sandbox_manager.rs) │ +│ call_handle_hook() inside wasmtime sandbox │ +│ ┌─────────────────────────────────┐ │ +│ │ Sandbox Enforcement │ │ +│ │ • Fuel budget (session-level) │ │ +│ │ • Memory limit │ │ +│ │ • Execution timeout (per-call) │ │ +│ │ • Network allowlist │ │ +│ │ • Filesystem permissions │ │ +│ │ • Environment variable filter │ │ +│ └─────────────────────────────────┘ │ +└──────────────────────┬───────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────┐ +│ WIT PluginResult → native PluginResult │ +│ returned to PluginManager pipeline │ +└──────────────────────────────────────────────────────────┘ +``` + +## Project Structure + +``` +cpex-wasm-host/ +├── Cargo.toml +├── Makefile # Build plugin + host +├── config/ +│ └── config.yaml # Plugin sandbox policy config +├── examples/ +│ └── wasm_plugin_demo.rs # Integration with PluginManager +├── src/ +│ ├── lib.rs # Module exports +│ ├── sandbox_manager.rs # Core: wasmtime engine, plugin loading, invocation +│ ├── policy_loader.rs # Parses sandbox config (filesystem, network, env, resources) +│ ├── conversions.rs # Native cpex-core types ↔ WIT types +│ └── factory.rs # PluginFactory bridge for PluginManager integration +├── wasm/ +│ └── plugin.wasm # Compiled WASM plugin (from cpex-wasm-plugin) +└── wit/ + ├── world.wit # Plugin interface definition + └── deps/ # WASI interface dependencies +``` + +## Components + +### SandboxManager (`sandbox_manager.rs`) + +The core component. Manages a single WASM plugin in an isolated wasmtime environment. + +- `new()` — Creates the wasmtime engine, linker, and epoch ticker thread +- `load_wasmplugin(path, config)` — Instantiates a WASM component with sandbox policies applied +- `invoke(payload, extensions, ctx)` — Calls the plugin's `handle-hook` function +- `is_loaded()` — Checks if a plugin is loaded + +**Sandbox enforcement:** +- Fuel budget is session-level — set once at load, depletes across all invocations +- Execution timeout is per-invocation — reset each call so no single call hangs +- Network requests are gated by an allowlist of hosts +- Filesystem access is limited to preopened directories with explicit permissions +- Only explicitly listed environment variables are visible to the plugin + +### Policy Loader (`policy_loader.rs`) + +Parses sandbox configuration from the plugin's `config.sandbox_policy` YAML key: + +```yaml +plugins: + - name: identity-checker + kind: "wasm://plugin.wasm" + config: + sandbox_policy: + allowed_filesystem: + - dir: /tmp/data + permission: "read" + allowed_network: + - "httpbin.org" + allowed_env: + - "API_KEY" + resources: + max_memory_bytes: 10485760 + max_fuel: 1000000000 + max_execution_time_ms: 5000 +``` + +If `sandbox_policy` is absent, deny-by-default applies (no filesystem, no network, no env vars). + +### Conversions (`conversions.rs`) + +Bidirectional type mappings between native cpex-core types and WIT types: + +| Direction | Purpose | +|---|---| +| Native → WIT | Before calling the WASM sandbox (payload, extensions, context) | +| WIT → Native | After the sandbox returns (plugin result, modified payload) | + +WIT can't represent `HashMap`, `HashSet`, `Arc`, or `serde_json::Value` directly, so these are serialized to JSON strings or flattened to lists/tuples at the boundary. + +### Factory (`factory.rs`) + +Bridges cpex-core's `PluginFactory` trait to the `SandboxManager`. Contains: + +- `WasmPluginFactory` — implements `PluginFactory::create()`, loads the plugin into the sandbox +- `WasmBridgePlugin` — implements `Plugin` trait (lifecycle) +- `WasmBridgeHandler` — implements `AnyHookHandler`, converts types and routes calls through the sandbox + +## Prerequisites + +- Rust toolchain (stable) +- `wasm32-wasip2` target installed: + ```sh + rustup target add wasm32-wasip2 + ``` +- (Optional) `wasm-tools` for validation/inspection: + ```sh + cargo install wasm-tools + ``` + +## Building End-to-End + +### 1. Build the WASM plugin + +The plugin source lives in `../cpex-wasm-plugin`. Build it and copy the artifact: + +```sh +make build-plugin +``` + +This runs `cargo build --target wasm32-wasip2 --release` in the plugin crate and copies the resulting `plugin.wasm` into `wasm/`. + +### 2. Build the host + +```sh +cargo build --release +``` + +Or build both in one step: + +```sh +make build +``` + +### 3. Run the demo + +```sh +cargo run --example wasm_plugin_demo +``` + +This loads `config/config.yaml`, registers the WASM plugin factory, and invokes the plugin through cpex-core's `PluginManager` pipeline. + +### 4. Run tests + +```sh +cargo test +``` + +## Usage + +### Direct (without PluginManager) + +```rust +use std::path::Path; +use cpex_wasm_host::policy_loader::SandboxPolicy; +use cpex_wasm_host::sandbox_manager::SandboxManager; + +let policy = SandboxPolicy::default(); // deny-all sandbox +let mut manager = SandboxManager::new()?; +manager.load_wasmplugin(Path::new("wasm/plugin.wasm"), Some(&policy)).await?; + +let result = manager.invoke(payload, extensions, ctx).await?; +``` + +### Via PluginManager + +```rust +use std::path::PathBuf; +use cpex_wasm_host::factory::WasmPluginFactory; +use cpex_core::manager::PluginManager; +use cpex_core::config::parse_config; + +let mgr = PluginManager::default(); + +mgr.register_factory( + "wasm://plugin.wasm", + Box::new(WasmPluginFactory::new(PathBuf::from("wasm"))), +); + +let config = parse_config(&yaml)?; +mgr.load_config(config)?; +mgr.initialize().await?; + +let (result, bg) = mgr + .invoke_named::("cmf.tool_pre_invoke", payload, ext, None) + .await; +bg.wait_for_background_tasks().await; +``` + +## Config Format + +The `kind` field uses the `wasm://` scheme following cpex-core's convention. +The `sandbox_policy` is nested under the plugin's `config` key: + +```yaml +plugins: + - name: my-plugin + kind: "wasm://plugin.wasm" + hooks: [cmf.tool_pre_invoke] + capabilities: [read_security] + config: + sandbox_policy: + allowed_filesystem: + - dir: /tmp/data + permission: "read" + allowed_network: ["api.example.com"] + allowed_env: ["API_KEY"] + resources: + max_fuel: 500000000 + max_memory_bytes: 5242880 + max_execution_time_ms: 5000 + max_instances: 10 + max_tables: 10 +``` + +If `sandbox_policy` is absent or all lists are empty, deny-by-default applies (no filesystem, no network, no env vars). diff --git a/crates/cpex-wasm-host/config/config.yaml b/crates/cpex-wasm-host/config/config.yaml new file mode 100644 index 00000000..818b488e --- /dev/null +++ b/crates/cpex-wasm-host/config/config.yaml @@ -0,0 +1,43 @@ +# CMF Capabilities Demo Configuration +# +# Three plugins with different capabilities see different views +# of the same extensions. Demonstrates capability-gated access +# across pre-invoke and post-invoke hooks. + +plugin_settings: + routing_enabled: true + +global: + policies: + all: + plugins: [identity-checker] + +plugins: + - name: identity-checker + kind: wasm://plugin.wasm + hooks: [cmf.tool_pre_invoke, cmf.tool_post_invoke] + mode: sequential + priority: 10 + on_error: fail + capabilities: + - read_labels + - read_subject + - read_roles + config: + # Sandbox policy controls what host resources the WASM plugin can access. + # Empty lists = full lockdown (deny-all): no filesystem, no network, no env vars. + # The plugin can only operate on data passed via handle-hook arguments. + sandbox_policy: + allowed_filesystem: [] # No host filesystem access + allowed_network: [] # No outbound HTTP allowed + allowed_env: [] # No host environment variables exposed + resources: + max_memory_bytes: 10485760 # 10 MB linear memory cap + max_fuel: 1000000000 # ~1 billion instructions (session-level budget) + max_execution_time_ms: 5000 # 5 seconds per invocation timeout + max_instances: 10 # Max WASM module instances + max_tables: 10 # Max WASM tables + +routes: + - tool: "*" + plugins: [] diff --git a/crates/cpex-wasm-host/examples/wasm_plugin_demo.rs b/crates/cpex-wasm-host/examples/wasm_plugin_demo.rs new file mode 100644 index 00000000..a61a1104 --- /dev/null +++ b/crates/cpex-wasm-host/examples/wasm_plugin_demo.rs @@ -0,0 +1,196 @@ +// Location: ./crates/cpex-wasm-host/examples/wasm_plugin_demo.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Shriti Priya +// +// Demonstrates invoking a WASM plugin through the PluginManager pipeline. +// Reads plugin configuration from config/config.yaml. + +use std::path::PathBuf; +use std::sync::Arc; + +use cpex_core::cmf::{CmfHook, ContentPart, Message, MessagePayload, Role, ToolCall}; +use cpex_core::config::parse_config; +use cpex_core::extensions::{HttpExtension, RequestExtension, SecurityExtension}; +use cpex_core::hooks::payload::{Extensions, MetaExtension}; +use cpex_core::extensions::security::SubjectExtension; +use cpex_core::manager::PluginManager; + +use cpex_wasm_host::factory::WasmPluginFactory; + +#[tokio::main] +async fn main() { + println!("=== WASM Plugin Demo (via PluginManager) ===\n"); + + // 1. Create plugin manager and register wasm factory under the exact kind string. + // Each plugin gets its own isolated SandboxManager instance. + let crate_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let config_path = crate_dir.join("config/config.yaml"); + println!("--- Loading config from {} ---\n", config_path.display()); + let yaml = std::fs::read_to_string(&config_path) + .unwrap_or_else(|e| panic!("Failed to read {}: {}", config_path.display(), e)); + let cpex_config = parse_config(&yaml).unwrap(); + + let mgr = PluginManager::default(); + mgr.register_factory( + "wasm://plugin.wasm", + Box::new(WasmPluginFactory::new(crate_dir.join("wasm"))), + ); + + mgr.load_config(cpex_config).unwrap(); + mgr.initialize().await.unwrap(); + + // 3. Build a test payload (assistant requesting a tool call) + let payload = MessagePayload { + message: Message { + schema_version: cpex_core::cmf::constants::SCHEMA_VERSION.into(), + role: Role::Assistant, + content: vec![ + ContentPart::Text { + text: "Looking up compensation.".into(), + }, + ContentPart::ToolCall { + content: ToolCall { + tool_call_id: "tc_001".into(), + name: "get_compensation".into(), + arguments: [("employee_id".to_string(), serde_json::json!(42))].into(), + namespace: None, + }, + }, + ], + channel: None, + }, + }; + + // 4. Build extensions with security context + let mut security = SecurityExtension::default(); + security.add_label("PII"); + security.add_label("HR_DATA"); + security.classification = Some("confidential".into()); + security.subject = Some(SubjectExtension { + id: Some("alice".into()), + subject_type: Some(cpex_core::extensions::security::SubjectType::User), + roles: ["hr_admin".to_string()].into(), + permissions: ["read_compensation".to_string()].into(), + ..Default::default() + }); + + let mut http = HttpExtension::default(); + http.set_header("Authorization", "Bearer eyJ..."); + http.set_header("X-Request-ID", "req-abc-123"); + + let ext = Extensions { + request: Some(Arc::new(RequestExtension { + environment: Some("production".into()), + request_id: Some("req-abc-123".into()), + ..Default::default() + })), + security: Some(Arc::new(security)), + http: Some(Arc::new(http)), + meta: Some(Arc::new(MetaExtension { + entity_type: Some("tool".into()), + entity_name: Some("get_compensation".into()), + tags: ["pii".to_string(), "hr".to_string()].into(), + ..Default::default() + })), + ..Default::default() + }; + + // --- Pre-invoke: type-safe dispatch via invoke_named --- + println!("=== Phase 1: cmf.tool_pre_invoke ===\n"); + let (pre_result, bg) = mgr + .invoke_named::( + "cmf.tool_pre_invoke", + payload, + ext, + None, // first hook — no context table + ) + .await; + + println!(); + + println!(); + if pre_result.continue_processing { + println!("Pre-invoke result: ALLOWED"); + if let Some(ref modified_ext) = pre_result.modified_extensions { + if let Some(ref sec) = modified_ext.security { + let labels: Vec<&String> = sec.labels.iter().collect(); + println!(" Labels after pre-invoke: {:?}", labels); + } + if let Some(ref http) = modified_ext.http { + println!(" Headers after pre-invoke: {:?}", http.request_headers); + } + } + } else { + println!( + "Pre-invoke result: DENIED — {}", + pre_result.violation.as_ref().unwrap().reason + ); + bg.wait_for_background_tasks().await; + println!("\n=== Demo complete ==="); + return; + } + bg.wait_for_background_tasks().await; + + println!("\n--- Tool 'get_compensation' executes... ---"); + println!(" Result: {{\"salary\": 150000, \"currency\": \"USD\"}}\n"); + + // --- Post-invoke: different CMF message with tool result --- + println!("=== Phase 2: cmf.tool_post_invoke ===\n"); + + + let post_payload = MessagePayload { + message: Message { + schema_version: cpex_core::cmf::constants::SCHEMA_VERSION.into(), + role: Role::Tool, + content: vec![ContentPart::ToolResult { + content: cpex_core::cmf::ToolResult { + tool_call_id: "tc_001".into(), + tool_name: "get_compensation".into(), + content: serde_json::json!({"salary": 150000, "currency": "USD"}), + is_error: false, + }, + }], + channel: None, + }, + }; + + // Build post-invoke extensions — carry forward any modifications + // from pre-invoke via the context table + let post_ext = pre_result.modified_extensions.unwrap_or_else(|| { + // Rebuild if no modifications + let mut security = SecurityExtension::default(); + security.add_label("PII"); + Extensions { + security: Some(Arc::new(security)), + meta: Some(Arc::new(MetaExtension { + entity_type: Some("tool".into()), + entity_name: Some("get_compensation".into()), + ..Default::default() + })), + ..Default::default() + } + }); + + let (post_result, post_bg) = mgr + .invoke_named::( + "cmf.tool_post_invoke", + post_payload, + post_ext, + Some(pre_result.context_table), + ) + .await; + + println!(); + if post_result.continue_processing { + println!("Post-invoke result: ALLOWED"); + } else { + println!( + "Post-invoke result: DENIED — {}", + post_result.violation.as_ref().unwrap().reason + ); + } + + post_bg.wait_for_background_tasks().await; + println!("\n=== Demo complete ==="); +} diff --git a/crates/cpex-wasm-host/src/conversions.rs b/crates/cpex-wasm-host/src/conversions.rs new file mode 100644 index 00000000..1aea5bf3 --- /dev/null +++ b/crates/cpex-wasm-host/src/conversions.rs @@ -0,0 +1,483 @@ +// Location: ./crates/cpex-wasm-host/src/conversions.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Shriti Priya +// +// Host-side type conversions: native cpex-core types ↔ WIT types. +// Used by WasmBridgeHandler to translate between the PluginManager's native +// types and the WIT types that the WASM sandbox expects. + +use std::collections::HashMap; + +use cpex_core::cmf::content as native_content; +use cpex_core::cmf::enums as native_enums; +use cpex_core::cmf::message as native_msg; +use cpex_core::context::PluginContext as NativePluginContext; +use cpex_core::error::PluginViolation as NativePluginViolation; +use cpex_core::extensions::container::Extensions as NativeExtensions; +use cpex_core::extensions::http::HttpExtension as NativeHttpExtension; +use cpex_core::extensions::meta::MetaExtension as NativeMetaExtension; +use cpex_core::extensions::request::RequestExtension as NativeRequestExtension; +use cpex_core::extensions::security::{ + SecurityExtension as NativeSecurityExtension, SubjectExtension as NativeSubjectExtension, + SubjectType as NativeSubjectType, +}; +use cpex_core::hooks::trait_def::PluginResult as NativePluginResult; + +use crate::sandbox_manager::types::*; + +// --------------------------------------------------------------------------- +// Native → WIT: MessagePayload +// --------------------------------------------------------------------------- + +/// Converts native cpex-core MessagePayload to WIT MessagePayload for the sandbox. +pub fn native_payload_to_wit(payload: &native_msg::MessagePayload) -> MessagePayload { + MessagePayload { + message: native_message_to_wit(&payload.message), + } +} + +fn native_message_to_wit(msg: &native_msg::Message) -> Message { + Message { + schema_version: msg.schema_version.clone(), + role: native_role_to_wit(msg.role), + content: msg.content.iter().map(native_content_part_to_wit).collect(), + channel: msg.channel.map(native_channel_to_wit), + } +} + +fn native_role_to_wit(role: native_enums::Role) -> Role { + match role { + native_enums::Role::System => Role::System, + native_enums::Role::Developer => Role::Developer, + native_enums::Role::User => Role::User, + native_enums::Role::Assistant => Role::Assistant, + native_enums::Role::Tool => Role::Tool, + } +} + +fn native_channel_to_wit(channel: native_enums::Channel) -> Channel { + match channel { + native_enums::Channel::Analysis => Channel::Analysis, + native_enums::Channel::Commentary => Channel::Commentary, + native_enums::Channel::Final => Channel::Final, + } +} + +fn native_content_part_to_wit(part: &native_content::ContentPart) -> ContentPart { + match part { + native_content::ContentPart::Text { text } => ContentPart::Text(text.clone()), + native_content::ContentPart::Thinking { text } => ContentPart::Thinking(text.clone()), + native_content::ContentPart::ToolCall { content } => { + ContentPart::ToolCall(native_tool_call_to_wit(content)) + } + native_content::ContentPart::ToolResult { content } => { + ContentPart::ToolResult(native_tool_result_to_wit(content)) + } + native_content::ContentPart::Resource { content } => { + ContentPart::CmfResource(native_resource_to_wit(content)) + } + native_content::ContentPart::ResourceRef { content } => { + ContentPart::ResourceRef(native_resource_ref_to_wit(content)) + } + native_content::ContentPart::PromptRequest { content } => { + ContentPart::PromptRequest(native_prompt_request_to_wit(content)) + } + native_content::ContentPart::PromptResult { content } => { + ContentPart::PromptResult(native_prompt_result_to_wit(content)) + } + native_content::ContentPart::Image { content } => ContentPart::Image(ImageSource { + source_type: content.source_type.clone(), + data: content.data.clone(), + media_type: content.media_type.clone(), + }), + native_content::ContentPart::Video { content } => ContentPart::Video(VideoSource { + source_type: content.source_type.clone(), + data: content.data.clone(), + media_type: content.media_type.clone(), + duration_ms: content.duration_ms, + }), + native_content::ContentPart::Audio { content } => ContentPart::Audio(AudioSource { + source_type: content.source_type.clone(), + data: content.data.clone(), + media_type: content.media_type.clone(), + duration_ms: content.duration_ms, + }), + native_content::ContentPart::Document { content } => { + ContentPart::Document(DocumentSource { + source_type: content.source_type.clone(), + data: content.data.clone(), + media_type: content.media_type.clone(), + title: content.title.clone(), + }) + } + } +} + +fn native_tool_call_to_wit(tc: &native_content::ToolCall) -> ToolCall { + ToolCall { + tool_call_id: tc.tool_call_id.clone(), + name: tc.name.clone(), + arguments: serde_json::to_string(&tc.arguments).unwrap_or_else(|_| "{}".to_string()), + namespace: tc.namespace.clone(), + } +} + +fn native_tool_result_to_wit(tr: &native_content::ToolResult) -> ToolResult { + ToolResult { + tool_call_id: tr.tool_call_id.clone(), + tool_name: tr.tool_name.clone(), + content: serde_json::to_string(&tr.content).unwrap_or_default(), + is_error: tr.is_error, + } +} + +fn native_resource_to_wit(r: &native_content::Resource) -> CmfResource { + CmfResource { + resource_request_id: r.resource_request_id.clone(), + uri: r.uri.clone(), + name: r.name.clone(), + description: r.description.clone(), + resource_type: native_resource_type_to_wit(r.resource_type), + content: r.content.clone(), + blob: r.blob.clone(), + mime_type: r.mime_type.clone(), + size_bytes: r.size_bytes, + annotations: serde_json::to_string(&r.annotations).unwrap_or_else(|_| "{}".to_string()), + version: r.version.clone(), + } +} + +fn native_resource_type_to_wit(rt: native_enums::ResourceType) -> ResourceType { + match rt { + native_enums::ResourceType::File => ResourceType::File, + native_enums::ResourceType::Blob => ResourceType::Blob, + native_enums::ResourceType::Uri => ResourceType::Uri, + native_enums::ResourceType::Database => ResourceType::Database, + native_enums::ResourceType::Api => ResourceType::Api, + native_enums::ResourceType::Memory => ResourceType::Memory, + native_enums::ResourceType::Artifact => ResourceType::Artifact, + } +} + +fn native_resource_ref_to_wit(rr: &native_content::ResourceReference) -> ResourceReference { + ResourceReference { + resource_request_id: rr.resource_request_id.clone(), + uri: rr.uri.clone(), + name: rr.name.clone(), + resource_type: native_resource_type_to_wit(rr.resource_type), + range_start: rr.range_start, + range_end: rr.range_end, + selector: rr.selector.clone(), + } +} + +fn native_prompt_request_to_wit(pr: &native_content::PromptRequest) -> PromptRequest { + PromptRequest { + prompt_request_id: pr.prompt_request_id.clone(), + name: pr.name.clone(), + arguments: serde_json::to_string(&pr.arguments).unwrap_or_else(|_| "{}".to_string()), + server_id: pr.server_id.clone(), + } +} + +fn native_prompt_result_to_wit(pr: &native_content::PromptResult) -> PromptResult { + PromptResult { + prompt_request_id: pr.prompt_request_id.clone(), + prompt_name: pr.prompt_name.clone(), + messages: serde_json::to_string(&pr.messages).unwrap_or_else(|_| "[]".to_string()), + content: pr.content.clone(), + is_error: pr.is_error, + error_message: pr.error_message.clone(), + } +} + +// --------------------------------------------------------------------------- +// Native → WIT: Extensions +// --------------------------------------------------------------------------- + +/// Converts native cpex-core Extensions to WIT Extensions. +/// Only maps fields that the WIT interface supports (request, security, http, meta). +pub fn native_extensions_to_wit(ext: &NativeExtensions) -> Extensions { + Extensions { + request: ext.request.as_ref().map(|r| native_request_to_wit(r)), + security: ext.security.as_ref().map(|s| native_security_to_wit(s)), + http: ext.http.as_ref().map(|h| native_http_to_wit(h)), + meta: ext.meta.as_ref().map(|m| native_meta_to_wit(m)), + } +} + +fn native_request_to_wit(r: &NativeRequestExtension) -> RequestExtension { + RequestExtension { + environment: r.environment.clone(), + request_id: r.request_id.clone(), + timestamp: r.timestamp.clone(), + trace_id: r.trace_id.clone(), + span_id: r.span_id.clone(), + } +} + +fn native_security_to_wit(s: &NativeSecurityExtension) -> SecurityExtension { + SecurityExtension { + labels: s.labels.iter().cloned().collect(), + classification: s.classification.clone(), + subject: s.subject.as_ref().map(native_subject_to_wit), + auth_method: s.auth_method.clone(), + } +} + +fn native_subject_to_wit(s: &NativeSubjectExtension) -> SubjectExtension { + SubjectExtension { + id: s.id.clone(), + subject_type: s.subject_type.as_ref().map(native_subject_type_to_wit), + roles: s.roles.iter().cloned().collect(), + permissions: s.permissions.iter().cloned().collect(), + teams: s.teams.iter().cloned().collect(), + claims: s.claims.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), + } +} + +fn native_subject_type_to_wit(st: &NativeSubjectType) -> SubjectType { + match st { + NativeSubjectType::User => SubjectType::User, + NativeSubjectType::Agent => SubjectType::Agent, + NativeSubjectType::Service => SubjectType::Service, + NativeSubjectType::System => SubjectType::System, + } +} + +fn native_http_to_wit(h: &NativeHttpExtension) -> HttpExtension { + HttpExtension { + request_headers: h.request_headers.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), + response_headers: h.response_headers.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), + } +} + +fn native_meta_to_wit(m: &NativeMetaExtension) -> MetaExtension { + MetaExtension { + entity_type: m.entity_type.clone(), + entity_name: m.entity_name.clone(), + tags: m.tags.iter().cloned().collect(), + scope: m.scope.clone(), + properties: m.properties.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), + } +} + +// --------------------------------------------------------------------------- +// Native → WIT: PluginContext +// --------------------------------------------------------------------------- + +/// Converts native PluginContext (HashMaps) to WIT PluginContext (JSON strings). +pub fn native_context_to_wit(ctx: &NativePluginContext) -> PluginContext { + PluginContext { + local_state: serde_json::to_string(&ctx.local_state).unwrap_or_else(|_| "{}".to_string()), + global_state: serde_json::to_string(&ctx.global_state).unwrap_or_else(|_| "{}".to_string()), + } +} + +// --------------------------------------------------------------------------- +// WIT → Native: PluginResult +// --------------------------------------------------------------------------- + +/// Converts a WIT PluginResult from the sandbox back to native PluginResult. +pub fn wit_result_to_native(result: crate::sandbox_manager::types::PluginResult) -> NativePluginResult { + NativePluginResult { + continue_processing: result.continue_processing, + modified_payload: result.modified_payload.map(wit_payload_to_native), + modified_extensions: None, // WIT modified_extensions would require OwnedExtensions conversion + violation: result.violation.map(wit_violation_to_native), + metadata: result.metadata.and_then(|s| serde_json::from_str(&s).ok()), + } +} + +fn wit_violation_to_native(v: PluginViolation) -> NativePluginViolation { + NativePluginViolation { + code: v.code, + reason: v.reason, + description: v.description, + details: serde_json::from_str(&v.details).unwrap_or_default(), + plugin_name: None, + proto_error_code: v.proto_error_code, + } +} + +// --------------------------------------------------------------------------- +// WIT → Native: MessagePayload (for modified_payload in results) +// --------------------------------------------------------------------------- + +fn wit_payload_to_native(payload: MessagePayload) -> native_msg::MessagePayload { + native_msg::MessagePayload { + message: wit_message_to_native(payload.message), + } +} + +fn wit_message_to_native(msg: Message) -> native_msg::Message { + native_msg::Message { + schema_version: msg.schema_version, + role: wit_role_to_native(msg.role), + content: msg.content.into_iter().map(wit_content_part_to_native).collect(), + channel: msg.channel.map(wit_channel_to_native), + } +} + +fn wit_role_to_native(role: Role) -> native_enums::Role { + match role { + Role::System => native_enums::Role::System, + Role::Developer => native_enums::Role::Developer, + Role::User => native_enums::Role::User, + Role::Assistant => native_enums::Role::Assistant, + Role::Tool => native_enums::Role::Tool, + } +} + +fn wit_channel_to_native(channel: Channel) -> native_enums::Channel { + match channel { + Channel::Analysis => native_enums::Channel::Analysis, + Channel::Commentary => native_enums::Channel::Commentary, + Channel::Final => native_enums::Channel::Final, + } +} + +fn wit_content_part_to_native(part: ContentPart) -> native_content::ContentPart { + match part { + ContentPart::Text(text) => native_content::ContentPart::Text { text }, + ContentPart::Thinking(text) => native_content::ContentPart::Thinking { text }, + ContentPart::ToolCall(tc) => native_content::ContentPart::ToolCall { + content: wit_tool_call_to_native(tc), + }, + ContentPart::ToolResult(tr) => native_content::ContentPart::ToolResult { + content: wit_tool_result_to_native(tr), + }, + ContentPart::CmfResource(r) => native_content::ContentPart::Resource { + content: wit_resource_to_native(r), + }, + ContentPart::ResourceRef(rr) => native_content::ContentPart::ResourceRef { + content: wit_resource_ref_to_native(rr), + }, + ContentPart::PromptRequest(pr) => native_content::ContentPart::PromptRequest { + content: wit_prompt_request_to_native(pr), + }, + ContentPart::PromptResult(pr) => native_content::ContentPart::PromptResult { + content: wit_prompt_result_to_native(pr), + }, + ContentPart::Image(img) => native_content::ContentPart::Image { + content: native_content::ImageSource { + source_type: img.source_type, + data: img.data, + media_type: img.media_type, + }, + }, + ContentPart::Video(v) => native_content::ContentPart::Video { + content: native_content::VideoSource { + source_type: v.source_type, + data: v.data, + media_type: v.media_type, + duration_ms: v.duration_ms, + }, + }, + ContentPart::Audio(a) => native_content::ContentPart::Audio { + content: native_content::AudioSource { + source_type: a.source_type, + data: a.data, + media_type: a.media_type, + duration_ms: a.duration_ms, + }, + }, + ContentPart::Document(d) => native_content::ContentPart::Document { + content: native_content::DocumentSource { + source_type: d.source_type, + data: d.data, + media_type: d.media_type, + title: d.title, + }, + }, + } +} + +fn wit_tool_call_to_native(tc: ToolCall) -> native_content::ToolCall { + let arguments: HashMap = + serde_json::from_str(&tc.arguments).unwrap_or_default(); + native_content::ToolCall { + tool_call_id: tc.tool_call_id, + name: tc.name, + arguments, + namespace: tc.namespace, + } +} + +fn wit_tool_result_to_native(tr: ToolResult) -> native_content::ToolResult { + let content: serde_json::Value = + serde_json::from_str(&tr.content).unwrap_or(serde_json::Value::String(tr.content.clone())); + native_content::ToolResult { + tool_call_id: tr.tool_call_id, + tool_name: tr.tool_name, + content, + is_error: tr.is_error, + } +} + +fn wit_resource_to_native(r: CmfResource) -> native_content::Resource { + let annotations: HashMap = + serde_json::from_str(&r.annotations).unwrap_or_default(); + native_content::Resource { + resource_request_id: r.resource_request_id, + uri: r.uri, + name: r.name, + description: r.description, + resource_type: wit_resource_type_to_native(r.resource_type), + content: r.content, + blob: r.blob, + mime_type: r.mime_type, + size_bytes: r.size_bytes, + annotations, + version: r.version, + } +} + +fn wit_resource_type_to_native(rt: ResourceType) -> native_enums::ResourceType { + match rt { + ResourceType::File => native_enums::ResourceType::File, + ResourceType::Blob => native_enums::ResourceType::Blob, + ResourceType::Uri => native_enums::ResourceType::Uri, + ResourceType::Database => native_enums::ResourceType::Database, + ResourceType::Api => native_enums::ResourceType::Api, + ResourceType::Memory => native_enums::ResourceType::Memory, + ResourceType::Artifact => native_enums::ResourceType::Artifact, + } +} + +fn wit_resource_ref_to_native(rr: ResourceReference) -> native_content::ResourceReference { + native_content::ResourceReference { + resource_request_id: rr.resource_request_id, + uri: rr.uri, + name: rr.name, + resource_type: wit_resource_type_to_native(rr.resource_type), + range_start: rr.range_start, + range_end: rr.range_end, + selector: rr.selector, + } +} + +fn wit_prompt_request_to_native(pr: PromptRequest) -> native_content::PromptRequest { + let arguments: HashMap = + serde_json::from_str(&pr.arguments).unwrap_or_default(); + native_content::PromptRequest { + prompt_request_id: pr.prompt_request_id, + name: pr.name, + arguments, + server_id: pr.server_id, + } +} + +fn wit_prompt_result_to_native(pr: PromptResult) -> native_content::PromptResult { + let messages: Vec = + serde_json::from_str(&pr.messages).unwrap_or_default(); + native_content::PromptResult { + prompt_request_id: pr.prompt_request_id, + prompt_name: pr.prompt_name, + messages, + content: pr.content, + is_error: pr.is_error, + error_message: pr.error_message, + } +} diff --git a/crates/cpex-wasm-host/src/factory.rs b/crates/cpex-wasm-host/src/factory.rs new file mode 100644 index 00000000..e9334410 --- /dev/null +++ b/crates/cpex-wasm-host/src/factory.rs @@ -0,0 +1,200 @@ +// Location: ./crates/cpex-wasm-host/src/factory.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Shriti Priya +// +// WasmPluginFactory — bridges cpex-core's PluginFactory trait to the +// SandboxManager. Implements PluginFactory so WASM plugins can be +// registered with the PluginManager and participate in the hook pipeline. +// Each plugin gets its own SandboxManager instance (isolated engine + store). + +use std::path::PathBuf; +use std::sync::Arc; + +use async_trait::async_trait; +use tokio::sync::Mutex; + +use cpex_core::cmf::message::MessagePayload; +use cpex_core::context::PluginContext; +use cpex_core::error::PluginError; +use cpex_core::extensions::Extensions; +use cpex_core::factory::{PluginFactory, PluginInstance}; +use cpex_core::hooks::payload::PluginPayload; +use cpex_core::plugin::{Plugin, PluginConfig}; +use cpex_core::registry::AnyHookHandler; + +use crate::conversions::{native_context_to_wit, native_extensions_to_wit, native_payload_to_wit, wit_result_to_native}; +use crate::sandbox_manager::SandboxManager; + +// --------------------------------------------------------------------------- +// WasmPluginFactory +// --------------------------------------------------------------------------- + +/// Factory that creates WASM plugin instances, each with its own SandboxManager. +/// Every plugin gets an isolated wasmtime engine and store — no contention between plugins. +pub struct WasmPluginFactory { + wasm_dir: PathBuf, +} + +impl WasmPluginFactory { + pub fn new(wasm_dir: PathBuf) -> Self { + Self { wasm_dir } + } +} + +impl PluginFactory for WasmPluginFactory { + fn create(&self, config: &PluginConfig) -> Result> { + // Parse wasm path from kind (e.g., "wasm://plugin.wasm" → "plugin.wasm") + let wasm_filename = config + .kind + .strip_prefix("wasm://") + .ok_or_else(|| { + Box::new(PluginError::Config { + message: format!( + "plugin '{}': kind '{}' must start with 'wasm://'", + config.name, config.kind + ), + }) + })?; + + let wasm_path = self.wasm_dir.join(wasm_filename); + + // Extract sandbox policy from plugin's config field. + // If absent, deny-by-default applies (no filesystem, no network, no env vars). + let sandbox_policy = config + .config + .as_ref() + .and_then(|v| v.get("sandbox_policy")) + .and_then(|v| serde_json::from_value::(v.clone()).ok()); + + // Create a new SandboxManager for this plugin (isolated engine + store) + let sandbox = tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + let mut mgr = SandboxManager::new() + .map_err(|e| format!("failed to create sandbox: {}", e))?; + mgr.load_wasmplugin(&wasm_path, sandbox_policy.as_ref()) + .await + .map_err(|e| format!("failed to load WASM: {}", e))?; + Ok::<_, String>(mgr) + }) + }) + .map_err(|e| { + Box::new(PluginError::Config { + message: format!("plugin '{}': {}", config.name, e), + }) + })?; + + let sandbox = Arc::new(Mutex::new(sandbox)); + + let plugin: Arc = Arc::new(WasmBridgePlugin { + config: config.clone(), + }); + + let handler: Arc = Arc::new(WasmBridgeHandler { + plugin_name: config.name.clone(), + sandbox, + }); + + // Register handler for each hook the plugin declares + let hooks: Vec<(&'static str, Arc)> = config + .hooks + .iter() + .map(|hook_name| { + let leaked: &'static str = Box::leak(hook_name.clone().into_boxed_str()); + (leaked, handler.clone()) + }) + .collect(); + + Ok(PluginInstance { + plugin, + handlers: hooks, + }) + } +} + +// --------------------------------------------------------------------------- +// WasmBridgePlugin — lifecycle wrapper +// --------------------------------------------------------------------------- + +/// Implements the Plugin trait for WASM plugins. Handles lifecycle. +struct WasmBridgePlugin { + config: PluginConfig, +} + +#[async_trait] +impl Plugin for WasmBridgePlugin { + fn config(&self) -> &PluginConfig { + &self.config + } + + async fn initialize(&self) -> Result<(), Box> { + Ok(()) + } + + async fn shutdown(&self) -> Result<(), Box> { + Ok(()) + } +} + +// --------------------------------------------------------------------------- +// WasmBridgeHandler — hook dispatch through the WASM sandbox +// --------------------------------------------------------------------------- + +/// Implements AnyHookHandler by converting native types to WIT, +/// invoking the WASM sandbox, and converting the result back. +/// Each handler owns its own SandboxManager — no contention with other plugins. +struct WasmBridgeHandler { + plugin_name: String, + sandbox: Arc>, +} + +#[async_trait] +impl AnyHookHandler for WasmBridgeHandler { + async fn invoke( + &self, + payload: &dyn PluginPayload, + extensions: &Extensions, + ctx: &mut PluginContext, + ) -> Result, Box> { + // Downcast the type-erased payload to MessagePayload + let native_payload = payload + .as_any() + .downcast_ref::() + .ok_or_else(|| { + Box::new(PluginError::Config { + message: format!( + "plugin '{}': payload type mismatch, expected MessagePayload", + self.plugin_name + ), + }) + })?; + + // Convert native types → WIT types + let wit_payload = native_payload_to_wit(native_payload); + let wit_extensions = native_extensions_to_wit(extensions); + let wit_ctx = native_context_to_wit(ctx); + + // Invoke the WASM plugin through its dedicated sandbox + let wit_result = { + let mut mgr = self.sandbox.lock().await; + mgr.invoke(wit_payload, wit_extensions, wit_ctx) + .await + .map_err(|e| { + Box::new(PluginError::Config { + message: format!( + "plugin '{}': WASM invocation failed: {}", + self.plugin_name, e + ), + }) + })? + }; + + // Convert WIT result → native PluginResult, then erase for the executor + let native_result = wit_result_to_native(wit_result); + Ok(cpex_core::executor::erase_result(native_result)) + } + + fn hook_type_name(&self) -> &'static str { + "cmf" + } +} diff --git a/crates/cpex-wasm-host/src/lib.rs b/crates/cpex-wasm-host/src/lib.rs new file mode 100644 index 00000000..974fd41b --- /dev/null +++ b/crates/cpex-wasm-host/src/lib.rs @@ -0,0 +1,9 @@ +// Location: ./crates/cpex-wasm-host/src/lib.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Shriti Priya + +pub mod conversions; +pub mod factory; +pub mod policy_loader; +pub mod sandbox_manager; diff --git a/crates/cpex-wasm-host/src/policy_loader.rs b/crates/cpex-wasm-host/src/policy_loader.rs new file mode 100644 index 00000000..17bf70aa --- /dev/null +++ b/crates/cpex-wasm-host/src/policy_loader.rs @@ -0,0 +1,201 @@ +// Location: ./crates/cpex-wasm-host/src/policy_loader.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Shriti Priya +// +// PolicyLoader — defines the SandboxPolicy schema and builds a WASI context +// from it. The sandbox policy controls what host resources a WASM plugin can +// access: filesystem paths, network hosts, and environment variables. +// When no policy is provided (or all lists are empty), the plugin runs in a +// fully locked-down sandbox with no access to the outside world. + +use std::{path::Path, sync::Arc}; + +use anyhow::Result; +use serde::Deserialize; +use wasmtime_wasi::{DirPerms, FilePerms, WasiCtx, WasiCtxBuilder}; +use wasmtime_wasi_http::WasiHttpCtx; + +/// Declarative sandbox policy deserialized from the plugin's config.sandbox_policy YAML key. +/// Controls filesystem, network, and environment access for the WASM plugin. +/// All fields default to empty/deny — a missing or empty policy means full lockdown. +#[derive(Debug, Clone, Default, Deserialize, serde::Serialize)] +pub struct SandboxPolicy { + /// Directories/files the plugin may access (empty = no filesystem access) + #[serde(default)] + pub allowed_filesystem: Vec, + /// Host names the plugin may make outbound HTTP requests to (empty = no network) + #[serde(default)] + pub allowed_network: Vec, + /// Environment variable names the plugin may read from the host (empty = no env access) + #[serde(default)] + pub allowed_env: Vec, + /// Resource limits (memory, fuel, execution time) for the WASM store + #[serde(default)] + pub resources: ResourceLimits, +} + +/// Resource limits enforced on the WASM store. +/// None means unlimited (wasmtime defaults apply). +#[derive(Debug, Clone, Deserialize, serde::Serialize)] +pub struct ResourceLimits { + /// Maximum linear memory the plugin can allocate (bytes) + #[serde(default)] + pub max_memory_bytes: Option, + /// Maximum instructions (fuel units) the plugin can execute across all invocations + #[serde(default)] + pub max_fuel: Option, + /// Maximum wall-clock time for a single invocation (milliseconds) + #[serde(default)] + pub max_execution_time_ms: Option, + /// Maximum number of WASM module instances + #[serde(default)] + pub max_instances: Option, + /// Maximum number of WASM tables + #[serde(default)] + pub max_tables: Option, +} + +impl Default for ResourceLimits { + fn default() -> Self { + Self { + max_memory_bytes: None, + max_fuel: None, + max_execution_time_ms: None, + max_instances: None, + max_tables: None, + } + } +} + +/// A single filesystem access rule — grants access to a directory or file with a permission level. +#[derive(Debug, Clone, Deserialize, serde::Serialize)] +pub struct FilesystemRule { + /// Directory path to preopen into the WASM sandbox + #[serde(default)] + pub dir: Option, + /// File path (its parent directory is preopened) + #[serde(default)] + pub file: Option, + /// Permission level: "read" or "write"/"mutate" + pub permission: String, +} + +/// The constructed WASI + HTTP context ready to be installed into a wasmtime Store. +pub struct PluginWasiContext { + pub wasi_ctx: WasiCtx, + pub http_ctx: WasiHttpCtx, + /// Network allow-list passed to the NetworkPolicy hook for outbound HTTP filtering + pub allowed_hosts: Arc>, +} + +/// Builds a WASI context from the given sandbox policy. +/// Preopens filesystem paths, injects allowed env vars, and captures the network allow-list. +/// If sandbox_policy is None, the context grants no host access (full lockdown). +pub fn build_wasi_context(sandbox_policy: Option<&SandboxPolicy>) -> Result { + let mut builder = WasiCtxBuilder::new(); + + if let Some(policy) = sandbox_policy { + for rule in &policy.allowed_filesystem { + let (dir_perms, file_perms) = match rule.permission.as_str() { + "read" => (DirPerms::READ, FilePerms::READ), + "write" | "mutate" => (DirPerms::READ | DirPerms::MUTATE, FilePerms::READ | FilePerms::WRITE), + other => anyhow::bail!("unknown filesystem permission: {}", other), + }; + + if let Some(dir) = &rule.dir { + builder + .preopened_dir(dir, dir, dir_perms, file_perms) + .map_err(|e| anyhow::anyhow!("failed to preopen dir '{}': {}", dir, e))?; + } else if let Some(file) = &rule.file { + let parent = Path::new(file) + .parent() + .ok_or_else(|| anyhow::anyhow!("file '{}' has no parent directory", file))?; + builder + .preopened_dir(parent, parent.to_string_lossy().as_ref(), dir_perms, file_perms) + .map_err(|e| anyhow::anyhow!("failed to preopen parent dir for file '{}': {}", file, e))?; + } + } + + for key in &policy.allowed_env { + if let Ok(val) = std::env::var(key) { + builder.env(key, &val); + } + } + } + + builder.inherit_stdio(); + + let wasi_ctx = builder.build(); + let http_ctx = WasiHttpCtx::new(); + let allowed_hosts = Arc::new( + sandbox_policy + .map(|p| p.allowed_network.clone()) + .unwrap_or_default(), + ); + + Ok(PluginWasiContext { + wasi_ctx, + http_ctx, + allowed_hosts, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + + #[test] + fn test_parse_sandbox_policy_from_config_file() { + let config_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("config/config.yaml"); + let raw = fs::read_to_string(&config_path) + .expect("failed to read config file"); + let config: serde_yaml::Value = serde_yaml::from_str(&raw) + .expect("failed to parse YAML"); + + let sandbox_policy_value = config["plugins"][0]["config"]["sandbox_policy"].clone(); + let policy: SandboxPolicy = serde_yaml::from_value(sandbox_policy_value) + .expect("failed to deserialize sandbox_policy"); + + assert!(policy.allowed_filesystem.is_empty()); + assert!(policy.allowed_network.is_empty()); + assert!(policy.allowed_env.is_empty()); + assert_eq!(policy.resources.max_memory_bytes, Some(10485760)); + assert_eq!(policy.resources.max_fuel, Some(1000000000)); + assert_eq!(policy.resources.max_execution_time_ms, Some(5000)); + assert_eq!(policy.resources.max_instances, Some(10)); + assert_eq!(policy.resources.max_tables, Some(10)); + } + + #[test] + fn test_deserialize_sandbox_policy() { + let yaml = r#" +allowed_filesystem: + - dir: /tmp/data + permission: "read" +allowed_network: + - "httpbin.org" +allowed_env: + - "API_KEY" +resources: + max_memory_bytes: 10485760 + max_fuel: 1000000000 +"#; + let policy: SandboxPolicy = serde_yaml::from_str(yaml).unwrap(); + assert_eq!(policy.allowed_network, vec!["httpbin.org"]); + assert_eq!(policy.allowed_env, vec!["API_KEY"]); + assert_eq!(policy.allowed_filesystem.len(), 1); + assert_eq!(policy.resources.max_memory_bytes, Some(10485760)); + assert_eq!(policy.resources.max_fuel, Some(1000000000)); + } + + #[test] + fn test_default_sandbox_policy_denies_all() { + let policy = SandboxPolicy::default(); + assert!(policy.allowed_filesystem.is_empty()); + assert!(policy.allowed_network.is_empty()); + assert!(policy.allowed_env.is_empty()); + assert!(policy.resources.max_memory_bytes.is_none()); + } +} diff --git a/crates/cpex-wasm-host/src/sandbox_manager.rs b/crates/cpex-wasm-host/src/sandbox_manager.rs new file mode 100644 index 00000000..a011e508 --- /dev/null +++ b/crates/cpex-wasm-host/src/sandbox_manager.rs @@ -0,0 +1,253 @@ +// Location: ./crates/cpex-wasm-host/src/sandbox_manager.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Shriti Priya +// +// SandboxManager — loads and invokes a single WASM plugin in a wasmtime sandbox. +// Enforces resource limits (fuel, memory, execution time) and network policy. + +use std::path::Path; +use std::sync::Arc; + +use anyhow::{Context, Result}; +use wasmtime::component::{Component, Linker, ResourceTable}; +use wasmtime::{Config, Engine, Store, StoreLimits, StoreLimitsBuilder}; +use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView}; +use wasmtime_wasi_http::WasiHttpCtx; +use wasmtime_wasi_http::p2::{WasiHttpCtxView, WasiHttpView}; +use wasmtime_wasi_http::p2::body::HyperOutgoingBody; +use wasmtime_wasi_http::p2::types::{HostFutureIncomingResponse, OutgoingRequestConfig}; +use wasmtime_wasi_http::p2::{HttpResult, WasiHttpHooks, default_send_request}; +use wasmtime_wasi_http::p2::bindings::http::types::ErrorCode; + +use crate::policy_loader::{build_wasi_context, SandboxPolicy, ResourceLimits}; + +// Generate Rust bindings from the WIT interface definition. +// This creates the `Plugin` struct with `call_handle_hook` and the WIT types. +wasmtime::component::bindgen!({ + path: "wit", + world: "plugin", + exports: { default: async }, +}); + +/// Re-export WIT-generated types for use by the factory and conversions modules. +pub mod types { + pub use super::cpex::plugin::types::*; +} + +/// Intercepts outbound HTTP requests from the WASM plugin and enforces the network allow-list. +/// Only requests to explicitly allowed hosts (or their subdomains) are permitted. +struct NetworkPolicy { + allowed_hosts: Arc>, +} + +impl WasiHttpHooks for NetworkPolicy { + fn send_request( + &mut self, + request: hyper::Request, + config: OutgoingRequestConfig, + ) -> HttpResult { + // Extract the target host from the request URI + let authority = request + .uri() + .authority() + .map(|a| a.host().to_string()) + .unwrap_or_default(); + + // Check exact match or subdomain match (e.g., "api.example.com" matches "example.com") + let is_allowed = self.allowed_hosts.iter().any(|allowed| { + authority == *allowed || authority.ends_with(&format!(".{}", allowed)) + }); + + if !is_allowed { + return Err(ErrorCode::HttpRequestDenied.into()); + } + + Ok(default_send_request(request, config)) + } +} + +/// Per-plugin state held in the wasmtime Store. +/// Contains WASI context, HTTP context, network policy, resource table, and store limits. +struct WasmPluginState { + wasi: WasiCtx, + http: WasiHttpCtx, + network: NetworkPolicy, + table: ResourceTable, + limits: StoreLimits, +} + +impl WasiView for WasmPluginState { + fn ctx(&mut self) -> WasiCtxView<'_> { + WasiCtxView { + ctx: &mut self.wasi, + table: &mut self.table, + } + } +} + +impl WasiHttpView for WasmPluginState { + fn http(&mut self) -> WasiHttpCtxView<'_> { + WasiHttpCtxView { + ctx: &mut self.http, + table: &mut self.table, + hooks: &mut self.network, + } + } +} + +/// A loaded and instantiated WASM plugin, ready for invocation. +struct WasmPluginInstance { + store: Store, + plugin: Plugin, + /// Epoch ticks before the store traps (used to reset timeout per invocation) + epoch_deadline: u64, +} + +/// Manages a single WASM plugin in a sandboxed wasmtime environment. +/// Enforces resource limits (fuel, memory, execution time) and network policy. +pub struct SandboxManager { + engine: Engine, + linker: Linker, + instance: Option, +} + +impl SandboxManager { + /// Create a new SandboxManager with no plugin loaded. + pub fn new() -> Result { + let mut config = Config::new(); + config.wasm_component_model(true); + config.consume_fuel(true); + config.epoch_interruption(true); + let engine = Engine::new(&config)?; + + let mut linker = Linker::new(&engine); + wasmtime_wasi::p2::add_to_linker_async(&mut linker)?; + wasmtime_wasi_http::p2::add_only_http_to_linker_async(&mut linker)?; + + // Start epoch ticker thread for timeout enforcement + let engine_clone = engine.clone(); + std::thread::spawn(move || loop { + std::thread::sleep(std::time::Duration::from_millis(1)); + engine_clone.increment_epoch(); + }); + + Ok(Self { + engine, + linker, + instance: None, + }) + } + + /// Load a plugin from a WASM file with the given sandbox policy. + /// Replaces any previously loaded plugin. + pub async fn load_wasmplugin( + &mut self, + wasm_path: &Path, + sandbox_policy: Option<&SandboxPolicy>, + ) -> Result<()> { + eprintln!("[SANDBOX] load_wasmplugin: path={}", wasm_path.display()); + let ctx = build_wasi_context(sandbox_policy)?; + eprintln!("[SANDBOX] WASI context built"); + let default_resources = ResourceLimits::default(); + let resources = sandbox_policy + .map(|p| &p.resources) + .unwrap_or(&default_resources); + + // Build store limits from resource config + let mut limits_builder = StoreLimitsBuilder::new(); + if let Some(max_mem) = resources.max_memory_bytes { + limits_builder = limits_builder.memory_size(max_mem); + } + if let Some(max_instances) = resources.max_instances { + limits_builder = limits_builder.instances(max_instances); + } + if let Some(max_tables) = resources.max_tables { + limits_builder = limits_builder.tables(max_tables); + } + let limits = limits_builder.trap_on_grow_failure(true).build(); + + eprintln!("[SANDBOX] Loading component from file..."); + let component = Component::from_file(&self.engine, wasm_path) + .map_err(|e| anyhow::anyhow!("failed to load wasm from {}: {}", wasm_path.display(), e))?; + eprintln!("[SANDBOX] Component loaded successfully"); + + let mut store = Store::new( + &self.engine, + WasmPluginState { + wasi: ctx.wasi_ctx, + http: ctx.http_ctx, + network: NetworkPolicy { + allowed_hosts: ctx.allowed_hosts, + }, + table: ResourceTable::new(), + limits, + }, + ); + + // Apply memory/table limits + store.limiter(|state| &mut state.limits); + + // Apply fuel budget (instruction count limit) + let fuel = resources.max_fuel.unwrap_or(u64::MAX); + store.set_fuel(fuel) + .map_err(|e| anyhow::anyhow!("failed to set fuel: {}", e))?; + + // Apply execution timeout via epoch deadline + // Each epoch tick is ~1ms (from ticker thread), so ms ≈ ticks + let epoch_deadline = resources.max_execution_time_ms.unwrap_or(3_600_000); + store.set_epoch_deadline(epoch_deadline); + store.epoch_deadline_trap(); + + eprintln!("[SANDBOX] Instantiating plugin component..."); + let plugin = Plugin::instantiate_async(&mut store, &component, &self.linker) + .await + .map_err(|e| anyhow::anyhow!("failed to instantiate plugin: {}", e))?; + eprintln!("[SANDBOX] Plugin instantiated OK"); + + self.instance = Some(WasmPluginInstance { + store, + plugin, + epoch_deadline, + }); + + Ok(()) + } + + /// Invoke the loaded plugin's handle-hook function. + /// Fuel is a session-level budget (not reset between invocations). + /// Epoch deadline is per-invocation (reset each call so no single call hangs). + pub async fn invoke( + &mut self, + payload: types::MessagePayload, + extensions: types::Extensions, + ctx: types::PluginContext, + ) -> Result { + eprintln!("[SANDBOX] invoke() called, plugin loaded: {}", self.is_loaded()); + let instance = self + .instance + .as_mut() + .with_context(|| "no plugin loaded")?; + + // Reset epoch deadline per invocation (timeout is per-call) + instance.store.set_epoch_deadline(instance.epoch_deadline); + + eprintln!("[SANDBOX] Calling handle_hook on WASM component..."); + let result = instance + .plugin + .call_handle_hook(&mut instance.store, &payload, &extensions, &ctx) + .await; + + match &result { + Ok(r) => eprintln!("[SANDBOX] handle_hook OK: continue_processing={}", r.continue_processing), + Err(e) => eprintln!("[SANDBOX] handle_hook FAILED: {}", e), + } + + result.map_err(|e| anyhow::anyhow!("plugin invocation failed: {}", e)) + } + + /// Returns whether a plugin is currently loaded. + pub fn is_loaded(&self) -> bool { + self.instance.is_some() + } +} diff --git a/crates/cpex-wasm-host/tests/test_policy_loader.rs b/crates/cpex-wasm-host/tests/test_policy_loader.rs new file mode 100644 index 00000000..e96aa8d1 --- /dev/null +++ b/crates/cpex-wasm-host/tests/test_policy_loader.rs @@ -0,0 +1,107 @@ +// Location: ./crates/cpex-wasm-host/tests/test_policy_loader.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Shriti Priya +// +// Integration tests for policy_loader. +// Validates that the sandbox policy in config/config.yaml deserializes correctly +// and matches the expected deny-all posture with resource limits. + +use std::path::Path; + +use cpex_wasm_host::policy_loader::SandboxPolicy; + +/// Helper: reads config.yaml and extracts the sandbox_policy for a named plugin. +fn load_sandbox_policy_from_config(plugin_name: &str) -> SandboxPolicy { + let config_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("config/config.yaml"); + let raw = std::fs::read_to_string(&config_path).expect("failed to read config.yaml"); + let config: serde_yaml::Value = serde_yaml::from_str(&raw).expect("failed to parse YAML"); + + let plugin = config["plugins"] + .as_sequence() + .expect("plugins should be a list") + .iter() + .find(|p| p["name"].as_str() == Some(plugin_name)) + .unwrap_or_else(|| panic!("plugin '{}' not found in config", plugin_name)); + + let sandbox_value = plugin["config"]["sandbox_policy"].clone(); + serde_yaml::from_value(sandbox_value).expect("failed to deserialize sandbox_policy") +} + +#[test] +fn config_file_is_valid_yaml() { + let config_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("config/config.yaml"); + let raw = std::fs::read_to_string(&config_path).expect("failed to read config.yaml"); + let _: serde_yaml::Value = serde_yaml::from_str(&raw).expect("config.yaml is not valid YAML"); +} + +#[test] +fn config_has_plugins_list() { + let config_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("config/config.yaml"); + let raw = std::fs::read_to_string(&config_path).expect("failed to read config.yaml"); + let config: serde_yaml::Value = serde_yaml::from_str(&raw).unwrap(); + + let plugins = config["plugins"].as_sequence().expect("plugins should be a list"); + assert!(!plugins.is_empty(), "plugins list should not be empty"); +} + +#[test] +fn identity_checker_plugin_exists_with_correct_kind() { + let config_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("config/config.yaml"); + let raw = std::fs::read_to_string(&config_path).expect("failed to read config.yaml"); + let config: serde_yaml::Value = serde_yaml::from_str(&raw).unwrap(); + + let plugin = config["plugins"] + .as_sequence() + .unwrap() + .iter() + .find(|p| p["name"].as_str() == Some("identity-checker")) + .expect("identity-checker plugin not found"); + + assert_eq!(plugin["kind"].as_str(), Some("wasm://plugin.wasm")); +} + +#[test] +fn sandbox_policy_allowed_network_is_empty() { + let policy = load_sandbox_policy_from_config("identity-checker"); + assert!(policy.allowed_network.is_empty()); +} + +#[test] +fn sandbox_policy_allowed_env_is_empty() { + let policy = load_sandbox_policy_from_config("identity-checker"); + assert!(policy.allowed_env.is_empty()); +} + +#[test] +fn sandbox_policy_allowed_filesystem_is_empty() { + let policy = load_sandbox_policy_from_config("identity-checker"); + assert!(policy.allowed_filesystem.is_empty()); +} + +#[test] +fn sandbox_policy_resource_limits() { + let policy = load_sandbox_policy_from_config("identity-checker"); + + assert_eq!(policy.resources.max_memory_bytes, Some(10_485_760)); + assert_eq!(policy.resources.max_fuel, Some(1_000_000_000)); + assert_eq!(policy.resources.max_execution_time_ms, Some(5000)); + assert_eq!(policy.resources.max_instances, Some(10)); + assert_eq!(policy.resources.max_tables, Some(10)); +} + +#[test] +fn sandbox_policy_deserializes_to_same_type_used_by_factory() { + let policy = load_sandbox_policy_from_config("identity-checker"); + let json = serde_json::to_value(&policy).expect("SandboxPolicy should serialize to JSON"); + + let roundtripped: SandboxPolicy = + serde_json::from_value(json).expect("SandboxPolicy should roundtrip through JSON"); + + assert_eq!(roundtripped.allowed_network, policy.allowed_network); + assert_eq!(roundtripped.allowed_env, policy.allowed_env); + assert_eq!( + roundtripped.resources.max_memory_bytes, + policy.resources.max_memory_bytes + ); +} diff --git a/crates/cpex-wasm-host/wasm/plugin.wasm b/crates/cpex-wasm-host/wasm/plugin.wasm new file mode 100644 index 0000000000000000000000000000000000000000..55472f0bc32b560e3fbc1b61ccc94b25d0a5c14c GIT binary patch literal 604424 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 z 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%0DUZEF z*olq4AmZ5;k5C3QYWD&pM`k(_NSQ#FMv2W@IEtM?i;iA&Bd zQ;LPJnZ*R>UiKU|zDmO{okceAxpiqa@?T3c&I*NrVp%YOacZhO^m_tzerhqZEiQ#v zC~ht$_rL%FSKAu=*wz?3wQ)x*EwrZ%{t?aiYRC#$=#ZAShtw^VEaWE@28@A1=Etc~ z?sN2n&8`32sEd-?;nqnBS7kC`i_N~Mi}}6`Be2$}-ASH2rZ|}+mPUuSc�!XR5%& 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 list>; + + /// Get the POSIX-style arguments to the program. + @since(version = 0.2.0) + get-arguments: func() -> list; + + /// Return a path that programs should use as their initial current working + /// directory, interpreting `.` as shorthand for this. + @since(version = 0.2.0) + initial-cwd: func() -> option; +} + +@since(version = 0.2.0) +interface exit { + /// Exit the current instance and any linked instances. + @since(version = 0.2.0) + exit: func(status: result); + + /// Exit the current instance and any linked instances, reporting the + /// specified status code to the host. + /// + /// The meaning of the code depends on the context, with 0 usually meaning + /// "success", and other values indicating various types of failure. + /// + /// This function does not return; the effect is analogous to a trap, but + /// without the connotation that something bad has happened. + @unstable(feature = cli-exit-with-code) + exit-with-code: func(status-code: u8); +} + +@since(version = 0.2.0) +interface run { + /// Run the program. + @since(version = 0.2.0) + run: func() -> result; +} + +@since(version = 0.2.0) +interface stdin { + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{input-stream}; + + @since(version = 0.2.0) + get-stdin: func() -> input-stream; +} + +@since(version = 0.2.0) +interface stdout { + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{output-stream}; + + @since(version = 0.2.0) + get-stdout: func() -> output-stream; +} + +@since(version = 0.2.0) +interface stderr { + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{output-stream}; + + @since(version = 0.2.0) + get-stderr: func() -> output-stream; +} + +/// Terminal input. +/// +/// In the future, this may include functions for disabling echoing, +/// disabling input buffering so that keyboard events are sent through +/// immediately, querying supported features, and so on. +@since(version = 0.2.0) +interface terminal-input { + /// The input side of a terminal. + @since(version = 0.2.0) + resource terminal-input; +} + +/// Terminal output. +/// +/// In the future, this may include functions for querying the terminal +/// size, being notified of terminal size changes, querying supported +/// features, and so on. +@since(version = 0.2.0) +interface terminal-output { + /// The output side of a terminal. + @since(version = 0.2.0) + resource terminal-output; +} + +/// An interface providing an optional `terminal-input` for stdin as a +/// link-time authority. +@since(version = 0.2.0) +interface terminal-stdin { + @since(version = 0.2.0) + use terminal-input.{terminal-input}; + + /// If stdin is connected to a terminal, return a `terminal-input` handle + /// allowing further interaction with it. + @since(version = 0.2.0) + get-terminal-stdin: func() -> option; +} + +/// An interface providing an optional `terminal-output` for stdout as a +/// link-time authority. +@since(version = 0.2.0) +interface terminal-stdout { + @since(version = 0.2.0) + use terminal-output.{terminal-output}; + + /// If stdout is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + @since(version = 0.2.0) + get-terminal-stdout: func() -> option; +} + +/// An interface providing an optional `terminal-output` for stderr as a +/// link-time authority. +@since(version = 0.2.0) +interface terminal-stderr { + @since(version = 0.2.0) + use terminal-output.{terminal-output}; + + /// If stderr is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + @since(version = 0.2.0) + get-terminal-stderr: func() -> option; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import environment; + @since(version = 0.2.0) + import exit; + @since(version = 0.2.0) + import wasi:io/error@0.2.6; + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import wasi:io/streams@0.2.6; + @since(version = 0.2.0) + import stdin; + @since(version = 0.2.0) + import stdout; + @since(version = 0.2.0) + import stderr; + @since(version = 0.2.0) + import terminal-input; + @since(version = 0.2.0) + import terminal-output; + @since(version = 0.2.0) + import terminal-stdin; + @since(version = 0.2.0) + import terminal-stdout; + @since(version = 0.2.0) + import terminal-stderr; + @since(version = 0.2.0) + import wasi:clocks/monotonic-clock@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/wall-clock@0.2.6; + @unstable(feature = clocks-timezone) + import wasi:clocks/timezone@0.2.6; + @since(version = 0.2.0) + import wasi:filesystem/types@0.2.6; + @since(version = 0.2.0) + import wasi:filesystem/preopens@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/network@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/instance-network@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/udp@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/udp-create-socket@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/tcp@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/tcp-create-socket@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/ip-name-lookup@0.2.6; + @since(version = 0.2.0) + import wasi:random/random@0.2.6; + @since(version = 0.2.0) + import wasi:random/insecure@0.2.6; + @since(version = 0.2.0) + import wasi:random/insecure-seed@0.2.6; +} +@since(version = 0.2.0) +world command { + @since(version = 0.2.0) + import environment; + @since(version = 0.2.0) + import exit; + @since(version = 0.2.0) + import wasi:io/error@0.2.6; + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import wasi:io/streams@0.2.6; + @since(version = 0.2.0) + import stdin; + @since(version = 0.2.0) + import stdout; + @since(version = 0.2.0) + import stderr; + @since(version = 0.2.0) + import terminal-input; + @since(version = 0.2.0) + import terminal-output; + @since(version = 0.2.0) + import terminal-stdin; + @since(version = 0.2.0) + import terminal-stdout; + @since(version = 0.2.0) + import terminal-stderr; + @since(version = 0.2.0) + import wasi:clocks/monotonic-clock@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/wall-clock@0.2.6; + @unstable(feature = clocks-timezone) + import wasi:clocks/timezone@0.2.6; + @since(version = 0.2.0) + import wasi:filesystem/types@0.2.6; + @since(version = 0.2.0) + import wasi:filesystem/preopens@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/network@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/instance-network@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/udp@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/udp-create-socket@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/tcp@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/tcp-create-socket@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/ip-name-lookup@0.2.6; + @since(version = 0.2.0) + import wasi:random/random@0.2.6; + @since(version = 0.2.0) + import wasi:random/insecure@0.2.6; + @since(version = 0.2.0) + import wasi:random/insecure-seed@0.2.6; + + @since(version = 0.2.0) + export run; +} diff --git a/crates/cpex-wasm-host/wit/deps/clocks.wit b/crates/cpex-wasm-host/wit/deps/clocks.wit new file mode 100644 index 00000000..d638f1a4 --- /dev/null +++ b/crates/cpex-wasm-host/wit/deps/clocks.wit @@ -0,0 +1,157 @@ +package wasi:clocks@0.2.6; + +/// WASI Monotonic Clock is a clock API intended to let users measure elapsed +/// time. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A monotonic clock is a clock which has an unspecified initial value, and +/// successive reads of the clock will produce non-decreasing values. +@since(version = 0.2.0) +interface monotonic-clock { + @since(version = 0.2.0) + use wasi:io/poll@0.2.6.{pollable}; + + /// An instant in time, in nanoseconds. An instant is relative to an + /// unspecified initial value, and can only be compared to instances from + /// the same monotonic-clock. + @since(version = 0.2.0) + type instant = u64; + + /// A duration of time, in nanoseconds. + @since(version = 0.2.0) + type duration = u64; + + /// Read the current value of the clock. + /// + /// The clock is monotonic, therefore calling this function repeatedly will + /// produce a sequence of non-decreasing values. + @since(version = 0.2.0) + now: func() -> instant; + + /// Query the resolution of the clock. Returns the duration of time + /// corresponding to a clock tick. + @since(version = 0.2.0) + resolution: func() -> duration; + + /// Create a `pollable` which will resolve once the specified instant + /// has occurred. + @since(version = 0.2.0) + subscribe-instant: func(when: instant) -> pollable; + + /// Create a `pollable` that will resolve after the specified duration has + /// elapsed from the time this function is invoked. + @since(version = 0.2.0) + subscribe-duration: func(when: duration) -> pollable; +} + +/// WASI Wall Clock is a clock API intended to let users query the current +/// time. The name "wall" makes an analogy to a "clock on the wall", which +/// is not necessarily monotonic as it may be reset. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A wall clock is a clock which measures the date and time according to +/// some external reference. +/// +/// External references may be reset, so this clock is not necessarily +/// monotonic, making it unsuitable for measuring elapsed time. +/// +/// It is intended for reporting the current date and time for humans. +@since(version = 0.2.0) +interface wall-clock { + /// A time and date in seconds plus nanoseconds. + @since(version = 0.2.0) + record datetime { + seconds: u64, + nanoseconds: u32, + } + + /// Read the current value of the clock. + /// + /// This clock is not monotonic, therefore calling this function repeatedly + /// will not necessarily produce a sequence of non-decreasing values. + /// + /// The returned timestamps represent the number of seconds since + /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], + /// also known as [Unix Time]. + /// + /// The nanoseconds field of the output is always less than 1000000000. + /// + /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 + /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time + @since(version = 0.2.0) + now: func() -> datetime; + + /// Query the resolution of the clock. + /// + /// The nanoseconds field of the output is always less than 1000000000. + @since(version = 0.2.0) + resolution: func() -> datetime; +} + +@unstable(feature = clocks-timezone) +interface timezone { + @unstable(feature = clocks-timezone) + use wall-clock.{datetime}; + + /// Information useful for displaying the timezone of a specific `datetime`. + /// + /// This information may vary within a single `timezone` to reflect daylight + /// saving time adjustments. + @unstable(feature = clocks-timezone) + record timezone-display { + /// The number of seconds difference between UTC time and the local + /// time of the timezone. + /// + /// The returned value will always be less than 86400 which is the + /// number of seconds in a day (24*60*60). + /// + /// In implementations that do not expose an actual time zone, this + /// should return 0. + utc-offset: s32, + /// The abbreviated name of the timezone to display to a user. The name + /// `UTC` indicates Coordinated Universal Time. Otherwise, this should + /// reference local standards for the name of the time zone. + /// + /// In implementations that do not expose an actual time zone, this + /// should be the string `UTC`. + /// + /// In time zones that do not have an applicable name, a formatted + /// representation of the UTC offset may be returned, such as `-04:00`. + name: string, + /// Whether daylight saving time is active. + /// + /// In implementations that do not expose an actual time zone, this + /// should return false. + in-daylight-saving-time: bool, + } + + /// Return information needed to display the given `datetime`. This includes + /// the UTC offset, the time zone name, and a flag indicating whether + /// daylight saving time is active. + /// + /// If the timezone cannot be determined for the given `datetime`, return a + /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight + /// saving time. + @unstable(feature = clocks-timezone) + display: func(when: datetime) -> timezone-display; + + /// The same as `display`, but only return the UTC offset. + @unstable(feature = clocks-timezone) + utc-offset: func(when: datetime) -> s32; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import monotonic-clock; + @since(version = 0.2.0) + import wall-clock; + @unstable(feature = clocks-timezone) + import timezone; +} diff --git a/crates/cpex-wasm-host/wit/deps/filesystem.wit b/crates/cpex-wasm-host/wit/deps/filesystem.wit new file mode 100644 index 00000000..9f4a8288 --- /dev/null +++ b/crates/cpex-wasm-host/wit/deps/filesystem.wit @@ -0,0 +1,587 @@ +package wasi:filesystem@0.2.6; + +/// WASI filesystem is a filesystem API primarily intended to let users run WASI +/// programs that access their files on their existing filesystems, without +/// significant overhead. +/// +/// It is intended to be roughly portable between Unix-family platforms and +/// Windows, though it does not hide many of the major differences. +/// +/// Paths are passed as interface-type `string`s, meaning they must consist of +/// a sequence of Unicode Scalar Values (USVs). Some filesystems may contain +/// paths which are not accessible by this API. +/// +/// The directory separator in WASI is always the forward-slash (`/`). +/// +/// All paths in WASI are relative paths, and are interpreted relative to a +/// `descriptor` referring to a base directory. If a `path` argument to any WASI +/// function starts with `/`, or if any step of resolving a `path`, including +/// `..` and symbolic link steps, reaches a directory outside of the base +/// directory, or reaches a symlink to an absolute or rooted path in the +/// underlying filesystem, the function fails with `error-code::not-permitted`. +/// +/// For more information about WASI path resolution and sandboxing, see +/// [WASI filesystem path resolution]. +/// +/// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md +@since(version = 0.2.0) +interface types { + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{input-stream, output-stream, error}; + @since(version = 0.2.0) + use wasi:clocks/wall-clock@0.2.6.{datetime}; + + /// File size or length of a region within a file. + @since(version = 0.2.0) + type filesize = u64; + + /// The type of a filesystem object referenced by a descriptor. + /// + /// Note: This was called `filetype` in earlier versions of WASI. + @since(version = 0.2.0) + enum descriptor-type { + /// The type of the descriptor or file is unknown or is different from + /// any of the other types specified. + unknown, + /// The descriptor refers to a block device inode. + block-device, + /// The descriptor refers to a character device inode. + character-device, + /// The descriptor refers to a directory inode. + directory, + /// The descriptor refers to a named pipe. + fifo, + /// The file refers to a symbolic link inode. + symbolic-link, + /// The descriptor refers to a regular file inode. + regular-file, + /// The descriptor refers to a socket. + socket, + } + + /// Descriptor flags. + /// + /// Note: This was called `fdflags` in earlier versions of WASI. + @since(version = 0.2.0) + flags descriptor-flags { + /// Read mode: Data can be read. + read, + /// Write mode: Data can be written to. + write, + /// Request that writes be performed according to synchronized I/O file + /// integrity completion. The data stored in the file and the file's + /// metadata are synchronized. This is similar to `O_SYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + file-integrity-sync, + /// Request that writes be performed according to synchronized I/O data + /// integrity completion. Only the data stored in the file is + /// synchronized. This is similar to `O_DSYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + data-integrity-sync, + /// Requests that reads be performed at the same level of integrity + /// requested for writes. This is similar to `O_RSYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + requested-write-sync, + /// Mutating directories mode: Directory contents may be mutated. + /// + /// When this flag is unset on a descriptor, operations using the + /// descriptor which would create, rename, delete, modify the data or + /// metadata of filesystem objects, or obtain another handle which + /// would permit any of those, shall fail with `error-code::read-only` if + /// they would otherwise succeed. + /// + /// This may only be set on directories. + mutate-directory, + } + + /// Flags determining the method of how paths are resolved. + @since(version = 0.2.0) + flags path-flags { + /// As long as the resolved path corresponds to a symbolic link, it is + /// expanded. + symlink-follow, + } + + /// Open flags used by `open-at`. + @since(version = 0.2.0) + flags open-flags { + /// Create file if it does not exist, similar to `O_CREAT` in POSIX. + create, + /// Fail if not a directory, similar to `O_DIRECTORY` in POSIX. + directory, + /// Fail if file already exists, similar to `O_EXCL` in POSIX. + exclusive, + /// Truncate file to size 0, similar to `O_TRUNC` in POSIX. + truncate, + } + + /// Number of hard links to an inode. + @since(version = 0.2.0) + type link-count = u64; + + /// File attributes. + /// + /// Note: This was called `filestat` in earlier versions of WASI. + @since(version = 0.2.0) + record descriptor-stat { + /// File type. + %type: descriptor-type, + /// Number of hard links to the file. + link-count: link-count, + /// For regular files, the file size in bytes. For symbolic links, the + /// length in bytes of the pathname contained in the symbolic link. + size: filesize, + /// Last data access timestamp. + /// + /// If the `option` is none, the platform doesn't maintain an access + /// timestamp for this file. + data-access-timestamp: option, + /// Last data modification timestamp. + /// + /// If the `option` is none, the platform doesn't maintain a + /// modification timestamp for this file. + data-modification-timestamp: option, + /// Last file status-change timestamp. + /// + /// If the `option` is none, the platform doesn't maintain a + /// status-change timestamp for this file. + status-change-timestamp: option, + } + + /// When setting a timestamp, this gives the value to set it to. + @since(version = 0.2.0) + variant new-timestamp { + /// Leave the timestamp set to its previous value. + no-change, + /// Set the timestamp to the current time of the system clock associated + /// with the filesystem. + now, + /// Set the timestamp to the given value. + timestamp(datetime), + } + + /// A directory entry. + record directory-entry { + /// The type of the file referred to by this directory entry. + %type: descriptor-type, + /// The name of the object. + name: string, + } + + /// Error codes returned by functions, similar to `errno` in POSIX. + /// Not all of these error codes are returned by the functions provided by this + /// API; some are used in higher-level library layers, and others are provided + /// merely for alignment with POSIX. + enum error-code { + /// Permission denied, similar to `EACCES` in POSIX. + access, + /// Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX. + would-block, + /// Connection already in progress, similar to `EALREADY` in POSIX. + already, + /// Bad descriptor, similar to `EBADF` in POSIX. + bad-descriptor, + /// Device or resource busy, similar to `EBUSY` in POSIX. + busy, + /// Resource deadlock would occur, similar to `EDEADLK` in POSIX. + deadlock, + /// Storage quota exceeded, similar to `EDQUOT` in POSIX. + quota, + /// File exists, similar to `EEXIST` in POSIX. + exist, + /// File too large, similar to `EFBIG` in POSIX. + file-too-large, + /// Illegal byte sequence, similar to `EILSEQ` in POSIX. + illegal-byte-sequence, + /// Operation in progress, similar to `EINPROGRESS` in POSIX. + in-progress, + /// Interrupted function, similar to `EINTR` in POSIX. + interrupted, + /// Invalid argument, similar to `EINVAL` in POSIX. + invalid, + /// I/O error, similar to `EIO` in POSIX. + io, + /// Is a directory, similar to `EISDIR` in POSIX. + is-directory, + /// Too many levels of symbolic links, similar to `ELOOP` in POSIX. + loop, + /// Too many links, similar to `EMLINK` in POSIX. + too-many-links, + /// Message too large, similar to `EMSGSIZE` in POSIX. + message-size, + /// Filename too long, similar to `ENAMETOOLONG` in POSIX. + name-too-long, + /// No such device, similar to `ENODEV` in POSIX. + no-device, + /// No such file or directory, similar to `ENOENT` in POSIX. + no-entry, + /// No locks available, similar to `ENOLCK` in POSIX. + no-lock, + /// Not enough space, similar to `ENOMEM` in POSIX. + insufficient-memory, + /// No space left on device, similar to `ENOSPC` in POSIX. + insufficient-space, + /// Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX. + not-directory, + /// Directory not empty, similar to `ENOTEMPTY` in POSIX. + not-empty, + /// State not recoverable, similar to `ENOTRECOVERABLE` in POSIX. + not-recoverable, + /// Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX. + unsupported, + /// Inappropriate I/O control operation, similar to `ENOTTY` in POSIX. + no-tty, + /// No such device or address, similar to `ENXIO` in POSIX. + no-such-device, + /// Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX. + overflow, + /// Operation not permitted, similar to `EPERM` in POSIX. + not-permitted, + /// Broken pipe, similar to `EPIPE` in POSIX. + pipe, + /// Read-only file system, similar to `EROFS` in POSIX. + read-only, + /// Invalid seek, similar to `ESPIPE` in POSIX. + invalid-seek, + /// Text file busy, similar to `ETXTBSY` in POSIX. + text-file-busy, + /// Cross-device link, similar to `EXDEV` in POSIX. + cross-device, + } + + /// File or memory access pattern advisory information. + @since(version = 0.2.0) + enum advice { + /// The application has no advice to give on its behavior with respect + /// to the specified data. + normal, + /// The application expects to access the specified data sequentially + /// from lower offsets to higher offsets. + sequential, + /// The application expects to access the specified data in a random + /// order. + random, + /// The application expects to access the specified data in the near + /// future. + will-need, + /// The application expects that it will not access the specified data + /// in the near future. + dont-need, + /// The application expects to access the specified data once and then + /// not reuse it thereafter. + no-reuse, + } + + /// A 128-bit hash value, split into parts because wasm doesn't have a + /// 128-bit integer type. + @since(version = 0.2.0) + record metadata-hash-value { + /// 64 bits of a 128-bit hash value. + lower: u64, + /// Another 64 bits of a 128-bit hash value. + upper: u64, + } + + /// A descriptor is a reference to a filesystem object, which may be a file, + /// directory, named pipe, special file, or other object on which filesystem + /// calls may be made. + @since(version = 0.2.0) + resource descriptor { + /// Return a stream for reading from a file, if available. + /// + /// May fail with an error-code describing why the file cannot be read. + /// + /// Multiple read, write, and append streams may be active on the same open + /// file and they do not interfere with each other. + /// + /// Note: This allows using `read-stream`, which is similar to `read` in POSIX. + @since(version = 0.2.0) + read-via-stream: func(offset: filesize) -> result; + /// Return a stream for writing to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be written. + /// + /// Note: This allows using `write-stream`, which is similar to `write` in + /// POSIX. + @since(version = 0.2.0) + write-via-stream: func(offset: filesize) -> result; + /// Return a stream for appending to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be appended. + /// + /// Note: This allows using `write-stream`, which is similar to `write` with + /// `O_APPEND` in POSIX. + @since(version = 0.2.0) + append-via-stream: func() -> result; + /// Provide file advisory information on a descriptor. + /// + /// This is similar to `posix_fadvise` in POSIX. + @since(version = 0.2.0) + advise: func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>; + /// Synchronize the data of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fdatasync` in POSIX. + @since(version = 0.2.0) + sync-data: func() -> result<_, error-code>; + /// Get flags associated with a descriptor. + /// + /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. + /// + /// Note: This returns the value that was the `fs_flags` value returned + /// from `fdstat_get` in earlier versions of WASI. + @since(version = 0.2.0) + get-flags: func() -> result; + /// Get the dynamic type of a descriptor. + /// + /// Note: This returns the same value as the `type` field of the `fd-stat` + /// returned by `stat`, `stat-at` and similar. + /// + /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided + /// by `fstat` in POSIX. + /// + /// Note: This returns the value that was the `fs_filetype` value returned + /// from `fdstat_get` in earlier versions of WASI. + @since(version = 0.2.0) + get-type: func() -> result; + /// Adjust the size of an open file. If this increases the file's size, the + /// extra bytes are filled with zeros. + /// + /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. + @since(version = 0.2.0) + set-size: func(size: filesize) -> result<_, error-code>; + /// Adjust the timestamps of an open file or directory. + /// + /// Note: This is similar to `futimens` in POSIX. + /// + /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. + @since(version = 0.2.0) + set-times: func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; + /// Read from a descriptor, without using and updating the descriptor's offset. + /// + /// This function returns a list of bytes containing the data that was + /// read, along with a bool which, when true, indicates that the end of the + /// file was reached. The returned list will contain up to `length` bytes; it + /// may return fewer than requested, if the end of the file is reached or + /// if the I/O operation is interrupted. + /// + /// In the future, this may change to return a `stream`. + /// + /// Note: This is similar to `pread` in POSIX. + @since(version = 0.2.0) + read: func(length: filesize, offset: filesize) -> result, bool>, error-code>; + /// Write to a descriptor, without using and updating the descriptor's offset. + /// + /// It is valid to write past the end of a file; the file is extended to the + /// extent of the write, with bytes between the previous end and the start of + /// the write set to zero. + /// + /// In the future, this may change to take a `stream`. + /// + /// Note: This is similar to `pwrite` in POSIX. + @since(version = 0.2.0) + write: func(buffer: list, offset: filesize) -> result; + /// Read directory entries from a directory. + /// + /// On filesystems where directories contain entries referring to themselves + /// and their parents, often named `.` and `..` respectively, these entries + /// are omitted. + /// + /// This always returns a new stream which starts at the beginning of the + /// directory. Multiple streams may be active on the same directory, and they + /// do not interfere with each other. + @since(version = 0.2.0) + read-directory: func() -> result; + /// Synchronize the data and metadata of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fsync` in POSIX. + @since(version = 0.2.0) + sync: func() -> result<_, error-code>; + /// Create a directory. + /// + /// Note: This is similar to `mkdirat` in POSIX. + @since(version = 0.2.0) + create-directory-at: func(path: string) -> result<_, error-code>; + /// Return the attributes of an open file or directory. + /// + /// Note: This is similar to `fstat` in POSIX, except that it does not return + /// device and inode information. For testing whether two descriptors refer to + /// the same underlying filesystem object, use `is-same-object`. To obtain + /// additional data that can be used do determine whether a file has been + /// modified, use `metadata-hash`. + /// + /// Note: This was called `fd_filestat_get` in earlier versions of WASI. + @since(version = 0.2.0) + stat: func() -> result; + /// Return the attributes of a file or directory. + /// + /// Note: This is similar to `fstatat` in POSIX, except that it does not + /// return device and inode information. See the `stat` description for a + /// discussion of alternatives. + /// + /// Note: This was called `path_filestat_get` in earlier versions of WASI. + @since(version = 0.2.0) + stat-at: func(path-flags: path-flags, path: string) -> result; + /// Adjust the timestamps of a file or directory. + /// + /// Note: This is similar to `utimensat` in POSIX. + /// + /// Note: This was called `path_filestat_set_times` in earlier versions of + /// WASI. + @since(version = 0.2.0) + set-times-at: func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; + /// Create a hard link. + /// + /// Fails with `error-code::no-entry` if the old path does not exist, + /// with `error-code::exist` if the new path already exists, and + /// `error-code::not-permitted` if the old path is not a file. + /// + /// Note: This is similar to `linkat` in POSIX. + @since(version = 0.2.0) + link-at: func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; + /// Open a file or directory. + /// + /// If `flags` contains `descriptor-flags::mutate-directory`, and the base + /// descriptor doesn't have `descriptor-flags::mutate-directory` set, + /// `open-at` fails with `error-code::read-only`. + /// + /// If `flags` contains `write` or `mutate-directory`, or `open-flags` + /// contains `truncate` or `create`, and the base descriptor doesn't have + /// `descriptor-flags::mutate-directory` set, `open-at` fails with + /// `error-code::read-only`. + /// + /// Note: This is similar to `openat` in POSIX. + @since(version = 0.2.0) + open-at: func(path-flags: path-flags, path: string, open-flags: open-flags, %flags: descriptor-flags) -> result; + /// Read the contents of a symbolic link. + /// + /// If the contents contain an absolute or rooted path in the underlying + /// filesystem, this function fails with `error-code::not-permitted`. + /// + /// Note: This is similar to `readlinkat` in POSIX. + @since(version = 0.2.0) + readlink-at: func(path: string) -> result; + /// Remove a directory. + /// + /// Return `error-code::not-empty` if the directory is not empty. + /// + /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + @since(version = 0.2.0) + remove-directory-at: func(path: string) -> result<_, error-code>; + /// Rename a filesystem object. + /// + /// Note: This is similar to `renameat` in POSIX. + @since(version = 0.2.0) + rename-at: func(old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; + /// Create a symbolic link (also known as a "symlink"). + /// + /// If `old-path` starts with `/`, the function fails with + /// `error-code::not-permitted`. + /// + /// Note: This is similar to `symlinkat` in POSIX. + @since(version = 0.2.0) + symlink-at: func(old-path: string, new-path: string) -> result<_, error-code>; + /// Unlink a filesystem object that is not a directory. + /// + /// Return `error-code::is-directory` if the path refers to a directory. + /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + @since(version = 0.2.0) + unlink-file-at: func(path: string) -> result<_, error-code>; + /// Test whether two descriptors refer to the same filesystem object. + /// + /// In POSIX, this corresponds to testing whether the two descriptors have the + /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. + /// wasi-filesystem does not expose device and inode numbers, so this function + /// may be used instead. + @since(version = 0.2.0) + is-same-object: func(other: borrow) -> bool; + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a descriptor. + /// + /// This returns a hash of the last-modification timestamp and file size, and + /// may also include the inode number, device number, birth timestamp, and + /// other metadata fields that may change when the file is modified or + /// replaced. It may also include a secret value chosen by the + /// implementation and not otherwise exposed. + /// + /// Implementations are encouraged to provide the following properties: + /// + /// - If the file is not modified or replaced, the computed hash value should + /// usually not change. + /// - If the object is modified or replaced, the computed hash value should + /// usually change. + /// - The inputs to the hash should not be easily computable from the + /// computed hash. + /// + /// However, none of these is required. + @since(version = 0.2.0) + metadata-hash: func() -> result; + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a directory descriptor and a relative path. + /// + /// This performs the same hash computation as `metadata-hash`. + @since(version = 0.2.0) + metadata-hash-at: func(path-flags: path-flags, path: string) -> result; + } + + /// A stream of directory entries. + @since(version = 0.2.0) + resource directory-entry-stream { + /// Read a single directory entry from a `directory-entry-stream`. + @since(version = 0.2.0) + read-directory-entry: func() -> result, error-code>; + } + + /// Attempts to extract a filesystem-related `error-code` from the stream + /// `error` provided. + /// + /// Stream operations which return `stream-error::last-operation-failed` + /// have a payload with more information about the operation that failed. + /// This payload can be passed through to this function to see if there's + /// filesystem-related information about the error to return. + /// + /// Note that this function is fallible because not all stream-related + /// errors are filesystem-related errors. + @since(version = 0.2.0) + filesystem-error-code: func(err: borrow) -> option; +} + +@since(version = 0.2.0) +interface preopens { + @since(version = 0.2.0) + use types.{descriptor}; + + /// Return the set of preopened directories, and their paths. + @since(version = 0.2.0) + get-directories: func() -> list>; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import wasi:io/error@0.2.6; + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import wasi:io/streams@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/wall-clock@0.2.6; + @since(version = 0.2.0) + import types; + @since(version = 0.2.0) + import preopens; +} diff --git a/crates/cpex-wasm-host/wit/deps/http.wit b/crates/cpex-wasm-host/wit/deps/http.wit new file mode 100644 index 00000000..eb1b25f0 --- /dev/null +++ b/crates/cpex-wasm-host/wit/deps/http.wit @@ -0,0 +1,733 @@ +package wasi:http@0.2.6; + +/// This interface defines all of the types and methods for implementing +/// HTTP Requests and Responses, both incoming and outgoing, as well as +/// their headers, trailers, and bodies. +@since(version = 0.2.0) +interface types { + @since(version = 0.2.0) + use wasi:clocks/monotonic-clock@0.2.6.{duration}; + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{input-stream, output-stream}; + @since(version = 0.2.0) + use wasi:io/error@0.2.6.{error as io-error}; + @since(version = 0.2.0) + use wasi:io/poll@0.2.6.{pollable}; + + /// This type corresponds to HTTP standard Methods. + @since(version = 0.2.0) + variant method { + get, + head, + post, + put, + delete, + connect, + options, + trace, + patch, + other(string), + } + + /// This type corresponds to HTTP standard Related Schemes. + @since(version = 0.2.0) + variant scheme { + HTTP, + HTTPS, + other(string), + } + + /// Defines the case payload type for `DNS-error` above: + @since(version = 0.2.0) + record DNS-error-payload { + rcode: option, + info-code: option, + } + + /// Defines the case payload type for `TLS-alert-received` above: + @since(version = 0.2.0) + record TLS-alert-received-payload { + alert-id: option, + alert-message: option, + } + + /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: + @since(version = 0.2.0) + record field-size-payload { + field-name: option, + field-size: option, + } + + /// These cases are inspired by the IANA HTTP Proxy Error Types: + /// + @since(version = 0.2.0) + variant error-code { + DNS-timeout, + DNS-error(DNS-error-payload), + destination-not-found, + destination-unavailable, + destination-IP-prohibited, + destination-IP-unroutable, + connection-refused, + connection-terminated, + connection-timeout, + connection-read-timeout, + connection-write-timeout, + connection-limit-reached, + TLS-protocol-error, + TLS-certificate-error, + TLS-alert-received(TLS-alert-received-payload), + HTTP-request-denied, + HTTP-request-length-required, + HTTP-request-body-size(option), + HTTP-request-method-invalid, + HTTP-request-URI-invalid, + HTTP-request-URI-too-long, + HTTP-request-header-section-size(option), + HTTP-request-header-size(option), + HTTP-request-trailer-section-size(option), + HTTP-request-trailer-size(field-size-payload), + HTTP-response-incomplete, + HTTP-response-header-section-size(option), + HTTP-response-header-size(field-size-payload), + HTTP-response-body-size(option), + HTTP-response-trailer-section-size(option), + HTTP-response-trailer-size(field-size-payload), + HTTP-response-transfer-coding(option), + HTTP-response-content-coding(option), + HTTP-response-timeout, + HTTP-upgrade-failed, + HTTP-protocol-error, + loop-detected, + configuration-error, + /// This is a catch-all error for anything that doesn't fit cleanly into a + /// more specific case. It also includes an optional string for an + /// unstructured description of the error. Users should not depend on the + /// string for diagnosing errors, as it's not required to be consistent + /// between implementations. + internal-error(option), + } + + /// This type enumerates the different kinds of errors that may occur when + /// setting or appending to a `fields` resource. + @since(version = 0.2.0) + variant header-error { + /// This error indicates that a `field-name` or `field-value` was + /// syntactically invalid when used with an operation that sets headers in a + /// `fields`. + invalid-syntax, + /// This error indicates that a forbidden `field-name` was used when trying + /// to set a header in a `fields`. + forbidden, + /// This error indicates that the operation on the `fields` was not + /// permitted because the fields are immutable. + immutable, + } + + /// Field keys are always strings. + /// + /// Field keys should always be treated as case insensitive by the `fields` + /// resource for the purposes of equality checking. + /// + /// # Deprecation + /// + /// This type has been deprecated in favor of the `field-name` type. + @since(version = 0.2.0) + @deprecated(version = 0.2.2) + type field-key = string; + + /// Field names are always strings. + /// + /// Field names should always be treated as case insensitive by the `fields` + /// resource for the purposes of equality checking. + @since(version = 0.2.1) + type field-name = field-key; + + /// Field values should always be ASCII strings. However, in + /// reality, HTTP implementations often have to interpret malformed values, + /// so they are provided as a list of bytes. + @since(version = 0.2.0) + type field-value = list; + + /// This following block defines the `fields` resource which corresponds to + /// HTTP standard Fields. Fields are a common representation used for both + /// Headers and Trailers. + /// + /// A `fields` may be mutable or immutable. A `fields` created using the + /// constructor, `from-list`, or `clone` will be mutable, but a `fields` + /// resource given by other means (including, but not limited to, + /// `incoming-request.headers`, `outgoing-request.headers`) might be + /// immutable. In an immutable fields, the `set`, `append`, and `delete` + /// operations will fail with `header-error.immutable`. + @since(version = 0.2.0) + resource fields { + /// Construct an empty HTTP Fields. + /// + /// The resulting `fields` is mutable. + @since(version = 0.2.0) + constructor(); + /// Construct an HTTP Fields. + /// + /// The resulting `fields` is mutable. + /// + /// The list represents each name-value pair in the Fields. Names + /// which have multiple values are represented by multiple entries in this + /// list with the same name. + /// + /// The tuple is a pair of the field name, represented as a string, and + /// Value, represented as a list of bytes. + /// + /// An error result will be returned if any `field-name` or `field-value` is + /// syntactically invalid, or if a field is forbidden. + @since(version = 0.2.0) + from-list: static func(entries: list>) -> result; + /// Get all of the values corresponding to a name. If the name is not present + /// in this `fields` or is syntactically invalid, an empty list is returned. + /// However, if the name is present but empty, this is represented by a list + /// with one or more empty field-values present. + @since(version = 0.2.0) + get: func(name: field-name) -> list; + /// Returns `true` when the name is present in this `fields`. If the name is + /// syntactically invalid, `false` is returned. + @since(version = 0.2.0) + has: func(name: field-name) -> bool; + /// Set all of the values for a name. Clears any existing values for that + /// name, if they have been set. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.invalid-syntax` if the `field-name` or any of + /// the `field-value`s are syntactically invalid. + @since(version = 0.2.0) + set: func(name: field-name, value: list) -> result<_, header-error>; + /// Delete all values for a name. Does nothing if no values for the name + /// exist. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.invalid-syntax` if the `field-name` is + /// syntactically invalid. + @since(version = 0.2.0) + delete: func(name: field-name) -> result<_, header-error>; + /// Append a value for a name. Does not change or delete any existing + /// values for that name. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.invalid-syntax` if the `field-name` or + /// `field-value` are syntactically invalid. + @since(version = 0.2.0) + append: func(name: field-name, value: field-value) -> result<_, header-error>; + /// Retrieve the full set of names and values in the Fields. Like the + /// constructor, the list represents each name-value pair. + /// + /// The outer list represents each name-value pair in the Fields. Names + /// which have multiple values are represented by multiple entries in this + /// list with the same name. + /// + /// The names and values are always returned in the original casing and in + /// the order in which they will be serialized for transport. + @since(version = 0.2.0) + entries: func() -> list>; + /// Make a deep copy of the Fields. Equivalent in behavior to calling the + /// `fields` constructor on the return value of `entries`. The resulting + /// `fields` is mutable. + @since(version = 0.2.0) + clone: func() -> fields; + } + + /// Headers is an alias for Fields. + @since(version = 0.2.0) + type headers = fields; + + /// Trailers is an alias for Fields. + @since(version = 0.2.0) + type trailers = fields; + + /// Represents an incoming HTTP Request. + @since(version = 0.2.0) + resource incoming-request { + /// Returns the method of the incoming request. + @since(version = 0.2.0) + method: func() -> method; + /// Returns the path with query parameters from the request, as a string. + @since(version = 0.2.0) + path-with-query: func() -> option; + /// Returns the protocol scheme from the request. + @since(version = 0.2.0) + scheme: func() -> option; + /// Returns the authority of the Request's target URI, if present. + @since(version = 0.2.0) + authority: func() -> option; + /// Get the `headers` associated with the request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// The `headers` returned are a child resource: it must be dropped before + /// the parent `incoming-request` is dropped. Dropping this + /// `incoming-request` before all children are dropped will trap. + @since(version = 0.2.0) + headers: func() -> headers; + /// Gives the `incoming-body` associated with this request. Will only + /// return success at most once, and subsequent calls will return error. + @since(version = 0.2.0) + consume: func() -> result; + } + + /// Represents an outgoing HTTP Request. + @since(version = 0.2.0) + resource outgoing-request { + /// Construct a new `outgoing-request` with a default `method` of `GET`, and + /// `none` values for `path-with-query`, `scheme`, and `authority`. + /// + /// * `headers` is the HTTP Headers for the Request. + /// + /// It is possible to construct, or manipulate with the accessor functions + /// below, an `outgoing-request` with an invalid combination of `scheme` + /// and `authority`, or `headers` which are not permitted to be sent. + /// It is the obligation of the `outgoing-handler.handle` implementation + /// to reject invalid constructions of `outgoing-request`. + @since(version = 0.2.0) + constructor(headers: headers); + /// Returns the resource corresponding to the outgoing Body for this + /// Request. + /// + /// Returns success on the first call: the `outgoing-body` resource for + /// this `outgoing-request` can be retrieved at most once. Subsequent + /// calls will return error. + @since(version = 0.2.0) + body: func() -> result; + /// Get the Method for the Request. + @since(version = 0.2.0) + method: func() -> method; + /// Set the Method for the Request. Fails if the string present in a + /// `method.other` argument is not a syntactically valid method. + @since(version = 0.2.0) + set-method: func(method: method) -> result; + /// Get the combination of the HTTP Path and Query for the Request. + /// When `none`, this represents an empty Path and empty Query. + @since(version = 0.2.0) + path-with-query: func() -> option; + /// Set the combination of the HTTP Path and Query for the Request. + /// When `none`, this represents an empty Path and empty Query. Fails is the + /// string given is not a syntactically valid path and query uri component. + @since(version = 0.2.0) + set-path-with-query: func(path-with-query: option) -> result; + /// Get the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. + @since(version = 0.2.0) + scheme: func() -> option; + /// Set the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. Fails if the + /// string given is not a syntactically valid uri scheme. + @since(version = 0.2.0) + set-scheme: func(scheme: option) -> result; + /// Get the authority of the Request's target URI. A value of `none` may be used + /// with Related Schemes which do not require an authority. The HTTP and + /// HTTPS schemes always require an authority. + @since(version = 0.2.0) + authority: func() -> option; + /// Set the authority of the Request's target URI. A value of `none` may be used + /// with Related Schemes which do not require an authority. The HTTP and + /// HTTPS schemes always require an authority. Fails if the string given is + /// not a syntactically valid URI authority. + @since(version = 0.2.0) + set-authority: func(authority: option) -> result; + /// Get the headers associated with the Request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `outgoing-request` is dropped, or its ownership is transferred to + /// another component by e.g. `outgoing-handler.handle`. + @since(version = 0.2.0) + headers: func() -> headers; + } + + /// Parameters for making an HTTP Request. Each of these parameters is + /// currently an optional timeout applicable to the transport layer of the + /// HTTP protocol. + /// + /// These timeouts are separate from any the user may use to bound a + /// blocking call to `wasi:io/poll.poll`. + @since(version = 0.2.0) + resource request-options { + /// Construct a default `request-options` value. + @since(version = 0.2.0) + constructor(); + /// The timeout for the initial connect to the HTTP Server. + @since(version = 0.2.0) + connect-timeout: func() -> option; + /// Set the timeout for the initial connect to the HTTP Server. An error + /// return value indicates that this timeout is not supported. + @since(version = 0.2.0) + set-connect-timeout: func(duration: option) -> result; + /// The timeout for receiving the first byte of the Response body. + @since(version = 0.2.0) + first-byte-timeout: func() -> option; + /// Set the timeout for receiving the first byte of the Response body. An + /// error return value indicates that this timeout is not supported. + @since(version = 0.2.0) + set-first-byte-timeout: func(duration: option) -> result; + /// The timeout for receiving subsequent chunks of bytes in the Response + /// body stream. + @since(version = 0.2.0) + between-bytes-timeout: func() -> option; + /// Set the timeout for receiving subsequent chunks of bytes in the Response + /// body stream. An error return value indicates that this timeout is not + /// supported. + @since(version = 0.2.0) + set-between-bytes-timeout: func(duration: option) -> result; + } + + /// Represents the ability to send an HTTP Response. + /// + /// This resource is used by the `wasi:http/incoming-handler` interface to + /// allow a Response to be sent corresponding to the Request provided as the + /// other argument to `incoming-handler.handle`. + @since(version = 0.2.0) + resource response-outparam { + /// Send an HTTP 1xx response. + /// + /// Unlike `response-outparam.set`, this does not consume the + /// `response-outparam`, allowing the guest to send an arbitrary number of + /// informational responses before sending the final response using + /// `response-outparam.set`. + /// + /// This will return an `HTTP-protocol-error` if `status` is not in the + /// range [100-199], or an `internal-error` if the implementation does not + /// support informational responses. + @unstable(feature = informational-outbound-responses) + send-informational: func(status: u16, headers: headers) -> result<_, error-code>; + /// Set the value of the `response-outparam` to either send a response, + /// or indicate an error. + /// + /// This method consumes the `response-outparam` to ensure that it is + /// called at most once. If it is never called, the implementation + /// will respond with an error. + /// + /// The user may provide an `error` to `response` to allow the + /// implementation determine how to respond with an HTTP error response. + @since(version = 0.2.0) + set: static func(param: response-outparam, response: result); + } + + /// This type corresponds to the HTTP standard Status Code. + @since(version = 0.2.0) + type status-code = u16; + + /// Represents an incoming HTTP Response. + @since(version = 0.2.0) + resource incoming-response { + /// Returns the status code from the incoming response. + @since(version = 0.2.0) + status: func() -> status-code; + /// Returns the headers from the incoming response. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `incoming-response` is dropped. + @since(version = 0.2.0) + headers: func() -> headers; + /// Returns the incoming body. May be called at most once. Returns error + /// if called additional times. + @since(version = 0.2.0) + consume: func() -> result; + } + + /// Represents an incoming HTTP Request or Response's Body. + /// + /// A body has both its contents - a stream of bytes - and a (possibly + /// empty) set of trailers, indicating that the full contents of the + /// body have been received. This resource represents the contents as + /// an `input-stream` and the delivery of trailers as a `future-trailers`, + /// and ensures that the user of this interface may only be consuming either + /// the body contents or waiting on trailers at any given time. + @since(version = 0.2.0) + resource incoming-body { + /// Returns the contents of the body, as a stream of bytes. + /// + /// Returns success on first call: the stream representing the contents + /// can be retrieved at most once. Subsequent calls will return error. + /// + /// The returned `input-stream` resource is a child: it must be dropped + /// before the parent `incoming-body` is dropped, or consumed by + /// `incoming-body.finish`. + /// + /// This invariant ensures that the implementation can determine whether + /// the user is consuming the contents of the body, waiting on the + /// `future-trailers` to be ready, or neither. This allows for network + /// backpressure is to be applied when the user is consuming the body, + /// and for that backpressure to not inhibit delivery of the trailers if + /// the user does not read the entire body. + @since(version = 0.2.0) + %stream: func() -> result; + /// Takes ownership of `incoming-body`, and returns a `future-trailers`. + /// This function will trap if the `input-stream` child is still alive. + @since(version = 0.2.0) + finish: static func(this: incoming-body) -> future-trailers; + } + + /// Represents a future which may eventually return trailers, or an error. + /// + /// In the case that the incoming HTTP Request or Response did not have any + /// trailers, this future will resolve to the empty set of trailers once the + /// complete Request or Response body has been received. + @since(version = 0.2.0) + resource future-trailers { + /// Returns a pollable which becomes ready when either the trailers have + /// been received, or an error has occurred. When this pollable is ready, + /// the `get` method will return `some`. + @since(version = 0.2.0) + subscribe: func() -> pollable; + /// Returns the contents of the trailers, or an error which occurred, + /// once the future is ready. + /// + /// The outer `option` represents future readiness. Users can wait on this + /// `option` to become `some` using the `subscribe` method. + /// + /// The outer `result` is used to retrieve the trailers or error at most + /// once. It will be success on the first call in which the outer option + /// is `some`, and error on subsequent calls. + /// + /// The inner `result` represents that either the HTTP Request or Response + /// body, as well as any trailers, were received successfully, or that an + /// error occurred receiving them. The optional `trailers` indicates whether + /// or not trailers were present in the body. + /// + /// When some `trailers` are returned by this method, the `trailers` + /// resource is immutable, and a child. Use of the `set`, `append`, or + /// `delete` methods will return an error, and the resource must be + /// dropped before the parent `future-trailers` is dropped. + @since(version = 0.2.0) + get: func() -> option, error-code>>>; + } + + /// Represents an outgoing HTTP Response. + @since(version = 0.2.0) + resource outgoing-response { + /// Construct an `outgoing-response`, with a default `status-code` of `200`. + /// If a different `status-code` is needed, it must be set via the + /// `set-status-code` method. + /// + /// * `headers` is the HTTP Headers for the Response. + @since(version = 0.2.0) + constructor(headers: headers); + /// Get the HTTP Status Code for the Response. + @since(version = 0.2.0) + status-code: func() -> status-code; + /// Set the HTTP Status Code for the Response. Fails if the status-code + /// given is not a valid http status code. + @since(version = 0.2.0) + set-status-code: func(status-code: status-code) -> result; + /// Get the headers associated with the Request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `outgoing-request` is dropped, or its ownership is transferred to + /// another component by e.g. `outgoing-handler.handle`. + @since(version = 0.2.0) + headers: func() -> headers; + /// Returns the resource corresponding to the outgoing Body for this Response. + /// + /// Returns success on the first call: the `outgoing-body` resource for + /// this `outgoing-response` can be retrieved at most once. Subsequent + /// calls will return error. + @since(version = 0.2.0) + body: func() -> result; + } + + /// Represents an outgoing HTTP Request or Response's Body. + /// + /// A body has both its contents - a stream of bytes - and a (possibly + /// empty) set of trailers, inducating the full contents of the body + /// have been sent. This resource represents the contents as an + /// `output-stream` child resource, and the completion of the body (with + /// optional trailers) with a static function that consumes the + /// `outgoing-body` resource, and ensures that the user of this interface + /// may not write to the body contents after the body has been finished. + /// + /// If the user code drops this resource, as opposed to calling the static + /// method `finish`, the implementation should treat the body as incomplete, + /// and that an error has occurred. The implementation should propagate this + /// error to the HTTP protocol by whatever means it has available, + /// including: corrupting the body on the wire, aborting the associated + /// Request, or sending a late status code for the Response. + @since(version = 0.2.0) + resource outgoing-body { + /// Returns a stream for writing the body contents. + /// + /// The returned `output-stream` is a child resource: it must be dropped + /// before the parent `outgoing-body` resource is dropped (or finished), + /// otherwise the `outgoing-body` drop or `finish` will trap. + /// + /// Returns success on the first call: the `output-stream` resource for + /// this `outgoing-body` may be retrieved at most once. Subsequent calls + /// will return error. + @since(version = 0.2.0) + write: func() -> result; + /// Finalize an outgoing body, optionally providing trailers. This must be + /// called to signal that the response is complete. If the `outgoing-body` + /// is dropped without calling `outgoing-body.finalize`, the implementation + /// should treat the body as corrupted. + /// + /// Fails if the body's `outgoing-request` or `outgoing-response` was + /// constructed with a Content-Length header, and the contents written + /// to the body (via `write`) does not match the value given in the + /// Content-Length. + @since(version = 0.2.0) + finish: static func(this: outgoing-body, trailers: option) -> result<_, error-code>; + } + + /// Represents a future which may eventually return an incoming HTTP + /// Response, or an error. + /// + /// This resource is returned by the `wasi:http/outgoing-handler` interface to + /// provide the HTTP Response corresponding to the sent Request. + @since(version = 0.2.0) + resource future-incoming-response { + /// Returns a pollable which becomes ready when either the Response has + /// been received, or an error has occurred. When this pollable is ready, + /// the `get` method will return `some`. + @since(version = 0.2.0) + subscribe: func() -> pollable; + /// Returns the incoming HTTP Response, or an error, once one is ready. + /// + /// The outer `option` represents future readiness. Users can wait on this + /// `option` to become `some` using the `subscribe` method. + /// + /// The outer `result` is used to retrieve the response or error at most + /// once. It will be success on the first call in which the outer option + /// is `some`, and error on subsequent calls. + /// + /// The inner `result` represents that either the incoming HTTP Response + /// status and headers have received successfully, or that an error + /// occurred. Errors may also occur while consuming the response body, + /// but those will be reported by the `incoming-body` and its + /// `output-stream` child. + @since(version = 0.2.0) + get: func() -> option>>; + } + + /// Attempts to extract a http-related `error` from the wasi:io `error` + /// provided. + /// + /// Stream operations which return + /// `wasi:io/stream/stream-error::last-operation-failed` have a payload of + /// type `wasi:io/error/error` with more information about the operation + /// that failed. This payload can be passed through to this function to see + /// if there's http-related information about the error to return. + /// + /// Note that this function is fallible because not all io-errors are + /// http-related errors. + @since(version = 0.2.0) + http-error-code: func(err: borrow) -> option; +} + +/// This interface defines a handler of incoming HTTP Requests. It should +/// be exported by components which can respond to HTTP Requests. +@since(version = 0.2.0) +interface incoming-handler { + @since(version = 0.2.0) + use types.{incoming-request, response-outparam}; + + /// This function is invoked with an incoming HTTP Request, and a resource + /// `response-outparam` which provides the capability to reply with an HTTP + /// Response. The response is sent by calling the `response-outparam.set` + /// method, which allows execution to continue after the response has been + /// sent. This enables both streaming to the response body, and performing other + /// work. + /// + /// The implementor of this function must write a response to the + /// `response-outparam` before returning, or else the caller will respond + /// with an error on its behalf. + @since(version = 0.2.0) + handle: func(request: incoming-request, response-out: response-outparam); +} + +/// This interface defines a handler of outgoing HTTP Requests. It should be +/// imported by components which wish to make HTTP Requests. +@since(version = 0.2.0) +interface outgoing-handler { + @since(version = 0.2.0) + use types.{outgoing-request, request-options, future-incoming-response, error-code}; + + /// This function is invoked with an outgoing HTTP Request, and it returns + /// a resource `future-incoming-response` which represents an HTTP Response + /// which may arrive in the future. + /// + /// The `options` argument accepts optional parameters for the HTTP + /// protocol's transport layer. + /// + /// This function may return an error if the `outgoing-request` is invalid + /// or not allowed to be made. Otherwise, protocol errors are reported + /// through the `future-incoming-response`. + @since(version = 0.2.0) + handle: func(request: outgoing-request, options: option) -> result; +} + +/// The `wasi:http/imports` world imports all the APIs for HTTP proxies. +/// It is intended to be `include`d in other worlds. +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/monotonic-clock@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/wall-clock@0.2.6; + @since(version = 0.2.0) + import wasi:random/random@0.2.6; + @since(version = 0.2.0) + import wasi:io/error@0.2.6; + @since(version = 0.2.0) + import wasi:io/streams@0.2.6; + @since(version = 0.2.0) + import wasi:cli/stdout@0.2.6; + @since(version = 0.2.0) + import wasi:cli/stderr@0.2.6; + @since(version = 0.2.0) + import wasi:cli/stdin@0.2.6; + @since(version = 0.2.0) + import types; + @since(version = 0.2.0) + import outgoing-handler; +} +/// The `wasi:http/proxy` world captures a widely-implementable intersection of +/// hosts that includes HTTP forward and reverse proxies. Components targeting +/// this world may concurrently stream in and out any number of incoming and +/// outgoing HTTP requests. +@since(version = 0.2.0) +world proxy { + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/monotonic-clock@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/wall-clock@0.2.6; + @since(version = 0.2.0) + import wasi:random/random@0.2.6; + @since(version = 0.2.0) + import wasi:io/error@0.2.6; + @since(version = 0.2.0) + import wasi:io/streams@0.2.6; + @since(version = 0.2.0) + import wasi:cli/stdout@0.2.6; + @since(version = 0.2.0) + import wasi:cli/stderr@0.2.6; + @since(version = 0.2.0) + import wasi:cli/stdin@0.2.6; + @since(version = 0.2.0) + import types; + @since(version = 0.2.0) + import outgoing-handler; + + @since(version = 0.2.0) + export incoming-handler; +} diff --git a/crates/cpex-wasm-host/wit/deps/io.wit b/crates/cpex-wasm-host/wit/deps/io.wit new file mode 100644 index 00000000..08ad78e6 --- /dev/null +++ b/crates/cpex-wasm-host/wit/deps/io.wit @@ -0,0 +1,331 @@ +package wasi:io@0.2.6; + +@since(version = 0.2.0) +interface error { + /// A resource which represents some error information. + /// + /// The only method provided by this resource is `to-debug-string`, + /// which provides some human-readable information about the error. + /// + /// In the `wasi:io` package, this resource is returned through the + /// `wasi:io/streams/stream-error` type. + /// + /// To provide more specific error information, other interfaces may + /// offer functions to "downcast" this error into more specific types. For example, + /// errors returned from streams derived from filesystem types can be described using + /// the filesystem's own error-code type. This is done using the function + /// `wasi:filesystem/types/filesystem-error-code`, which takes a `borrow` + /// parameter and returns an `option`. + /// + /// The set of functions which can "downcast" an `error` into a more + /// concrete type is open. + @since(version = 0.2.0) + resource error { + /// Returns a string that is suitable to assist humans in debugging + /// this error. + /// + /// WARNING: The returned string should not be consumed mechanically! + /// It may change across platforms, hosts, or other implementation + /// details. Parsing this string is a major platform-compatibility + /// hazard. + @since(version = 0.2.0) + to-debug-string: func() -> string; + } +} + +/// A poll API intended to let users wait for I/O events on multiple handles +/// at once. +@since(version = 0.2.0) +interface poll { + /// `pollable` represents a single I/O event which may be ready, or not. + @since(version = 0.2.0) + resource pollable { + /// Return the readiness of a pollable. This function never blocks. + /// + /// Returns `true` when the pollable is ready, and `false` otherwise. + @since(version = 0.2.0) + ready: func() -> bool; + /// `block` returns immediately if the pollable is ready, and otherwise + /// blocks until ready. + /// + /// This function is equivalent to calling `poll.poll` on a list + /// containing only this pollable. + @since(version = 0.2.0) + block: func(); + } + + /// Poll for completion on a set of pollables. + /// + /// This function takes a list of pollables, which identify I/O sources of + /// interest, and waits until one or more of the events is ready for I/O. + /// + /// The result `list` contains one or more indices of handles in the + /// argument list that is ready for I/O. + /// + /// This function traps if either: + /// - the list is empty, or: + /// - the list contains more elements than can be indexed with a `u32` value. + /// + /// A timeout can be implemented by adding a pollable from the + /// wasi-clocks API to the list. + /// + /// This function does not return a `result`; polling in itself does not + /// do any I/O so it doesn't fail. If any of the I/O sources identified by + /// the pollables has an error, it is indicated by marking the source as + /// being ready for I/O. + @since(version = 0.2.0) + poll: func(in: list>) -> list; +} + +/// WASI I/O is an I/O abstraction API which is currently focused on providing +/// stream types. +/// +/// In the future, the component model is expected to add built-in stream types; +/// when it does, they are expected to subsume this API. +@since(version = 0.2.0) +interface streams { + @since(version = 0.2.0) + use error.{error}; + @since(version = 0.2.0) + use poll.{pollable}; + + /// An error for input-stream and output-stream operations. + @since(version = 0.2.0) + variant stream-error { + /// The last operation (a write or flush) failed before completion. + /// + /// More information is available in the `error` payload. + /// + /// After this, the stream will be closed. All future operations return + /// `stream-error::closed`. + last-operation-failed(error), + /// The stream is closed: no more input will be accepted by the + /// stream. A closed output-stream will return this error on all + /// future operations. + closed, + } + + /// An input bytestream. + /// + /// `input-stream`s are *non-blocking* to the extent practical on underlying + /// platforms. I/O operations always return promptly; if fewer bytes are + /// promptly available than requested, they return the number of bytes promptly + /// available, which could even be zero. To wait for data to be available, + /// use the `subscribe` function to obtain a `pollable` which can be polled + /// for using `wasi:io/poll`. + @since(version = 0.2.0) + resource input-stream { + /// Perform a non-blocking read from the stream. + /// + /// When the source of a `read` is binary data, the bytes from the source + /// are returned verbatim. When the source of a `read` is known to the + /// implementation to be text, bytes containing the UTF-8 encoding of the + /// text are returned. + /// + /// This function returns a list of bytes containing the read data, + /// when successful. The returned list will contain up to `len` bytes; + /// it may return fewer than requested, but not more. The list is + /// empty when no bytes are available for reading at this time. The + /// pollable given by `subscribe` will be ready when more bytes are + /// available. + /// + /// This function fails with a `stream-error` when the operation + /// encounters an error, giving `last-operation-failed`, or when the + /// stream is closed, giving `closed`. + /// + /// When the caller gives a `len` of 0, it represents a request to + /// read 0 bytes. If the stream is still open, this call should + /// succeed and return an empty list, or otherwise fail with `closed`. + /// + /// The `len` parameter is a `u64`, which could represent a list of u8 which + /// is not possible to allocate in wasm32, or not desirable to allocate as + /// as a return value by the callee. The callee may return a list of bytes + /// less than `len` in size while more bytes are available for reading. + @since(version = 0.2.0) + read: func(len: u64) -> result, stream-error>; + /// Read bytes from a stream, after blocking until at least one byte can + /// be read. Except for blocking, behavior is identical to `read`. + @since(version = 0.2.0) + blocking-read: func(len: u64) -> result, stream-error>; + /// Skip bytes from a stream. Returns number of bytes skipped. + /// + /// Behaves identical to `read`, except instead of returning a list + /// of bytes, returns the number of bytes consumed from the stream. + @since(version = 0.2.0) + skip: func(len: u64) -> result; + /// Skip bytes from a stream, after blocking until at least one byte + /// can be skipped. Except for blocking behavior, identical to `skip`. + @since(version = 0.2.0) + blocking-skip: func(len: u64) -> result; + /// Create a `pollable` which will resolve once either the specified stream + /// has bytes available to read or the other end of the stream has been + /// closed. + /// The created `pollable` is a child resource of the `input-stream`. + /// Implementations may trap if the `input-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + /// An output bytestream. + /// + /// `output-stream`s are *non-blocking* to the extent practical on + /// underlying platforms. Except where specified otherwise, I/O operations also + /// always return promptly, after the number of bytes that can be written + /// promptly, which could even be zero. To wait for the stream to be ready to + /// accept data, the `subscribe` function to obtain a `pollable` which can be + /// polled for using `wasi:io/poll`. + /// + /// Dropping an `output-stream` while there's still an active write in + /// progress may result in the data being lost. Before dropping the stream, + /// be sure to fully flush your writes. + @since(version = 0.2.0) + resource output-stream { + /// Check readiness for writing. This function never blocks. + /// + /// Returns the number of bytes permitted for the next call to `write`, + /// or an error. Calling `write` with more bytes than this function has + /// permitted will trap. + /// + /// When this function returns 0 bytes, the `subscribe` pollable will + /// become ready when this function will report at least 1 byte, or an + /// error. + @since(version = 0.2.0) + check-write: func() -> result; + /// Perform a write. This function never blocks. + /// + /// When the destination of a `write` is binary data, the bytes from + /// `contents` are written verbatim. When the destination of a `write` is + /// known to the implementation to be text, the bytes of `contents` are + /// transcoded from UTF-8 into the encoding of the destination and then + /// written. + /// + /// Precondition: check-write gave permit of Ok(n) and contents has a + /// length of less than or equal to n. Otherwise, this function will trap. + /// + /// returns Err(closed) without writing if the stream has closed since + /// the last call to check-write provided a permit. + @since(version = 0.2.0) + write: func(contents: list) -> result<_, stream-error>; + /// Perform a write of up to 4096 bytes, and then flush the stream. Block + /// until all of these operations are complete, or an error occurs. + /// + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe`, `write`, and `flush`, and is implemented with the + /// following pseudo-code: + /// + /// ```text + /// let pollable = this.subscribe(); + /// while !contents.is_empty() { + /// // Wait for the stream to become writable + /// pollable.block(); + /// let Ok(n) = this.check-write(); // eliding error handling + /// let len = min(n, contents.len()); + /// let (chunk, rest) = contents.split_at(len); + /// this.write(chunk ); // eliding error handling + /// contents = rest; + /// } + /// this.flush(); + /// // Wait for completion of `flush` + /// pollable.block(); + /// // Check for any errors that arose during `flush` + /// let _ = this.check-write(); // eliding error handling + /// ``` + @since(version = 0.2.0) + blocking-write-and-flush: func(contents: list) -> result<_, stream-error>; + /// Request to flush buffered output. This function never blocks. + /// + /// This tells the output-stream that the caller intends any buffered + /// output to be flushed. the output which is expected to be flushed + /// is all that has been passed to `write` prior to this call. + /// + /// Upon calling this function, the `output-stream` will not accept any + /// writes (`check-write` will return `ok(0)`) until the flush has + /// completed. The `subscribe` pollable will become ready when the + /// flush has completed and the stream can accept more writes. + @since(version = 0.2.0) + flush: func() -> result<_, stream-error>; + /// Request to flush buffered output, and block until flush completes + /// and stream is ready for writing again. + @since(version = 0.2.0) + blocking-flush: func() -> result<_, stream-error>; + /// Create a `pollable` which will resolve once the output-stream + /// is ready for more writing, or an error has occurred. When this + /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an + /// error. + /// + /// If the stream is closed, this pollable is always ready immediately. + /// + /// The created `pollable` is a child resource of the `output-stream`. + /// Implementations may trap if the `output-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + @since(version = 0.2.0) + subscribe: func() -> pollable; + /// Write zeroes to a stream. + /// + /// This should be used precisely like `write` with the exact same + /// preconditions (must use check-write first), but instead of + /// passing a list of bytes, you simply pass the number of zero-bytes + /// that should be written. + @since(version = 0.2.0) + write-zeroes: func(len: u64) -> result<_, stream-error>; + /// Perform a write of up to 4096 zeroes, and then flush the stream. + /// Block until all of these operations are complete, or an error + /// occurs. + /// + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe`, `write-zeroes`, and `flush`, and is implemented with + /// the following pseudo-code: + /// + /// ```text + /// let pollable = this.subscribe(); + /// while num_zeroes != 0 { + /// // Wait for the stream to become writable + /// pollable.block(); + /// let Ok(n) = this.check-write(); // eliding error handling + /// let len = min(n, num_zeroes); + /// this.write-zeroes(len); // eliding error handling + /// num_zeroes -= len; + /// } + /// this.flush(); + /// // Wait for completion of `flush` + /// pollable.block(); + /// // Check for any errors that arose during `flush` + /// let _ = this.check-write(); // eliding error handling + /// ``` + @since(version = 0.2.0) + blocking-write-zeroes-and-flush: func(len: u64) -> result<_, stream-error>; + /// Read from one stream and write to another. + /// + /// The behavior of splice is equivalent to: + /// 1. calling `check-write` on the `output-stream` + /// 2. calling `read` on the `input-stream` with the smaller of the + /// `check-write` permitted length and the `len` provided to `splice` + /// 3. calling `write` on the `output-stream` with that read data. + /// + /// Any error reported by the call to `check-write`, `read`, or + /// `write` ends the splice and reports that error. + /// + /// This function returns the number of bytes transferred; it may be less + /// than `len`. + @since(version = 0.2.0) + splice: func(src: borrow, len: u64) -> result; + /// Read from one stream and write to another, with blocking. + /// + /// This is similar to `splice`, except that it blocks until the + /// `output-stream` is ready for writing, and the `input-stream` + /// is ready for reading, before performing the `splice`. + @since(version = 0.2.0) + blocking-splice: func(src: borrow, len: u64) -> result; + } +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import error; + @since(version = 0.2.0) + import poll; + @since(version = 0.2.0) + import streams; +} diff --git a/crates/cpex-wasm-host/wit/deps/random.wit b/crates/cpex-wasm-host/wit/deps/random.wit new file mode 100644 index 00000000..73edf5b6 --- /dev/null +++ b/crates/cpex-wasm-host/wit/deps/random.wit @@ -0,0 +1,92 @@ +package wasi:random@0.2.6; + +/// The insecure-seed interface for seeding hash-map DoS resistance. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +@since(version = 0.2.0) +interface insecure-seed { + /// Return a 128-bit value that may contain a pseudo-random value. + /// + /// The returned value is not required to be computed from a CSPRNG, and may + /// even be entirely deterministic. Host implementations are encouraged to + /// provide pseudo-random values to any program exposed to + /// attacker-controlled content, to enable DoS protection built into many + /// languages' hash-map implementations. + /// + /// This function is intended to only be called once, by a source language + /// to initialize Denial Of Service (DoS) protection in its hash-map + /// implementation. + /// + /// # Expected future evolution + /// + /// This will likely be changed to a value import, to prevent it from being + /// called multiple times and potentially used for purposes other than DoS + /// protection. + @since(version = 0.2.0) + insecure-seed: func() -> tuple; +} + +/// The insecure interface for insecure pseudo-random numbers. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +@since(version = 0.2.0) +interface insecure { + /// Return `len` insecure pseudo-random bytes. + /// + /// This function is not cryptographically secure. Do not use it for + /// anything related to security. + /// + /// There are no requirements on the values of the returned bytes, however + /// implementations are encouraged to return evenly distributed values with + /// a long period. + @since(version = 0.2.0) + get-insecure-random-bytes: func(len: u64) -> list; + + /// Return an insecure pseudo-random `u64` value. + /// + /// This function returns the same type of pseudo-random data as + /// `get-insecure-random-bytes`, represented as a `u64`. + @since(version = 0.2.0) + get-insecure-random-u64: func() -> u64; +} + +/// WASI Random is a random data API. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +@since(version = 0.2.0) +interface random { + /// Return `len` cryptographically-secure random or pseudo-random bytes. + /// + /// This function must produce data at least as cryptographically secure and + /// fast as an adequately seeded cryptographically-secure pseudo-random + /// number generator (CSPRNG). It must not block, from the perspective of + /// the calling program, under any circumstances, including on the first + /// request and on requests for numbers of bytes. The returned data must + /// always be unpredictable. + /// + /// This function must always return fresh data. Deterministic environments + /// must omit this function, rather than implementing it with deterministic + /// data. + @since(version = 0.2.0) + get-random-bytes: func(len: u64) -> list; + + /// Return a cryptographically-secure random or pseudo-random `u64` value. + /// + /// This function returns the same type of data as `get-random-bytes`, + /// represented as a `u64`. + @since(version = 0.2.0) + get-random-u64: func() -> u64; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import random; + @since(version = 0.2.0) + import insecure; + @since(version = 0.2.0) + import insecure-seed; +} diff --git a/crates/cpex-wasm-host/wit/deps/sockets.wit b/crates/cpex-wasm-host/wit/deps/sockets.wit new file mode 100644 index 00000000..db6d1a23 --- /dev/null +++ b/crates/cpex-wasm-host/wit/deps/sockets.wit @@ -0,0 +1,949 @@ +package wasi:sockets@0.2.6; + +@since(version = 0.2.0) +interface network { + @unstable(feature = network-error-code) + use wasi:io/error@0.2.6.{error}; + + /// An opaque resource that represents access to (a subset of) the network. + /// This enables context-based security for networking. + /// There is no need for this to map 1:1 to a physical network interface. + @since(version = 0.2.0) + resource network; + + /// Error codes. + /// + /// In theory, every API can return any error code. + /// In practice, API's typically only return the errors documented per API + /// combined with a couple of errors that are always possible: + /// - `unknown` + /// - `access-denied` + /// - `not-supported` + /// - `out-of-memory` + /// - `concurrency-conflict` + /// + /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. + @since(version = 0.2.0) + enum error-code { + /// Unknown error + unknown, + /// Access denied. + /// + /// POSIX equivalent: EACCES, EPERM + access-denied, + /// The operation is not supported. + /// + /// POSIX equivalent: EOPNOTSUPP + not-supported, + /// One of the arguments is invalid. + /// + /// POSIX equivalent: EINVAL + invalid-argument, + /// Not enough memory to complete the operation. + /// + /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY + out-of-memory, + /// The operation timed out before it could finish completely. + timeout, + /// This operation is incompatible with another asynchronous operation that is already in progress. + /// + /// POSIX equivalent: EALREADY + concurrency-conflict, + /// Trying to finish an asynchronous operation that: + /// - has not been started yet, or: + /// - was already finished by a previous `finish-*` call. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + not-in-progress, + /// The operation has been aborted because it could not be completed immediately. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + would-block, + /// The operation is not valid in the socket's current state. + invalid-state, + /// A new socket resource could not be created because of a system limit. + new-socket-limit, + /// A bind operation failed because the provided address is not an address that the `network` can bind to. + address-not-bindable, + /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. + address-in-use, + /// The remote address is not reachable + remote-unreachable, + /// The TCP connection was forcefully rejected + connection-refused, + /// The TCP connection was reset. + connection-reset, + /// A TCP connection was aborted. + connection-aborted, + /// The size of a datagram sent to a UDP socket exceeded the maximum + /// supported size. + datagram-too-large, + /// Name does not exist or has no suitable associated IP addresses. + name-unresolvable, + /// A temporary failure in name resolution occurred. + temporary-resolver-failure, + /// A permanent failure in name resolution occurred. + permanent-resolver-failure, + } + + @since(version = 0.2.0) + enum ip-address-family { + /// Similar to `AF_INET` in POSIX. + ipv4, + /// Similar to `AF_INET6` in POSIX. + ipv6, + } + + @since(version = 0.2.0) + type ipv4-address = tuple; + + @since(version = 0.2.0) + type ipv6-address = tuple; + + @since(version = 0.2.0) + variant ip-address { + ipv4(ipv4-address), + ipv6(ipv6-address), + } + + @since(version = 0.2.0) + record ipv4-socket-address { + /// sin_port + port: u16, + /// sin_addr + address: ipv4-address, + } + + @since(version = 0.2.0) + record ipv6-socket-address { + /// sin6_port + port: u16, + /// sin6_flowinfo + flow-info: u32, + /// sin6_addr + address: ipv6-address, + /// sin6_scope_id + scope-id: u32, + } + + @since(version = 0.2.0) + variant ip-socket-address { + ipv4(ipv4-socket-address), + ipv6(ipv6-socket-address), + } + + /// Attempts to extract a network-related `error-code` from the stream + /// `error` provided. + /// + /// Stream operations which return `stream-error::last-operation-failed` + /// have a payload with more information about the operation that failed. + /// This payload can be passed through to this function to see if there's + /// network-related information about the error to return. + /// + /// Note that this function is fallible because not all stream-related + /// errors are network-related errors. + @unstable(feature = network-error-code) + network-error-code: func(err: borrow) -> option; +} + +/// This interface provides a value-export of the default network handle.. +@since(version = 0.2.0) +interface instance-network { + @since(version = 0.2.0) + use network.{network}; + + /// Get a handle to the default network. + @since(version = 0.2.0) + instance-network: func() -> network; +} + +@since(version = 0.2.0) +interface ip-name-lookup { + @since(version = 0.2.0) + use wasi:io/poll@0.2.6.{pollable}; + @since(version = 0.2.0) + use network.{network, error-code, ip-address}; + + @since(version = 0.2.0) + resource resolve-address-stream { + /// Returns the next address from the resolver. + /// + /// This function should be called multiple times. On each call, it will + /// return the next address in connection order preference. If all + /// addresses have been exhausted, this function returns `none`. + /// + /// This function never returns IPv4-mapped IPv6 addresses. + /// + /// # Typical errors + /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) + /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) + /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) + /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) + @since(version = 0.2.0) + resolve-next-address: func() -> result, error-code>; + /// Create a `pollable` which will resolve once the stream is ready for I/O. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + /// Resolve an internet host name to a list of IP addresses. + /// + /// Unicode domain names are automatically converted to ASCII using IDNA encoding. + /// If the input is an IP address string, the address is parsed and returned + /// as-is without making any external requests. + /// + /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. + /// + /// This function never blocks. It either immediately fails or immediately + /// returns successfully with a `resolve-address-stream` that can be used + /// to (asynchronously) fetch the results. + /// + /// # Typical errors + /// - `invalid-argument`: `name` is a syntactically invalid domain name or IP address. + /// + /// # References: + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + resolve-addresses: func(network: borrow, name: string) -> result; +} + +@since(version = 0.2.0) +interface tcp { + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{input-stream, output-stream}; + @since(version = 0.2.0) + use wasi:io/poll@0.2.6.{pollable}; + @since(version = 0.2.0) + use wasi:clocks/monotonic-clock@0.2.6.{duration}; + @since(version = 0.2.0) + use network.{network, error-code, ip-socket-address, ip-address-family}; + + @since(version = 0.2.0) + enum shutdown-type { + /// Similar to `SHUT_RD` in POSIX. + receive, + /// Similar to `SHUT_WR` in POSIX. + send, + /// Similar to `SHUT_RDWR` in POSIX. + both, + } + + /// A TCP socket resource. + /// + /// The socket can be in one of the following states: + /// - `unbound` + /// - `bind-in-progress` + /// - `bound` (See note below) + /// - `listen-in-progress` + /// - `listening` + /// - `connect-in-progress` + /// - `connected` + /// - `closed` + /// See + /// for more information. + /// + /// Note: Except where explicitly mentioned, whenever this documentation uses + /// the term "bound" without backticks it actually means: in the `bound` state *or higher*. + /// (i.e. `bound`, `listen-in-progress`, `listening`, `connect-in-progress` or `connected`) + /// + /// In addition to the general error codes documented on the + /// `network::error-code` type, TCP socket methods may always return + /// `error(invalid-state)` when in the `closed` state. + @since(version = 0.2.0) + resource tcp-socket { + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// + /// Bind can be attempted multiple times on the same socket, even with + /// different arguments on each iteration. But never concurrently and + /// only as long as the previous bind failed. Once a bind succeeds, the + /// binding can't be changed anymore. + /// + /// # Typical errors + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL) + /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address. (EINVAL) + /// - `invalid-state`: The socket is already bound. (EINVAL) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT + /// state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR + /// socket option should be set implicitly on all platforms, except on Windows where this is the default behavior + /// and SO_REUSEADDR performs something different entirely. + /// + /// Unlike in POSIX, in WASI the bind operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `bind` as part of either `start-bind` or `finish-bind`. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + @since(version = 0.2.0) + finish-bind: func() -> result<_, error-code>; + /// Connect to a remote endpoint. + /// + /// On success: + /// - the socket is transitioned into the `connected` state. + /// - a pair of streams is returned that can be used to read & write to the connection + /// + /// After a failed connection attempt, the socket will be in the `closed` + /// state and the only valid action left is to `drop` the socket. A single + /// socket can not be used to connect more than once. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS) + /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address. (EINVAL, EADDRNOTAVAIL on Illumos) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN) + /// - `invalid-state`: The socket is already in the `listening` state. (EOPNOTSUPP, EINVAL on Windows) + /// - `timeout`: Connection timed out. (ETIMEDOUT) + /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) + /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `connection-aborted`: The connection was aborted. (ECONNABORTED) + /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `not-in-progress`: A connect operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// The POSIX equivalent of `start-connect` is the regular `connect` syscall. + /// Because all WASI sockets are non-blocking this is expected to return + /// EINPROGRESS, which should be translated to `ok()` in WASI. + /// + /// The POSIX equivalent of `finish-connect` is a `poll` for event `POLLOUT` + /// with a timeout of 0 on the socket descriptor. Followed by a check for + /// the `SO_ERROR` socket option, in case the poll signaled readiness. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; + @since(version = 0.2.0) + finish-connect: func() -> result, error-code>; + /// Start listening for new connections. + /// + /// Transitions the socket into the `listening` state. + /// + /// Unlike POSIX, the socket must already be explicitly bound. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. (EDESTADDRREQ) + /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN, EINVAL on BSD) + /// - `invalid-state`: The socket is already in the `listening` state. + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// - `not-in-progress`: A listen operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// Unlike in POSIX, in WASI the listen operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `listen` as part of either `start-listen` or `finish-listen`. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + start-listen: func() -> result<_, error-code>; + @since(version = 0.2.0) + finish-listen: func() -> result<_, error-code>; + /// Accept a new client socket. + /// + /// The returned socket is bound and in the `connected` state. The following properties are inherited from the listener socket: + /// - `address-family` + /// - `keep-alive-enabled` + /// - `keep-alive-idle-time` + /// - `keep-alive-interval` + /// - `keep-alive-count` + /// - `hop-limit` + /// - `receive-buffer-size` + /// - `send-buffer-size` + /// + /// On success, this function returns the newly accepted client socket along with + /// a pair of streams that can be used to read & write to the connection. + /// + /// # Typical errors + /// - `invalid-state`: Socket is not in the `listening` state. (EINVAL) + /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) + /// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + accept: func() -> result, error-code>; + /// Get the bound local address. + /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + local-address: func() -> result; + /// Get the remote address. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + remote-address: func() -> result; + /// Whether the socket is in the `listening` state. + /// + /// Equivalent to the SO_ACCEPTCONN socket option. + @since(version = 0.2.0) + is-listening: func() -> bool; + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + @since(version = 0.2.0) + address-family: func() -> ip-address-family; + /// Hints the desired listen queue size. Implementations are free to ignore this. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// + /// # Typical errors + /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. + /// - `invalid-argument`: (set) The provided value was 0. + /// - `invalid-state`: (set) The socket is in the `connect-in-progress` or `connected` state. + @since(version = 0.2.0) + set-listen-backlog-size: func(value: u64) -> result<_, error-code>; + /// Enables or disables keepalive. + /// + /// The keepalive behavior can be adjusted using: + /// - `keep-alive-idle-time` + /// - `keep-alive-interval` + /// - `keep-alive-count` + /// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true. + /// + /// Equivalent to the SO_KEEPALIVE socket option. + @since(version = 0.2.0) + keep-alive-enabled: func() -> result; + @since(version = 0.2.0) + set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; + /// Amount of time the connection has to be idle before TCP starts sending keepalive packets. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS) + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + keep-alive-idle-time: func() -> result; + @since(version = 0.2.0) + set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; + /// The time between keepalive packets. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPINTVL socket option. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + keep-alive-interval: func() -> result; + @since(version = 0.2.0) + set-keep-alive-interval: func(value: duration) -> result<_, error-code>; + /// The maximum amount of keepalive packets TCP should send before aborting the connection. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPCNT socket option. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + keep-alive-count: func() -> result; + @since(version = 0.2.0) + set-keep-alive-count: func(value: u32) -> result<_, error-code>; + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + @since(version = 0.2.0) + hop-limit: func() -> result; + @since(version = 0.2.0) + set-hop-limit: func(value: u8) -> result<_, error-code>; + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + receive-buffer-size: func() -> result; + @since(version = 0.2.0) + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + @since(version = 0.2.0) + send-buffer-size: func() -> result; + @since(version = 0.2.0) + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + /// Create a `pollable` which can be used to poll for, or block on, + /// completion of any of the asynchronous operations of this socket. + /// + /// When `finish-bind`, `finish-listen`, `finish-connect` or `accept` + /// return `error(would-block)`, this pollable can be used to wait for + /// their success or failure, after which the method can be retried. + /// + /// The pollable is not limited to the async operation that happens to be + /// in progress at the time of calling `subscribe` (if any). Theoretically, + /// `subscribe` only has to be called once per socket and can then be + /// (re)used for the remainder of the socket's lifetime. + /// + /// See + /// for more information. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + /// Initiate a graceful shutdown. + /// + /// - `receive`: The socket is not expecting to receive any data from + /// the peer. The `input-stream` associated with this socket will be + /// closed. Any data still in the receive queue at time of calling + /// this method will be discarded. + /// - `send`: The socket has no more data to send to the peer. The `output-stream` + /// associated with this socket will be closed and a FIN packet will be sent. + /// - `both`: Same effect as `receive` & `send` combined. + /// + /// This function is idempotent; shutting down a direction more than once + /// has no effect and returns `ok`. + /// + /// The shutdown function does not close (drop) the socket. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>; + } +} + +@since(version = 0.2.0) +interface tcp-create-socket { + @since(version = 0.2.0) + use network.{network, error-code, ip-address-family}; + @since(version = 0.2.0) + use tcp.{tcp-socket}; + + /// Create a new TCP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. + /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// + /// This function does not require a network capability handle. This is considered to be safe because + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` + /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + create-tcp-socket: func(address-family: ip-address-family) -> result; +} + +@since(version = 0.2.0) +interface udp { + @since(version = 0.2.0) + use wasi:io/poll@0.2.6.{pollable}; + @since(version = 0.2.0) + use network.{network, error-code, ip-socket-address, ip-address-family}; + + /// A received datagram. + @since(version = 0.2.0) + record incoming-datagram { + /// The payload. + /// + /// Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. + data: list, + /// The source address. + /// + /// This field is guaranteed to match the remote address the stream was initialized with, if any. + /// + /// Equivalent to the `src_addr` out parameter of `recvfrom`. + remote-address: ip-socket-address, + } + + /// A datagram to be sent out. + @since(version = 0.2.0) + record outgoing-datagram { + /// The payload. + data: list, + /// The destination address. + /// + /// The requirements on this field depend on how the stream was initialized: + /// - with a remote address: this field must be None or match the stream's remote address exactly. + /// - without a remote address: this field is required. + /// + /// If this value is None, the send operation is equivalent to `send` in POSIX. Otherwise it is equivalent to `sendto`. + remote-address: option, + } + + /// A UDP socket handle. + @since(version = 0.2.0) + resource udp-socket { + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the port is zero, the socket will be bound to a random free port. + /// + /// # Typical errors + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-state`: The socket is already bound. (EINVAL) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// Unlike in POSIX, in WASI the bind operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `bind` as part of either `start-bind` or `finish-bind`. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + @since(version = 0.2.0) + finish-bind: func() -> result<_, error-code>; + /// Set up inbound & outbound communication channels, optionally to a specific peer. + /// + /// This function only changes the local socket configuration and does not generate any network traffic. + /// On success, the `remote-address` of the socket is updated. The `local-address` may be updated as well, + /// based on the best network path to `remote-address`. + /// + /// When a `remote-address` is provided, the returned streams are limited to communicating with that specific peer: + /// - `send` can only be used to send to this destination. + /// - `receive` will only return datagrams sent from the provided `remote-address`. + /// + /// This method may be called multiple times on the same socket to change its association, but + /// only the most recently returned pair of streams will be operational. Implementations may trap if + /// the streams returned by a previous invocation haven't been dropped yet before calling `stream` again. + /// + /// The POSIX equivalent in pseudo-code is: + /// ```text + /// if (was previously connected) { + /// connect(s, AF_UNSPEC) + /// } + /// if (remote_address is Some) { + /// connect(s, remote_address) + /// } + /// ``` + /// + /// Unlike in POSIX, the socket must already be explicitly bound. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-state`: The socket is not bound. + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + %stream: func(remote-address: option) -> result, error-code>; + /// Get the current bound address. + /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + local-address: func() -> result; + /// Get the address the socket is currently streaming to. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not streaming to a specific remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + remote-address: func() -> result; + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + @since(version = 0.2.0) + address-family: func() -> ip-address-family; + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + @since(version = 0.2.0) + unicast-hop-limit: func() -> result; + @since(version = 0.2.0) + set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + receive-buffer-size: func() -> result; + @since(version = 0.2.0) + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + @since(version = 0.2.0) + send-buffer-size: func() -> result; + @since(version = 0.2.0) + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + /// Create a `pollable` which will resolve once the socket is ready for I/O. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + @since(version = 0.2.0) + resource incoming-datagram-stream { + /// Receive messages on the socket. + /// + /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. + /// The returned list may contain fewer elements than requested, but never more. + /// + /// This function returns successfully with an empty list when either: + /// - `max-results` is 0, or: + /// - `max-results` is greater than 0, but no results are immediately available. + /// This function never returns `error(would-block)`. + /// + /// # Typical errors + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + receive: func(max-results: u64) -> result, error-code>; + /// Create a `pollable` which will resolve once the stream is ready to receive again. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + @since(version = 0.2.0) + resource outgoing-datagram-stream { + /// Check readiness for sending. This function never blocks. + /// + /// Returns the number of datagrams permitted for the next call to `send`, + /// or an error. Calling `send` with more datagrams than this function has + /// permitted will trap. + /// + /// When this function returns ok(0), the `subscribe` pollable will + /// become ready when this function will report at least ok(1), or an + /// error. + /// + /// Never returns `would-block`. + check-send: func() -> result; + /// Send messages on the socket. + /// + /// This function attempts to send all provided `datagrams` on the socket without blocking and + /// returns how many messages were actually sent (or queued for sending). This function never + /// returns `error(would-block)`. If none of the datagrams were able to be sent, `ok(0)` is returned. + /// + /// This function semantically behaves the same as iterating the `datagrams` list and sequentially + /// sending each individual datagram until either the end of the list has been reached or the first error occurred. + /// If at least one datagram has been sent successfully, this function never returns an error. + /// + /// If the input list is empty, the function returns `ok(0)`. + /// + /// Each call to `send` must be permitted by a preceding `check-send`. Implementations must trap if + /// either `check-send` was not called or `datagrams` contains more items than `check-send` permitted. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The socket is in "connected" mode and `remote-address` is `some` value that does not match the address passed to `stream`. (EISCONN) + /// - `invalid-argument`: The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + send: func(datagrams: list) -> result; + /// Create a `pollable` which will resolve once the stream is ready to send again. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } +} + +@since(version = 0.2.0) +interface udp-create-socket { + @since(version = 0.2.0) + use network.{network, error-code, ip-address-family}; + @since(version = 0.2.0) + use udp.{udp-socket}; + + /// Create a new UDP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. + /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// + /// This function does not require a network capability handle. This is considered to be safe because + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called, + /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References: + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + create-udp-socket: func(address-family: ip-address-family) -> result; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import wasi:io/error@0.2.6; + @since(version = 0.2.0) + import network; + @since(version = 0.2.0) + import instance-network; + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import udp; + @since(version = 0.2.0) + import udp-create-socket; + @since(version = 0.2.0) + import wasi:io/streams@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/monotonic-clock@0.2.6; + @since(version = 0.2.0) + import tcp; + @since(version = 0.2.0) + import tcp-create-socket; + @since(version = 0.2.0) + import ip-name-lookup; +} diff --git a/crates/cpex-wasm-host/wit/world.wit b/crates/cpex-wasm-host/wit/world.wit new file mode 100644 index 00000000..0990f8b1 --- /dev/null +++ b/crates/cpex-wasm-host/wit/world.wit @@ -0,0 +1,231 @@ +// Location: ./crates/cpex-wasm-host/wit/world.wit +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Shriti Priya +// +// WIT world definition for the CPEX plugin component. +// Defines the types and exported function that the WASM host uses to invoke plugins. + +package cpex:plugin; + +interface types { + enum role { + system, + developer, + user, + assistant, + tool, + } + + enum channel { + analysis, + commentary, + final, + } + + enum resource-type { + file, + blob, + uri, + database, + api, + memory, + artifact, + } + + enum subject-type { + user, + agent, + service, + system, + } + + record image-source { + source-type: string, + data: string, + media-type: option, + } + + record video-source { + source-type: string, + data: string, + media-type: option, + duration-ms: option, + } + + record audio-source { + source-type: string, + data: string, + media-type: option, + duration-ms: option, + } + + record document-source { + source-type: string, + data: string, + media-type: option, + title: option, + } + + record tool-call { + tool-call-id: string, + name: string, + arguments: string, + namespace: option, + } + + record tool-result { + tool-call-id: string, + tool-name: string, + content: string, + is-error: bool, + } + + record cmf-resource { + resource-request-id: string, + uri: string, + name: option, + description: option, + resource-type: resource-type, + content: option, + blob: option>, + mime-type: option, + size-bytes: option, + annotations: string, + version: option, + } + + record resource-reference { + resource-request-id: string, + uri: string, + name: option, + resource-type: resource-type, + range-start: option, + range-end: option, + selector: option, + } + + record prompt-request { + prompt-request-id: string, + name: string, + arguments: string, + server-id: option, + } + + record prompt-result { + prompt-request-id: string, + prompt-name: string, + messages: string, + content: option, + is-error: bool, + error-message: option, + } + + variant content-part { + text(string), + thinking(string), + tool-call(tool-call), + tool-result(tool-result), + cmf-resource(cmf-resource), + resource-ref(resource-reference), + prompt-request(prompt-request), + prompt-result(prompt-result), + image(image-source), + video(video-source), + audio(audio-source), + document(document-source), + } + + record message { + schema-version: string, + role: role, + content: list, + channel: option, + } + + record message-payload { + message: message, + } + + record request-extension { + environment: option, + request-id: option, + timestamp: option, + trace-id: option, + span-id: option, + } + + record subject-extension { + id: option, + subject-type: option, + roles: list, + permissions: list, + teams: list, + claims: list>, + } + + record security-extension { + labels: list, + classification: option, + subject: option, + auth-method: option, + } + + record http-extension { + request-headers: list>, + response-headers: list>, + } + + record meta-extension { + entity-type: option, + entity-name: option, + tags: list, + scope: option, + properties: list>, + } + + record extensions { + request: option, + security: option, + http: option, + meta: option, + } + + record plugin-context { + local-state: string, + global-state: string, + } + + record plugin-violation { + code: string, + reason: string, + description: option, + details: string, + proto-error-code: option, + } + + record plugin-result { + continue-processing: bool, + modified-payload: option, + modified-extensions: option, + violation: option, + metadata: option, + } +} + +world plugin { + import wasi:io/poll@0.2.6; + import wasi:io/error@0.2.6; + import wasi:io/streams@0.2.6; + import wasi:clocks/monotonic-clock@0.2.6; + import wasi:http/types@0.2.6; + import wasi:http/outgoing-handler@0.2.6; + + use types.{message-payload, extensions, plugin-context, plugin-result}; + + export handle-hook: func( + payload: message-payload, + extensions: extensions, + ctx: plugin-context + ) -> plugin-result; +} diff --git a/crates/cpex-wasm-plugin/Cargo.lock b/crates/cpex-wasm-plugin/Cargo.lock new file mode 100644 index 00000000..27f96516 --- /dev/null +++ b/crates/cpex-wasm-plugin/Cargo.lock @@ -0,0 +1,787 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cpex-payload" +version = "0.1.0" +dependencies = [ + "async-trait", + "serde", + "serde_json", + "serde_yaml", + "thiserror", + "tokio", + "tracing", + "uuid", + "wildmatch", +] + +[[package]] +name = "cpex-wasm-plugin" +version = "0.1.0" +dependencies = [ + "cpex-payload", + "serde", + "serde_json", + "wit-bindgen 0.57.1", + "wit-bindgen-rt", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "foldhash 0.2.0", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "macro-string" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a9dbbfc75d2688ed057456ce8a3ee3f48d12eec09229f560f3643b9f275653" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "uuid" +version = "1.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" +dependencies = [ + "getrandom", + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser 0.244.0", +] + +[[package]] +name = "wasm-encoder" +version = "0.247.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b6733b8b91d010a6ac5b0fb237dc46a19650bc4c67db66857e2e787d437204" +dependencies = [ + "leb128fmt", + "wasmparser 0.247.0", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder 0.244.0", + "wasmparser 0.244.0", +] + +[[package]] +name = "wasm-metadata" +version = "0.247.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "665fe59e56cc9b419ca6fcca56673e3421d1a5011e3b65caf6b726fd9e041d10" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder 0.247.0", + "wasmparser 0.247.0", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "wasmparser" +version = "0.247.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6fb4c2bee46c5ea4d40f8cdb5c131725cd976718ec56f1c8e82fbde5fa2a80" +dependencies = [ + "bitflags", + "hashbrown 0.17.1", + "indexmap", + "semver", +] + +[[package]] +name = "wildmatch" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29333c3ea1ba8b17211763463ff24ee84e41c78224c16b001cd907e663a38c68" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro 0.51.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" +dependencies = [ + "bitflags", + "wit-bindgen-rust-macro 0.57.1", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser 0.244.0", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02dee27a2dc20d1008016c742ec9fc6ea498492994ba3750be7454cbc97ff04c" +dependencies = [ + "anyhow", + "heck", + "wit-parser 0.247.0", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653c85dd7aee6fe6f4bded0d242406deadae9819029ce6f7d258c920c384358a" + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata 0.244.0", + "wit-bindgen-core 0.51.0", + "wit-component 0.244.0", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5007dae772945b7a5003d69d90a3a4a78929d41f19d004e980c4259a6af4484" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata 0.247.0", + "wit-bindgen-core 0.57.1", + "wit-component 0.247.0", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core 0.51.0", + "wit-bindgen-rust 0.51.0", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af9237d678e3513ad24e96fe98beacdc0db6405284ba2a2400418cf0d42caa89" +dependencies = [ + "anyhow", + "macro-string", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core 0.57.1", + "wit-bindgen-rust 0.57.1", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.244.0", + "wasm-metadata 0.244.0", + "wasmparser 0.244.0", + "wit-parser 0.244.0", +] + +[[package]] +name = "wit-component" +version = "0.247.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d567162a6b9843080e5e0053f696623ff694bae8ae017c9ec536d1873bbe3d8" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.247.0", + "wasm-metadata 0.247.0", + "wasmparser 0.247.0", + "wit-parser 0.247.0", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.244.0", +] + +[[package]] +name = "wit-parser" +version = "0.247.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ffe4064318cdf3c08cb99343b44c039fcefe61ccdf58aa9975285f13d74d1fc" +dependencies = [ + "anyhow", + "hashbrown 0.17.1", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.247.0", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/crates/cpex-wasm-plugin/Cargo.toml b/crates/cpex-wasm-plugin/Cargo.toml new file mode 100644 index 00000000..0ebf8f68 --- /dev/null +++ b/crates/cpex-wasm-plugin/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "cpex-wasm-plugin" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen = "0.57" +wit-bindgen-rt = "0.44" +serde = { version = "1", features = ["derive", "rc"] } +serde_json = "1" +cpex-payload = { path = "../cpex-payload" } + +[package.metadata.component] +package = "cpex:plugin" diff --git a/crates/cpex-wasm-plugin/Makefile b/crates/cpex-wasm-plugin/Makefile new file mode 100644 index 00000000..50d6e0a7 --- /dev/null +++ b/crates/cpex-wasm-plugin/Makefile @@ -0,0 +1,21 @@ +.PHONY: build validate inspect clean + +TARGET = wasm32-wasip2 +WORKSPACE_TARGET_DIR = target +WASM_HOST_DIR = ../cpex-wasm-host/wasm + +build: + cargo build --target $(TARGET) --release + cp $(WORKSPACE_TARGET_DIR)/$(TARGET)/release/cpex_wasm_plugin.wasm plugin.wasm + cp plugin.wasm $(WASM_HOST_DIR)/ + +validate: plugin.wasm + wasm-tools validate plugin.wasm + @echo "plugin.wasm is valid" + +inspect: plugin.wasm + wasm-tools component wit plugin.wasm + +clean: + cargo clean + rm -f plugin.wasm diff --git a/crates/cpex-wasm-plugin/README.md b/crates/cpex-wasm-plugin/README.md new file mode 100644 index 00000000..aae0005e --- /dev/null +++ b/crates/cpex-wasm-plugin/README.md @@ -0,0 +1,215 @@ +# cpex-wasm-plugin + +A WebAssembly (WASM) component that compiles CPEX plugins into a portable `plugin.wasm` binary. The host (`cpex-wasm-host`) loads and executes this component at runtime, enabling sandboxed plugin execution across platforms. + +## How It Works + +1. **WIT Interface (`wit/world.wit`)** defines the contract between host and plugin. The plugin exports a single function: + + ```wit + export handle-hook: func( + payload: message-payload, + extensions: extensions, + ctx: plugin-context + ) -> plugin-result; + ``` + + The host calls `handle-hook` with a CMF message payload, extensions (security, HTTP, meta, request), and plugin context. The plugin returns a result indicating whether to allow/deny processing, optionally with modified payload or extensions. + +2. **Plugin Logic (`src/lib.rs`)** implements the `Guest` trait generated by `wit-bindgen`. It converts WIT types to native Rust types, delegates to the plugin logic in `cpex-payload`, and converts the result back to WIT types. + +3. **Type Conversions (`src/conversions.rs`)** handles bidirectional mapping between WIT-generated types and native `cpex-payload` types (MessagePayload, Extensions, PluginContext, PluginResult). + +## Why `conversions.rs` Exists + +The WASM boundary cannot pass rich Rust types directly. WIT only supports simple primitives, lists, tuples, strings, and records. The native `cpex-payload` types use `HashMap`, `HashSet`, `Arc`, `serde_json::Value`, etc. — none of which can cross the WASM boundary as-is. + +`conversions.rs` bridges this gap so plugin authors can write normal Rust using native types: + +| Native Rust type | WIT representation | +|---|---| +| `HashMap` | `string` (JSON-serialized) | +| `HashSet` | `list` | +| `HashMap` | `list>` | +| `MonotonicSet` | `list` | +| `Arc` | `security-extension` (flat record) | +| `serde_json::Value` | `string` (JSON-serialized) | +| `Vec` | `string` (JSON-serialized) | + +Without this layer, you'd have to rewrite all plugin logic to work directly with WIT's flat types, losing access to the entire `cpex-payload` library. The conversion layer handles the impedance mismatch at the WASM boundary transparently. + +## Project Structure + +``` +cpex-wasm-plugin/ +├── Cargo.toml # Crate config (cdylib target, wit-bindgen deps) +├── Makefile # Build commands +├── src/ +│ ├── lib.rs # Plugin entry point (implements Guest trait) +│ └── conversions.rs # WIT <-> native type mappings +└── wit/ + ├── world.wit # Plugin interface definition + └── deps/ # WASI interface dependencies (io, http, clocks, etc.) +``` + +## Prerequisites + +- Rust toolchain with the `wasm32-wasip2` target: + ```sh + rustup target add wasm32-wasip2 + ``` +- `wasm-tools` CLI (for validation and inspection): + ```sh + cargo install wasm-tools + ``` + +## Building + +From this directory: + +```sh +make build +``` + +This will: +1. Compile the crate to `wasm32-wasip2` in release mode +2. Copy the output to `plugin.wasm` +3. Copy `plugin.wasm` to `../cpex-wasm-host/wasm/` for the host to load + +## Validating + +After building, verify that `plugin.wasm` is a well-formed WASM component: + +```sh +make validate +``` + +This runs `wasm-tools validate` on the binary. Silent output (no errors) means the component is valid. + +## Inspecting + +To see the full WIT interface embedded in the compiled component: + +```sh +make inspect +``` + +This prints all exported functions, imported interfaces, and type definitions. You should see: + +``` +export handle-hook: func(payload: message-payload, extensions: extensions, ctx: plugin-context) -> plugin-result; +``` + +This confirms the host will be able to call your plugin correctly. + +## Cleaning + +To clean build artifacts: + +```sh +make clean +``` + +## Writing a New Plugin + +You can write any plugin in `cpex-payload/src/plugins/` and invoke it from here. All plugin functions must follow the same signature: + +```rust +pub fn my_plugin( + payload: &MessagePayload, + extensions: &Extensions, + ctx: &PluginContext, +) -> PluginResult +``` + +### Step 1: Create your plugin in `cpex-payload` + +Add a new file at `crates/cpex-payload/src/plugins/my_plugin.rs`: + +```rust +use crate::cmf::message::MessagePayload; +use crate::context::PluginContext; +use crate::error::PluginViolation; +use crate::extensions::Extensions; +use crate::hooks::PluginResult; + +pub fn my_plugin( + payload: &MessagePayload, + extensions: &Extensions, + _ctx: &PluginContext, +) -> PluginResult { + // Your logic here — inspect payload/extensions, return allow or deny + PluginResult::allow() +} +``` + +Then export it in `crates/cpex-payload/src/plugins/mod.rs`: + +```rust +pub mod my_plugin; +``` + +### Step 2: Point `lib.rs` to your plugin + +In `crates/cpex-wasm-plugin/src/lib.rs`, change the function call: + +```rust +let result = cpex_payload::plugins::my_plugin::my_plugin( + &native_payload, + &native_extensions, + &native_ctx, +); +``` + +### Step 3: Build + +```sh +make build +``` + +This produces `plugin.wasm` ready for the host to load. + +### Example: Identity Checker + +The default plugin is the identity checker (`cpex-payload/src/plugins/identity_checker.rs`). It demonstrates: + +- Reading security extensions (labels, subject identity, roles) +- Distinguishing pre-invoke vs post-invoke by checking if the message contains tool calls or tool results +- Denying requests when the caller lacks the required role for PII data +- Returning `PluginResult::allow()` or `PluginResult::deny(violation)` + +```rust +pub fn identity_check( + payload: &MessagePayload, + extensions: &Extensions, + _ctx: &PluginContext, +) -> PluginResult { + let is_result = payload.message.is_tool_result(); + + if !is_result { + // PRE-INVOKE: check caller identity and roles + if let Some(ref security) = extensions.security { + if let Some(ref subject) = security.subject { + if security.has_label("PII") && !subject.roles.contains("hr_admin") { + return PluginResult::deny(PluginViolation::new( + "insufficient_role", + "Tool requires 'hr_admin' role for PII data", + )); + } + } + } + } + + PluginResult::allow() +} +``` + +This is wired up in `lib.rs` as: + +```rust +let result = cpex_payload::plugins::identity_checker::identity_check( + &native_payload, + &native_extensions, + &native_ctx, +); +``` diff --git a/crates/cpex-wasm-plugin/plugin.wasm b/crates/cpex-wasm-plugin/plugin.wasm new file mode 100644 index 0000000000000000000000000000000000000000..55472f0bc32b560e3fbc1b61ccc94b25d0a5c14c GIT binary patch literal 604424 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 z z)eVe+F98OO!dr*^&}Lt&zJG8&Gu8XG*d;o}fcLCRnO>fn{7-UCd1~_Snnd1 z4$|01>c-+}8%zJk|>nY?{T?Uj`z4!tLCm{*>L|aTVTZ-#g zn&J6!`K03?%6TPW9YZre96|8GxghL@8QByZnAM>qmdalfqx6bIP*qCmDb^8nMt@Xc zV=2bzQ$j#ZgcyNgtCzFk8g0l(qs^&J7+)N1UHN78kgD}uYko>CD+l9TUZHSvSC`Y5 zfr(6EC8;tG`KO+3l}_pACd7PB2tkn$h?+%A%ZNFJwFgnZl#~a5$7JiTi+DAC%vs(~ zs+l`HXzi%L4v+Y!x`@-6B#Q?>{O&LP$?yEiI}2>Uj4sFAx=!5!A{PQ=#@A2mB=@eS zGeDYi+Aff^0t@;d+Gu+>EzX(p40p3V&#ii@3`8*=mmwxBh9y5$|8!NAs6f|Y5y6k< z%_cG(+A{=BKm(yss@c(>p35c@QZgFs&z8TqeJ-C!C&GvMS@D!vL-vn8ldvt&4>1{c z^d|^|I-7!?DXfja0cP%5oW7wyXAxW(GzXuP&kTncg`GmsC9{wou^;bJ=#^<^3HO5e zpiC4~VNjL_28B*c7&8$8ga!v{_waxYSUJt10#Pe|)|3(oC_htlAv7MkJ|tnF%W`R=;-N$x(6zO<9H>+Jv9{R zCOc5n7K&gK9gN{Ejnm|8TCi89%dIt02#Xt4h2b%wfN-eH(46+ya)N}$iI#aWJjy@j z0zx-kAM-l?#?=jV+=(<$qlrRzf~IdCd8v#aoSva_dfk|_af`-yYdS<%^`trJB`^=Em&V&vZhV^~5snqjULZ4#ViUl*57 zh8m_~K+WL+)L0^|hQ$4Yi9{QqwSb*+X&~`^t$YFr+>t{#+;e@OLE6ElSbBlAJ==pWTJXKDj4D)kNv936m&z$>UpHIP6*X{rw!PP+m zvfS(0{dmCA0RRBvFFik(pszFrnCBKfu}tp}?;PFYg>eQ%b}v100G*Wu2}Mb&FL7|5 z32+J+D=-x=2Kd+ju|5GGKm#9O6>DqQW(C-+0OQ^oi4fRTdb z@rK+;P!MZ+C^J^NY&ynpEB z%Pf|Qyktx9xR}E{aFq4>r~6>mVmo1{PUF{rWe!+1128ToD2#LMG)WRI_t2!x%D>*9 z%!IFnGrIh*NT;6HQqSgG!dk4KznkOHiuGataeO~KeeXn~rJk6Xu9{Ivd)xt(`ea$@a=ihb~#)vRf3&wVaWyK%nMHwlm*a#_dOxfFGaRjSPZ zTUzw`lM)u4GLt?=t{FD-Q9ZS=A&4M+mVmli`WY+lGT+cWuOP;()TaI#Y4%CxJiT4c zY5`2#V)071_xu-E}6uj zff;M~1QEh+U-38iJA#q0Y4Z)02cQ9rsJ7@P)BSwlh;YR~ZH*Dme}C{gaztgT{E=Vo zPrOKLhl%nX83UCwP?sd(zyCsnX_S*^ftt=G2akMh??a#X!mi)YGOGMNz#SZCARosf6Yd3}*>V75Wb=*+fdEI2CU{$tJw_7SvF150iL9vm`x zP*a#ZjHICGPk|F=zPBy0|Y6@beo6?Wg_Or7ogRhw__(I*?l7$_128Oc&La>z|XVN6H z%}kb>KLnXcnJ--F!2ki!Ra$3hick2Z%hgP{0=Kb;Z5}W?M&W%_@la2q;G(y zE~lj}vfdF5|Hv5DeZn;nKcAaXd*rB$8^NYH0&WMFRXN?WM& zbe>UM*EfH&UcX~Qr0swS@jq#FLLXxi%o;42Ar5*9`fSn@whNYliXl7&{sqo7&|+XM zZ&|mpRbPyMb>E$)?OmeH2t;&x>vy=w1qK3_qIp)P?ui3~LJ3g;Cy$3cW)7xAUZ(}E_AfWwTeXl#)x=XS!MAR$ez=fAB#bi^&XJ901X@@6lj}$X)LJYjtOkUr%>hr z+}N6h%#^%ZLr)tlW0u?1%1jQHG3{ZsGIljc-66Zhezh{wgY9Ief`r+@GGc?PBHQl$ zs9=-WadR0HL#Ab|vNnlzf?RIEtd@GUwn`RPR~TsxApb;W4ddcP|1|IE)`oXMW<(5S zqXyc&VxaOYHmb^HHnJ(Ae`4jSbxr@o$^o&ejz#Xv=hCT$Tply?6ogGR_S>e4f-P*c zJ4&KA+Dx>S>*%!mN6~W83b9m}>n+s1YRb0Z=WzP@iH)}jYw%oN)A=bXCstUQUc3Ei zX|nNlrD6R+hqMpZysw%9!pqR{hpT2)rRaLMqvj>+vU>rR};uGzq(wyH^-?3Lu%kh5)K1 z%e>T5uY%n>22((6yJo&sX3S>RhEO@?Vb3zne5`@ErjIx65^*Nas9~gCr#$Dn^0;>& zcQ=-U4SY!K14={??3nyB^@j>U(zCP>No<>G?98*PtWsNA*9huV2qFOOFoNWFqY)FK zh7r@Ub6~_2Rd(eOOG+rC_xBm%YQqDJ(x@%2J#d~5wn?-RWb~u6t5M5 zG_0}TEYMuV8V?%S5ScWFBNZVWgBda+&l9!eTADFtF338vjt>l{Q#1ICgLNueLNTaW z7`WQ%ga5>xhQsbA^pMmgY1k_#`J5gQ-^tk$-_JVH3bNz~H#$bXWmo+sdVz_8@0<$cgn>r+mpBhmIyM z+EH|_DRqbnxo1BZk4{#P#BZ5jy~Ef+ZX15K&g&qhCog5!)LKDCYGG;Ts#b1FOJoI@ zo9}q32|s{E!3f*L2yEIGpmK|(zG7#|qNQH!hnxqrAsF#N9Te4mB`ZV}pCV&PFUtqW zq^JTsN|r+ISI=i)+;OZ;sWL}jB4Ov%z)XU-VD&x|%N6z2&spQy*J;zw1SoRBOcjxj zK*}uCVL{sA8>q>00UO*;fM>EI(FeYm8INVNXoa;gw?4KM7Mka{@3wzns*uFa;6{w% zOG&vjko3pxOj~kv*)%~|FRZYS(vyg!jI2;&ZEc49>r;(71k(D?M1r)-2Us497GY)p zgMha6ytX{rP=|hghJ55?6;8H}OU@3RGFJ)Tlh0L~jJT?;+R0UrG&v5jQ-*cROGE;m z5cN)$Z~WlCS7cD{e|ff{jA{0Svshv#Hha>X@jU3Mx#r zjRK0xgi<2}A9P{8wSy^U_s23V^PHh8WIPbNzy4}7P~a@vyKyfhfYCw@I`lbXc|l*b zgu!;3;`Y&>Qgr31MPXKd`+FOKmid0{y?%Y?|FLrqZ|dNOUi#YKc_3+K+p~KhAoCOf znI$i#JQ6eU;QqTL15nU%Aj-poyMdV2-0oH*g(bd`hk&NL%B$twSOX8jwB`Ew@w39yXsY_6M|gLuP+7gry4dSXmJY~^SBKhV8ti%et`JJHd6 zJFGiklUjGQK$YZ}FEhfLRF?g)?jVtTpw^cmnloX|!4j4gt1UU$on|?z7DE@BSoL+N z0;Pu4oa}9Ah#Ip}U=MSgK~#&@e?Q_1>A*br!~@LO*JqPEWQE2_1$rD>nAu8&yguvkyf9?S z8qWfr4YQ7)@e_iz?uTN8XDLe#mQ~8g2t>a754oA#E%6D1XM%F(5tJBMp2-c?ASu=* ziiAZJoxqNdIXjM;w$+r!p1~6}e1far3EgA!X}EjBkJ|L^*&$#7$87ZO`K`)Ujc@hT z{h~HQJP3>HDvKE)Bm@;1W@kGzW<(ho8*QRgomn1~EzN!&AM7Usy?vAMa)eVe-_e15 zjRQ@GA6i~w#T<{)tV0M0H&UXCwC8aWYCrrB%L%jlkybhxrAIyWKo;~^RH0{dHL}q% zx}qkAIj|=4X%5jD;enj1p&5I&pwJ!L&OsqHMN8z=F&@`ZKxi4Zm>uR*Mb9jBue4BC zv7}(dV8@nr($KvOszUd+Tp?va2y2a!0@8|Mpp8Yx7GB9C? z0si<(Nh~Qh2Ss!Rl0xOl5+(P zK!H}0#lM>PCDx7W5bG#FCayN#Dy0QQ3UXcC|3}GEKiAIvGt#+9+UZC)mGz8V=Kg?Z(N0Qs_y-N`m`na(CGQ- z=Rff6OqFws^(j}*F7vp`r&Zy>JKlWzzpJWWdi`7e=B-~!)xhLP19z1mKI# z9UV2WwC4wJ`HL^Ct(!Rb$s@;(eCZ{-)Cf0q5A|X!cEsn$)Sp2U*uht8q%-3ezwx4}DL3+|B0w?mai-+y-XLw=)N2ySyrpu4EiYiOYz`#u}>Sh~` z$KRj!@moDVKbGoZ5y>0-ZSmbt}b@%7OkC)TILBkAIGH}uyO zb2}F=zL~aL`}W{wIa{`I;J#;He^&MJzO%n}QA>aQcP!btm|Z!WE^>k2ivf1ci;Fo* z{^Y)$%6a96Vr>P_!Ck+!0ajti!YWLzw$SoI?qLC--+bq7cm4LazI)rR?y4awm&vBY zPv3s~AHU`P2X}uR|H}$LTU14F&&s8Xl?q4lP@fm1Se>75;f|T2<2c$%0G-A3m+sjE zJ*C{)^Ww#YB}Qi924%d|^Iy@(|C~BMePL39bo?9{yRp5pgmFFcQ*X4<+u5HjatCJp zPCE&g?E_Ve9pZ2&HwX3S{xe1~c)8Fyi^r597TU~tGtlP8O0Mwfl;BrlbkS z4M@m*QFp1?f64Bm^Pd@*Zh7B}=QV{+oY-yr=9g$epOA18>z2lGg}h=&zksO${|dmE zp~r_IGu;fAF{nFHPdW2|9wQ2W_2<8pURCz4+g{E=`uFr_0e>+GGB?bKNzDkG4B8Cw z$-u~0gME(jMjE;Hi_{mhJq1lLl|(yL4NIiy8)Oy6IAn|E)h`T-sH6{O4B&F;lJPvjxtuaX-Ur}r(seCfWW<2No(>|XpX-q%)Ss~FDG z5AxlM3pel4*<;?AZephmS4a zxclbY4s6+ZQGzZ}t|_`33fVtxo^-6|H@uiTxxg5UvXQRjpnYstEMaU{EQPLs*4P!z zm(UeG)?LXPUCA3=$*Zp9W4prLVKT4T&h3XW#KI01(>?$FF6Pa3+nF2ERhdd{X5X`e zIBvITg4oL+r}$~vOTN+lXHQ7x8s zHvS_p)Z&$~yk)KUY>_6NlBZ?+MRNG(+|aWGP&+E0(}A$3sFAN2%9YojWN*-?R{e={iS~(U5v#Xwe&mv)UvO(3?ju<6!Ipiz8m+7ugykDP79nFZ zbx_~cTpi%-SLP*7%Ua*x90LZ!x~$rBH`H9pBm+dFbi0RK?f(VH|`PbJE^x= z(Tn1x-HPB*Hme92=W%vLcg3>At_}!&^0$X(1UoEjvt6rAK>0*zGa^UYTSFBfw>TgH z*#?N}9XsbnTZr`{ec{2ue1Xq{rm=OU{C)+tBsoB6O6&9-Z!6F4uZ_+rxpI$Ida_DJ zz-3xzAq*NzvTr#tv1TZb``RlWFl3;_OB!a~FLhSpYCh5@`_}`XoRg#qeA1bioSN>= z%+9UptyLpRG;fw{BU>DS?PV(?=C#9vOrO?TJ$B59Ill2_r&7Y z5>A%<$6WC2Kb3l_6GJrco=z*H z`|~dwnp2<~g)+|KTYd6`C{?6smm$pXR}H6ex;Li1!PA0WiEK=~Niprio>s%c@;|11+S5X4E9ztp#wA)6&EuioNyntaUj}wt$8MS_}Os zqzJ>W^QLJSxl}^Ha$vDk4%AkQj@ZR0hNH`5JU<-}pO&xkp8OriE$_5J=!HnjKQq9a zJFYGv7}?X{SVG*w(LHUUKlfR2Y_zz;DINuKvS|1I${nEoy=sf2{hluyV*i%0DUZEF z*olq4AmZ5;k5C3QYWD&pM`k(_NSQ#FMv2W@IEtM?i;iA&Bd zQ;LPJnZ*R>UiKU|zDmO{okceAxpiqa@?T3c&I*NrVp%YOacZhO^m_tzerhqZEiQ#v zC~ht$_rL%FSKAu=*wz?3wQ)x*EwrZ%{t?aiYRC#$=#ZAShtw^VEaWE@28@A1=Etc~ z?sN2n&8`32sEd-?;nqnBS7kC`i_N~Mi}}6`Be2$}-ASH2rZ|}+mPUuSc�!XR5%& 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 native_msg::MessagePayload { + native_msg::MessagePayload { + message: wit_message_to_native(payload.message), + } +} + +/// Converts a WIT Message (role, content parts, channel) into native Message. +fn wit_message_to_native(msg: Message) -> native_msg::Message { + native_msg::Message { + schema_version: msg.schema_version, + role: wit_role_to_native(msg.role), + content: msg.content.into_iter().map(wit_content_part_to_native).collect(), + channel: msg.channel.map(wit_channel_to_native), + } +} + +/// Maps WIT Role enum to native Role enum. +fn wit_role_to_native(role: Role) -> native_enums::Role { + match role { + Role::System => native_enums::Role::System, + Role::Developer => native_enums::Role::Developer, + Role::User => native_enums::Role::User, + Role::Assistant => native_enums::Role::Assistant, + Role::Tool => native_enums::Role::Tool, + } +} + +/// Maps WIT Channel enum to native Channel enum. +fn wit_channel_to_native(channel: Channel) -> native_enums::Channel { + match channel { + Channel::Analysis => native_enums::Channel::Analysis, + Channel::Commentary => native_enums::Channel::Commentary, + Channel::Final => native_enums::Channel::Final, + } +} + +/// Converts each WIT ContentPart variant to its native equivalent. +/// Handles all 12 content types (text, thinking, tool-call, tool-result, etc.). +fn wit_content_part_to_native(part: ContentPart) -> native_content::ContentPart { + match part { + ContentPart::Text(text) => native_content::ContentPart::Text { text }, + ContentPart::Thinking(text) => native_content::ContentPart::Thinking { text }, + ContentPart::ToolCall(tc) => native_content::ContentPart::ToolCall { + content: wit_tool_call_to_native(tc), + }, + ContentPart::ToolResult(tr) => native_content::ContentPart::ToolResult { + content: wit_tool_result_to_native(tr), + }, + ContentPart::CmfResource(r) => native_content::ContentPart::Resource { + content: wit_resource_to_native(r), + }, + ContentPart::ResourceRef(rr) => native_content::ContentPart::ResourceRef { + content: wit_resource_ref_to_native(rr), + }, + ContentPart::PromptRequest(pr) => native_content::ContentPart::PromptRequest { + content: wit_prompt_request_to_native(pr), + }, + ContentPart::PromptResult(pr) => native_content::ContentPart::PromptResult { + content: wit_prompt_result_to_native(pr), + }, + ContentPart::Image(img) => native_content::ContentPart::Image { + content: native_content::ImageSource { + source_type: img.source_type, + data: img.data, + media_type: img.media_type, + }, + }, + ContentPart::Video(v) => native_content::ContentPart::Video { + content: native_content::VideoSource { + source_type: v.source_type, + data: v.data, + media_type: v.media_type, + duration_ms: v.duration_ms, + }, + }, + ContentPart::Audio(a) => native_content::ContentPart::Audio { + content: native_content::AudioSource { + source_type: a.source_type, + data: a.data, + media_type: a.media_type, + duration_ms: a.duration_ms, + }, + }, + ContentPart::Document(d) => native_content::ContentPart::Document { + content: native_content::DocumentSource { + source_type: d.source_type, + data: d.data, + media_type: d.media_type, + title: d.title, + }, + }, + } +} + +/// Deserializes the JSON arguments string into a HashMap for the native ToolCall. +fn wit_tool_call_to_native(tc: ToolCall) -> native_content::ToolCall { + let arguments: HashMap = + serde_json::from_str(&tc.arguments).unwrap_or_default(); + native_content::ToolCall { + tool_call_id: tc.tool_call_id, + name: tc.name, + arguments, + namespace: tc.namespace, + } +} + +/// Deserializes the JSON content string into a serde_json::Value for the native ToolResult. +fn wit_tool_result_to_native(tr: ToolResult) -> native_content::ToolResult { + let content: serde_json::Value = + serde_json::from_str(&tr.content).unwrap_or(serde_json::Value::String(tr.content.clone())); + native_content::ToolResult { + tool_call_id: tr.tool_call_id, + tool_name: tr.tool_name, + content, + is_error: tr.is_error, + } +} + +/// Converts a WIT CmfResource to native Resource, deserializing the annotations JSON string. +fn wit_resource_to_native(r: CmfResource) -> native_content::Resource { + let annotations: HashMap = + serde_json::from_str(&r.annotations).unwrap_or_default(); + native_content::Resource { + resource_request_id: r.resource_request_id, + uri: r.uri, + name: r.name, + description: r.description, + resource_type: wit_resource_type_to_native(r.resource_type), + content: r.content, + blob: r.blob, + mime_type: r.mime_type, + size_bytes: r.size_bytes, + annotations, + version: r.version, + } +} + +/// Maps WIT ResourceType enum to native ResourceType enum. +fn wit_resource_type_to_native(rt: ResourceType) -> native_enums::ResourceType { + match rt { + ResourceType::File => native_enums::ResourceType::File, + ResourceType::Blob => native_enums::ResourceType::Blob, + ResourceType::Uri => native_enums::ResourceType::Uri, + ResourceType::Database => native_enums::ResourceType::Database, + ResourceType::Api => native_enums::ResourceType::Api, + ResourceType::Memory => native_enums::ResourceType::Memory, + ResourceType::Artifact => native_enums::ResourceType::Artifact, + } +} + +/// Converts a WIT ResourceReference to native ResourceReference. +fn wit_resource_ref_to_native(rr: ResourceReference) -> native_content::ResourceReference { + native_content::ResourceReference { + resource_request_id: rr.resource_request_id, + uri: rr.uri, + name: rr.name, + resource_type: wit_resource_type_to_native(rr.resource_type), + range_start: rr.range_start, + range_end: rr.range_end, + selector: rr.selector, + } +} + +/// Converts a WIT PromptRequest to native, deserializing the arguments JSON string. +fn wit_prompt_request_to_native(pr: PromptRequest) -> native_content::PromptRequest { + let arguments: HashMap = + serde_json::from_str(&pr.arguments).unwrap_or_default(); + native_content::PromptRequest { + prompt_request_id: pr.prompt_request_id, + name: pr.name, + arguments, + server_id: pr.server_id, + } +} + +/// Converts a WIT PromptResult to native, deserializing the messages JSON string into Vec. +fn wit_prompt_result_to_native(pr: PromptResult) -> native_content::PromptResult { + let messages: Vec = + serde_json::from_str(&pr.messages).unwrap_or_default(); + native_content::PromptResult { + prompt_request_id: pr.prompt_request_id, + prompt_name: pr.prompt_name, + messages, + content: pr.content, + is_error: pr.is_error, + error_message: pr.error_message, + } +} + +// --------------------------------------------------------------------------- +// WIT → Native: Extensions +// --------------------------------------------------------------------------- + +/// Converts WIT Extensions to native Extensions, wrapping each sub-extension in Arc. +pub fn wit_extensions_to_native(ext: Extensions) -> NativeExtensions { + NativeExtensions { + request: ext.request.map(|r| Arc::new(wit_request_to_native(r))), + security: ext.security.map(|s| Arc::new(wit_security_to_native(s))), + http: ext.http.map(|h| Arc::new(wit_http_to_native(h))), + meta: ext.meta.map(|m| Arc::new(wit_meta_to_native(m))), + ..Default::default() + } +} + +/// Converts WIT RequestExtension (environment, request-id, timestamps, tracing) to native. +fn wit_request_to_native(r: RequestExtension) -> NativeRequestExtension { + NativeRequestExtension { + environment: r.environment, + request_id: r.request_id, + timestamp: r.timestamp, + trace_id: r.trace_id, + span_id: r.span_id, + } +} + +/// Converts WIT SecurityExtension to native, rebuilding MonotonicSet from the labels list. +fn wit_security_to_native(s: SecurityExtension) -> NativeSecurityExtension { + NativeSecurityExtension { + labels: MonotonicSet::from_set(s.labels.into_iter().collect()), + classification: s.classification, + subject: s.subject.map(wit_subject_to_native), + auth_method: s.auth_method, + ..Default::default() + } +} + +/// Converts WIT SubjectExtension to native, collecting lists into HashSets/HashMaps. +fn wit_subject_to_native(s: SubjectExtension) -> NativeSubjectExtension { + NativeSubjectExtension { + id: s.id, + subject_type: s.subject_type.map(wit_subject_type_to_native), + roles: s.roles.into_iter().collect::>(), + permissions: s.permissions.into_iter().collect::>(), + teams: s.teams.into_iter().collect::>(), + claims: s.claims.into_iter().collect::>(), + } +} + +/// Maps WIT SubjectType enum to native SubjectType enum. +fn wit_subject_type_to_native(st: SubjectType) -> NativeSubjectType { + match st { + SubjectType::User => NativeSubjectType::User, + SubjectType::Agent => NativeSubjectType::Agent, + SubjectType::Service => NativeSubjectType::Service, + SubjectType::System => NativeSubjectType::System, + } +} + +/// Converts WIT HttpExtension (list of tuples) to native (HashMap-based headers). +fn wit_http_to_native(h: HttpExtension) -> NativeHttpExtension { + NativeHttpExtension { + request_headers: h.request_headers.into_iter().collect::>(), + response_headers: h.response_headers.into_iter().collect::>(), + } +} + +/// Converts WIT MetaExtension to native, collecting lists into HashSets/HashMaps. +fn wit_meta_to_native(m: MetaExtension) -> NativeMetaExtension { + NativeMetaExtension { + entity_type: m.entity_type, + entity_name: m.entity_name, + tags: m.tags.into_iter().collect::>(), + scope: m.scope, + properties: m.properties.into_iter().collect::>(), + } +} + +// --------------------------------------------------------------------------- +// WIT → Native: PluginContext +// --------------------------------------------------------------------------- + +/// Deserializes the JSON state strings into HashMaps for the native PluginContext. +pub fn wit_context_to_native(ctx: PluginContext) -> NativePluginContext { + let local_state = serde_json::from_str(&ctx.local_state).unwrap_or_default(); + let global_state = serde_json::from_str(&ctx.global_state).unwrap_or_default(); + NativePluginContext { + local_state, + global_state, + } +} + +// --------------------------------------------------------------------------- +// Native → WIT: PluginResult → PluginResult (record) +// --------------------------------------------------------------------------- + +/// Converts the native PluginResult back to WIT, including any modified payload, +/// modified extensions, violation, or metadata. +pub fn native_result_to_wit(result: NativePluginResult) -> PluginResult { + PluginResult { + continue_processing: result.continue_processing, + modified_payload: result.modified_payload.map(native_payload_to_wit), + modified_extensions: result.modified_extensions.map(|ext| native_owned_extensions_to_wit(&ext)), + violation: result.violation.map(native_violation_to_wit), + metadata: result.metadata.map(|v| serde_json::to_string(&v).unwrap_or_default()), + } +} + +/// Converts a native PluginViolation to WIT, serializing details HashMap to JSON string. +fn native_violation_to_wit(v: NativePluginViolation) -> PluginViolation { + PluginViolation { + code: v.code, + reason: v.reason, + description: v.description, + details: serde_json::to_string(&v.details).unwrap_or_else(|_| "{}".to_string()), + proto_error_code: v.proto_error_code, + } +} + +// --------------------------------------------------------------------------- +// Native → WIT: MessagePayload +// --------------------------------------------------------------------------- + +/// Converts a native MessagePayload back to WIT format for the return value. +fn native_payload_to_wit(payload: native_msg::MessagePayload) -> MessagePayload { + MessagePayload { + message: native_message_to_wit(payload.message), + } +} + +/// Converts a native Message back to WIT Message. +fn native_message_to_wit(msg: native_msg::Message) -> Message { + Message { + schema_version: msg.schema_version, + role: native_role_to_wit(msg.role), + content: msg.content.into_iter().map(native_content_part_to_wit).collect(), + channel: msg.channel.map(native_channel_to_wit), + } +} + +/// Maps native Role enum to WIT Role enum. +fn native_role_to_wit(role: native_enums::Role) -> Role { + match role { + native_enums::Role::System => Role::System, + native_enums::Role::Developer => Role::Developer, + native_enums::Role::User => Role::User, + native_enums::Role::Assistant => Role::Assistant, + native_enums::Role::Tool => Role::Tool, + } +} + +/// Maps native Channel enum to WIT Channel enum. +fn native_channel_to_wit(channel: native_enums::Channel) -> Channel { + match channel { + native_enums::Channel::Analysis => Channel::Analysis, + native_enums::Channel::Commentary => Channel::Commentary, + native_enums::Channel::Final => Channel::Final, + } +} + +/// Converts each native ContentPart variant back to its WIT equivalent. +fn native_content_part_to_wit(part: native_content::ContentPart) -> ContentPart { + match part { + native_content::ContentPart::Text { text } => ContentPart::Text(text), + native_content::ContentPart::Thinking { text } => ContentPart::Thinking(text), + native_content::ContentPart::ToolCall { content } => { + ContentPart::ToolCall(native_tool_call_to_wit(content)) + } + native_content::ContentPart::ToolResult { content } => { + ContentPart::ToolResult(native_tool_result_to_wit(content)) + } + native_content::ContentPart::Resource { content } => { + ContentPart::CmfResource(native_resource_to_wit(content)) + } + native_content::ContentPart::ResourceRef { content } => { + ContentPart::ResourceRef(native_resource_ref_to_wit(content)) + } + native_content::ContentPart::PromptRequest { content } => { + ContentPart::PromptRequest(native_prompt_request_to_wit(content)) + } + native_content::ContentPart::PromptResult { content } => { + ContentPart::PromptResult(native_prompt_result_to_wit(content)) + } + native_content::ContentPart::Image { content } => ContentPart::Image(ImageSource { + source_type: content.source_type, + data: content.data, + media_type: content.media_type, + }), + native_content::ContentPart::Video { content } => ContentPart::Video(VideoSource { + source_type: content.source_type, + data: content.data, + media_type: content.media_type, + duration_ms: content.duration_ms, + }), + native_content::ContentPart::Audio { content } => ContentPart::Audio(AudioSource { + source_type: content.source_type, + data: content.data, + media_type: content.media_type, + duration_ms: content.duration_ms, + }), + native_content::ContentPart::Document { content } => { + ContentPart::Document(DocumentSource { + source_type: content.source_type, + data: content.data, + media_type: content.media_type, + title: content.title, + }) + } + } +} + +/// Serializes the native ToolCall arguments HashMap back to a JSON string for WIT. +fn native_tool_call_to_wit(tc: native_content::ToolCall) -> ToolCall { + ToolCall { + tool_call_id: tc.tool_call_id, + name: tc.name, + arguments: serde_json::to_string(&tc.arguments).unwrap_or_else(|_| "{}".to_string()), + namespace: tc.namespace, + } +} + +/// Serializes the native ToolResult content Value back to a JSON string for WIT. +fn native_tool_result_to_wit(tr: native_content::ToolResult) -> ToolResult { + ToolResult { + tool_call_id: tr.tool_call_id, + tool_name: tr.tool_name, + content: serde_json::to_string(&tr.content).unwrap_or_default(), + is_error: tr.is_error, + } +} + +/// Converts native Resource to WIT CmfResource, serializing annotations to JSON string. +fn native_resource_to_wit(r: native_content::Resource) -> CmfResource { + CmfResource { + resource_request_id: r.resource_request_id, + uri: r.uri, + name: r.name, + description: r.description, + resource_type: native_resource_type_to_wit(r.resource_type), + content: r.content, + blob: r.blob, + mime_type: r.mime_type, + size_bytes: r.size_bytes, + annotations: serde_json::to_string(&r.annotations).unwrap_or_else(|_| "{}".to_string()), + version: r.version, + } +} + +/// Maps native ResourceType enum to WIT ResourceType enum. +fn native_resource_type_to_wit(rt: native_enums::ResourceType) -> ResourceType { + match rt { + native_enums::ResourceType::File => ResourceType::File, + native_enums::ResourceType::Blob => ResourceType::Blob, + native_enums::ResourceType::Uri => ResourceType::Uri, + native_enums::ResourceType::Database => ResourceType::Database, + native_enums::ResourceType::Api => ResourceType::Api, + native_enums::ResourceType::Memory => ResourceType::Memory, + native_enums::ResourceType::Artifact => ResourceType::Artifact, + } +} + +/// Converts native ResourceReference to WIT ResourceReference. +fn native_resource_ref_to_wit(rr: native_content::ResourceReference) -> ResourceReference { + ResourceReference { + resource_request_id: rr.resource_request_id, + uri: rr.uri, + name: rr.name, + resource_type: native_resource_type_to_wit(rr.resource_type), + range_start: rr.range_start, + range_end: rr.range_end, + selector: rr.selector, + } +} + +/// Converts native PromptRequest to WIT, serializing arguments HashMap to JSON string. +fn native_prompt_request_to_wit(pr: native_content::PromptRequest) -> PromptRequest { + PromptRequest { + prompt_request_id: pr.prompt_request_id, + name: pr.name, + arguments: serde_json::to_string(&pr.arguments).unwrap_or_else(|_| "{}".to_string()), + server_id: pr.server_id, + } +} + +/// Converts native PromptResult to WIT, serializing messages Vec to JSON string. +fn native_prompt_result_to_wit(pr: native_content::PromptResult) -> PromptResult { + PromptResult { + prompt_request_id: pr.prompt_request_id, + prompt_name: pr.prompt_name, + messages: serde_json::to_string(&pr.messages).unwrap_or_else(|_| "[]".to_string()), + content: pr.content, + is_error: pr.is_error, + error_message: pr.error_message, + } +} + +// --------------------------------------------------------------------------- +// Native → WIT: OwnedExtensions → Extensions +// --------------------------------------------------------------------------- + +/// Converts native OwnedExtensions (used in modified results) back to WIT Extensions. +fn native_owned_extensions_to_wit( + ext: &cpex_payload::extensions::container::OwnedExtensions, +) -> Extensions { + Extensions { + request: ext.request.as_ref().map(|r| native_request_to_wit(r)), + security: ext.security.as_ref().map(|s| native_security_to_wit(s)), + http: ext.http.as_ref().map(|h| native_http_to_wit(h.read())), + meta: ext.meta.as_ref().map(|m| native_meta_to_wit(m)), + } +} + +/// Converts native RequestExtension to WIT RequestExtension. +fn native_request_to_wit(r: &NativeRequestExtension) -> RequestExtension { + RequestExtension { + environment: r.environment.clone(), + request_id: r.request_id.clone(), + timestamp: r.timestamp.clone(), + trace_id: r.trace_id.clone(), + span_id: r.span_id.clone(), + } +} + +/// Converts native SecurityExtension to WIT, collecting MonotonicSet labels into a list. +fn native_security_to_wit(s: &NativeSecurityExtension) -> SecurityExtension { + SecurityExtension { + labels: s.labels.iter().cloned().collect(), + classification: s.classification.clone(), + subject: s.subject.as_ref().map(native_subject_to_wit), + auth_method: s.auth_method.clone(), + } +} + +/// Converts native SubjectExtension to WIT, collecting HashSets into lists. +fn native_subject_to_wit(s: &NativeSubjectExtension) -> SubjectExtension { + SubjectExtension { + id: s.id.clone(), + subject_type: s.subject_type.as_ref().map(native_subject_type_to_wit), + roles: s.roles.iter().cloned().collect(), + permissions: s.permissions.iter().cloned().collect(), + teams: s.teams.iter().cloned().collect(), + claims: s.claims.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), + } +} + +/// Maps native SubjectType enum to WIT SubjectType enum. +fn native_subject_type_to_wit(st: &NativeSubjectType) -> SubjectType { + match st { + NativeSubjectType::User => SubjectType::User, + NativeSubjectType::Agent => SubjectType::Agent, + NativeSubjectType::Service => SubjectType::Service, + NativeSubjectType::System => SubjectType::System, + } +} + +/// Converts native HttpExtension (HashMaps) to WIT (list of tuples). +fn native_http_to_wit(h: &NativeHttpExtension) -> HttpExtension { + HttpExtension { + request_headers: h.request_headers.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), + response_headers: h.response_headers.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), + } +} + +/// Converts native MetaExtension (HashSets/HashMaps) to WIT (lists of strings/tuples). +fn native_meta_to_wit(m: &NativeMetaExtension) -> MetaExtension { + MetaExtension { + entity_type: m.entity_type.clone(), + entity_name: m.entity_name.clone(), + tags: m.tags.iter().cloned().collect(), + scope: m.scope.clone(), + properties: m.properties.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), + } +} diff --git a/crates/cpex-wasm-plugin/src/lib.rs b/crates/cpex-wasm-plugin/src/lib.rs new file mode 100644 index 00000000..5bab38d7 --- /dev/null +++ b/crates/cpex-wasm-plugin/src/lib.rs @@ -0,0 +1,78 @@ +// Location: ./crates/cpex-wasm-plugin/src/lib.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Shriti Priya +// +// CPEX WASM Plugin — Entry point for the WebAssembly plugin component. +// +// Implements the Guest trait generated by wit-bindgen, which the WASM host +// calls via the exported `handle-hook` function. Converts between WIT types +// and native cpex-payload types, delegating to the actual plugin logic. + +mod conversions; + +// Generate Rust bindings from the WIT world definition. +// This produces the `Guest` trait and all WIT types (MessagePayload, Extensions, etc.) +// that the plugin must implement and use. +wit_bindgen::generate!({ + path: "wit", + world: "plugin", + generate_all, +}); + +use conversions::{native_result_to_wit, wit_context_to_native, wit_extensions_to_native, wit_payload_to_native}; + +// --------------------------------------------------------------------------- +// Plugin Template +// +// To use a different plugin, change the function call below to point to your +// plugin function in cpex-payload. All plugin functions share the same signature: +// +// fn(&MessagePayload, &Extensions, &PluginContext) -> PluginResult +// +// Example: to use the audit logger instead, replace the identity_check call with: +// cpex_payload::plugins::audit_logger::audit_log(&native_payload, &native_extensions, &native_ctx) +// --------------------------------------------------------------------------- + +struct Plugin; + +impl Guest for Plugin { + /// Entry point called by the WASM host for each hook invocation. + /// Converts WIT types to native Rust types, delegates to the plugin + /// logic in cpex-payload, and converts the result back to WIT. + fn handle_hook( + payload: MessagePayload, + extensions: Extensions, + ctx: PluginContext, + ) -> PluginResult { + eprintln!("[WASM] handle_hook called"); + eprintln!("[WASM] payload content parts {:?} : with length {}",payload.message.content, payload.message.content.len()); + + // Convert the WIT MessagePayload to native cpex-payload MessagePayload + let native_payload = wit_payload_to_native(payload); + eprintln!("[WASM] payload converted to native={:?}",native_payload); + + // Convert the WIT Extensions to native Extensions (wrapped in Arc) + let native_extensions = wit_extensions_to_native(extensions); + eprintln!("[WASM] extensions converted to native={:?}",native_extensions); + + // Convert the WIT PluginContext JSON strings to native HashMap-based context + let native_ctx = wit_context_to_native(ctx); + eprintln!("[WASM] context converted to native={:?}",native_ctx); + + // Call the plugin logic — swap this line to use a different plugin + eprintln!("[WASM] calling identity_check..."); + let result = cpex_payload::plugins::identity_checker::identity_check( + &native_payload, // the CMF message being processed + &native_extensions, // security, HTTP, meta, request extensions + &native_ctx, // plugin-local and global shared state + ); + eprintln!("[WASM] identity_check returned: continue_processing={}", result.continue_processing); + + // Convert the native PluginResult back to WIT for the host to consume + native_result_to_wit(result) + } +} + +// Registers the Plugin struct as the WASM component's exported implementation of the Guest trait +export!(Plugin); diff --git a/crates/cpex-wasm-plugin/wit/deps/cli.wit b/crates/cpex-wasm-plugin/wit/deps/cli.wit new file mode 100644 index 00000000..d7a3ca4d --- /dev/null +++ b/crates/cpex-wasm-plugin/wit/deps/cli.wit @@ -0,0 +1,261 @@ +package wasi:cli@0.2.6; + +@since(version = 0.2.0) +interface environment { + /// Get the POSIX-style environment variables. + /// + /// Each environment variable is provided as a pair of string variable names + /// and string value. + /// + /// Morally, these are a value import, but until value imports are available + /// in the component model, this import function should return the same + /// values each time it is called. + @since(version = 0.2.0) + get-environment: func() -> list>; + + /// Get the POSIX-style arguments to the program. + @since(version = 0.2.0) + get-arguments: func() -> list; + + /// Return a path that programs should use as their initial current working + /// directory, interpreting `.` as shorthand for this. + @since(version = 0.2.0) + initial-cwd: func() -> option; +} + +@since(version = 0.2.0) +interface exit { + /// Exit the current instance and any linked instances. + @since(version = 0.2.0) + exit: func(status: result); + + /// Exit the current instance and any linked instances, reporting the + /// specified status code to the host. + /// + /// The meaning of the code depends on the context, with 0 usually meaning + /// "success", and other values indicating various types of failure. + /// + /// This function does not return; the effect is analogous to a trap, but + /// without the connotation that something bad has happened. + @unstable(feature = cli-exit-with-code) + exit-with-code: func(status-code: u8); +} + +@since(version = 0.2.0) +interface run { + /// Run the program. + @since(version = 0.2.0) + run: func() -> result; +} + +@since(version = 0.2.0) +interface stdin { + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{input-stream}; + + @since(version = 0.2.0) + get-stdin: func() -> input-stream; +} + +@since(version = 0.2.0) +interface stdout { + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{output-stream}; + + @since(version = 0.2.0) + get-stdout: func() -> output-stream; +} + +@since(version = 0.2.0) +interface stderr { + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{output-stream}; + + @since(version = 0.2.0) + get-stderr: func() -> output-stream; +} + +/// Terminal input. +/// +/// In the future, this may include functions for disabling echoing, +/// disabling input buffering so that keyboard events are sent through +/// immediately, querying supported features, and so on. +@since(version = 0.2.0) +interface terminal-input { + /// The input side of a terminal. + @since(version = 0.2.0) + resource terminal-input; +} + +/// Terminal output. +/// +/// In the future, this may include functions for querying the terminal +/// size, being notified of terminal size changes, querying supported +/// features, and so on. +@since(version = 0.2.0) +interface terminal-output { + /// The output side of a terminal. + @since(version = 0.2.0) + resource terminal-output; +} + +/// An interface providing an optional `terminal-input` for stdin as a +/// link-time authority. +@since(version = 0.2.0) +interface terminal-stdin { + @since(version = 0.2.0) + use terminal-input.{terminal-input}; + + /// If stdin is connected to a terminal, return a `terminal-input` handle + /// allowing further interaction with it. + @since(version = 0.2.0) + get-terminal-stdin: func() -> option; +} + +/// An interface providing an optional `terminal-output` for stdout as a +/// link-time authority. +@since(version = 0.2.0) +interface terminal-stdout { + @since(version = 0.2.0) + use terminal-output.{terminal-output}; + + /// If stdout is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + @since(version = 0.2.0) + get-terminal-stdout: func() -> option; +} + +/// An interface providing an optional `terminal-output` for stderr as a +/// link-time authority. +@since(version = 0.2.0) +interface terminal-stderr { + @since(version = 0.2.0) + use terminal-output.{terminal-output}; + + /// If stderr is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + @since(version = 0.2.0) + get-terminal-stderr: func() -> option; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import environment; + @since(version = 0.2.0) + import exit; + @since(version = 0.2.0) + import wasi:io/error@0.2.6; + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import wasi:io/streams@0.2.6; + @since(version = 0.2.0) + import stdin; + @since(version = 0.2.0) + import stdout; + @since(version = 0.2.0) + import stderr; + @since(version = 0.2.0) + import terminal-input; + @since(version = 0.2.0) + import terminal-output; + @since(version = 0.2.0) + import terminal-stdin; + @since(version = 0.2.0) + import terminal-stdout; + @since(version = 0.2.0) + import terminal-stderr; + @since(version = 0.2.0) + import wasi:clocks/monotonic-clock@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/wall-clock@0.2.6; + @unstable(feature = clocks-timezone) + import wasi:clocks/timezone@0.2.6; + @since(version = 0.2.0) + import wasi:filesystem/types@0.2.6; + @since(version = 0.2.0) + import wasi:filesystem/preopens@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/network@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/instance-network@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/udp@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/udp-create-socket@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/tcp@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/tcp-create-socket@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/ip-name-lookup@0.2.6; + @since(version = 0.2.0) + import wasi:random/random@0.2.6; + @since(version = 0.2.0) + import wasi:random/insecure@0.2.6; + @since(version = 0.2.0) + import wasi:random/insecure-seed@0.2.6; +} +@since(version = 0.2.0) +world command { + @since(version = 0.2.0) + import environment; + @since(version = 0.2.0) + import exit; + @since(version = 0.2.0) + import wasi:io/error@0.2.6; + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import wasi:io/streams@0.2.6; + @since(version = 0.2.0) + import stdin; + @since(version = 0.2.0) + import stdout; + @since(version = 0.2.0) + import stderr; + @since(version = 0.2.0) + import terminal-input; + @since(version = 0.2.0) + import terminal-output; + @since(version = 0.2.0) + import terminal-stdin; + @since(version = 0.2.0) + import terminal-stdout; + @since(version = 0.2.0) + import terminal-stderr; + @since(version = 0.2.0) + import wasi:clocks/monotonic-clock@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/wall-clock@0.2.6; + @unstable(feature = clocks-timezone) + import wasi:clocks/timezone@0.2.6; + @since(version = 0.2.0) + import wasi:filesystem/types@0.2.6; + @since(version = 0.2.0) + import wasi:filesystem/preopens@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/network@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/instance-network@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/udp@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/udp-create-socket@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/tcp@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/tcp-create-socket@0.2.6; + @since(version = 0.2.0) + import wasi:sockets/ip-name-lookup@0.2.6; + @since(version = 0.2.0) + import wasi:random/random@0.2.6; + @since(version = 0.2.0) + import wasi:random/insecure@0.2.6; + @since(version = 0.2.0) + import wasi:random/insecure-seed@0.2.6; + + @since(version = 0.2.0) + export run; +} diff --git a/crates/cpex-wasm-plugin/wit/deps/clocks.wit b/crates/cpex-wasm-plugin/wit/deps/clocks.wit new file mode 100644 index 00000000..d638f1a4 --- /dev/null +++ b/crates/cpex-wasm-plugin/wit/deps/clocks.wit @@ -0,0 +1,157 @@ +package wasi:clocks@0.2.6; + +/// WASI Monotonic Clock is a clock API intended to let users measure elapsed +/// time. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A monotonic clock is a clock which has an unspecified initial value, and +/// successive reads of the clock will produce non-decreasing values. +@since(version = 0.2.0) +interface monotonic-clock { + @since(version = 0.2.0) + use wasi:io/poll@0.2.6.{pollable}; + + /// An instant in time, in nanoseconds. An instant is relative to an + /// unspecified initial value, and can only be compared to instances from + /// the same monotonic-clock. + @since(version = 0.2.0) + type instant = u64; + + /// A duration of time, in nanoseconds. + @since(version = 0.2.0) + type duration = u64; + + /// Read the current value of the clock. + /// + /// The clock is monotonic, therefore calling this function repeatedly will + /// produce a sequence of non-decreasing values. + @since(version = 0.2.0) + now: func() -> instant; + + /// Query the resolution of the clock. Returns the duration of time + /// corresponding to a clock tick. + @since(version = 0.2.0) + resolution: func() -> duration; + + /// Create a `pollable` which will resolve once the specified instant + /// has occurred. + @since(version = 0.2.0) + subscribe-instant: func(when: instant) -> pollable; + + /// Create a `pollable` that will resolve after the specified duration has + /// elapsed from the time this function is invoked. + @since(version = 0.2.0) + subscribe-duration: func(when: duration) -> pollable; +} + +/// WASI Wall Clock is a clock API intended to let users query the current +/// time. The name "wall" makes an analogy to a "clock on the wall", which +/// is not necessarily monotonic as it may be reset. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A wall clock is a clock which measures the date and time according to +/// some external reference. +/// +/// External references may be reset, so this clock is not necessarily +/// monotonic, making it unsuitable for measuring elapsed time. +/// +/// It is intended for reporting the current date and time for humans. +@since(version = 0.2.0) +interface wall-clock { + /// A time and date in seconds plus nanoseconds. + @since(version = 0.2.0) + record datetime { + seconds: u64, + nanoseconds: u32, + } + + /// Read the current value of the clock. + /// + /// This clock is not monotonic, therefore calling this function repeatedly + /// will not necessarily produce a sequence of non-decreasing values. + /// + /// The returned timestamps represent the number of seconds since + /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], + /// also known as [Unix Time]. + /// + /// The nanoseconds field of the output is always less than 1000000000. + /// + /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 + /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time + @since(version = 0.2.0) + now: func() -> datetime; + + /// Query the resolution of the clock. + /// + /// The nanoseconds field of the output is always less than 1000000000. + @since(version = 0.2.0) + resolution: func() -> datetime; +} + +@unstable(feature = clocks-timezone) +interface timezone { + @unstable(feature = clocks-timezone) + use wall-clock.{datetime}; + + /// Information useful for displaying the timezone of a specific `datetime`. + /// + /// This information may vary within a single `timezone` to reflect daylight + /// saving time adjustments. + @unstable(feature = clocks-timezone) + record timezone-display { + /// The number of seconds difference between UTC time and the local + /// time of the timezone. + /// + /// The returned value will always be less than 86400 which is the + /// number of seconds in a day (24*60*60). + /// + /// In implementations that do not expose an actual time zone, this + /// should return 0. + utc-offset: s32, + /// The abbreviated name of the timezone to display to a user. The name + /// `UTC` indicates Coordinated Universal Time. Otherwise, this should + /// reference local standards for the name of the time zone. + /// + /// In implementations that do not expose an actual time zone, this + /// should be the string `UTC`. + /// + /// In time zones that do not have an applicable name, a formatted + /// representation of the UTC offset may be returned, such as `-04:00`. + name: string, + /// Whether daylight saving time is active. + /// + /// In implementations that do not expose an actual time zone, this + /// should return false. + in-daylight-saving-time: bool, + } + + /// Return information needed to display the given `datetime`. This includes + /// the UTC offset, the time zone name, and a flag indicating whether + /// daylight saving time is active. + /// + /// If the timezone cannot be determined for the given `datetime`, return a + /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight + /// saving time. + @unstable(feature = clocks-timezone) + display: func(when: datetime) -> timezone-display; + + /// The same as `display`, but only return the UTC offset. + @unstable(feature = clocks-timezone) + utc-offset: func(when: datetime) -> s32; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import monotonic-clock; + @since(version = 0.2.0) + import wall-clock; + @unstable(feature = clocks-timezone) + import timezone; +} diff --git a/crates/cpex-wasm-plugin/wit/deps/filesystem.wit b/crates/cpex-wasm-plugin/wit/deps/filesystem.wit new file mode 100644 index 00000000..9f4a8288 --- /dev/null +++ b/crates/cpex-wasm-plugin/wit/deps/filesystem.wit @@ -0,0 +1,587 @@ +package wasi:filesystem@0.2.6; + +/// WASI filesystem is a filesystem API primarily intended to let users run WASI +/// programs that access their files on their existing filesystems, without +/// significant overhead. +/// +/// It is intended to be roughly portable between Unix-family platforms and +/// Windows, though it does not hide many of the major differences. +/// +/// Paths are passed as interface-type `string`s, meaning they must consist of +/// a sequence of Unicode Scalar Values (USVs). Some filesystems may contain +/// paths which are not accessible by this API. +/// +/// The directory separator in WASI is always the forward-slash (`/`). +/// +/// All paths in WASI are relative paths, and are interpreted relative to a +/// `descriptor` referring to a base directory. If a `path` argument to any WASI +/// function starts with `/`, or if any step of resolving a `path`, including +/// `..` and symbolic link steps, reaches a directory outside of the base +/// directory, or reaches a symlink to an absolute or rooted path in the +/// underlying filesystem, the function fails with `error-code::not-permitted`. +/// +/// For more information about WASI path resolution and sandboxing, see +/// [WASI filesystem path resolution]. +/// +/// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md +@since(version = 0.2.0) +interface types { + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{input-stream, output-stream, error}; + @since(version = 0.2.0) + use wasi:clocks/wall-clock@0.2.6.{datetime}; + + /// File size or length of a region within a file. + @since(version = 0.2.0) + type filesize = u64; + + /// The type of a filesystem object referenced by a descriptor. + /// + /// Note: This was called `filetype` in earlier versions of WASI. + @since(version = 0.2.0) + enum descriptor-type { + /// The type of the descriptor or file is unknown or is different from + /// any of the other types specified. + unknown, + /// The descriptor refers to a block device inode. + block-device, + /// The descriptor refers to a character device inode. + character-device, + /// The descriptor refers to a directory inode. + directory, + /// The descriptor refers to a named pipe. + fifo, + /// The file refers to a symbolic link inode. + symbolic-link, + /// The descriptor refers to a regular file inode. + regular-file, + /// The descriptor refers to a socket. + socket, + } + + /// Descriptor flags. + /// + /// Note: This was called `fdflags` in earlier versions of WASI. + @since(version = 0.2.0) + flags descriptor-flags { + /// Read mode: Data can be read. + read, + /// Write mode: Data can be written to. + write, + /// Request that writes be performed according to synchronized I/O file + /// integrity completion. The data stored in the file and the file's + /// metadata are synchronized. This is similar to `O_SYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + file-integrity-sync, + /// Request that writes be performed according to synchronized I/O data + /// integrity completion. Only the data stored in the file is + /// synchronized. This is similar to `O_DSYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + data-integrity-sync, + /// Requests that reads be performed at the same level of integrity + /// requested for writes. This is similar to `O_RSYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + requested-write-sync, + /// Mutating directories mode: Directory contents may be mutated. + /// + /// When this flag is unset on a descriptor, operations using the + /// descriptor which would create, rename, delete, modify the data or + /// metadata of filesystem objects, or obtain another handle which + /// would permit any of those, shall fail with `error-code::read-only` if + /// they would otherwise succeed. + /// + /// This may only be set on directories. + mutate-directory, + } + + /// Flags determining the method of how paths are resolved. + @since(version = 0.2.0) + flags path-flags { + /// As long as the resolved path corresponds to a symbolic link, it is + /// expanded. + symlink-follow, + } + + /// Open flags used by `open-at`. + @since(version = 0.2.0) + flags open-flags { + /// Create file if it does not exist, similar to `O_CREAT` in POSIX. + create, + /// Fail if not a directory, similar to `O_DIRECTORY` in POSIX. + directory, + /// Fail if file already exists, similar to `O_EXCL` in POSIX. + exclusive, + /// Truncate file to size 0, similar to `O_TRUNC` in POSIX. + truncate, + } + + /// Number of hard links to an inode. + @since(version = 0.2.0) + type link-count = u64; + + /// File attributes. + /// + /// Note: This was called `filestat` in earlier versions of WASI. + @since(version = 0.2.0) + record descriptor-stat { + /// File type. + %type: descriptor-type, + /// Number of hard links to the file. + link-count: link-count, + /// For regular files, the file size in bytes. For symbolic links, the + /// length in bytes of the pathname contained in the symbolic link. + size: filesize, + /// Last data access timestamp. + /// + /// If the `option` is none, the platform doesn't maintain an access + /// timestamp for this file. + data-access-timestamp: option, + /// Last data modification timestamp. + /// + /// If the `option` is none, the platform doesn't maintain a + /// modification timestamp for this file. + data-modification-timestamp: option, + /// Last file status-change timestamp. + /// + /// If the `option` is none, the platform doesn't maintain a + /// status-change timestamp for this file. + status-change-timestamp: option, + } + + /// When setting a timestamp, this gives the value to set it to. + @since(version = 0.2.0) + variant new-timestamp { + /// Leave the timestamp set to its previous value. + no-change, + /// Set the timestamp to the current time of the system clock associated + /// with the filesystem. + now, + /// Set the timestamp to the given value. + timestamp(datetime), + } + + /// A directory entry. + record directory-entry { + /// The type of the file referred to by this directory entry. + %type: descriptor-type, + /// The name of the object. + name: string, + } + + /// Error codes returned by functions, similar to `errno` in POSIX. + /// Not all of these error codes are returned by the functions provided by this + /// API; some are used in higher-level library layers, and others are provided + /// merely for alignment with POSIX. + enum error-code { + /// Permission denied, similar to `EACCES` in POSIX. + access, + /// Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX. + would-block, + /// Connection already in progress, similar to `EALREADY` in POSIX. + already, + /// Bad descriptor, similar to `EBADF` in POSIX. + bad-descriptor, + /// Device or resource busy, similar to `EBUSY` in POSIX. + busy, + /// Resource deadlock would occur, similar to `EDEADLK` in POSIX. + deadlock, + /// Storage quota exceeded, similar to `EDQUOT` in POSIX. + quota, + /// File exists, similar to `EEXIST` in POSIX. + exist, + /// File too large, similar to `EFBIG` in POSIX. + file-too-large, + /// Illegal byte sequence, similar to `EILSEQ` in POSIX. + illegal-byte-sequence, + /// Operation in progress, similar to `EINPROGRESS` in POSIX. + in-progress, + /// Interrupted function, similar to `EINTR` in POSIX. + interrupted, + /// Invalid argument, similar to `EINVAL` in POSIX. + invalid, + /// I/O error, similar to `EIO` in POSIX. + io, + /// Is a directory, similar to `EISDIR` in POSIX. + is-directory, + /// Too many levels of symbolic links, similar to `ELOOP` in POSIX. + loop, + /// Too many links, similar to `EMLINK` in POSIX. + too-many-links, + /// Message too large, similar to `EMSGSIZE` in POSIX. + message-size, + /// Filename too long, similar to `ENAMETOOLONG` in POSIX. + name-too-long, + /// No such device, similar to `ENODEV` in POSIX. + no-device, + /// No such file or directory, similar to `ENOENT` in POSIX. + no-entry, + /// No locks available, similar to `ENOLCK` in POSIX. + no-lock, + /// Not enough space, similar to `ENOMEM` in POSIX. + insufficient-memory, + /// No space left on device, similar to `ENOSPC` in POSIX. + insufficient-space, + /// Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX. + not-directory, + /// Directory not empty, similar to `ENOTEMPTY` in POSIX. + not-empty, + /// State not recoverable, similar to `ENOTRECOVERABLE` in POSIX. + not-recoverable, + /// Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX. + unsupported, + /// Inappropriate I/O control operation, similar to `ENOTTY` in POSIX. + no-tty, + /// No such device or address, similar to `ENXIO` in POSIX. + no-such-device, + /// Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX. + overflow, + /// Operation not permitted, similar to `EPERM` in POSIX. + not-permitted, + /// Broken pipe, similar to `EPIPE` in POSIX. + pipe, + /// Read-only file system, similar to `EROFS` in POSIX. + read-only, + /// Invalid seek, similar to `ESPIPE` in POSIX. + invalid-seek, + /// Text file busy, similar to `ETXTBSY` in POSIX. + text-file-busy, + /// Cross-device link, similar to `EXDEV` in POSIX. + cross-device, + } + + /// File or memory access pattern advisory information. + @since(version = 0.2.0) + enum advice { + /// The application has no advice to give on its behavior with respect + /// to the specified data. + normal, + /// The application expects to access the specified data sequentially + /// from lower offsets to higher offsets. + sequential, + /// The application expects to access the specified data in a random + /// order. + random, + /// The application expects to access the specified data in the near + /// future. + will-need, + /// The application expects that it will not access the specified data + /// in the near future. + dont-need, + /// The application expects to access the specified data once and then + /// not reuse it thereafter. + no-reuse, + } + + /// A 128-bit hash value, split into parts because wasm doesn't have a + /// 128-bit integer type. + @since(version = 0.2.0) + record metadata-hash-value { + /// 64 bits of a 128-bit hash value. + lower: u64, + /// Another 64 bits of a 128-bit hash value. + upper: u64, + } + + /// A descriptor is a reference to a filesystem object, which may be a file, + /// directory, named pipe, special file, or other object on which filesystem + /// calls may be made. + @since(version = 0.2.0) + resource descriptor { + /// Return a stream for reading from a file, if available. + /// + /// May fail with an error-code describing why the file cannot be read. + /// + /// Multiple read, write, and append streams may be active on the same open + /// file and they do not interfere with each other. + /// + /// Note: This allows using `read-stream`, which is similar to `read` in POSIX. + @since(version = 0.2.0) + read-via-stream: func(offset: filesize) -> result; + /// Return a stream for writing to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be written. + /// + /// Note: This allows using `write-stream`, which is similar to `write` in + /// POSIX. + @since(version = 0.2.0) + write-via-stream: func(offset: filesize) -> result; + /// Return a stream for appending to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be appended. + /// + /// Note: This allows using `write-stream`, which is similar to `write` with + /// `O_APPEND` in POSIX. + @since(version = 0.2.0) + append-via-stream: func() -> result; + /// Provide file advisory information on a descriptor. + /// + /// This is similar to `posix_fadvise` in POSIX. + @since(version = 0.2.0) + advise: func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>; + /// Synchronize the data of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fdatasync` in POSIX. + @since(version = 0.2.0) + sync-data: func() -> result<_, error-code>; + /// Get flags associated with a descriptor. + /// + /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. + /// + /// Note: This returns the value that was the `fs_flags` value returned + /// from `fdstat_get` in earlier versions of WASI. + @since(version = 0.2.0) + get-flags: func() -> result; + /// Get the dynamic type of a descriptor. + /// + /// Note: This returns the same value as the `type` field of the `fd-stat` + /// returned by `stat`, `stat-at` and similar. + /// + /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided + /// by `fstat` in POSIX. + /// + /// Note: This returns the value that was the `fs_filetype` value returned + /// from `fdstat_get` in earlier versions of WASI. + @since(version = 0.2.0) + get-type: func() -> result; + /// Adjust the size of an open file. If this increases the file's size, the + /// extra bytes are filled with zeros. + /// + /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. + @since(version = 0.2.0) + set-size: func(size: filesize) -> result<_, error-code>; + /// Adjust the timestamps of an open file or directory. + /// + /// Note: This is similar to `futimens` in POSIX. + /// + /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. + @since(version = 0.2.0) + set-times: func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; + /// Read from a descriptor, without using and updating the descriptor's offset. + /// + /// This function returns a list of bytes containing the data that was + /// read, along with a bool which, when true, indicates that the end of the + /// file was reached. The returned list will contain up to `length` bytes; it + /// may return fewer than requested, if the end of the file is reached or + /// if the I/O operation is interrupted. + /// + /// In the future, this may change to return a `stream`. + /// + /// Note: This is similar to `pread` in POSIX. + @since(version = 0.2.0) + read: func(length: filesize, offset: filesize) -> result, bool>, error-code>; + /// Write to a descriptor, without using and updating the descriptor's offset. + /// + /// It is valid to write past the end of a file; the file is extended to the + /// extent of the write, with bytes between the previous end and the start of + /// the write set to zero. + /// + /// In the future, this may change to take a `stream`. + /// + /// Note: This is similar to `pwrite` in POSIX. + @since(version = 0.2.0) + write: func(buffer: list, offset: filesize) -> result; + /// Read directory entries from a directory. + /// + /// On filesystems where directories contain entries referring to themselves + /// and their parents, often named `.` and `..` respectively, these entries + /// are omitted. + /// + /// This always returns a new stream which starts at the beginning of the + /// directory. Multiple streams may be active on the same directory, and they + /// do not interfere with each other. + @since(version = 0.2.0) + read-directory: func() -> result; + /// Synchronize the data and metadata of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fsync` in POSIX. + @since(version = 0.2.0) + sync: func() -> result<_, error-code>; + /// Create a directory. + /// + /// Note: This is similar to `mkdirat` in POSIX. + @since(version = 0.2.0) + create-directory-at: func(path: string) -> result<_, error-code>; + /// Return the attributes of an open file or directory. + /// + /// Note: This is similar to `fstat` in POSIX, except that it does not return + /// device and inode information. For testing whether two descriptors refer to + /// the same underlying filesystem object, use `is-same-object`. To obtain + /// additional data that can be used do determine whether a file has been + /// modified, use `metadata-hash`. + /// + /// Note: This was called `fd_filestat_get` in earlier versions of WASI. + @since(version = 0.2.0) + stat: func() -> result; + /// Return the attributes of a file or directory. + /// + /// Note: This is similar to `fstatat` in POSIX, except that it does not + /// return device and inode information. See the `stat` description for a + /// discussion of alternatives. + /// + /// Note: This was called `path_filestat_get` in earlier versions of WASI. + @since(version = 0.2.0) + stat-at: func(path-flags: path-flags, path: string) -> result; + /// Adjust the timestamps of a file or directory. + /// + /// Note: This is similar to `utimensat` in POSIX. + /// + /// Note: This was called `path_filestat_set_times` in earlier versions of + /// WASI. + @since(version = 0.2.0) + set-times-at: func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; + /// Create a hard link. + /// + /// Fails with `error-code::no-entry` if the old path does not exist, + /// with `error-code::exist` if the new path already exists, and + /// `error-code::not-permitted` if the old path is not a file. + /// + /// Note: This is similar to `linkat` in POSIX. + @since(version = 0.2.0) + link-at: func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; + /// Open a file or directory. + /// + /// If `flags` contains `descriptor-flags::mutate-directory`, and the base + /// descriptor doesn't have `descriptor-flags::mutate-directory` set, + /// `open-at` fails with `error-code::read-only`. + /// + /// If `flags` contains `write` or `mutate-directory`, or `open-flags` + /// contains `truncate` or `create`, and the base descriptor doesn't have + /// `descriptor-flags::mutate-directory` set, `open-at` fails with + /// `error-code::read-only`. + /// + /// Note: This is similar to `openat` in POSIX. + @since(version = 0.2.0) + open-at: func(path-flags: path-flags, path: string, open-flags: open-flags, %flags: descriptor-flags) -> result; + /// Read the contents of a symbolic link. + /// + /// If the contents contain an absolute or rooted path in the underlying + /// filesystem, this function fails with `error-code::not-permitted`. + /// + /// Note: This is similar to `readlinkat` in POSIX. + @since(version = 0.2.0) + readlink-at: func(path: string) -> result; + /// Remove a directory. + /// + /// Return `error-code::not-empty` if the directory is not empty. + /// + /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + @since(version = 0.2.0) + remove-directory-at: func(path: string) -> result<_, error-code>; + /// Rename a filesystem object. + /// + /// Note: This is similar to `renameat` in POSIX. + @since(version = 0.2.0) + rename-at: func(old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; + /// Create a symbolic link (also known as a "symlink"). + /// + /// If `old-path` starts with `/`, the function fails with + /// `error-code::not-permitted`. + /// + /// Note: This is similar to `symlinkat` in POSIX. + @since(version = 0.2.0) + symlink-at: func(old-path: string, new-path: string) -> result<_, error-code>; + /// Unlink a filesystem object that is not a directory. + /// + /// Return `error-code::is-directory` if the path refers to a directory. + /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + @since(version = 0.2.0) + unlink-file-at: func(path: string) -> result<_, error-code>; + /// Test whether two descriptors refer to the same filesystem object. + /// + /// In POSIX, this corresponds to testing whether the two descriptors have the + /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. + /// wasi-filesystem does not expose device and inode numbers, so this function + /// may be used instead. + @since(version = 0.2.0) + is-same-object: func(other: borrow) -> bool; + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a descriptor. + /// + /// This returns a hash of the last-modification timestamp and file size, and + /// may also include the inode number, device number, birth timestamp, and + /// other metadata fields that may change when the file is modified or + /// replaced. It may also include a secret value chosen by the + /// implementation and not otherwise exposed. + /// + /// Implementations are encouraged to provide the following properties: + /// + /// - If the file is not modified or replaced, the computed hash value should + /// usually not change. + /// - If the object is modified or replaced, the computed hash value should + /// usually change. + /// - The inputs to the hash should not be easily computable from the + /// computed hash. + /// + /// However, none of these is required. + @since(version = 0.2.0) + metadata-hash: func() -> result; + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a directory descriptor and a relative path. + /// + /// This performs the same hash computation as `metadata-hash`. + @since(version = 0.2.0) + metadata-hash-at: func(path-flags: path-flags, path: string) -> result; + } + + /// A stream of directory entries. + @since(version = 0.2.0) + resource directory-entry-stream { + /// Read a single directory entry from a `directory-entry-stream`. + @since(version = 0.2.0) + read-directory-entry: func() -> result, error-code>; + } + + /// Attempts to extract a filesystem-related `error-code` from the stream + /// `error` provided. + /// + /// Stream operations which return `stream-error::last-operation-failed` + /// have a payload with more information about the operation that failed. + /// This payload can be passed through to this function to see if there's + /// filesystem-related information about the error to return. + /// + /// Note that this function is fallible because not all stream-related + /// errors are filesystem-related errors. + @since(version = 0.2.0) + filesystem-error-code: func(err: borrow) -> option; +} + +@since(version = 0.2.0) +interface preopens { + @since(version = 0.2.0) + use types.{descriptor}; + + /// Return the set of preopened directories, and their paths. + @since(version = 0.2.0) + get-directories: func() -> list>; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import wasi:io/error@0.2.6; + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import wasi:io/streams@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/wall-clock@0.2.6; + @since(version = 0.2.0) + import types; + @since(version = 0.2.0) + import preopens; +} diff --git a/crates/cpex-wasm-plugin/wit/deps/http.wit b/crates/cpex-wasm-plugin/wit/deps/http.wit new file mode 100644 index 00000000..eb1b25f0 --- /dev/null +++ b/crates/cpex-wasm-plugin/wit/deps/http.wit @@ -0,0 +1,733 @@ +package wasi:http@0.2.6; + +/// This interface defines all of the types and methods for implementing +/// HTTP Requests and Responses, both incoming and outgoing, as well as +/// their headers, trailers, and bodies. +@since(version = 0.2.0) +interface types { + @since(version = 0.2.0) + use wasi:clocks/monotonic-clock@0.2.6.{duration}; + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{input-stream, output-stream}; + @since(version = 0.2.0) + use wasi:io/error@0.2.6.{error as io-error}; + @since(version = 0.2.0) + use wasi:io/poll@0.2.6.{pollable}; + + /// This type corresponds to HTTP standard Methods. + @since(version = 0.2.0) + variant method { + get, + head, + post, + put, + delete, + connect, + options, + trace, + patch, + other(string), + } + + /// This type corresponds to HTTP standard Related Schemes. + @since(version = 0.2.0) + variant scheme { + HTTP, + HTTPS, + other(string), + } + + /// Defines the case payload type for `DNS-error` above: + @since(version = 0.2.0) + record DNS-error-payload { + rcode: option, + info-code: option, + } + + /// Defines the case payload type for `TLS-alert-received` above: + @since(version = 0.2.0) + record TLS-alert-received-payload { + alert-id: option, + alert-message: option, + } + + /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: + @since(version = 0.2.0) + record field-size-payload { + field-name: option, + field-size: option, + } + + /// These cases are inspired by the IANA HTTP Proxy Error Types: + /// + @since(version = 0.2.0) + variant error-code { + DNS-timeout, + DNS-error(DNS-error-payload), + destination-not-found, + destination-unavailable, + destination-IP-prohibited, + destination-IP-unroutable, + connection-refused, + connection-terminated, + connection-timeout, + connection-read-timeout, + connection-write-timeout, + connection-limit-reached, + TLS-protocol-error, + TLS-certificate-error, + TLS-alert-received(TLS-alert-received-payload), + HTTP-request-denied, + HTTP-request-length-required, + HTTP-request-body-size(option), + HTTP-request-method-invalid, + HTTP-request-URI-invalid, + HTTP-request-URI-too-long, + HTTP-request-header-section-size(option), + HTTP-request-header-size(option), + HTTP-request-trailer-section-size(option), + HTTP-request-trailer-size(field-size-payload), + HTTP-response-incomplete, + HTTP-response-header-section-size(option), + HTTP-response-header-size(field-size-payload), + HTTP-response-body-size(option), + HTTP-response-trailer-section-size(option), + HTTP-response-trailer-size(field-size-payload), + HTTP-response-transfer-coding(option), + HTTP-response-content-coding(option), + HTTP-response-timeout, + HTTP-upgrade-failed, + HTTP-protocol-error, + loop-detected, + configuration-error, + /// This is a catch-all error for anything that doesn't fit cleanly into a + /// more specific case. It also includes an optional string for an + /// unstructured description of the error. Users should not depend on the + /// string for diagnosing errors, as it's not required to be consistent + /// between implementations. + internal-error(option), + } + + /// This type enumerates the different kinds of errors that may occur when + /// setting or appending to a `fields` resource. + @since(version = 0.2.0) + variant header-error { + /// This error indicates that a `field-name` or `field-value` was + /// syntactically invalid when used with an operation that sets headers in a + /// `fields`. + invalid-syntax, + /// This error indicates that a forbidden `field-name` was used when trying + /// to set a header in a `fields`. + forbidden, + /// This error indicates that the operation on the `fields` was not + /// permitted because the fields are immutable. + immutable, + } + + /// Field keys are always strings. + /// + /// Field keys should always be treated as case insensitive by the `fields` + /// resource for the purposes of equality checking. + /// + /// # Deprecation + /// + /// This type has been deprecated in favor of the `field-name` type. + @since(version = 0.2.0) + @deprecated(version = 0.2.2) + type field-key = string; + + /// Field names are always strings. + /// + /// Field names should always be treated as case insensitive by the `fields` + /// resource for the purposes of equality checking. + @since(version = 0.2.1) + type field-name = field-key; + + /// Field values should always be ASCII strings. However, in + /// reality, HTTP implementations often have to interpret malformed values, + /// so they are provided as a list of bytes. + @since(version = 0.2.0) + type field-value = list; + + /// This following block defines the `fields` resource which corresponds to + /// HTTP standard Fields. Fields are a common representation used for both + /// Headers and Trailers. + /// + /// A `fields` may be mutable or immutable. A `fields` created using the + /// constructor, `from-list`, or `clone` will be mutable, but a `fields` + /// resource given by other means (including, but not limited to, + /// `incoming-request.headers`, `outgoing-request.headers`) might be + /// immutable. In an immutable fields, the `set`, `append`, and `delete` + /// operations will fail with `header-error.immutable`. + @since(version = 0.2.0) + resource fields { + /// Construct an empty HTTP Fields. + /// + /// The resulting `fields` is mutable. + @since(version = 0.2.0) + constructor(); + /// Construct an HTTP Fields. + /// + /// The resulting `fields` is mutable. + /// + /// The list represents each name-value pair in the Fields. Names + /// which have multiple values are represented by multiple entries in this + /// list with the same name. + /// + /// The tuple is a pair of the field name, represented as a string, and + /// Value, represented as a list of bytes. + /// + /// An error result will be returned if any `field-name` or `field-value` is + /// syntactically invalid, or if a field is forbidden. + @since(version = 0.2.0) + from-list: static func(entries: list>) -> result; + /// Get all of the values corresponding to a name. If the name is not present + /// in this `fields` or is syntactically invalid, an empty list is returned. + /// However, if the name is present but empty, this is represented by a list + /// with one or more empty field-values present. + @since(version = 0.2.0) + get: func(name: field-name) -> list; + /// Returns `true` when the name is present in this `fields`. If the name is + /// syntactically invalid, `false` is returned. + @since(version = 0.2.0) + has: func(name: field-name) -> bool; + /// Set all of the values for a name. Clears any existing values for that + /// name, if they have been set. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.invalid-syntax` if the `field-name` or any of + /// the `field-value`s are syntactically invalid. + @since(version = 0.2.0) + set: func(name: field-name, value: list) -> result<_, header-error>; + /// Delete all values for a name. Does nothing if no values for the name + /// exist. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.invalid-syntax` if the `field-name` is + /// syntactically invalid. + @since(version = 0.2.0) + delete: func(name: field-name) -> result<_, header-error>; + /// Append a value for a name. Does not change or delete any existing + /// values for that name. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.invalid-syntax` if the `field-name` or + /// `field-value` are syntactically invalid. + @since(version = 0.2.0) + append: func(name: field-name, value: field-value) -> result<_, header-error>; + /// Retrieve the full set of names and values in the Fields. Like the + /// constructor, the list represents each name-value pair. + /// + /// The outer list represents each name-value pair in the Fields. Names + /// which have multiple values are represented by multiple entries in this + /// list with the same name. + /// + /// The names and values are always returned in the original casing and in + /// the order in which they will be serialized for transport. + @since(version = 0.2.0) + entries: func() -> list>; + /// Make a deep copy of the Fields. Equivalent in behavior to calling the + /// `fields` constructor on the return value of `entries`. The resulting + /// `fields` is mutable. + @since(version = 0.2.0) + clone: func() -> fields; + } + + /// Headers is an alias for Fields. + @since(version = 0.2.0) + type headers = fields; + + /// Trailers is an alias for Fields. + @since(version = 0.2.0) + type trailers = fields; + + /// Represents an incoming HTTP Request. + @since(version = 0.2.0) + resource incoming-request { + /// Returns the method of the incoming request. + @since(version = 0.2.0) + method: func() -> method; + /// Returns the path with query parameters from the request, as a string. + @since(version = 0.2.0) + path-with-query: func() -> option; + /// Returns the protocol scheme from the request. + @since(version = 0.2.0) + scheme: func() -> option; + /// Returns the authority of the Request's target URI, if present. + @since(version = 0.2.0) + authority: func() -> option; + /// Get the `headers` associated with the request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// The `headers` returned are a child resource: it must be dropped before + /// the parent `incoming-request` is dropped. Dropping this + /// `incoming-request` before all children are dropped will trap. + @since(version = 0.2.0) + headers: func() -> headers; + /// Gives the `incoming-body` associated with this request. Will only + /// return success at most once, and subsequent calls will return error. + @since(version = 0.2.0) + consume: func() -> result; + } + + /// Represents an outgoing HTTP Request. + @since(version = 0.2.0) + resource outgoing-request { + /// Construct a new `outgoing-request` with a default `method` of `GET`, and + /// `none` values for `path-with-query`, `scheme`, and `authority`. + /// + /// * `headers` is the HTTP Headers for the Request. + /// + /// It is possible to construct, or manipulate with the accessor functions + /// below, an `outgoing-request` with an invalid combination of `scheme` + /// and `authority`, or `headers` which are not permitted to be sent. + /// It is the obligation of the `outgoing-handler.handle` implementation + /// to reject invalid constructions of `outgoing-request`. + @since(version = 0.2.0) + constructor(headers: headers); + /// Returns the resource corresponding to the outgoing Body for this + /// Request. + /// + /// Returns success on the first call: the `outgoing-body` resource for + /// this `outgoing-request` can be retrieved at most once. Subsequent + /// calls will return error. + @since(version = 0.2.0) + body: func() -> result; + /// Get the Method for the Request. + @since(version = 0.2.0) + method: func() -> method; + /// Set the Method for the Request. Fails if the string present in a + /// `method.other` argument is not a syntactically valid method. + @since(version = 0.2.0) + set-method: func(method: method) -> result; + /// Get the combination of the HTTP Path and Query for the Request. + /// When `none`, this represents an empty Path and empty Query. + @since(version = 0.2.0) + path-with-query: func() -> option; + /// Set the combination of the HTTP Path and Query for the Request. + /// When `none`, this represents an empty Path and empty Query. Fails is the + /// string given is not a syntactically valid path and query uri component. + @since(version = 0.2.0) + set-path-with-query: func(path-with-query: option) -> result; + /// Get the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. + @since(version = 0.2.0) + scheme: func() -> option; + /// Set the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. Fails if the + /// string given is not a syntactically valid uri scheme. + @since(version = 0.2.0) + set-scheme: func(scheme: option) -> result; + /// Get the authority of the Request's target URI. A value of `none` may be used + /// with Related Schemes which do not require an authority. The HTTP and + /// HTTPS schemes always require an authority. + @since(version = 0.2.0) + authority: func() -> option; + /// Set the authority of the Request's target URI. A value of `none` may be used + /// with Related Schemes which do not require an authority. The HTTP and + /// HTTPS schemes always require an authority. Fails if the string given is + /// not a syntactically valid URI authority. + @since(version = 0.2.0) + set-authority: func(authority: option) -> result; + /// Get the headers associated with the Request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `outgoing-request` is dropped, or its ownership is transferred to + /// another component by e.g. `outgoing-handler.handle`. + @since(version = 0.2.0) + headers: func() -> headers; + } + + /// Parameters for making an HTTP Request. Each of these parameters is + /// currently an optional timeout applicable to the transport layer of the + /// HTTP protocol. + /// + /// These timeouts are separate from any the user may use to bound a + /// blocking call to `wasi:io/poll.poll`. + @since(version = 0.2.0) + resource request-options { + /// Construct a default `request-options` value. + @since(version = 0.2.0) + constructor(); + /// The timeout for the initial connect to the HTTP Server. + @since(version = 0.2.0) + connect-timeout: func() -> option; + /// Set the timeout for the initial connect to the HTTP Server. An error + /// return value indicates that this timeout is not supported. + @since(version = 0.2.0) + set-connect-timeout: func(duration: option) -> result; + /// The timeout for receiving the first byte of the Response body. + @since(version = 0.2.0) + first-byte-timeout: func() -> option; + /// Set the timeout for receiving the first byte of the Response body. An + /// error return value indicates that this timeout is not supported. + @since(version = 0.2.0) + set-first-byte-timeout: func(duration: option) -> result; + /// The timeout for receiving subsequent chunks of bytes in the Response + /// body stream. + @since(version = 0.2.0) + between-bytes-timeout: func() -> option; + /// Set the timeout for receiving subsequent chunks of bytes in the Response + /// body stream. An error return value indicates that this timeout is not + /// supported. + @since(version = 0.2.0) + set-between-bytes-timeout: func(duration: option) -> result; + } + + /// Represents the ability to send an HTTP Response. + /// + /// This resource is used by the `wasi:http/incoming-handler` interface to + /// allow a Response to be sent corresponding to the Request provided as the + /// other argument to `incoming-handler.handle`. + @since(version = 0.2.0) + resource response-outparam { + /// Send an HTTP 1xx response. + /// + /// Unlike `response-outparam.set`, this does not consume the + /// `response-outparam`, allowing the guest to send an arbitrary number of + /// informational responses before sending the final response using + /// `response-outparam.set`. + /// + /// This will return an `HTTP-protocol-error` if `status` is not in the + /// range [100-199], or an `internal-error` if the implementation does not + /// support informational responses. + @unstable(feature = informational-outbound-responses) + send-informational: func(status: u16, headers: headers) -> result<_, error-code>; + /// Set the value of the `response-outparam` to either send a response, + /// or indicate an error. + /// + /// This method consumes the `response-outparam` to ensure that it is + /// called at most once. If it is never called, the implementation + /// will respond with an error. + /// + /// The user may provide an `error` to `response` to allow the + /// implementation determine how to respond with an HTTP error response. + @since(version = 0.2.0) + set: static func(param: response-outparam, response: result); + } + + /// This type corresponds to the HTTP standard Status Code. + @since(version = 0.2.0) + type status-code = u16; + + /// Represents an incoming HTTP Response. + @since(version = 0.2.0) + resource incoming-response { + /// Returns the status code from the incoming response. + @since(version = 0.2.0) + status: func() -> status-code; + /// Returns the headers from the incoming response. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `incoming-response` is dropped. + @since(version = 0.2.0) + headers: func() -> headers; + /// Returns the incoming body. May be called at most once. Returns error + /// if called additional times. + @since(version = 0.2.0) + consume: func() -> result; + } + + /// Represents an incoming HTTP Request or Response's Body. + /// + /// A body has both its contents - a stream of bytes - and a (possibly + /// empty) set of trailers, indicating that the full contents of the + /// body have been received. This resource represents the contents as + /// an `input-stream` and the delivery of trailers as a `future-trailers`, + /// and ensures that the user of this interface may only be consuming either + /// the body contents or waiting on trailers at any given time. + @since(version = 0.2.0) + resource incoming-body { + /// Returns the contents of the body, as a stream of bytes. + /// + /// Returns success on first call: the stream representing the contents + /// can be retrieved at most once. Subsequent calls will return error. + /// + /// The returned `input-stream` resource is a child: it must be dropped + /// before the parent `incoming-body` is dropped, or consumed by + /// `incoming-body.finish`. + /// + /// This invariant ensures that the implementation can determine whether + /// the user is consuming the contents of the body, waiting on the + /// `future-trailers` to be ready, or neither. This allows for network + /// backpressure is to be applied when the user is consuming the body, + /// and for that backpressure to not inhibit delivery of the trailers if + /// the user does not read the entire body. + @since(version = 0.2.0) + %stream: func() -> result; + /// Takes ownership of `incoming-body`, and returns a `future-trailers`. + /// This function will trap if the `input-stream` child is still alive. + @since(version = 0.2.0) + finish: static func(this: incoming-body) -> future-trailers; + } + + /// Represents a future which may eventually return trailers, or an error. + /// + /// In the case that the incoming HTTP Request or Response did not have any + /// trailers, this future will resolve to the empty set of trailers once the + /// complete Request or Response body has been received. + @since(version = 0.2.0) + resource future-trailers { + /// Returns a pollable which becomes ready when either the trailers have + /// been received, or an error has occurred. When this pollable is ready, + /// the `get` method will return `some`. + @since(version = 0.2.0) + subscribe: func() -> pollable; + /// Returns the contents of the trailers, or an error which occurred, + /// once the future is ready. + /// + /// The outer `option` represents future readiness. Users can wait on this + /// `option` to become `some` using the `subscribe` method. + /// + /// The outer `result` is used to retrieve the trailers or error at most + /// once. It will be success on the first call in which the outer option + /// is `some`, and error on subsequent calls. + /// + /// The inner `result` represents that either the HTTP Request or Response + /// body, as well as any trailers, were received successfully, or that an + /// error occurred receiving them. The optional `trailers` indicates whether + /// or not trailers were present in the body. + /// + /// When some `trailers` are returned by this method, the `trailers` + /// resource is immutable, and a child. Use of the `set`, `append`, or + /// `delete` methods will return an error, and the resource must be + /// dropped before the parent `future-trailers` is dropped. + @since(version = 0.2.0) + get: func() -> option, error-code>>>; + } + + /// Represents an outgoing HTTP Response. + @since(version = 0.2.0) + resource outgoing-response { + /// Construct an `outgoing-response`, with a default `status-code` of `200`. + /// If a different `status-code` is needed, it must be set via the + /// `set-status-code` method. + /// + /// * `headers` is the HTTP Headers for the Response. + @since(version = 0.2.0) + constructor(headers: headers); + /// Get the HTTP Status Code for the Response. + @since(version = 0.2.0) + status-code: func() -> status-code; + /// Set the HTTP Status Code for the Response. Fails if the status-code + /// given is not a valid http status code. + @since(version = 0.2.0) + set-status-code: func(status-code: status-code) -> result; + /// Get the headers associated with the Request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `outgoing-request` is dropped, or its ownership is transferred to + /// another component by e.g. `outgoing-handler.handle`. + @since(version = 0.2.0) + headers: func() -> headers; + /// Returns the resource corresponding to the outgoing Body for this Response. + /// + /// Returns success on the first call: the `outgoing-body` resource for + /// this `outgoing-response` can be retrieved at most once. Subsequent + /// calls will return error. + @since(version = 0.2.0) + body: func() -> result; + } + + /// Represents an outgoing HTTP Request or Response's Body. + /// + /// A body has both its contents - a stream of bytes - and a (possibly + /// empty) set of trailers, inducating the full contents of the body + /// have been sent. This resource represents the contents as an + /// `output-stream` child resource, and the completion of the body (with + /// optional trailers) with a static function that consumes the + /// `outgoing-body` resource, and ensures that the user of this interface + /// may not write to the body contents after the body has been finished. + /// + /// If the user code drops this resource, as opposed to calling the static + /// method `finish`, the implementation should treat the body as incomplete, + /// and that an error has occurred. The implementation should propagate this + /// error to the HTTP protocol by whatever means it has available, + /// including: corrupting the body on the wire, aborting the associated + /// Request, or sending a late status code for the Response. + @since(version = 0.2.0) + resource outgoing-body { + /// Returns a stream for writing the body contents. + /// + /// The returned `output-stream` is a child resource: it must be dropped + /// before the parent `outgoing-body` resource is dropped (or finished), + /// otherwise the `outgoing-body` drop or `finish` will trap. + /// + /// Returns success on the first call: the `output-stream` resource for + /// this `outgoing-body` may be retrieved at most once. Subsequent calls + /// will return error. + @since(version = 0.2.0) + write: func() -> result; + /// Finalize an outgoing body, optionally providing trailers. This must be + /// called to signal that the response is complete. If the `outgoing-body` + /// is dropped without calling `outgoing-body.finalize`, the implementation + /// should treat the body as corrupted. + /// + /// Fails if the body's `outgoing-request` or `outgoing-response` was + /// constructed with a Content-Length header, and the contents written + /// to the body (via `write`) does not match the value given in the + /// Content-Length. + @since(version = 0.2.0) + finish: static func(this: outgoing-body, trailers: option) -> result<_, error-code>; + } + + /// Represents a future which may eventually return an incoming HTTP + /// Response, or an error. + /// + /// This resource is returned by the `wasi:http/outgoing-handler` interface to + /// provide the HTTP Response corresponding to the sent Request. + @since(version = 0.2.0) + resource future-incoming-response { + /// Returns a pollable which becomes ready when either the Response has + /// been received, or an error has occurred. When this pollable is ready, + /// the `get` method will return `some`. + @since(version = 0.2.0) + subscribe: func() -> pollable; + /// Returns the incoming HTTP Response, or an error, once one is ready. + /// + /// The outer `option` represents future readiness. Users can wait on this + /// `option` to become `some` using the `subscribe` method. + /// + /// The outer `result` is used to retrieve the response or error at most + /// once. It will be success on the first call in which the outer option + /// is `some`, and error on subsequent calls. + /// + /// The inner `result` represents that either the incoming HTTP Response + /// status and headers have received successfully, or that an error + /// occurred. Errors may also occur while consuming the response body, + /// but those will be reported by the `incoming-body` and its + /// `output-stream` child. + @since(version = 0.2.0) + get: func() -> option>>; + } + + /// Attempts to extract a http-related `error` from the wasi:io `error` + /// provided. + /// + /// Stream operations which return + /// `wasi:io/stream/stream-error::last-operation-failed` have a payload of + /// type `wasi:io/error/error` with more information about the operation + /// that failed. This payload can be passed through to this function to see + /// if there's http-related information about the error to return. + /// + /// Note that this function is fallible because not all io-errors are + /// http-related errors. + @since(version = 0.2.0) + http-error-code: func(err: borrow) -> option; +} + +/// This interface defines a handler of incoming HTTP Requests. It should +/// be exported by components which can respond to HTTP Requests. +@since(version = 0.2.0) +interface incoming-handler { + @since(version = 0.2.0) + use types.{incoming-request, response-outparam}; + + /// This function is invoked with an incoming HTTP Request, and a resource + /// `response-outparam` which provides the capability to reply with an HTTP + /// Response. The response is sent by calling the `response-outparam.set` + /// method, which allows execution to continue after the response has been + /// sent. This enables both streaming to the response body, and performing other + /// work. + /// + /// The implementor of this function must write a response to the + /// `response-outparam` before returning, or else the caller will respond + /// with an error on its behalf. + @since(version = 0.2.0) + handle: func(request: incoming-request, response-out: response-outparam); +} + +/// This interface defines a handler of outgoing HTTP Requests. It should be +/// imported by components which wish to make HTTP Requests. +@since(version = 0.2.0) +interface outgoing-handler { + @since(version = 0.2.0) + use types.{outgoing-request, request-options, future-incoming-response, error-code}; + + /// This function is invoked with an outgoing HTTP Request, and it returns + /// a resource `future-incoming-response` which represents an HTTP Response + /// which may arrive in the future. + /// + /// The `options` argument accepts optional parameters for the HTTP + /// protocol's transport layer. + /// + /// This function may return an error if the `outgoing-request` is invalid + /// or not allowed to be made. Otherwise, protocol errors are reported + /// through the `future-incoming-response`. + @since(version = 0.2.0) + handle: func(request: outgoing-request, options: option) -> result; +} + +/// The `wasi:http/imports` world imports all the APIs for HTTP proxies. +/// It is intended to be `include`d in other worlds. +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/monotonic-clock@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/wall-clock@0.2.6; + @since(version = 0.2.0) + import wasi:random/random@0.2.6; + @since(version = 0.2.0) + import wasi:io/error@0.2.6; + @since(version = 0.2.0) + import wasi:io/streams@0.2.6; + @since(version = 0.2.0) + import wasi:cli/stdout@0.2.6; + @since(version = 0.2.0) + import wasi:cli/stderr@0.2.6; + @since(version = 0.2.0) + import wasi:cli/stdin@0.2.6; + @since(version = 0.2.0) + import types; + @since(version = 0.2.0) + import outgoing-handler; +} +/// The `wasi:http/proxy` world captures a widely-implementable intersection of +/// hosts that includes HTTP forward and reverse proxies. Components targeting +/// this world may concurrently stream in and out any number of incoming and +/// outgoing HTTP requests. +@since(version = 0.2.0) +world proxy { + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/monotonic-clock@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/wall-clock@0.2.6; + @since(version = 0.2.0) + import wasi:random/random@0.2.6; + @since(version = 0.2.0) + import wasi:io/error@0.2.6; + @since(version = 0.2.0) + import wasi:io/streams@0.2.6; + @since(version = 0.2.0) + import wasi:cli/stdout@0.2.6; + @since(version = 0.2.0) + import wasi:cli/stderr@0.2.6; + @since(version = 0.2.0) + import wasi:cli/stdin@0.2.6; + @since(version = 0.2.0) + import types; + @since(version = 0.2.0) + import outgoing-handler; + + @since(version = 0.2.0) + export incoming-handler; +} diff --git a/crates/cpex-wasm-plugin/wit/deps/io.wit b/crates/cpex-wasm-plugin/wit/deps/io.wit new file mode 100644 index 00000000..08ad78e6 --- /dev/null +++ b/crates/cpex-wasm-plugin/wit/deps/io.wit @@ -0,0 +1,331 @@ +package wasi:io@0.2.6; + +@since(version = 0.2.0) +interface error { + /// A resource which represents some error information. + /// + /// The only method provided by this resource is `to-debug-string`, + /// which provides some human-readable information about the error. + /// + /// In the `wasi:io` package, this resource is returned through the + /// `wasi:io/streams/stream-error` type. + /// + /// To provide more specific error information, other interfaces may + /// offer functions to "downcast" this error into more specific types. For example, + /// errors returned from streams derived from filesystem types can be described using + /// the filesystem's own error-code type. This is done using the function + /// `wasi:filesystem/types/filesystem-error-code`, which takes a `borrow` + /// parameter and returns an `option`. + /// + /// The set of functions which can "downcast" an `error` into a more + /// concrete type is open. + @since(version = 0.2.0) + resource error { + /// Returns a string that is suitable to assist humans in debugging + /// this error. + /// + /// WARNING: The returned string should not be consumed mechanically! + /// It may change across platforms, hosts, or other implementation + /// details. Parsing this string is a major platform-compatibility + /// hazard. + @since(version = 0.2.0) + to-debug-string: func() -> string; + } +} + +/// A poll API intended to let users wait for I/O events on multiple handles +/// at once. +@since(version = 0.2.0) +interface poll { + /// `pollable` represents a single I/O event which may be ready, or not. + @since(version = 0.2.0) + resource pollable { + /// Return the readiness of a pollable. This function never blocks. + /// + /// Returns `true` when the pollable is ready, and `false` otherwise. + @since(version = 0.2.0) + ready: func() -> bool; + /// `block` returns immediately if the pollable is ready, and otherwise + /// blocks until ready. + /// + /// This function is equivalent to calling `poll.poll` on a list + /// containing only this pollable. + @since(version = 0.2.0) + block: func(); + } + + /// Poll for completion on a set of pollables. + /// + /// This function takes a list of pollables, which identify I/O sources of + /// interest, and waits until one or more of the events is ready for I/O. + /// + /// The result `list` contains one or more indices of handles in the + /// argument list that is ready for I/O. + /// + /// This function traps if either: + /// - the list is empty, or: + /// - the list contains more elements than can be indexed with a `u32` value. + /// + /// A timeout can be implemented by adding a pollable from the + /// wasi-clocks API to the list. + /// + /// This function does not return a `result`; polling in itself does not + /// do any I/O so it doesn't fail. If any of the I/O sources identified by + /// the pollables has an error, it is indicated by marking the source as + /// being ready for I/O. + @since(version = 0.2.0) + poll: func(in: list>) -> list; +} + +/// WASI I/O is an I/O abstraction API which is currently focused on providing +/// stream types. +/// +/// In the future, the component model is expected to add built-in stream types; +/// when it does, they are expected to subsume this API. +@since(version = 0.2.0) +interface streams { + @since(version = 0.2.0) + use error.{error}; + @since(version = 0.2.0) + use poll.{pollable}; + + /// An error for input-stream and output-stream operations. + @since(version = 0.2.0) + variant stream-error { + /// The last operation (a write or flush) failed before completion. + /// + /// More information is available in the `error` payload. + /// + /// After this, the stream will be closed. All future operations return + /// `stream-error::closed`. + last-operation-failed(error), + /// The stream is closed: no more input will be accepted by the + /// stream. A closed output-stream will return this error on all + /// future operations. + closed, + } + + /// An input bytestream. + /// + /// `input-stream`s are *non-blocking* to the extent practical on underlying + /// platforms. I/O operations always return promptly; if fewer bytes are + /// promptly available than requested, they return the number of bytes promptly + /// available, which could even be zero. To wait for data to be available, + /// use the `subscribe` function to obtain a `pollable` which can be polled + /// for using `wasi:io/poll`. + @since(version = 0.2.0) + resource input-stream { + /// Perform a non-blocking read from the stream. + /// + /// When the source of a `read` is binary data, the bytes from the source + /// are returned verbatim. When the source of a `read` is known to the + /// implementation to be text, bytes containing the UTF-8 encoding of the + /// text are returned. + /// + /// This function returns a list of bytes containing the read data, + /// when successful. The returned list will contain up to `len` bytes; + /// it may return fewer than requested, but not more. The list is + /// empty when no bytes are available for reading at this time. The + /// pollable given by `subscribe` will be ready when more bytes are + /// available. + /// + /// This function fails with a `stream-error` when the operation + /// encounters an error, giving `last-operation-failed`, or when the + /// stream is closed, giving `closed`. + /// + /// When the caller gives a `len` of 0, it represents a request to + /// read 0 bytes. If the stream is still open, this call should + /// succeed and return an empty list, or otherwise fail with `closed`. + /// + /// The `len` parameter is a `u64`, which could represent a list of u8 which + /// is not possible to allocate in wasm32, or not desirable to allocate as + /// as a return value by the callee. The callee may return a list of bytes + /// less than `len` in size while more bytes are available for reading. + @since(version = 0.2.0) + read: func(len: u64) -> result, stream-error>; + /// Read bytes from a stream, after blocking until at least one byte can + /// be read. Except for blocking, behavior is identical to `read`. + @since(version = 0.2.0) + blocking-read: func(len: u64) -> result, stream-error>; + /// Skip bytes from a stream. Returns number of bytes skipped. + /// + /// Behaves identical to `read`, except instead of returning a list + /// of bytes, returns the number of bytes consumed from the stream. + @since(version = 0.2.0) + skip: func(len: u64) -> result; + /// Skip bytes from a stream, after blocking until at least one byte + /// can be skipped. Except for blocking behavior, identical to `skip`. + @since(version = 0.2.0) + blocking-skip: func(len: u64) -> result; + /// Create a `pollable` which will resolve once either the specified stream + /// has bytes available to read or the other end of the stream has been + /// closed. + /// The created `pollable` is a child resource of the `input-stream`. + /// Implementations may trap if the `input-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + /// An output bytestream. + /// + /// `output-stream`s are *non-blocking* to the extent practical on + /// underlying platforms. Except where specified otherwise, I/O operations also + /// always return promptly, after the number of bytes that can be written + /// promptly, which could even be zero. To wait for the stream to be ready to + /// accept data, the `subscribe` function to obtain a `pollable` which can be + /// polled for using `wasi:io/poll`. + /// + /// Dropping an `output-stream` while there's still an active write in + /// progress may result in the data being lost. Before dropping the stream, + /// be sure to fully flush your writes. + @since(version = 0.2.0) + resource output-stream { + /// Check readiness for writing. This function never blocks. + /// + /// Returns the number of bytes permitted for the next call to `write`, + /// or an error. Calling `write` with more bytes than this function has + /// permitted will trap. + /// + /// When this function returns 0 bytes, the `subscribe` pollable will + /// become ready when this function will report at least 1 byte, or an + /// error. + @since(version = 0.2.0) + check-write: func() -> result; + /// Perform a write. This function never blocks. + /// + /// When the destination of a `write` is binary data, the bytes from + /// `contents` are written verbatim. When the destination of a `write` is + /// known to the implementation to be text, the bytes of `contents` are + /// transcoded from UTF-8 into the encoding of the destination and then + /// written. + /// + /// Precondition: check-write gave permit of Ok(n) and contents has a + /// length of less than or equal to n. Otherwise, this function will trap. + /// + /// returns Err(closed) without writing if the stream has closed since + /// the last call to check-write provided a permit. + @since(version = 0.2.0) + write: func(contents: list) -> result<_, stream-error>; + /// Perform a write of up to 4096 bytes, and then flush the stream. Block + /// until all of these operations are complete, or an error occurs. + /// + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe`, `write`, and `flush`, and is implemented with the + /// following pseudo-code: + /// + /// ```text + /// let pollable = this.subscribe(); + /// while !contents.is_empty() { + /// // Wait for the stream to become writable + /// pollable.block(); + /// let Ok(n) = this.check-write(); // eliding error handling + /// let len = min(n, contents.len()); + /// let (chunk, rest) = contents.split_at(len); + /// this.write(chunk ); // eliding error handling + /// contents = rest; + /// } + /// this.flush(); + /// // Wait for completion of `flush` + /// pollable.block(); + /// // Check for any errors that arose during `flush` + /// let _ = this.check-write(); // eliding error handling + /// ``` + @since(version = 0.2.0) + blocking-write-and-flush: func(contents: list) -> result<_, stream-error>; + /// Request to flush buffered output. This function never blocks. + /// + /// This tells the output-stream that the caller intends any buffered + /// output to be flushed. the output which is expected to be flushed + /// is all that has been passed to `write` prior to this call. + /// + /// Upon calling this function, the `output-stream` will not accept any + /// writes (`check-write` will return `ok(0)`) until the flush has + /// completed. The `subscribe` pollable will become ready when the + /// flush has completed and the stream can accept more writes. + @since(version = 0.2.0) + flush: func() -> result<_, stream-error>; + /// Request to flush buffered output, and block until flush completes + /// and stream is ready for writing again. + @since(version = 0.2.0) + blocking-flush: func() -> result<_, stream-error>; + /// Create a `pollable` which will resolve once the output-stream + /// is ready for more writing, or an error has occurred. When this + /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an + /// error. + /// + /// If the stream is closed, this pollable is always ready immediately. + /// + /// The created `pollable` is a child resource of the `output-stream`. + /// Implementations may trap if the `output-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + @since(version = 0.2.0) + subscribe: func() -> pollable; + /// Write zeroes to a stream. + /// + /// This should be used precisely like `write` with the exact same + /// preconditions (must use check-write first), but instead of + /// passing a list of bytes, you simply pass the number of zero-bytes + /// that should be written. + @since(version = 0.2.0) + write-zeroes: func(len: u64) -> result<_, stream-error>; + /// Perform a write of up to 4096 zeroes, and then flush the stream. + /// Block until all of these operations are complete, or an error + /// occurs. + /// + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe`, `write-zeroes`, and `flush`, and is implemented with + /// the following pseudo-code: + /// + /// ```text + /// let pollable = this.subscribe(); + /// while num_zeroes != 0 { + /// // Wait for the stream to become writable + /// pollable.block(); + /// let Ok(n) = this.check-write(); // eliding error handling + /// let len = min(n, num_zeroes); + /// this.write-zeroes(len); // eliding error handling + /// num_zeroes -= len; + /// } + /// this.flush(); + /// // Wait for completion of `flush` + /// pollable.block(); + /// // Check for any errors that arose during `flush` + /// let _ = this.check-write(); // eliding error handling + /// ``` + @since(version = 0.2.0) + blocking-write-zeroes-and-flush: func(len: u64) -> result<_, stream-error>; + /// Read from one stream and write to another. + /// + /// The behavior of splice is equivalent to: + /// 1. calling `check-write` on the `output-stream` + /// 2. calling `read` on the `input-stream` with the smaller of the + /// `check-write` permitted length and the `len` provided to `splice` + /// 3. calling `write` on the `output-stream` with that read data. + /// + /// Any error reported by the call to `check-write`, `read`, or + /// `write` ends the splice and reports that error. + /// + /// This function returns the number of bytes transferred; it may be less + /// than `len`. + @since(version = 0.2.0) + splice: func(src: borrow, len: u64) -> result; + /// Read from one stream and write to another, with blocking. + /// + /// This is similar to `splice`, except that it blocks until the + /// `output-stream` is ready for writing, and the `input-stream` + /// is ready for reading, before performing the `splice`. + @since(version = 0.2.0) + blocking-splice: func(src: borrow, len: u64) -> result; + } +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import error; + @since(version = 0.2.0) + import poll; + @since(version = 0.2.0) + import streams; +} diff --git a/crates/cpex-wasm-plugin/wit/deps/random.wit b/crates/cpex-wasm-plugin/wit/deps/random.wit new file mode 100644 index 00000000..73edf5b6 --- /dev/null +++ b/crates/cpex-wasm-plugin/wit/deps/random.wit @@ -0,0 +1,92 @@ +package wasi:random@0.2.6; + +/// The insecure-seed interface for seeding hash-map DoS resistance. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +@since(version = 0.2.0) +interface insecure-seed { + /// Return a 128-bit value that may contain a pseudo-random value. + /// + /// The returned value is not required to be computed from a CSPRNG, and may + /// even be entirely deterministic. Host implementations are encouraged to + /// provide pseudo-random values to any program exposed to + /// attacker-controlled content, to enable DoS protection built into many + /// languages' hash-map implementations. + /// + /// This function is intended to only be called once, by a source language + /// to initialize Denial Of Service (DoS) protection in its hash-map + /// implementation. + /// + /// # Expected future evolution + /// + /// This will likely be changed to a value import, to prevent it from being + /// called multiple times and potentially used for purposes other than DoS + /// protection. + @since(version = 0.2.0) + insecure-seed: func() -> tuple; +} + +/// The insecure interface for insecure pseudo-random numbers. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +@since(version = 0.2.0) +interface insecure { + /// Return `len` insecure pseudo-random bytes. + /// + /// This function is not cryptographically secure. Do not use it for + /// anything related to security. + /// + /// There are no requirements on the values of the returned bytes, however + /// implementations are encouraged to return evenly distributed values with + /// a long period. + @since(version = 0.2.0) + get-insecure-random-bytes: func(len: u64) -> list; + + /// Return an insecure pseudo-random `u64` value. + /// + /// This function returns the same type of pseudo-random data as + /// `get-insecure-random-bytes`, represented as a `u64`. + @since(version = 0.2.0) + get-insecure-random-u64: func() -> u64; +} + +/// WASI Random is a random data API. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +@since(version = 0.2.0) +interface random { + /// Return `len` cryptographically-secure random or pseudo-random bytes. + /// + /// This function must produce data at least as cryptographically secure and + /// fast as an adequately seeded cryptographically-secure pseudo-random + /// number generator (CSPRNG). It must not block, from the perspective of + /// the calling program, under any circumstances, including on the first + /// request and on requests for numbers of bytes. The returned data must + /// always be unpredictable. + /// + /// This function must always return fresh data. Deterministic environments + /// must omit this function, rather than implementing it with deterministic + /// data. + @since(version = 0.2.0) + get-random-bytes: func(len: u64) -> list; + + /// Return a cryptographically-secure random or pseudo-random `u64` value. + /// + /// This function returns the same type of data as `get-random-bytes`, + /// represented as a `u64`. + @since(version = 0.2.0) + get-random-u64: func() -> u64; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import random; + @since(version = 0.2.0) + import insecure; + @since(version = 0.2.0) + import insecure-seed; +} diff --git a/crates/cpex-wasm-plugin/wit/deps/sockets.wit b/crates/cpex-wasm-plugin/wit/deps/sockets.wit new file mode 100644 index 00000000..db6d1a23 --- /dev/null +++ b/crates/cpex-wasm-plugin/wit/deps/sockets.wit @@ -0,0 +1,949 @@ +package wasi:sockets@0.2.6; + +@since(version = 0.2.0) +interface network { + @unstable(feature = network-error-code) + use wasi:io/error@0.2.6.{error}; + + /// An opaque resource that represents access to (a subset of) the network. + /// This enables context-based security for networking. + /// There is no need for this to map 1:1 to a physical network interface. + @since(version = 0.2.0) + resource network; + + /// Error codes. + /// + /// In theory, every API can return any error code. + /// In practice, API's typically only return the errors documented per API + /// combined with a couple of errors that are always possible: + /// - `unknown` + /// - `access-denied` + /// - `not-supported` + /// - `out-of-memory` + /// - `concurrency-conflict` + /// + /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. + @since(version = 0.2.0) + enum error-code { + /// Unknown error + unknown, + /// Access denied. + /// + /// POSIX equivalent: EACCES, EPERM + access-denied, + /// The operation is not supported. + /// + /// POSIX equivalent: EOPNOTSUPP + not-supported, + /// One of the arguments is invalid. + /// + /// POSIX equivalent: EINVAL + invalid-argument, + /// Not enough memory to complete the operation. + /// + /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY + out-of-memory, + /// The operation timed out before it could finish completely. + timeout, + /// This operation is incompatible with another asynchronous operation that is already in progress. + /// + /// POSIX equivalent: EALREADY + concurrency-conflict, + /// Trying to finish an asynchronous operation that: + /// - has not been started yet, or: + /// - was already finished by a previous `finish-*` call. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + not-in-progress, + /// The operation has been aborted because it could not be completed immediately. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + would-block, + /// The operation is not valid in the socket's current state. + invalid-state, + /// A new socket resource could not be created because of a system limit. + new-socket-limit, + /// A bind operation failed because the provided address is not an address that the `network` can bind to. + address-not-bindable, + /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. + address-in-use, + /// The remote address is not reachable + remote-unreachable, + /// The TCP connection was forcefully rejected + connection-refused, + /// The TCP connection was reset. + connection-reset, + /// A TCP connection was aborted. + connection-aborted, + /// The size of a datagram sent to a UDP socket exceeded the maximum + /// supported size. + datagram-too-large, + /// Name does not exist or has no suitable associated IP addresses. + name-unresolvable, + /// A temporary failure in name resolution occurred. + temporary-resolver-failure, + /// A permanent failure in name resolution occurred. + permanent-resolver-failure, + } + + @since(version = 0.2.0) + enum ip-address-family { + /// Similar to `AF_INET` in POSIX. + ipv4, + /// Similar to `AF_INET6` in POSIX. + ipv6, + } + + @since(version = 0.2.0) + type ipv4-address = tuple; + + @since(version = 0.2.0) + type ipv6-address = tuple; + + @since(version = 0.2.0) + variant ip-address { + ipv4(ipv4-address), + ipv6(ipv6-address), + } + + @since(version = 0.2.0) + record ipv4-socket-address { + /// sin_port + port: u16, + /// sin_addr + address: ipv4-address, + } + + @since(version = 0.2.0) + record ipv6-socket-address { + /// sin6_port + port: u16, + /// sin6_flowinfo + flow-info: u32, + /// sin6_addr + address: ipv6-address, + /// sin6_scope_id + scope-id: u32, + } + + @since(version = 0.2.0) + variant ip-socket-address { + ipv4(ipv4-socket-address), + ipv6(ipv6-socket-address), + } + + /// Attempts to extract a network-related `error-code` from the stream + /// `error` provided. + /// + /// Stream operations which return `stream-error::last-operation-failed` + /// have a payload with more information about the operation that failed. + /// This payload can be passed through to this function to see if there's + /// network-related information about the error to return. + /// + /// Note that this function is fallible because not all stream-related + /// errors are network-related errors. + @unstable(feature = network-error-code) + network-error-code: func(err: borrow) -> option; +} + +/// This interface provides a value-export of the default network handle.. +@since(version = 0.2.0) +interface instance-network { + @since(version = 0.2.0) + use network.{network}; + + /// Get a handle to the default network. + @since(version = 0.2.0) + instance-network: func() -> network; +} + +@since(version = 0.2.0) +interface ip-name-lookup { + @since(version = 0.2.0) + use wasi:io/poll@0.2.6.{pollable}; + @since(version = 0.2.0) + use network.{network, error-code, ip-address}; + + @since(version = 0.2.0) + resource resolve-address-stream { + /// Returns the next address from the resolver. + /// + /// This function should be called multiple times. On each call, it will + /// return the next address in connection order preference. If all + /// addresses have been exhausted, this function returns `none`. + /// + /// This function never returns IPv4-mapped IPv6 addresses. + /// + /// # Typical errors + /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) + /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) + /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) + /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) + @since(version = 0.2.0) + resolve-next-address: func() -> result, error-code>; + /// Create a `pollable` which will resolve once the stream is ready for I/O. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + /// Resolve an internet host name to a list of IP addresses. + /// + /// Unicode domain names are automatically converted to ASCII using IDNA encoding. + /// If the input is an IP address string, the address is parsed and returned + /// as-is without making any external requests. + /// + /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. + /// + /// This function never blocks. It either immediately fails or immediately + /// returns successfully with a `resolve-address-stream` that can be used + /// to (asynchronously) fetch the results. + /// + /// # Typical errors + /// - `invalid-argument`: `name` is a syntactically invalid domain name or IP address. + /// + /// # References: + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + resolve-addresses: func(network: borrow, name: string) -> result; +} + +@since(version = 0.2.0) +interface tcp { + @since(version = 0.2.0) + use wasi:io/streams@0.2.6.{input-stream, output-stream}; + @since(version = 0.2.0) + use wasi:io/poll@0.2.6.{pollable}; + @since(version = 0.2.0) + use wasi:clocks/monotonic-clock@0.2.6.{duration}; + @since(version = 0.2.0) + use network.{network, error-code, ip-socket-address, ip-address-family}; + + @since(version = 0.2.0) + enum shutdown-type { + /// Similar to `SHUT_RD` in POSIX. + receive, + /// Similar to `SHUT_WR` in POSIX. + send, + /// Similar to `SHUT_RDWR` in POSIX. + both, + } + + /// A TCP socket resource. + /// + /// The socket can be in one of the following states: + /// - `unbound` + /// - `bind-in-progress` + /// - `bound` (See note below) + /// - `listen-in-progress` + /// - `listening` + /// - `connect-in-progress` + /// - `connected` + /// - `closed` + /// See + /// for more information. + /// + /// Note: Except where explicitly mentioned, whenever this documentation uses + /// the term "bound" without backticks it actually means: in the `bound` state *or higher*. + /// (i.e. `bound`, `listen-in-progress`, `listening`, `connect-in-progress` or `connected`) + /// + /// In addition to the general error codes documented on the + /// `network::error-code` type, TCP socket methods may always return + /// `error(invalid-state)` when in the `closed` state. + @since(version = 0.2.0) + resource tcp-socket { + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// + /// Bind can be attempted multiple times on the same socket, even with + /// different arguments on each iteration. But never concurrently and + /// only as long as the previous bind failed. Once a bind succeeds, the + /// binding can't be changed anymore. + /// + /// # Typical errors + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL) + /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address. (EINVAL) + /// - `invalid-state`: The socket is already bound. (EINVAL) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT + /// state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR + /// socket option should be set implicitly on all platforms, except on Windows where this is the default behavior + /// and SO_REUSEADDR performs something different entirely. + /// + /// Unlike in POSIX, in WASI the bind operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `bind` as part of either `start-bind` or `finish-bind`. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + @since(version = 0.2.0) + finish-bind: func() -> result<_, error-code>; + /// Connect to a remote endpoint. + /// + /// On success: + /// - the socket is transitioned into the `connected` state. + /// - a pair of streams is returned that can be used to read & write to the connection + /// + /// After a failed connection attempt, the socket will be in the `closed` + /// state and the only valid action left is to `drop` the socket. A single + /// socket can not be used to connect more than once. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS) + /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address. (EINVAL, EADDRNOTAVAIL on Illumos) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN) + /// - `invalid-state`: The socket is already in the `listening` state. (EOPNOTSUPP, EINVAL on Windows) + /// - `timeout`: Connection timed out. (ETIMEDOUT) + /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) + /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `connection-aborted`: The connection was aborted. (ECONNABORTED) + /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `not-in-progress`: A connect operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// The POSIX equivalent of `start-connect` is the regular `connect` syscall. + /// Because all WASI sockets are non-blocking this is expected to return + /// EINPROGRESS, which should be translated to `ok()` in WASI. + /// + /// The POSIX equivalent of `finish-connect` is a `poll` for event `POLLOUT` + /// with a timeout of 0 on the socket descriptor. Followed by a check for + /// the `SO_ERROR` socket option, in case the poll signaled readiness. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; + @since(version = 0.2.0) + finish-connect: func() -> result, error-code>; + /// Start listening for new connections. + /// + /// Transitions the socket into the `listening` state. + /// + /// Unlike POSIX, the socket must already be explicitly bound. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. (EDESTADDRREQ) + /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN, EINVAL on BSD) + /// - `invalid-state`: The socket is already in the `listening` state. + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// - `not-in-progress`: A listen operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// Unlike in POSIX, in WASI the listen operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `listen` as part of either `start-listen` or `finish-listen`. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + start-listen: func() -> result<_, error-code>; + @since(version = 0.2.0) + finish-listen: func() -> result<_, error-code>; + /// Accept a new client socket. + /// + /// The returned socket is bound and in the `connected` state. The following properties are inherited from the listener socket: + /// - `address-family` + /// - `keep-alive-enabled` + /// - `keep-alive-idle-time` + /// - `keep-alive-interval` + /// - `keep-alive-count` + /// - `hop-limit` + /// - `receive-buffer-size` + /// - `send-buffer-size` + /// + /// On success, this function returns the newly accepted client socket along with + /// a pair of streams that can be used to read & write to the connection. + /// + /// # Typical errors + /// - `invalid-state`: Socket is not in the `listening` state. (EINVAL) + /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) + /// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + accept: func() -> result, error-code>; + /// Get the bound local address. + /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + local-address: func() -> result; + /// Get the remote address. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + remote-address: func() -> result; + /// Whether the socket is in the `listening` state. + /// + /// Equivalent to the SO_ACCEPTCONN socket option. + @since(version = 0.2.0) + is-listening: func() -> bool; + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + @since(version = 0.2.0) + address-family: func() -> ip-address-family; + /// Hints the desired listen queue size. Implementations are free to ignore this. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// + /// # Typical errors + /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. + /// - `invalid-argument`: (set) The provided value was 0. + /// - `invalid-state`: (set) The socket is in the `connect-in-progress` or `connected` state. + @since(version = 0.2.0) + set-listen-backlog-size: func(value: u64) -> result<_, error-code>; + /// Enables or disables keepalive. + /// + /// The keepalive behavior can be adjusted using: + /// - `keep-alive-idle-time` + /// - `keep-alive-interval` + /// - `keep-alive-count` + /// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true. + /// + /// Equivalent to the SO_KEEPALIVE socket option. + @since(version = 0.2.0) + keep-alive-enabled: func() -> result; + @since(version = 0.2.0) + set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; + /// Amount of time the connection has to be idle before TCP starts sending keepalive packets. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS) + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + keep-alive-idle-time: func() -> result; + @since(version = 0.2.0) + set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; + /// The time between keepalive packets. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPINTVL socket option. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + keep-alive-interval: func() -> result; + @since(version = 0.2.0) + set-keep-alive-interval: func(value: duration) -> result<_, error-code>; + /// The maximum amount of keepalive packets TCP should send before aborting the connection. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPCNT socket option. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + keep-alive-count: func() -> result; + @since(version = 0.2.0) + set-keep-alive-count: func(value: u32) -> result<_, error-code>; + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + @since(version = 0.2.0) + hop-limit: func() -> result; + @since(version = 0.2.0) + set-hop-limit: func(value: u8) -> result<_, error-code>; + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + receive-buffer-size: func() -> result; + @since(version = 0.2.0) + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + @since(version = 0.2.0) + send-buffer-size: func() -> result; + @since(version = 0.2.0) + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + /// Create a `pollable` which can be used to poll for, or block on, + /// completion of any of the asynchronous operations of this socket. + /// + /// When `finish-bind`, `finish-listen`, `finish-connect` or `accept` + /// return `error(would-block)`, this pollable can be used to wait for + /// their success or failure, after which the method can be retried. + /// + /// The pollable is not limited to the async operation that happens to be + /// in progress at the time of calling `subscribe` (if any). Theoretically, + /// `subscribe` only has to be called once per socket and can then be + /// (re)used for the remainder of the socket's lifetime. + /// + /// See + /// for more information. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + /// Initiate a graceful shutdown. + /// + /// - `receive`: The socket is not expecting to receive any data from + /// the peer. The `input-stream` associated with this socket will be + /// closed. Any data still in the receive queue at time of calling + /// this method will be discarded. + /// - `send`: The socket has no more data to send to the peer. The `output-stream` + /// associated with this socket will be closed and a FIN packet will be sent. + /// - `both`: Same effect as `receive` & `send` combined. + /// + /// This function is idempotent; shutting down a direction more than once + /// has no effect and returns `ok`. + /// + /// The shutdown function does not close (drop) the socket. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>; + } +} + +@since(version = 0.2.0) +interface tcp-create-socket { + @since(version = 0.2.0) + use network.{network, error-code, ip-address-family}; + @since(version = 0.2.0) + use tcp.{tcp-socket}; + + /// Create a new TCP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. + /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// + /// This function does not require a network capability handle. This is considered to be safe because + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` + /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + create-tcp-socket: func(address-family: ip-address-family) -> result; +} + +@since(version = 0.2.0) +interface udp { + @since(version = 0.2.0) + use wasi:io/poll@0.2.6.{pollable}; + @since(version = 0.2.0) + use network.{network, error-code, ip-socket-address, ip-address-family}; + + /// A received datagram. + @since(version = 0.2.0) + record incoming-datagram { + /// The payload. + /// + /// Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. + data: list, + /// The source address. + /// + /// This field is guaranteed to match the remote address the stream was initialized with, if any. + /// + /// Equivalent to the `src_addr` out parameter of `recvfrom`. + remote-address: ip-socket-address, + } + + /// A datagram to be sent out. + @since(version = 0.2.0) + record outgoing-datagram { + /// The payload. + data: list, + /// The destination address. + /// + /// The requirements on this field depend on how the stream was initialized: + /// - with a remote address: this field must be None or match the stream's remote address exactly. + /// - without a remote address: this field is required. + /// + /// If this value is None, the send operation is equivalent to `send` in POSIX. Otherwise it is equivalent to `sendto`. + remote-address: option, + } + + /// A UDP socket handle. + @since(version = 0.2.0) + resource udp-socket { + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the port is zero, the socket will be bound to a random free port. + /// + /// # Typical errors + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-state`: The socket is already bound. (EINVAL) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// Unlike in POSIX, in WASI the bind operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `bind` as part of either `start-bind` or `finish-bind`. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + @since(version = 0.2.0) + finish-bind: func() -> result<_, error-code>; + /// Set up inbound & outbound communication channels, optionally to a specific peer. + /// + /// This function only changes the local socket configuration and does not generate any network traffic. + /// On success, the `remote-address` of the socket is updated. The `local-address` may be updated as well, + /// based on the best network path to `remote-address`. + /// + /// When a `remote-address` is provided, the returned streams are limited to communicating with that specific peer: + /// - `send` can only be used to send to this destination. + /// - `receive` will only return datagrams sent from the provided `remote-address`. + /// + /// This method may be called multiple times on the same socket to change its association, but + /// only the most recently returned pair of streams will be operational. Implementations may trap if + /// the streams returned by a previous invocation haven't been dropped yet before calling `stream` again. + /// + /// The POSIX equivalent in pseudo-code is: + /// ```text + /// if (was previously connected) { + /// connect(s, AF_UNSPEC) + /// } + /// if (remote_address is Some) { + /// connect(s, remote_address) + /// } + /// ``` + /// + /// Unlike in POSIX, the socket must already be explicitly bound. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-state`: The socket is not bound. + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + %stream: func(remote-address: option) -> result, error-code>; + /// Get the current bound address. + /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + local-address: func() -> result; + /// Get the address the socket is currently streaming to. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not streaming to a specific remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + remote-address: func() -> result; + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + @since(version = 0.2.0) + address-family: func() -> ip-address-family; + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + @since(version = 0.2.0) + unicast-hop-limit: func() -> result; + @since(version = 0.2.0) + set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + receive-buffer-size: func() -> result; + @since(version = 0.2.0) + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + @since(version = 0.2.0) + send-buffer-size: func() -> result; + @since(version = 0.2.0) + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + /// Create a `pollable` which will resolve once the socket is ready for I/O. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + @since(version = 0.2.0) + resource incoming-datagram-stream { + /// Receive messages on the socket. + /// + /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. + /// The returned list may contain fewer elements than requested, but never more. + /// + /// This function returns successfully with an empty list when either: + /// - `max-results` is 0, or: + /// - `max-results` is greater than 0, but no results are immediately available. + /// This function never returns `error(would-block)`. + /// + /// # Typical errors + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + receive: func(max-results: u64) -> result, error-code>; + /// Create a `pollable` which will resolve once the stream is ready to receive again. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + @since(version = 0.2.0) + resource outgoing-datagram-stream { + /// Check readiness for sending. This function never blocks. + /// + /// Returns the number of datagrams permitted for the next call to `send`, + /// or an error. Calling `send` with more datagrams than this function has + /// permitted will trap. + /// + /// When this function returns ok(0), the `subscribe` pollable will + /// become ready when this function will report at least ok(1), or an + /// error. + /// + /// Never returns `would-block`. + check-send: func() -> result; + /// Send messages on the socket. + /// + /// This function attempts to send all provided `datagrams` on the socket without blocking and + /// returns how many messages were actually sent (or queued for sending). This function never + /// returns `error(would-block)`. If none of the datagrams were able to be sent, `ok(0)` is returned. + /// + /// This function semantically behaves the same as iterating the `datagrams` list and sequentially + /// sending each individual datagram until either the end of the list has been reached or the first error occurred. + /// If at least one datagram has been sent successfully, this function never returns an error. + /// + /// If the input list is empty, the function returns `ok(0)`. + /// + /// Each call to `send` must be permitted by a preceding `check-send`. Implementations must trap if + /// either `check-send` was not called or `datagrams` contains more items than `check-send` permitted. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The socket is in "connected" mode and `remote-address` is `some` value that does not match the address passed to `stream`. (EISCONN) + /// - `invalid-argument`: The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + send: func(datagrams: list) -> result; + /// Create a `pollable` which will resolve once the stream is ready to send again. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } +} + +@since(version = 0.2.0) +interface udp-create-socket { + @since(version = 0.2.0) + use network.{network, error-code, ip-address-family}; + @since(version = 0.2.0) + use udp.{udp-socket}; + + /// Create a new UDP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. + /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// + /// This function does not require a network capability handle. This is considered to be safe because + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called, + /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References: + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + create-udp-socket: func(address-family: ip-address-family) -> result; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import wasi:io/error@0.2.6; + @since(version = 0.2.0) + import network; + @since(version = 0.2.0) + import instance-network; + @since(version = 0.2.0) + import wasi:io/poll@0.2.6; + @since(version = 0.2.0) + import udp; + @since(version = 0.2.0) + import udp-create-socket; + @since(version = 0.2.0) + import wasi:io/streams@0.2.6; + @since(version = 0.2.0) + import wasi:clocks/monotonic-clock@0.2.6; + @since(version = 0.2.0) + import tcp; + @since(version = 0.2.0) + import tcp-create-socket; + @since(version = 0.2.0) + import ip-name-lookup; +} diff --git a/crates/cpex-wasm-plugin/wit/world.wit b/crates/cpex-wasm-plugin/wit/world.wit new file mode 100644 index 00000000..19f6b3d8 --- /dev/null +++ b/crates/cpex-wasm-plugin/wit/world.wit @@ -0,0 +1,231 @@ +// Location: ./crates/cpex-wasm-plugin/wit/world.wit +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Shriti Priya +// +// WIT world definition for the CPEX plugin component. +// Defines the types and exported function that the WASM host uses to invoke plugins. + +package cpex:plugin; + +interface types { + enum role { + system, + developer, + user, + assistant, + tool, + } + + enum channel { + analysis, + commentary, + final, + } + + enum resource-type { + file, + blob, + uri, + database, + api, + memory, + artifact, + } + + enum subject-type { + user, + agent, + service, + system, + } + + record image-source { + source-type: string, + data: string, + media-type: option, + } + + record video-source { + source-type: string, + data: string, + media-type: option, + duration-ms: option, + } + + record audio-source { + source-type: string, + data: string, + media-type: option, + duration-ms: option, + } + + record document-source { + source-type: string, + data: string, + media-type: option, + title: option, + } + + record tool-call { + tool-call-id: string, + name: string, + arguments: string, + namespace: option, + } + + record tool-result { + tool-call-id: string, + tool-name: string, + content: string, + is-error: bool, + } + + record cmf-resource { + resource-request-id: string, + uri: string, + name: option, + description: option, + resource-type: resource-type, + content: option, + blob: option>, + mime-type: option, + size-bytes: option, + annotations: string, + version: option, + } + + record resource-reference { + resource-request-id: string, + uri: string, + name: option, + resource-type: resource-type, + range-start: option, + range-end: option, + selector: option, + } + + record prompt-request { + prompt-request-id: string, + name: string, + arguments: string, + server-id: option, + } + + record prompt-result { + prompt-request-id: string, + prompt-name: string, + messages: string, + content: option, + is-error: bool, + error-message: option, + } + + variant content-part { + text(string), + thinking(string), + tool-call(tool-call), + tool-result(tool-result), + cmf-resource(cmf-resource), + resource-ref(resource-reference), + prompt-request(prompt-request), + prompt-result(prompt-result), + image(image-source), + video(video-source), + audio(audio-source), + document(document-source), + } + + record message { + schema-version: string, + role: role, + content: list, + channel: option, + } + + record message-payload { + message: message, + } + + record request-extension { + environment: option, + request-id: option, + timestamp: option, + trace-id: option, + span-id: option, + } + + record subject-extension { + id: option, + subject-type: option, + roles: list, + permissions: list, + teams: list, + claims: list>, + } + + record security-extension { + labels: list, + classification: option, + subject: option, + auth-method: option, + } + + record http-extension { + request-headers: list>, + response-headers: list>, + } + + record meta-extension { + entity-type: option, + entity-name: option, + tags: list, + scope: option, + properties: list>, + } + + record extensions { + request: option, + security: option, + http: option, + meta: option, + } + + record plugin-context { + local-state: string, + global-state: string, + } + + record plugin-violation { + code: string, + reason: string, + description: option, + details: string, + proto-error-code: option, + } + + record plugin-result { + continue-processing: bool, + modified-payload: option, + modified-extensions: option, + violation: option, + metadata: option, + } +} + +world plugin { + import wasi:io/poll@0.2.6; + import wasi:io/error@0.2.6; + import wasi:io/streams@0.2.6; + import wasi:clocks/monotonic-clock@0.2.6; + import wasi:http/types@0.2.6; + import wasi:http/outgoing-handler@0.2.6; + + use types.{message-payload, extensions, plugin-context, plugin-result}; + + export handle-hook: func( + payload: message-payload, + extensions: extensions, + ctx: plugin-context + ) -> plugin-result; +} From dc0c636a2dacda428235689d9a6e1c07d52a3151 Mon Sep 17 00:00:00 2001 From: Shriti Priya Date: Tue, 9 Jun 2026 14:24:45 -0400 Subject: [PATCH 2/7] feat: adding lightweight cpex-payload crate for adding plugin related functionality in wasm compilation Signed-off-by: Shriti Priya --- Cargo.lock | 3316 ++++++++++++----- Cargo.toml | 5 + crates/cpex-payload/Cargo.toml | 26 + crates/cpex-payload/src/cmf/constants.rs | 65 + crates/cpex-payload/src/cmf/content.rs | 486 +++ crates/cpex-payload/src/cmf/enums.rs | 176 + crates/cpex-payload/src/cmf/message.rs | 460 +++ crates/cpex-payload/src/cmf/mod.rs | 100 + crates/cpex-payload/src/cmf/view.rs | 864 +++++ crates/cpex-payload/src/config.rs | 1297 +++++++ crates/cpex-payload/src/context.rs | 195 + crates/cpex-payload/src/error.rs | 275 ++ crates/cpex-payload/src/extensions/agent.rs | 60 + .../cpex-payload/src/extensions/completion.rs | 71 + .../cpex-payload/src/extensions/container.rs | 711 ++++ .../cpex-payload/src/extensions/delegation.rs | 165 + crates/cpex-payload/src/extensions/filter.rs | 547 +++ .../cpex-payload/src/extensions/framework.rs | 38 + crates/cpex-payload/src/extensions/guarded.rs | 144 + crates/cpex-payload/src/extensions/http.rs | 210 ++ crates/cpex-payload/src/extensions/llm.rs | 27 + crates/cpex-payload/src/extensions/mcp.rs | 115 + crates/cpex-payload/src/extensions/meta.rs | 45 + crates/cpex-payload/src/extensions/mod.rs | 52 + .../cpex-payload/src/extensions/monotonic.rs | 179 + .../cpex-payload/src/extensions/provenance.rs | 27 + crates/cpex-payload/src/extensions/request.rs | 35 + .../cpex-payload/src/extensions/security.rs | 348 ++ crates/cpex-payload/src/extensions/tiers.rs | 100 + crates/cpex-payload/src/hooks/macros.rs | 70 + crates/cpex-payload/src/hooks/mod.rs | 27 + crates/cpex-payload/src/hooks/payload.rs | 133 + crates/cpex-payload/src/hooks/trait_def.rs | 322 ++ crates/cpex-payload/src/hooks/types.rs | 190 + crates/cpex-payload/src/lib.rs | 32 + crates/cpex-payload/src/plugin.rs | 553 +++ .../src/plugins/identity_checker.rs | 75 + crates/cpex-payload/src/plugins/mod.rs | 1 + 38 files changed, 10603 insertions(+), 939 deletions(-) create mode 100644 crates/cpex-payload/Cargo.toml create mode 100644 crates/cpex-payload/src/cmf/constants.rs create mode 100644 crates/cpex-payload/src/cmf/content.rs create mode 100644 crates/cpex-payload/src/cmf/enums.rs create mode 100644 crates/cpex-payload/src/cmf/message.rs create mode 100644 crates/cpex-payload/src/cmf/mod.rs create mode 100644 crates/cpex-payload/src/cmf/view.rs create mode 100644 crates/cpex-payload/src/config.rs create mode 100644 crates/cpex-payload/src/context.rs create mode 100644 crates/cpex-payload/src/error.rs create mode 100644 crates/cpex-payload/src/extensions/agent.rs create mode 100644 crates/cpex-payload/src/extensions/completion.rs create mode 100644 crates/cpex-payload/src/extensions/container.rs create mode 100644 crates/cpex-payload/src/extensions/delegation.rs create mode 100644 crates/cpex-payload/src/extensions/filter.rs create mode 100644 crates/cpex-payload/src/extensions/framework.rs create mode 100644 crates/cpex-payload/src/extensions/guarded.rs create mode 100644 crates/cpex-payload/src/extensions/http.rs create mode 100644 crates/cpex-payload/src/extensions/llm.rs create mode 100644 crates/cpex-payload/src/extensions/mcp.rs create mode 100644 crates/cpex-payload/src/extensions/meta.rs create mode 100644 crates/cpex-payload/src/extensions/mod.rs create mode 100644 crates/cpex-payload/src/extensions/monotonic.rs create mode 100644 crates/cpex-payload/src/extensions/provenance.rs create mode 100644 crates/cpex-payload/src/extensions/request.rs create mode 100644 crates/cpex-payload/src/extensions/security.rs create mode 100644 crates/cpex-payload/src/extensions/tiers.rs create mode 100644 crates/cpex-payload/src/hooks/macros.rs create mode 100644 crates/cpex-payload/src/hooks/mod.rs create mode 100644 crates/cpex-payload/src/hooks/payload.rs create mode 100644 crates/cpex-payload/src/hooks/trait_def.rs create mode 100644 crates/cpex-payload/src/hooks/types.rs create mode 100644 crates/cpex-payload/src/lib.rs create mode 100644 crates/cpex-payload/src/plugin.rs create mode 100644 crates/cpex-payload/src/plugins/identity_checker.rs create mode 100644 crates/cpex-payload/src/plugins/mod.rs diff --git a/Cargo.lock b/Cargo.lock index b2927d83..40036aa6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,33 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "addr2line" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59317f77929f0e679d39364702289274de2f0f0b22cbf50b2b8cff2169a0b27a" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -17,6 +44,12 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "ambient-authority" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -27,31 +60,45 @@ dependencies = [ ] [[package]] -name = "antlr4rust" -version = "0.5.2" +name = "anyhow" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093d520274bfff7278d776f7ea12981a0a0a6f96db90964658e0f38fc6e9a6a6" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "apl-audit-logger" +version = "0.1.0" dependencies = [ - "better_any", - "bit-set", - "byteorder", - "lazy_static", - "murmur3", - "once_cell", - "parking_lot", - "typed-arena", - "uuid", + "async-trait", + "chrono", + "cpex-core", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tracing", ] [[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +name = "apl-cedarling" +version = "0.1.0" +dependencies = [ + "apl-core", + "async-trait", + "cedar-policy", + "cedarling", + "cpex-core", + "serde", + "serde_json", + "serde_yaml", + "thiserror 2.0.18", + "tokio", + "tracing", +] [[package]] name = "apl-cmf" -version = "0.2.0" +version = "0.1.0" dependencies = [ "apl-core", "async-trait", @@ -62,7 +109,7 @@ dependencies = [ [[package]] name = "apl-core" -version = "0.2.0" +version = "0.1.0" dependencies = [ "async-trait", "cpex-orchestration", @@ -77,7 +124,7 @@ dependencies = [ [[package]] name = "apl-cpex" -version = "0.2.0" +version = "0.1.0" dependencies = [ "apl-cmf", "apl-core", @@ -88,6 +135,98 @@ dependencies = [ "serde_json", "serde_yaml", "sha2 0.10.9", + "tokio", + "tracing", +] + +[[package]] +name = "apl-delegator-biscuit" +version = "0.1.0" +dependencies = [ + "apl-core", + "async-trait", + "biscuit-auth", + "chrono", + "cpex-core", + "hex", + "serde", + "serde_json", + "serde_yaml", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "apl-delegator-oauth" +version = "0.1.0" +dependencies = [ + "apl-core", + "async-trait", + "chrono", + "cpex-core", + "mockito", + "reqwest 0.12.28", + "serde", + "serde_json", + "serde_urlencoded", + "serde_yaml", + "thiserror 2.0.18", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "apl-identity-jwt" +version = "0.1.0" +dependencies = [ + "apl-core", + "async-trait", + "base64 0.22.1", + "chrono", + "cpex-core", + "futures", + "jsonwebtoken 9.3.1", + "mockito", + "rand 0.8.6", + "reqwest 0.12.28", + "rsa", + "serde", + "serde_json", + "serde_yaml", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "apl-pdp-cedar-direct" +version = "0.1.0" +dependencies = [ + "apl-cmf", + "apl-core", + "apl-cpex", + "async-trait", + "cedar-policy", + "cpex-core", + "serde", + "serde_json", + "serde_yaml", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "apl-pii-scanner" +version = "0.1.0" +dependencies = [ + "async-trait", + "cpex-core", + "regex", + "serde", + "serde_json", "thiserror 2.0.18", "tokio", "tracing", @@ -99,9 +238,15 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" dependencies = [ - "object", + "object 0.37.3", ] +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + [[package]] name = "arc-swap" version = "1.9.1" @@ -112,10 +257,10 @@ dependencies = [ ] [[package]] -name = "arcstr" -version = "1.2.0" +name = "arraydeque" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03918c3dbd7701a85c6b9887732e2921175f26c350b4563841d0958c21d57e6d" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" [[package]] name = "arrayvec" @@ -142,55 +287,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "astral-tokio-tar" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec179a06c1769b1e42e1e2cbe74c7dcdb3d6383c838454d063eaac5bbb7ebbe5" -dependencies = [ - "filetime", - "futures-core", - "libc", - "portable-atomic", - "rustc-hash", - "tokio", - "tokio-stream", - "xattr", -] - -[[package]] -name = "async-lock" -version = "3.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "async-trait" version = "0.1.89" @@ -221,7 +317,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" dependencies = [ "aws-lc-sys", - "untrusted 0.7.1", "zeroize", ] @@ -237,58 +332,6 @@ dependencies = [ "fs_extra", ] -[[package]] -name = "axum" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" -dependencies = [ - "axum-core", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "serde_core", - "sync_wrapper", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "sync_wrapper", - "tower-layer", - "tower-service", -] - -[[package]] -name = "backon" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" -dependencies = [ - "fastrand", -] - [[package]] name = "base16ct" version = "0.2.0" @@ -301,6 +344,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -313,12 +362,6 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" -[[package]] -name = "better_any" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4372b9543397a4b86050cc5e7ee36953edf4bac9518e8a774c2da694977fb6e4" - [[package]] name = "biscuit-auth" version = "6.0.0" @@ -336,8 +379,8 @@ dependencies = [ "nom", "p256", "pkcs8 0.9.0", - "prost 0.10.4", - "prost-types 0.10.1", + "prost", + "prost-types", "rand 0.8.6", "rand_core 0.6.4", "regex", @@ -395,6 +438,9 @@ name = "bitflags" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +dependencies = [ + "serde_core", +] [[package]] name = "block-buffer" @@ -415,118 +461,130 @@ dependencies = [ ] [[package]] -name = "bollard" -version = "0.19.4" +name = "block-buffer" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87a52479c9237eb04047ddb94788c41ca0d26eaff8b697ecfbb4c32f7fdc3b1b" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "borsh" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" dependencies = [ - "async-stream", - "base64 0.22.1", - "bitflags", - "bollard-buildkit-proto", - "bollard-stubs", "bytes", - "chrono", - "futures-core", - "futures-util", - "hex", - "home", - "http", - "http-body-util", - "hyper", - "hyper-named-pipe", - "hyper-rustls", - "hyper-util", - "hyperlocal", - "log", - "num", - "pin-project-lite", - "rand 0.9.4", - "rustls", - "rustls-native-certs", - "rustls-pemfile", - "rustls-pki-types", - "serde", - "serde_derive", - "serde_json", - "serde_repr", - "serde_urlencoded", - "thiserror 2.0.18", - "tokio", - "tokio-stream", - "tokio-util", - "tonic", - "tower-service", - "url", - "winapi", + "cfg_aliases", ] [[package]] -name = "bollard-buildkit-proto" -version = "0.7.0" +name = "bs58" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a885520bf6249ab931a764ffdb87b0ceef48e6e7d807cfdb21b751e086e1ad" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ - "prost 0.14.4", - "prost-types 0.14.4", - "tonic", - "tonic-prost", - "ureq", + "tinyvec", ] [[package]] -name = "bollard-stubs" -version = "1.49.1-rc.28.4.0" +name = "bumpalo" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5731fe885755e92beff1950774068e0cae67ea6ec7587381536fca84f1779623" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" dependencies = [ - "base64 0.22.1", - "bollard-buildkit-proto", - "bytes", - "chrono", - "prost 0.14.4", - "serde", - "serde_json", - "serde_repr", - "serde_with", + "allocator-api2", ] [[package]] -name = "borsh" -version = "1.6.1" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "bzip2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" dependencies = [ - "bytes", - "cfg_aliases", + "libbz2-rs-sys", ] [[package]] -name = "bs58" -version = "0.5.1" +name = "cap-fs-ext" +version = "3.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +checksum = "d5528f85b1e134ae811704e41ef80930f56e795923f866813255bc342cc20654" dependencies = [ - "tinyvec", + "cap-primitives", + "cap-std", + "io-lifetimes", + "windows-sys 0.52.0", ] [[package]] -name = "bumpalo" -version = "3.20.2" +name = "cap-net-ext" +version = "3.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "20a158160765c6a7d0d8c072a53d772e4cb243f38b04bfcf6b4939cfbe7482e7" +dependencies = [ + "cap-primitives", + "cap-std", + "rustix", + "smallvec", +] [[package]] -name = "byteorder" -version = "1.5.0" +name = "cap-primitives" +version = "3.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "b6cf3aea8a5081171859ef57bc1606b1df6999df4f1110f8eef68b30098d1d3a" +dependencies = [ + "ambient-authority", + "fs-set-times", + "io-extras", + "io-lifetimes", + "ipnet", + "maybe-owned", + "rustix", + "rustix-linux-procfs", + "windows-sys 0.52.0", + "winx", +] [[package]] -name = "bytes" -version = "1.11.1" +name = "cap-std" +version = "3.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "b6dc3090992a735d23219de5c204927163d922f42f575a0189b005c62d37549a" +dependencies = [ + "cap-primitives", + "io-extras", + "io-lifetimes", + "rustix", +] + +[[package]] +name = "cap-time-ext" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "def102506ce40c11710a9b16e614af0cde8e76ae51b1f48c04b8d79f4b671a80" +dependencies = [ + "ambient-authority", + "cap-primitives", + "iana-time-zone", + "once_cell", + "rustix", + "winx", +] [[package]] name = "cc" @@ -542,9 +600,9 @@ dependencies = [ [[package]] name = "cedar-policy" -version = "4.11.2" +version = "4.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fdd98501120bc31fc09f92d3f58c489f4ee4b846904109ac63dd8256de7b84d" +checksum = "674ca6ef6e44a1e29e6c07f6b2ab7e6913938d6049204d48e7849703d02443ce" dependencies = [ "cedar-policy-core", "cedar-policy-formatter", @@ -562,9 +620,9 @@ dependencies = [ [[package]] name = "cedar-policy-core" -version = "4.11.2" +version = "4.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1d41088b497e39b6789e87e05ad6549b7f64a663a4d6ecd73a748dc3a9a98a9" +checksum = "3df781c108240c3b3778c586c6a1b234355f1a2f9d35e08630b63b2590c59dbb" dependencies = [ "chrono", "educe", @@ -590,9 +648,9 @@ dependencies = [ [[package]] name = "cedar-policy-formatter" -version = "4.11.2" +version = "4.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb6c28be47d77dcbc04c2a80181a2c0db9725ed60d24832c6401396cc2ae114b" +checksum = "e293607563253e249d19cf4d36ac05821955170a63cf6d65deb4db60138b192e" dependencies = [ "cedar-policy-core", "itertools 0.14.0", @@ -604,19 +662,47 @@ dependencies = [ ] [[package]] -name = "cel" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47a40f338a8c3505921000b609279775792c07cc21f97a3011578c0c5e1738ae" +name = "cedarling" +version = "2.1.0" +source = "git+https://github.com/JanssenProject/jans?tag=v2.1.0#3a089405993a1832135092857258c774cbbbb215" dependencies = [ - "antlr4rust", + "ahash", + "async-trait", + "base64 0.22.1", + "cedar-policy", + "cedar-policy-core", "chrono", - "lazy_static", - "nom", - "pastey", - "regex", + "config", + "derive_more", + "flate2", + "futures", + "getrandom 0.2.17", + "getrandom 0.3.4", + "getrandom 0.4.2", + "gloo-timers", + "hdrhistogram", + "http_utils", + "jsonwebtoken 10.4.0", + "rand 0.10.1", + "reqwest 0.13.3", + "semver", "serde", - "thiserror 1.0.69", + "serde_json", + "serde_yaml_ng", + "smol_str", + "sparkv", + "strum", + "thiserror 2.0.18", + "time", + "tokio", + "tokio-util", + "typed-builder", + "url", + "uuid7", + "vfs", + "wasm-bindgen-futures", + "web-sys", + "zip", ] [[package]] @@ -631,11 +717,22 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "chrono" -version = "0.4.45" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", @@ -654,6 +751,15 @@ dependencies = [ "cc", ] +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror 2.0.18", +] + [[package]] name = "colored" version = "3.1.1" @@ -670,20 +776,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", - "futures-core", "memchr", - "pin-project-lite", - "tokio", - "tokio-util", ] [[package]] -name = "concurrent-queue" -version = "2.5.0" +name = "config" +version = "0.15.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +checksum = "f316c6237b2d38be61949ecd15268a4c6ca32570079394a2444d9ce2c72a72d8" dependencies = [ - "crossbeam-utils", + "async-trait", + "convert_case 0.6.0", + "json5", + "pathdiff", + "ron", + "rust-ini", + "serde-untagged", + "serde_core", + "serde_json", + "toml 1.1.2+spec-1.1.0", + "winnow 1.0.3", + "yaml-rust2", ] [[package]] @@ -693,58 +806,85 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] -name = "core-foundation" -version = "0.10.1" +name = "const-oid" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" dependencies = [ - "core-foundation-sys", - "libc", + "const-random-macro", ] [[package]] -name = "core-foundation-sys" -version = "0.8.7" +name = "const-random-macro" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "tiny-keccak", +] [[package]] -name = "cpex" -version = "0.2.0" +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" dependencies = [ - "apl-cmf", - "apl-core", - "apl-cpex", - "cpex-builtins", - "cpex-core", + "unicode-segmentation", ] [[package]] -name = "cpex-builtins" -version = "0.2.0" +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" dependencies = [ - "apl-core", - "apl-cpex", - "cpex-core", - "cpex-pdp-cedar-direct", - "cpex-pdp-cel", - "cpex-plugin-audit-logger", - "cpex-plugin-delegator-oauth", - "cpex-plugin-identity-jwt", - "cpex-plugin-pii-scanner", - "cpex-session-valkey", - "tokio", + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpex-core" -version = "0.2.0" +version = "0.1.0" dependencies = [ "arc-swap", "async-trait", "chrono", "cpex-orchestration", - "hashbrown 0.17.0", + "futures", + "hashbrown 0.15.5", "serde", "serde_json", "serde_yaml", @@ -764,16 +904,22 @@ dependencies = [ "async-trait", "cpex-core", "cpex-ffi", + "serde_json", "tracing", ] [[package]] name = "cpex-ffi" -version = "0.2.0" +version = "0.1.0" dependencies = [ + "apl-audit-logger", + "apl-cedarling", "apl-cpex", + "apl-delegator-oauth", + "apl-identity-jwt", + "apl-pdp-cedar-direct", + "apl-pii-scanner", "async-trait", - "cpex-builtins", "cpex-core", "rmp-serde", "serde", @@ -785,153 +931,258 @@ dependencies = [ [[package]] name = "cpex-orchestration" -version = "0.2.0" +version = "0.1.0" dependencies = [ "futures", "tokio", ] [[package]] -name = "cpex-pdp-cedar-direct" -version = "0.2.0" +name = "cpex-payload" +version = "0.1.0" dependencies = [ - "apl-cmf", - "apl-core", - "apl-cpex", "async-trait", - "cedar-policy", - "cpex-core", - "futures", + "serde", "serde_json", "serde_yaml", - "stacker", - "thiserror 2.0.18", - "tokio", -] - -[[package]] -name = "cpex-pdp-cel" -version = "0.2.0" -dependencies = [ - "apl-cmf", - "apl-core", - "apl-cpex", - "async-trait", - "cel", - "cpex-core", - "serde_yaml", "thiserror 2.0.18", "tokio", "tracing", + "uuid", + "wildmatch", ] [[package]] -name = "cpex-plugin-audit-logger" -version = "0.2.0" +name = "cpex-sdk" +version = "0.1.0" dependencies = [ "async-trait", - "chrono", "cpex-core", "serde", "serde_json", - "tokio", - "tracing", ] [[package]] -name = "cpex-plugin-delegator-biscuit" -version = "0.2.0" +name = "cpex-wasm-host" +version = "0.1.0" dependencies = [ + "anyhow", "async-trait", - "biscuit-auth", - "chrono", "cpex-core", - "hex", + "hyper", "serde", "serde_json", + "serde_yaml", "tokio", + "wasmtime", + "wasmtime-wasi", + "wasmtime-wasi-http", ] [[package]] -name = "cpex-plugin-delegator-oauth" -version = "0.2.0" +name = "cpp_demangle" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2bb79cb74d735044c972aae58ed0aaa9a837e85b01106a54c39e42e97f62253" dependencies = [ - "async-trait", - "chrono", - "cpex-core", - "mockito", - "reqwest", - "serde", - "serde_json", - "tokio", - "zeroize", + "cfg-if", ] [[package]] -name = "cpex-plugin-identity-jwt" -version = "0.2.0" -dependencies = [ - "async-trait", - "base64 0.22.1", - "chrono", - "cpex-core", - "futures", - "jsonwebtoken", - "mockito", - "rand 0.8.6", - "reqwest", - "rsa", +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-assembler-x64" +version = "0.132.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c4ebb31662e2051dcc49b7342d222405a99e951720756cc4b93315972abd67" +dependencies = [ + "cranelift-assembler-x64-meta", +] + +[[package]] +name = "cranelift-assembler-x64-meta" +version = "0.132.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dfc2ec96ec1c3a8a250602e936712e00a381df032f7a8ad175c8f768c03bb" +dependencies = [ + "cranelift-srcgen", +] + +[[package]] +name = "cranelift-bforest" +version = "0.132.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5694aa8a2eb2571a15b3feee38d16ccaf2712200e7b5c9ae0479069bdfb46949" +dependencies = [ + "cranelift-entity", + "wasmtime-internal-core", +] + +[[package]] +name = "cranelift-bitset" +version = "0.132.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ab349d30a5fad9699440ee7ccb435374e8a8735dcca26696a4245bcefcc47e" +dependencies = [ "serde", - "serde_json", - "tokio", - "tracing", + "serde_derive", + "wasmtime-internal-core", ] [[package]] -name = "cpex-plugin-pii-scanner" -version = "0.2.0" +name = "cranelift-codegen" +version = "0.132.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e95970bdb51d145c828a114a1084cb8b63e65569a51600ad398cb49fa78b062" dependencies = [ - "async-trait", - "cpex-core", - "regex", + "bumpalo", + "cranelift-assembler-x64", + "cranelift-bforest", + "cranelift-bitset", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "gimli", + "hashbrown 0.17.0", + "libm", + "log", + "pulley-interpreter", + "regalloc2", + "rustc-hash", "serde", - "serde_json", - "tokio", + "smallvec", + "target-lexicon", + "wasmtime-internal-core", ] [[package]] -name = "cpex-sdk" -version = "0.2.0" +name = "cranelift-codegen-meta" +version = "0.132.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e8414b8ecc81f89f8a3f2c5cbc785b9ed690200cd6f9d780e96a92f88879704" dependencies = [ - "cpex-core", + "cranelift-assembler-x64-meta", + "cranelift-codegen-shared", + "cranelift-srcgen", + "heck", + "pulley-interpreter", ] [[package]] -name = "cpex-session-valkey" -version = "0.2.0" +name = "cranelift-codegen-shared" +version = "0.132.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631c4e5db42e6a0f9e7a68f18f7faba3692862114870c7c598aee7e0e5677e59" + +[[package]] +name = "cranelift-control" +version = "0.132.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09c6e92c825abfbb739a4beaa5db3988f98a96a68d6ea656f562098efc142976" dependencies = [ - "apl-cpex", - "async-trait", - "deadpool-redis", - "redis", + "arbitrary", +] + +[[package]] +name = "cranelift-entity" +version = "0.132.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55e57cd185782abada9ab2606bfe88d0abc0d42d83a7d432dcf69991e17fe76e" +dependencies = [ + "cranelift-bitset", "serde", - "serde_yaml", - "sha2 0.10.9", - "testcontainers", - "testcontainers-modules", - "thiserror 2.0.18", - "tokio", - "tracing", - "url", + "serde_derive", + "wasmtime-internal-core", ] [[package]] -name = "cpufeatures" -version = "0.2.17" +name = "cranelift-frontend" +version = "0.132.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +checksum = "8a0f17e48d15e29552e2f264d302c31a661a830196d879bbdaf4d7b2bf2f7011" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.132.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "407b80b46934c9dce9a6581f0e80d079b7a69e11372fb03d7dce4c7ba3fee4e3" + +[[package]] +name = "cranelift-native" +version = "0.132.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a40e056e421d9a6c757983f2184f765ae1c28a51555d3877e98afc97ce5705b" dependencies = [ + "cranelift-codegen", "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-srcgen" +version = "0.132.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cdbadda21e49798825a1ec795dab30bcb03235891f662b5ccf23fa45b39682f" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", ] [[package]] @@ -940,6 +1191,12 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -962,6 +1219,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + [[package]] name = "curve25519-dalek" version = "4.1.3" @@ -969,7 +1235,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", @@ -1024,34 +1290,19 @@ dependencies = [ ] [[package]] -name = "deadpool" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883466cb8db62725aee5f4a6011e8a5d42912b42632df32aad57fc91127c6e04" -dependencies = [ - "deadpool-runtime", - "num_cpus", - "tokio", -] - -[[package]] -name = "deadpool-redis" -version = "0.23.0" +name = "debugid" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bafa30c49dafe086d10116074e422ad7fc1c3cf554697e744a3ab112599ebd09" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" dependencies = [ - "deadpool", - "redis", + "uuid", ] [[package]] -name = "deadpool-runtime" -version = "0.3.1" +name = "deflate64" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2657f61fb1dd8bf37a8d51093cc7cee4e77125b22f7753f49b289f831bec2bae" -dependencies = [ - "tokio", -] +checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2" [[package]] name = "der" @@ -1059,7 +1310,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ - "const-oid", + "const-oid 0.9.6", ] [[package]] @@ -1068,7 +1319,7 @@ version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ - "const-oid", + "const-oid 0.9.6", "pem-rfc7468", "zeroize", ] @@ -1083,6 +1334,29 @@ dependencies = [ "serde_core", ] +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case 0.10.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", + "unicode-xid", +] + [[package]] name = "digest" version = "0.9.0" @@ -1099,11 +1373,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", - "const-oid", - "crypto-common", + "const-oid 0.9.6", + "crypto-common 0.1.7", "subtle", ] +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.0", + "const-oid 0.10.2", + "crypto-common 0.2.2", +] + +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1116,14 +1422,12 @@ dependencies = [ ] [[package]] -name = "docker_credential" -version = "1.4.0" +name = "dlv-list" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29547a1dc60885a552306986316bc9701ba120c1a8db6769fa68691529ad373d" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" dependencies = [ - "base64 0.22.1", - "serde", - "serde_json", + "const-random", ] [[package]] @@ -1208,6 +1512,7 @@ dependencies = [ "ff", "generic-array", "group", + "hkdf", "pem-rfc7468", "pkcs8 0.10.2", "rand_core 0.6.4", @@ -1217,6 +1522,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "ena" version = "0.14.4" @@ -1226,6 +1543,15 @@ dependencies = [ "log", ] +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "enum-ordinalize" version = "4.3.2" @@ -1253,63 +1579,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "errno" -version = "0.3.14" +name = "erased-serde" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" dependencies = [ - "libc", - "windows-sys 0.61.2", + "serde", + "serde_core", + "typeid", ] [[package]] -name = "etcetera" -version = "0.11.0" +name = "errno" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ - "cfg-if", + "libc", "windows-sys 0.61.2", ] -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", -] - [[package]] name = "fastrand" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" -[[package]] -name = "ferroid" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb330bbd4cb7a5b9f559427f06f98a4f853a137c8298f3bd3f8ca57663e21986" -dependencies = [ - "portable-atomic", - "rand 0.9.4", - "web-time", -] - [[package]] name = "ff" version = "0.13.1" @@ -1342,12 +1637,29 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "fixedbitset" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1375,12 +1687,29 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs-set-times" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e7099f6313ecacbe1256e8ff9d617b75d1bcb16a6fddef94866d225a01a14a" +dependencies = [ + "io-lifetimes", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "fs_extra" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "fstr" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3b8f793a77bb6d48059953a3e9820fd860d19a9bed8164ed3572eb1981ec8aa" + [[package]] name = "futures" version = "0.3.32" @@ -1469,6 +1798,20 @@ dependencies = [ "slab", ] +[[package]] +name = "fxprof-processed-profile" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25234f20a3ec0a962a61770cfe39ecf03cb529a6e474ad8cff025ed497eda557" +dependencies = [ + "bitflags", + "debugid", + "rustc-hash", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1514,10 +1857,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", + "rand_core 0.10.1", "wasip2", "wasip3", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7f043f89559805f8c7cacc432749b2fa0d0a0a9ee46ce47164ed5ba7f126c" +dependencies = [ + "fnv", + "hashbrown 0.16.1", + "indexmap 2.14.0", + "stable_deref_trait", +] + +[[package]] +name = "gloo-timers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "482ce8a491a501da4cd806bd190275363d674f2845005c6ddbd5d3e1dd54495d" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", ] [[package]] @@ -1556,37 +1926,71 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.1.5", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash 0.2.0", +] + [[package]] name = "hashbrown" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" dependencies = [ - "allocator-api2", - "equivalent", "foldhash 0.2.0", + "serde", + "serde_core", ] [[package]] -name = "heck" -version = "0.5.0" +name = "hashlink" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230" +dependencies = [ + "hashbrown 0.16.1", +] [[package]] -name = "hermit-abi" -version = "0.5.2" +name = "hdrhistogram" +version = "7.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +dependencies = [ + "base64 0.21.7", + "byteorder", + "crossbeam-channel", + "flate2", + "nom", + "num-traits", +] + +[[package]] +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hex" @@ -1595,21 +1999,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "hmac" -version = "0.12.1" +name = "hkdf" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "digest 0.10.7", + "hmac", ] [[package]] -name = "home" -version = "0.5.12" +name = "hmac" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "windows-sys 0.61.2", + "digest 0.10.7", ] [[package]] @@ -1645,6 +2049,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http_utils" +version = "0.1.0" +source = "git+https://github.com/JanssenProject/jans?tag=v2.1.0#3a089405993a1832135092857258c774cbbbb215" +dependencies = [ + "reqwest 0.13.3", + "serde", + "thiserror 2.0.18", + "tokio", +] + [[package]] name = "httparse" version = "1.10.1" @@ -1657,6 +2072,15 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" version = "1.9.0" @@ -1679,21 +2103,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-named-pipe" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" -dependencies = [ - "hex", - "hyper", - "hyper-util", - "pin-project-lite", - "tokio", - "tower-service", - "winapi", -] - [[package]] name = "hyper-rustls" version = "0.27.9" @@ -1707,20 +2116,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-timeout" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" -dependencies = [ - "hyper", - "hyper-util", - "pin-project-lite", - "tokio", - "tower-service", + "webpki-roots 1.0.7", ] [[package]] @@ -1741,24 +2137,11 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2", + "system-configuration", "tokio", "tower-service", "tracing", -] - -[[package]] -name = "hyperlocal" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7" -dependencies = [ - "hex", - "http-body-util", - "hyper", - "hyper-util", - "pin-project-lite", - "tokio", - "tower-service", + "windows-registry", ] [[package]] @@ -1923,6 +2306,22 @@ dependencies = [ "serde_core", ] +[[package]] +name = "io-extras" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2285ddfe3054097ef4b2fe909ef8c3bcd1ea52a8f0d274416caebeef39f04a65" +dependencies = [ + "io-lifetimes", + "windows-sys 0.52.0", +] + +[[package]] +name = "io-lifetimes" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" + [[package]] name = "ipnet" version = "2.12.0" @@ -1953,6 +2352,75 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "ittapi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b996fe614c41395cdaedf3cf408a9534851090959d90d54a535f675550b64b1" +dependencies = [ + "anyhow", + "ittapi-sys", + "log", +] + +[[package]] +name = "ittapi-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f5385394064fa2c886205dba02598013ce83d3e92d33dbdc0c52fe0e7bf4fc" +dependencies = [ + "cc", +] + +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.117", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "jobserver" version = "0.1.34" @@ -1975,19 +2443,51 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] +name = "jsonwebtoken" +version = "9.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" +dependencies = [ + "base64 0.22.1", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "jsonwebtoken" version = "10.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc" dependencies = [ - "aws-lc-rs", "base64 0.22.1", + "ed25519-dalek", "getrandom 0.2.17", + "hmac", "js-sys", + "p256", + "p384", "pem", + "rand 0.8.6", + "rsa", "serde", "serde_json", + "sha2 0.10.9", "signature", "simple_asn1", "zeroize", @@ -1999,7 +2499,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" dependencies = [ - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -2013,7 +2513,7 @@ dependencies = [ "ena", "itertools 0.14.0", "lalrpop-util", - "petgraph", + "petgraph 0.7.1", "pico-args", "regex", "regex-syntax", @@ -2043,17 +2543,29 @@ dependencies = [ "spin", ] +[[package]] +name = "leb128" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cc46bac87ef8093eed6f272babb833b6443374399985ac8ed28471ee0918545" + [[package]] name = "leb128fmt" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +[[package]] +name = "libbz2-rs-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b357333733e8260735ba5894eb928c02ecc69c78715f01a8019e7fa7f2db4c" + [[package]] name = "libc" -version = "0.2.184" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libm" @@ -2061,6 +2573,15 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +[[package]] +name = "libredox" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +dependencies = [ + "libc", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -2145,10 +2666,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] -name = "matchit" -version = "0.8.4" +name = "lzma-rust2" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9ceaec84b54518262de7cf06b8b43e83c808349960f1610b21b0bfc9640f20" +dependencies = [ + "sha2 0.11.0", +] + +[[package]] +name = "mach2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +dependencies = [ + "libc", +] + +[[package]] +name = "maybe-owned" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" [[package]] name = "memchr" @@ -2156,6 +2695,15 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "memfd" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227" +dependencies = [ + "rustix", +] + [[package]] name = "miette" version = "7.6.0" @@ -2192,12 +2740,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] -name = "mio" -version = "1.2.0" +name = "miniz_oxide" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ - "libc", + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", "wasi", "windows-sys 0.61.2", ] @@ -2227,15 +2785,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "murmur3" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a198f9589efc03f544388dfc4a19fe8af4323662b62f598b8dcfdac62c14771c" -dependencies = [ - "byteorder", -] - [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -2261,20 +2810,6 @@ dependencies = [ "serde", ] -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - [[package]] name = "num-bigint" version = "0.4.6" @@ -2301,15 +2836,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", -] - [[package]] name = "num-conv" version = "0.2.2" @@ -2336,17 +2862,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -2358,21 +2873,23 @@ dependencies = [ ] [[package]] -name = "num_cpus" -version = "1.17.0" +name = "object" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ - "hermit-abi", - "libc", + "memchr", ] [[package]] name = "object" -version = "0.37.3" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b" dependencies = [ + "crc32fast", + "hashbrown 0.17.0", + "indexmap 2.14.0", "memchr", ] @@ -2394,6 +2911,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list", + "hashbrown 0.14.5", +] + [[package]] name = "p256" version = "0.13.2" @@ -2407,10 +2934,16 @@ dependencies = [ ] [[package]] -name = "parking" -version = "2.2.1" +name = "p384" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] [[package]] name = "parking_lot" @@ -2436,35 +2969,10 @@ dependencies = [ ] [[package]] -name = "parse-display" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914a1c2265c98e2446911282c6ac86d8524f495792c38c5bd884f80499c7538a" -dependencies = [ - "parse-display-derive", - "regex", - "regex-syntax", -] - -[[package]] -name = "parse-display-derive" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ae7800a4c974efd12df917266338e79a7a74415173caf7e70aa0a0707345281" -dependencies = [ - "proc-macro2", - "quote", - "regex", - "regex-syntax", - "structmeta", - "syn 2.0.117", -] - -[[package]] -name = "pastey" +name = "pathdiff" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "pem" @@ -2491,13 +2999,66 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pest_meta" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +dependencies = [ + "pest", + "sha2 0.10.9", +] + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset 0.4.2", + "indexmap 2.14.0", +] + [[package]] name = "petgraph" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ - "fixedbitset", + "fixedbitset 0.5.7", "indexmap 2.14.0", ] @@ -2516,26 +3077,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" -[[package]] -name = "pin-project" -version = "1.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "pin-project-lite" version = "0.2.17" @@ -2574,10 +3115,22 @@ dependencies = [ ] [[package]] -name = "portable-atomic" -version = "1.13.1" +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "postcard" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] [[package]] name = "potential_utf" @@ -2594,6 +3147,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppmd-rust" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -2677,17 +3236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71adf41db68aa0daaefc69bb30bcd68ded9b9abaad5d1fbb6304c4fb390e083e" dependencies = [ "bytes", - "prost-derive 0.10.1", -] - -[[package]] -name = "prost" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" -dependencies = [ - "bytes", - "prost-derive 0.14.4", + "prost-derive", ] [[package]] @@ -2704,45 +3253,46 @@ dependencies = [ ] [[package]] -name = "prost-derive" -version = "0.14.4" +name = "prost-types" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" +checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68" dependencies = [ - "anyhow", - "itertools 0.14.0", - "proc-macro2", - "quote", - "syn 2.0.117", + "bytes", + "prost", ] [[package]] -name = "prost-types" -version = "0.10.1" +name = "psm" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68" +checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" dependencies = [ - "bytes", - "prost 0.10.4", + "ar_archive_writer", + "cc", ] [[package]] -name = "prost-types" -version = "0.14.4" +name = "pulley-interpreter" +version = "45.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a" +checksum = "dd6ccdd6fc70f6c33eb21cb8fe05a71a5f7ee4cbef7a093a8a87dd8a908697cd" dependencies = [ - "prost 0.14.4", + "cranelift-bitset", + "log", + "pulley-macros", + "wasmtime-internal-core", ] [[package]] -name = "psm" -version = "0.1.31" +name = "pulley-macros" +version = "45.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" +checksum = "e00101fdb6fcaf9ea98a1994be11861d7e0c910c718c41bae373c44179e3160c" dependencies = [ - "ar_archive_writer", - "cc", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -2767,10 +3317,11 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.15" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" dependencies = [ + "aws-lc-rs", "bytes", "getrandom 0.3.4", "lru-slab", @@ -2842,6 +3393,17 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -2881,32 +3443,29 @@ dependencies = [ ] [[package]] -name = "redis" -version = "1.2.4" +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae41a63fd0b8a5372f82b21e810e09a316f5dd7efd96bf08e678fb240fc1918" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ - "arc-swap", - "arcstr", - "async-lock", - "backon", - "bytes", - "cfg-if", - "combine", - "futures-channel", - "futures-util", - "itoa", - "percent-encoding", - "pin-project-lite", - "rustls", - "rustls-native-certs", - "ryu", - "socket2", - "tokio", - "tokio-rustls", - "tokio-util", - "url", - "xxhash-rust", + "crossbeam-deque", + "crossbeam-utils", ] [[package]] @@ -2918,6 +3477,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + [[package]] name = "ref-cast" version = "1.0.25" @@ -2938,11 +3508,25 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "regalloc2" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de2c52737737f8609e94f975dee22854a2d5c125772d4b1cf292120f4d45c186" +dependencies = [ + "allocator-api2", + "bumpalo", + "hashbrown 0.17.0", + "log", + "rustc-hash", + "smallvec", +] + [[package]] name = "regex" -version = "1.12.4" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -2963,9 +3547,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.11" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "reqwest" @@ -3002,33 +3586,74 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", + "webpki-roots 1.0.7", ] [[package]] -name = "ring" -version = "0.17.14" +name = "reqwest" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0" dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted 0.9.0", - "windows-sys 0.52.0", -] - + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rmp" version = "0.8.15" @@ -3048,13 +3673,27 @@ dependencies = [ "serde", ] +[[package]] +name = "ron" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4147b952f3f819eca0e99527022f7d6a8d05f111aeb0a62960c74eb283bec8fc" +dependencies = [ + "bitflags", + "once_cell", + "serde", + "serde_derive", + "typeid", + "unicode-ident", +] + [[package]] name = "rsa" version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" dependencies = [ - "const-oid", + "const-oid 0.9.6", "digest 0.10.7", "num-bigint-dig", "num-integer", @@ -3068,6 +3707,22 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rust-ini" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" + [[package]] name = "rustc-hash" version = "2.1.2" @@ -3102,12 +3757,23 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustix-linux-procfs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc84bf7e9aa16c4f2c758f27412dc9841341e16aa682d9c7ac308fe3ee12056" +dependencies = [ + "once_cell", + "rustix", +] + [[package]] name = "rustls" version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ + "aws-lc-rs", "log", "once_cell", "ring", @@ -3129,15 +3795,6 @@ dependencies = [ "security-framework", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" version = "1.14.1" @@ -3148,15 +3805,43 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", - "untrusted 0.9.0", + "untrusted", ] [[package]] @@ -3241,7 +3926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags", - "core-foundation", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -3262,6 +3947,10 @@ name = "semver" version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] [[package]] name = "serde" @@ -3273,6 +3962,18 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + [[package]] name = "serde_bytes" version = "0.11.19" @@ -3305,9 +4006,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "indexmap 2.14.0", "itoa", @@ -3318,14 +4019,12 @@ dependencies = [ ] [[package]] -name = "serde_repr" -version = "0.1.20" +name = "serde_spanned" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "serde_core", ] [[package]] @@ -3385,6 +4084,19 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "serde_yaml_ng" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4db627b98b36d4203a7b458cf3573730f2bb591b28871d916dfa9efabfd41f" +dependencies = [ + "indexmap 2.14.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "serdect" version = "0.2.0" @@ -3403,7 +4115,7 @@ checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.9.0", "opaque-debug", ] @@ -3415,10 +4127,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", +] + [[package]] name = "sha3" version = "0.10.9" @@ -3455,6 +4178,28 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "similar" version = "2.7.0" @@ -3490,6 +4235,9 @@ name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] [[package]] name = "smol_str" @@ -3511,6 +4259,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "sparkv" +version = "0.1.1" +source = "git+https://github.com/JanssenProject/jans?tag=v2.1.0#3a089405993a1832135092857258c774cbbbb215" +dependencies = [ + "chrono", + "thiserror 2.0.18", +] + [[package]] name = "spin" version = "0.9.8" @@ -3574,23 +4331,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "structmeta" -version = "0.3.0" +name = "strum" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e1575d8d40908d70f6fd05537266b90ae71b15dbbe7a8b7dffa2b759306d329" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" dependencies = [ - "proc-macro2", - "quote", - "structmeta-derive", - "syn 2.0.117", + "strum_macros", ] [[package]] -name = "structmeta-derive" -version = "0.3.0" +name = "strum_macros" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" dependencies = [ + "heck", "proc-macro2", "quote", "syn 2.0.117", @@ -3645,51 +4400,61 @@ dependencies = [ ] [[package]] -name = "term" -version = "1.2.1" +name = "system-configuration" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "target-lexicon" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", "windows-sys 0.61.2", ] [[package]] -name = "testcontainers" -version = "0.26.3" +name = "term" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a81ec0158db5fbb9831e09d1813fe5ea9023a2b5e6e8e0a5fe67e2a820733629" +checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "astral-tokio-tar", - "async-trait", - "bollard", - "bytes", - "docker_credential", - "either", - "etcetera", - "ferroid", - "futures", - "itertools 0.14.0", - "log", - "memchr", - "parse-display", - "pin-project-lite", - "serde", - "serde_json", - "serde_with", - "thiserror 2.0.18", - "tokio", - "tokio-stream", - "tokio-util", - "url", + "windows-sys 0.61.2", ] [[package]] -name = "testcontainers-modules" -version = "0.14.0" +name = "termcolor" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e75e78ff453128a2c7da9a5d5a3325ea34ea214d4bf51eab3417de23a4e5147" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ - "testcontainers", + "winapi-util", ] [[package]] @@ -3740,6 +4505,7 @@ checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", + "js-sys", "num-conv", "powerfmt", "serde_core", @@ -3763,6 +4529,15 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinystr" version = "0.8.3" @@ -3790,9 +4565,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.3" +version = "1.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" dependencies = [ "bytes", "libc", @@ -3826,17 +4601,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-stream" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-util" version = "0.7.18" @@ -3852,45 +4616,66 @@ dependencies = [ ] [[package]] -name = "tonic" -version = "0.14.6" +name = "toml" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ - "async-trait", - "axum", - "base64 0.22.1", - "bytes", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-timeout", - "hyper-util", - "percent-encoding", - "pin-project", - "socket2", - "sync_wrapper", - "tokio", - "tokio-stream", - "tower", - "tower-layer", - "tower-service", - "tracing", + "indexmap 2.14.0", + "serde_core", + "serde_spanned", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.15", ] [[package]] -name = "tonic-prost" -version = "0.14.6" +name = "toml" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ - "bytes", - "prost 0.14.4", - "tonic", + "serde_core", + "serde_spanned", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.3", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", ] +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.3", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + [[package]] name = "tower" version = "0.5.3" @@ -3899,15 +4684,11 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", - "indexmap 2.14.0", "pin-project-lite", - "slab", "sync_wrapper", "tokio", - "tokio-util", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -3983,12 +4764,50 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" +[[package]] +name = "typed-builder" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31aa81521b70f94402501d848ccc0ecaa8f93c8eb6999eb9747e72287757ffda" +dependencies = [ + "typed-builder-macro", +] + +[[package]] +name = "typed-builder-macro" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076a02dc54dd46795c2e9c8282ed40bcfb1e22747e955de9389a1de28190fb26" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "typed-path" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "typenum" version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -4020,6 +4839,12 @@ dependencies = [ "unicode-script", ] +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + [[package]] name = "unicode-width" version = "0.1.14" @@ -4044,45 +4869,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "ureq" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" -dependencies = [ - "base64 0.22.1", - "log", - "percent-encoding", - "rustls", - "rustls-pki-types", - "ureq-proto", - "utf8-zero", -] - -[[package]] -name = "ureq-proto" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" -dependencies = [ - "base64 0.22.1", - "http", - "httparse", - "log", -] - [[package]] name = "url" version = "2.5.8" @@ -4093,15 +4885,8 @@ dependencies = [ "idna", "percent-encoding", "serde", - "serde_derive", ] -[[package]] -name = "utf8-zero" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -4110,9 +4895,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.23.4" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" +checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" dependencies = [ "getrandom 0.4.2", "js-sys", @@ -4120,6 +4905,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "uuid7" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f14c93e6dd46ded457afc647964ac685427f9f001815d07ba30398cb79d9c9ce" +dependencies = [ + "fstr", + "rand_core 0.10.1", + "rand_core 0.6.4", + "serde", + "uuid", +] + [[package]] name = "version_check" version = "0.9.5" @@ -4127,135 +4925,570 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] -name = "walkdir" -version = "2.5.0" +name = "vfs" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e723b9e1c02a3cf9f9d0de6a4ddb8cdc1df859078902fe0ae0589d615711ae6" +dependencies = [ + "filetime", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-compose" +version = "0.248.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ba953e2b9b4b4b52a31cf4e3ee1c1374c872b6e012cf2138d1c37cba00bfd6" +dependencies = [ + "anyhow", + "heck", + "indexmap 2.14.0", + "log", + "petgraph 0.6.5", + "smallvec", + "wasm-encoder 0.248.0", + "wasmparser 0.248.0", + "wat", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser 0.244.0", +] + +[[package]] +name = "wasm-encoder" +version = "0.248.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac92cf547bc18d27ecc521015c08c353b4f18b84ab388bb6d1b6b682c620d9b6" +dependencies = [ + "leb128fmt", + "wasmparser 0.248.0", +] + +[[package]] +name = "wasm-encoder" +version = "0.251.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a879a421bd17c528b74721b2abf4c62e8f1d1889c2ba8c3c50d02deaf2ce395" +dependencies = [ + "leb128fmt", + "wasmparser 0.251.0", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.14.0", + "wasm-encoder 0.244.0", + "wasmparser 0.244.0", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap 2.14.0", + "semver", +] + +[[package]] +name = "wasmparser" +version = "0.248.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4439c5eee9df71ee0c6efb37f63b1fcb1fec38f85f5142c54e7ed05d33091a" +dependencies = [ + "bitflags", + "hashbrown 0.17.0", + "indexmap 2.14.0", + "semver", + "serde", +] + +[[package]] +name = "wasmparser" +version = "0.251.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "437970b35b1a85cfde9c74b2398352d8d653f3bd8e3a3db0c063ea8f5b4b36ff" +dependencies = [ + "bitflags", + "indexmap 2.14.0", + "semver", +] + +[[package]] +name = "wasmprinter" +version = "0.248.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b264a5410b008d4d199a92bf536eae703cbd614482fc1ec53831cf19e1c183" +dependencies = [ + "anyhow", + "termcolor", + "wasmparser 0.248.0", +] + +[[package]] +name = "wasmtime" +version = "45.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c191891081c3bf9f10cb579819b32b0e81695641dc639eef34b57cf10c59ad81" +dependencies = [ + "addr2line", + "async-trait", + "bitflags", + "bumpalo", + "cc", + "cfg-if", + "encoding_rs", + "futures", + "fxprof-processed-profile", + "gimli", + "ittapi", + "libc", + "log", + "mach2", + "memfd", + "object 0.39.1", + "once_cell", + "postcard", + "pulley-interpreter", + "rayon", + "rustix", + "semver", + "serde", + "serde_derive", + "serde_json", + "smallvec", + "target-lexicon", + "tempfile", + "wasm-compose", + "wasm-encoder 0.248.0", + "wasmparser 0.248.0", + "wasmtime-environ", + "wasmtime-internal-cache", + "wasmtime-internal-component-macro", + "wasmtime-internal-component-util", + "wasmtime-internal-core", + "wasmtime-internal-cranelift", + "wasmtime-internal-fiber", + "wasmtime-internal-jit-debug", + "wasmtime-internal-jit-icache-coherence", + "wasmtime-internal-unwinder", + "wasmtime-internal-versioned-export-macros", + "wasmtime-internal-winch", + "wat", + "windows-sys 0.61.2", +] + +[[package]] +name = "wasmtime-environ" +version = "45.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f337d68a62d868f3c297517b46d20dc7e293f0da36bbee2f6ec3c30eab938bd" +dependencies = [ + "anyhow", + "cpp_demangle", + "cranelift-bforest", + "cranelift-bitset", + "cranelift-entity", + "gimli", + "hashbrown 0.17.0", + "indexmap 2.14.0", + "log", + "object 0.39.1", + "postcard", + "rustc-demangle", + "semver", + "serde", + "serde_derive", + "sha2 0.10.9", + "smallvec", + "target-lexicon", + "wasm-encoder 0.248.0", + "wasmparser 0.248.0", + "wasmprinter", + "wasmtime-internal-component-util", + "wasmtime-internal-core", +] + +[[package]] +name = "wasmtime-internal-cache" +version = "45.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f45a4fb2a6a269100b845404f2210d874eaee9ecd9efb6fc6c1e80896fab40f" +dependencies = [ + "base64 0.22.1", + "directories-next", + "log", + "postcard", + "rustix", + "serde", + "serde_derive", + "sha2 0.10.9", + "toml 0.9.12+spec-1.1.0", + "wasmtime-environ", + "windows-sys 0.61.2", + "zstd", +] + +[[package]] +name = "wasmtime-internal-component-macro" +version = "45.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab1cc8df1940657960571d7bc9bc0eadf869c0eb95cff831a6554409f89b25b0" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasmtime-internal-component-util", + "wasmtime-internal-wit-bindgen", + "wit-parser 0.248.0", +] + +[[package]] +name = "wasmtime-internal-component-util" +version = "45.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73ccd2e0e6bd91fb0161a17ee5e2d7badbeba6eb7461d0e0e3991492bd3835d" + +[[package]] +name = "wasmtime-internal-core" +version = "45.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "110bf85122cd451d3b9ff67f8911d428ec9b729208abe950a0333c3244660e88" +dependencies = [ + "anyhow", + "hashbrown 0.17.0", + "libm", + "serde", +] + +[[package]] +name = "wasmtime-internal-cranelift" +version = "45.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0236a21553c5b30ee953f413a8dc99729548b747219eef7e70f8288ed313ebe" +dependencies = [ + "cfg-if", + "cranelift-codegen", + "cranelift-control", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "gimli", + "itertools 0.14.0", + "log", + "object 0.39.1", + "pulley-interpreter", + "smallvec", + "target-lexicon", + "thiserror 2.0.18", + "wasmparser 0.248.0", + "wasmtime-environ", + "wasmtime-internal-core", + "wasmtime-internal-unwinder", + "wasmtime-internal-versioned-export-macros", +] + +[[package]] +name = "wasmtime-internal-fiber" +version = "45.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ccd50931b61ad593ae91e62d41a96b997b26c9308305cae4523cfef9301d9e8" +dependencies = [ + "cc", + "cfg-if", + "libc", + "rustix", + "wasmtime-environ", + "wasmtime-internal-versioned-export-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "wasmtime-internal-jit-debug" +version = "45.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +checksum = "b0a52ca7429272234b44f81fdf80d4793593e5c368b0f960d41fd401b1117bec" dependencies = [ - "same-file", - "winapi-util", + "cc", + "object 0.39.1", + "rustix", + "wasmtime-internal-versioned-export-macros", ] [[package]] -name = "want" -version = "0.3.1" +name = "wasmtime-internal-jit-icache-coherence" +version = "45.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +checksum = "aa6818a4864719772680694f4e4649a8600bb5efcf71111ebaf7419b266463e8" dependencies = [ - "try-lock", + "cfg-if", + "libc", + "wasmtime-internal-core", + "windows-sys 0.61.2", ] [[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.2+wasi-0.2.9" +name = "wasmtime-internal-unwinder" +version = "45.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "1fdc6a69c42fbbf13104ccbf0d3c096d0392f00102e2c70b542d9fca402eb162" dependencies = [ - "wit-bindgen", + "cfg-if", + "cranelift-codegen", + "log", + "object 0.39.1", + "wasmtime-environ", ] [[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +name = "wasmtime-internal-versioned-export-macros" +version = "45.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "fa0bd1cd696ec4b7e6f46357c0479e7739c3858e54afb69a15765402bbc1f9dd" dependencies = [ - "wit-bindgen", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] -name = "wasm-bindgen" -version = "0.2.118" +name = "wasmtime-internal-winch" +version = "45.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +checksum = "5c2ca01234ef55acd10c0fa5a2f96c3fd422eba03b221e2e9572944d19a476a7" dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", + "cranelift-codegen", + "gimli", + "log", + "object 0.39.1", + "target-lexicon", + "wasmparser 0.248.0", + "wasmtime-environ", + "wasmtime-internal-cranelift", + "winch-codegen", ] [[package]] -name = "wasm-bindgen-futures" -version = "0.4.68" +name = "wasmtime-internal-wit-bindgen" +version = "45.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" +checksum = "708b3d8cfaad2d33a92ec4ed93d69e9836c04f7eb5b8dfe1dde9df47c41f20b7" dependencies = [ - "js-sys", - "wasm-bindgen", + "anyhow", + "bitflags", + "heck", + "indexmap 2.14.0", + "wit-parser 0.248.0", ] [[package]] -name = "wasm-bindgen-macro" -version = "0.2.118" +name = "wasmtime-wasi" +version = "45.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +checksum = "79d0c0121dcc5152390d099fb853b9a0d79e7e0a172c12cc668ea8c5d8f883c2" dependencies = [ - "quote", - "wasm-bindgen-macro-support", + "async-trait", + "bitflags", + "bytes", + "cap-fs-ext", + "cap-net-ext", + "cap-std", + "cap-time-ext", + "cfg-if", + "fs-set-times", + "futures", + "io-extras", + "io-lifetimes", + "rand 0.10.1", + "rustix", + "thiserror 2.0.18", + "tokio", + "tracing", + "url", + "wasmtime", + "wasmtime-wasi-io", + "wiggle", + "windows-sys 0.61.2", ] [[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.118" +name = "wasmtime-wasi-http" +version = "45.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +checksum = "10e03132bf0eb02270f1a1efb8ad0144c3924e0eb8258543803866e47ab808ce" dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.117", - "wasm-bindgen-shared", + "async-trait", + "bytes", + "futures", + "http", + "http-body", + "http-body-util", + "hyper", + "rustls", + "tokio", + "tokio-rustls", + "tracing", + "wasmtime", + "wasmtime-wasi", + "wasmtime-wasi-io", + "webpki-roots 0.26.11", ] [[package]] -name = "wasm-bindgen-shared" -version = "0.2.118" +name = "wasmtime-wasi-io" +version = "45.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +checksum = "02ca2fd53dbd62f5f95b470b43ff1e711933e8f30ebc89d9050351a72f6e5c28" dependencies = [ - "unicode-ident", + "async-trait", + "bytes", + "futures", + "tracing", + "wasmtime", ] [[package]] -name = "wasm-encoder" -version = "0.244.0" +name = "wast" +version = "35.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" dependencies = [ - "leb128fmt", - "wasmparser", + "leb128", ] [[package]] -name = "wasm-metadata" -version = "0.244.0" +name = "wast" +version = "251.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +checksum = "5cc7467dda0a96142eb2c980329dfb62480b1e1d3622fdeb1a44e2bca6ceed74" dependencies = [ - "anyhow", - "indexmap 2.14.0", - "wasm-encoder", - "wasmparser", + "bumpalo", + "leb128fmt", + "memchr", + "unicode-width 0.2.2", + "wasm-encoder 0.251.0", ] [[package]] -name = "wasmparser" -version = "0.244.0" +name = "wat" +version = "1.251.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +checksum = "81b1086c9e85b95bd6a229a928bc6c6d0662e42af0250c88d067b418831ea4d4" dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap 2.14.0", - "semver", + "wast 251.0.0", ] [[package]] @@ -4278,6 +5511,24 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.7", +] + [[package]] name = "webpki-roots" version = "1.0.7" @@ -4287,6 +5538,46 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "wiggle" +version = "45.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4cd8015611a3bc95e449ad198dfd133b7488759b0dc49b515052101eb954587" +dependencies = [ + "bitflags", + "thiserror 2.0.18", + "tracing", + "wasmtime", + "wasmtime-environ", + "wiggle-macro", +] + +[[package]] +name = "wiggle-generate" +version = "45.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0f655ec3eba7df68408018796911a6acdda3a41e67d6551a3766563495e333" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasmtime-environ", + "witx", +] + +[[package]] +name = "wiggle-macro" +version = "45.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a22f38e7b0f2a3b58bae4dd655dcedd65d56f257916a5eda5f08c40910ee9d4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "wiggle-generate", +] + [[package]] name = "wildmatch" version = "2.6.1" @@ -4324,6 +5615,25 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "winch-codegen" +version = "45.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fdfab04a4d446c558a9ea994ded662d35580a17b15385b862002703aa43245" +dependencies = [ + "cranelift-assembler-x64", + "cranelift-codegen", + "gimli", + "regalloc2", + "smallvec", + "target-lexicon", + "thiserror 2.0.18", + "wasmparser 0.248.0", + "wasmtime-environ", + "wasmtime-internal-core", + "wasmtime-internal-cranelift", +] + [[package]] name = "windows-core" version = "0.62.2" @@ -4365,6 +5675,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + [[package]] name = "windows-result" version = "0.4.1" @@ -4539,6 +5860,31 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "winx" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d" +dependencies = [ + "bitflags", + "windows-sys 0.52.0", +] + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -4556,7 +5902,7 @@ checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" dependencies = [ "anyhow", "heck", - "wit-parser", + "wit-parser 0.244.0", ] [[package]] @@ -4603,10 +5949,10 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder", + "wasm-encoder 0.244.0", "wasm-metadata", - "wasmparser", - "wit-parser", + "wasmparser 0.244.0", + "wit-parser 0.244.0", ] [[package]] @@ -4624,30 +5970,56 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser", + "wasmparser 0.244.0", ] [[package]] -name = "writeable" -version = "0.6.3" +name = "wit-parser" +version = "0.248.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +checksum = "247ad505da2915a082fe13204c5ba8788425aea1de54f43b284818cf82637856" +dependencies = [ + "anyhow", + "hashbrown 0.17.0", + "id-arena", + "indexmap 2.14.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.248.0", +] [[package]] -name = "xattr" -version = "1.6.1" +name = "witx" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" dependencies = [ - "libc", - "rustix", + "anyhow", + "log", + "thiserror 1.0.69", + "wast 35.0.2", ] [[package]] -name = "xxhash-rust" -version = "0.8.15" +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "yaml-rust2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" +checksum = "631a50d867fafb7093e709d75aaee9e0e0d5deb934021fcea25ac2fe09edc51e" +dependencies = [ + "arraydeque", + "encoding_rs", + "hashlink", +] [[package]] name = "yoke" @@ -4715,18 +6087,18 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.9.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.5.0" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", @@ -4766,8 +6138,74 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "zip" +version = "8.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e499faf5c6b97a0d086f4a8733de6d47aee2252b8127962439d8d4311a73f72" +dependencies = [ + "bzip2", + "crc32fast", + "deflate64", + "flate2", + "indexmap 2.14.0", + "lzma-rust2", + "memchr", + "ppmd-rust", + "time", + "typed-path", + "zopfli", + "zstd", +] + +[[package]] +name = "zlib-rs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" + [[package]] name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index 55f6fb3c..ac3c978c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,8 @@ members = [ "crates/cpex-sdk", "crates/cpex-builtins", "crates/cpex-ffi", + "crates/cpex-payload", + "crates/cpex-wasm-host", "crates/apl-core", "crates/apl-cmf", "crates/apl-cpex", @@ -27,6 +29,9 @@ members = [ "builtins/session/valkey", "examples/go-demo/ffi", ] +exclude = [ + "crates/cpex-wasm-plugin", +] # `default-members` controls what `cargo build` / `cargo test` (with no # `-p` or `--workspace` flag) picks up. `cpex-session-valkey` pulls a redis diff --git a/crates/cpex-payload/Cargo.toml b/crates/cpex-payload/Cargo.toml new file mode 100644 index 00000000..6f11b5e3 --- /dev/null +++ b/crates/cpex-payload/Cargo.toml @@ -0,0 +1,26 @@ +# Location: ./crates/cpex-payload/Cargo.toml +# Copyright 2025 +# SPDX-License-Identifier: Apache-2.0 +# Authors: Teryl Taylor +# +# CPEX Payload — minimal types for WASM plugins. + +[package] +name = "cpex-payload" +description = "CPEX payload types — minimal types for WASM plugins." +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true + +[dependencies] +# WASM-compatible tokio features only +tokio = { version = "1", features = ["sync", "macros", "io-util", "rt", "time"] } +serde = { workspace = true } +serde_json = { workspace = true } +serde_yaml = { workspace = true } +async-trait = { workspace = true } +thiserror = { workspace = true } +tracing = { workspace = true } +uuid = { workspace = true } +wildmatch = { workspace = true } diff --git a/crates/cpex-payload/src/cmf/constants.rs b/crates/cpex-payload/src/cmf/constants.rs new file mode 100644 index 00000000..12a8ac5e --- /dev/null +++ b/crates/cpex-payload/src/cmf/constants.rs @@ -0,0 +1,65 @@ +// Location: ./crates/cpex-core/src/cmf/constants.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// CMF constants — schema version, serialization field names, and defaults. + +/// Current CMF message schema version. +pub const SCHEMA_VERSION: &str = "2.0"; + +// --------------------------------------------------------------------------- +// Serialization field names for MessageView::to_dict() / to_opa_input() +// --------------------------------------------------------------------------- + +// Core view fields +pub const FIELD_KIND: &str = "kind"; +pub const FIELD_ROLE: &str = "role"; +pub const FIELD_IS_PRE: &str = "is_pre"; +pub const FIELD_IS_POST: &str = "is_post"; +pub const FIELD_ACTION: &str = "action"; +pub const FIELD_HOOK: &str = "hook"; +pub const FIELD_URI: &str = "uri"; +pub const FIELD_NAME: &str = "name"; +pub const FIELD_CONTENT: &str = "content"; +pub const FIELD_SIZE_BYTES: &str = "size_bytes"; +pub const FIELD_MIME_TYPE: &str = "mime_type"; +pub const FIELD_ARGUMENTS: &str = "arguments"; + +// Extensions container +pub const FIELD_EXTENSIONS: &str = "extensions"; + +// Subject fields +pub const FIELD_SUBJECT: &str = "subject"; +pub const FIELD_ID: &str = "id"; +pub const FIELD_TYPE: &str = "type"; +pub const FIELD_ROLES: &str = "roles"; +pub const FIELD_PERMISSIONS: &str = "permissions"; +pub const FIELD_TEAMS: &str = "teams"; + +// Security fields +pub const FIELD_LABELS: &str = "labels"; + +// Request fields +pub const FIELD_ENVIRONMENT: &str = "environment"; + +// HTTP fields +pub const FIELD_HEADERS: &str = "headers"; + +// Agent fields +pub const FIELD_AGENT: &str = "agent"; +pub const FIELD_INPUT: &str = "input"; +pub const FIELD_SESSION_ID: &str = "session_id"; +pub const FIELD_CONVERSATION_ID: &str = "conversation_id"; +pub const FIELD_TURN: &str = "turn"; +pub const FIELD_AGENT_ID: &str = "agent_id"; +pub const FIELD_PARENT_AGENT_ID: &str = "parent_agent_id"; + +// Meta fields +pub const FIELD_META: &str = "meta"; +pub const FIELD_ENTITY_TYPE: &str = "entity_type"; +pub const FIELD_ENTITY_NAME: &str = "entity_name"; +pub const FIELD_TAGS: &str = "tags"; + +// OPA envelope +pub const FIELD_OPA_INPUT: &str = "input"; diff --git a/crates/cpex-payload/src/cmf/content.rs b/crates/cpex-payload/src/cmf/content.rs new file mode 100644 index 00000000..3cde9b65 --- /dev/null +++ b/crates/cpex-payload/src/cmf/content.rs @@ -0,0 +1,486 @@ +// Location: ./crates/cpex-core/src/cmf/content.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// CMF domain objects and ContentPart hierarchy. +// +// Domain objects (ToolCall, Resource, etc.) are standalone structs +// reusable outside of message content parts. ContentPart is a tagged +// enum that wraps them for message serialization. +// +// Mirrors the Python types in cpex/framework/cmf/message.py. + +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +use super::enums::ResourceType; +use super::message::Message; + +// --------------------------------------------------------------------------- +// Domain Objects +// --------------------------------------------------------------------------- + +/// Normalized tool/function invocation request. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ToolCall { + /// Unique request correlation ID. + pub tool_call_id: String, + /// Tool name. + pub name: String, + /// Arguments as a JSON-serializable map. + #[serde(default)] + pub arguments: HashMap, + /// Optional namespace for namespaced tools. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, +} + +/// Result from tool execution. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ToolResult { + /// Correlation ID linking to the corresponding tool call. + pub tool_call_id: String, + /// Name of the tool that was executed. + pub tool_name: String, + /// Result content (any JSON-serializable value). + #[serde(default)] + pub content: serde_json::Value, + /// Whether the result represents an error. + #[serde(default)] + pub is_error: bool, +} + +/// Embedded resource with content (MCP). +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct Resource { + /// Unique request correlation ID. + pub resource_request_id: String, + /// Unique identifier in URI format. + pub uri: String, + /// Human-readable name. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + /// What this resource contains. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + /// The kind of resource. + pub resource_type: ResourceType, + /// Text content if embedded. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub content: Option, + /// Binary content if embedded (base64 in JSON). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub blob: Option>, + /// MIME type of content. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mime_type: Option, + /// Size information. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub size_bytes: Option, + /// Metadata (classification, retention, etc.). + #[serde(default, skip_serializing_if = "HashMap::is_empty")] + pub annotations: HashMap, + /// Version tracking. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub version: Option, +} + +impl Resource { + /// Whether content or blob is embedded. + pub fn is_embedded(&self) -> bool { + self.content.is_some() || self.blob.is_some() + } + + /// Get text content if available. + pub fn get_text_content(&self) -> Option<&str> { + self.content.as_deref() + } +} + +/// Lightweight resource reference without embedded content. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ResourceReference { + /// Correlation ID linking to the originating resource request. + pub resource_request_id: String, + /// Resource URI. + pub uri: String, + /// Human-readable name. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + /// Type of resource. + pub resource_type: ResourceType, + /// Line number or byte offset for partial references. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub range_start: Option, + /// End of range. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub range_end: Option, + /// CSS/XPath/JSONPath selector. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub selector: Option, +} + +/// Prompt template invocation request (MCP). +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PromptRequest { + /// Request ID for correlation. + pub prompt_request_id: String, + /// Prompt template name. + pub name: String, + /// Arguments to pass to the template. + #[serde(default)] + pub arguments: HashMap, + /// Source server for multi-server scenarios. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server_id: Option, +} + +/// Rendered prompt template result. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PromptResult { + /// ID of the corresponding prompt request. + pub prompt_request_id: String, + /// Name of the prompt that was rendered. + pub prompt_name: String, + /// Rendered messages (prompts produce messages). + #[serde(default)] + pub messages: Vec, + /// Single text result for simple prompts. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub content: Option, + /// Whether rendering failed. + #[serde(default)] + pub is_error: bool, + /// Error details if rendering failed. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error_message: Option, +} + +// --------------------------------------------------------------------------- +// Media Source Types +// --------------------------------------------------------------------------- + +/// Image source data. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ImageSource { + /// Source type: "url" or "base64". + #[serde(rename = "type")] + pub source_type: String, + /// URL or base64-encoded string. + pub data: String, + /// MIME type (e.g., image/jpeg). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub media_type: Option, +} + +/// Video source data. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VideoSource { + /// Source type: "url" or "base64". + #[serde(rename = "type")] + pub source_type: String, + /// URL or base64-encoded string. + pub data: String, + /// MIME type (e.g., video/mp4). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub media_type: Option, + /// Duration in milliseconds. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub duration_ms: Option, +} + +/// Audio source data. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AudioSource { + /// Source type: "url" or "base64". + #[serde(rename = "type")] + pub source_type: String, + /// URL or base64-encoded string. + pub data: String, + /// MIME type (e.g., audio/mp3). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub media_type: Option, + /// Duration in milliseconds. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub duration_ms: Option, +} + +/// Document source data (PDF, Word, etc.). +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DocumentSource { + /// Source type: "url" or "base64". + #[serde(rename = "type")] + pub source_type: String, + /// URL or base64-encoded string. + pub data: String, + /// MIME type (e.g., application/pdf). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub media_type: Option, + /// Document title. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub title: Option, +} + +// --------------------------------------------------------------------------- +// ContentPart — Tagged Enum +// --------------------------------------------------------------------------- + +/// A typed content part in a CMF message. +/// +/// Discriminated by the `content_type` field. Each variant wraps +/// either a text string or a domain object. +/// +/// Mirrors the Python `ContentPartUnion` discriminated union in +/// `cpex/framework/cmf/message.py`. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "content_type")] +pub enum ContentPart { + /// Plain text content. + #[serde(rename = "text")] + Text { text: String }, + + /// Chain-of-thought reasoning. + #[serde(rename = "thinking")] + Thinking { text: String }, + + /// Tool/function invocation request. + #[serde(rename = "tool_call")] + ToolCall { content: ToolCall }, + + /// Result from tool execution. + #[serde(rename = "tool_result")] + ToolResult { content: ToolResult }, + + /// Embedded resource with content. + #[serde(rename = "resource")] + Resource { content: Resource }, + + /// Lightweight resource reference. + #[serde(rename = "resource_ref")] + ResourceRef { content: ResourceReference }, + + /// Prompt template invocation request. + #[serde(rename = "prompt_request")] + PromptRequest { content: PromptRequest }, + + /// Rendered prompt template result. + #[serde(rename = "prompt_result")] + PromptResult { content: PromptResult }, + + /// Image content. + #[serde(rename = "image")] + Image { content: ImageSource }, + + /// Video content. + #[serde(rename = "video")] + Video { content: VideoSource }, + + /// Audio content. + #[serde(rename = "audio")] + Audio { content: AudioSource }, + + /// Document content. + #[serde(rename = "document")] + Document { content: DocumentSource }, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_text_content_part_serde() { + let json = r#"{"content_type":"text","text":"Hello, world!"}"#; + let part: ContentPart = serde_json::from_str(json).unwrap(); + match &part { + ContentPart::Text { text } => assert_eq!(text, "Hello, world!"), + _ => panic!("expected Text variant"), + } + let roundtrip = serde_json::to_string(&part).unwrap(); + let part2: ContentPart = serde_json::from_str(&roundtrip).unwrap(); + match part2 { + ContentPart::Text { text } => assert_eq!(text, "Hello, world!"), + _ => panic!("expected Text variant"), + } + } + + #[test] + fn test_tool_call_content_part_serde() { + let json = r#"{ + "content_type": "tool_call", + "content": { + "tool_call_id": "tc_001", + "name": "get_weather", + "arguments": {"city": "London"} + } + }"#; + let part: ContentPart = serde_json::from_str(json).unwrap(); + match &part { + ContentPart::ToolCall { content } => { + assert_eq!(content.name, "get_weather"); + assert_eq!(content.tool_call_id, "tc_001"); + assert_eq!(content.arguments["city"], "London"); + } + _ => panic!("expected ToolCall variant"), + } + } + + #[test] + fn test_tool_result_content_part_serde() { + let json = r#"{ + "content_type": "tool_result", + "content": { + "tool_call_id": "tc_001", + "tool_name": "get_weather", + "content": {"temp": 20, "unit": "C"}, + "is_error": false + } + }"#; + let part: ContentPart = serde_json::from_str(json).unwrap(); + match &part { + ContentPart::ToolResult { content } => { + assert_eq!(content.tool_name, "get_weather"); + assert!(!content.is_error); + } + _ => panic!("expected ToolResult variant"), + } + } + + #[test] + fn test_resource_content_part_serde() { + let json = r#"{ + "content_type": "resource", + "content": { + "resource_request_id": "rr_001", + "uri": "file:///data.txt", + "resource_type": "file", + "content": "Hello from file" + } + }"#; + let part: ContentPart = serde_json::from_str(json).unwrap(); + match &part { + ContentPart::Resource { content } => { + assert_eq!(content.uri, "file:///data.txt"); + assert!(content.is_embedded()); + assert_eq!(content.get_text_content(), Some("Hello from file")); + } + _ => panic!("expected Resource variant"), + } + } + + #[test] + fn test_resource_ref_content_part_serde() { + let json = r#"{ + "content_type": "resource_ref", + "content": { + "resource_request_id": "rr_002", + "uri": "db://users/42", + "resource_type": "database" + } + }"#; + let part: ContentPart = serde_json::from_str(json).unwrap(); + match &part { + ContentPart::ResourceRef { content } => { + assert_eq!(content.uri, "db://users/42"); + assert_eq!(content.resource_type, ResourceType::Database); + } + _ => panic!("expected ResourceRef variant"), + } + } + + #[test] + fn test_image_content_part_serde() { + let json = r#"{ + "content_type": "image", + "content": { + "type": "url", + "data": "https://example.com/photo.jpg", + "media_type": "image/jpeg" + } + }"#; + let part: ContentPart = serde_json::from_str(json).unwrap(); + match &part { + ContentPart::Image { content } => { + assert_eq!(content.source_type, "url"); + assert_eq!(content.data, "https://example.com/photo.jpg"); + } + _ => panic!("expected Image variant"), + } + } + + #[test] + fn test_prompt_request_content_part_serde() { + let json = r#"{ + "content_type": "prompt_request", + "content": { + "prompt_request_id": "pr_001", + "name": "summarize", + "arguments": {"text": "Long document..."} + } + }"#; + let part: ContentPart = serde_json::from_str(json).unwrap(); + match &part { + ContentPart::PromptRequest { content } => { + assert_eq!(content.name, "summarize"); + } + _ => panic!("expected PromptRequest variant"), + } + } + + #[test] + fn test_thinking_content_part_serde() { + let json = r#"{"content_type":"thinking","text":"Let me analyze..."}"#; + let part: ContentPart = serde_json::from_str(json).unwrap(); + match &part { + ContentPart::Thinking { text } => assert_eq!(text, "Let me analyze..."), + _ => panic!("expected Thinking variant"), + } + } + + #[test] + fn test_tool_call_construction() { + let tc = ToolCall { + tool_call_id: "tc_001".into(), + name: "search".into(), + arguments: [("query".to_string(), serde_json::json!("rust"))].into(), + namespace: None, + }; + assert_eq!(tc.name, "search"); + assert_eq!(tc.arguments["query"], "rust"); + } + + #[test] + fn test_resource_is_embedded() { + let embedded = Resource { + resource_request_id: "rr_001".into(), + uri: "file:///data.txt".into(), + name: None, + description: None, + resource_type: ResourceType::File, + content: Some("data".into()), + blob: None, + mime_type: None, + size_bytes: None, + annotations: HashMap::new(), + version: None, + }; + assert!(embedded.is_embedded()); + + let not_embedded = Resource { + resource_request_id: "rr_002".into(), + uri: "file:///other.txt".into(), + name: None, + description: None, + resource_type: ResourceType::File, + content: None, + blob: None, + mime_type: None, + size_bytes: None, + annotations: HashMap::new(), + version: None, + }; + assert!(!not_embedded.is_embedded()); + } +} diff --git a/crates/cpex-payload/src/cmf/enums.rs b/crates/cpex-payload/src/cmf/enums.rs new file mode 100644 index 00000000..97f1dcc2 --- /dev/null +++ b/crates/cpex-payload/src/cmf/enums.rs @@ -0,0 +1,176 @@ +// Location: ./crates/cpex-core/src/cmf/enums.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// CMF enums — Role, Channel, ContentType, ResourceType. +// +// Mirrors the Python enums in cpex/framework/cmf/message.py. +// All use snake_case serialization to match Python string values. + +use serde::{Deserialize, Serialize}; + +// --------------------------------------------------------------------------- +// Role +// --------------------------------------------------------------------------- + +/// Identifies WHO is speaking in a conversation turn. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum Role { + /// System-level instructions. + System, + /// Developer-provided instructions. + Developer, + /// Human user input. + User, + /// LLM/agent response. + Assistant, + /// Tool execution result. + Tool, +} + +// --------------------------------------------------------------------------- +// Channel +// --------------------------------------------------------------------------- + +/// Classifies the kind of output a message represents. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum Channel { + /// Intermediate analytical output (chain-of-thought). + Analysis, + /// Meta-level observations about the task. + Commentary, + /// Terminal response intended for delivery. + Final, +} + +// --------------------------------------------------------------------------- +// ContentType +// --------------------------------------------------------------------------- + +/// Discriminator for the typed ContentPart hierarchy. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ContentType { + /// Plain text content. + Text, + /// Chain-of-thought reasoning. + Thinking, + /// Tool/function invocation request. + ToolCall, + /// Result from tool execution. + ToolResult, + /// Embedded resource with content (MCP). + Resource, + /// Lightweight resource reference without embedded content. + ResourceRef, + /// Prompt template invocation request (MCP). + PromptRequest, + /// Rendered prompt template result. + PromptResult, + /// Image content (URL or base64). + Image, + /// Video content (URL or base64). + Video, + /// Audio content (URL or base64). + Audio, + /// Document content (PDF, Word, etc.). + Document, +} + +// --------------------------------------------------------------------------- +// ResourceType +// --------------------------------------------------------------------------- + +/// Type of resource being referenced. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ResourceType { + /// File-system resource. + #[default] + File, + /// Binary large object. + Blob, + /// Generic URI-addressable resource. + Uri, + /// Database entity. + Database, + /// API endpoint. + Api, + /// In-memory or ephemeral resource. + Memory, + /// Produced artifact (generated output, build result). + Artifact, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_role_serde_roundtrip() { + let role = Role::Assistant; + let json = serde_json::to_string(&role).unwrap(); + assert_eq!(json, "\"assistant\""); + let deserialized: Role = serde_json::from_str(&json).unwrap(); + assert_eq!(deserialized, Role::Assistant); + } + + #[test] + fn test_channel_serde_roundtrip() { + let channel = Channel::Final; + let json = serde_json::to_string(&channel).unwrap(); + assert_eq!(json, "\"final\""); + let deserialized: Channel = serde_json::from_str(&json).unwrap(); + assert_eq!(deserialized, Channel::Final); + } + + #[test] + fn test_content_type_serde_roundtrip() { + let ct = ContentType::ToolCall; + let json = serde_json::to_string(&ct).unwrap(); + assert_eq!(json, "\"tool_call\""); + let deserialized: ContentType = serde_json::from_str(&json).unwrap(); + assert_eq!(deserialized, ContentType::ToolCall); + } + + #[test] + fn test_content_type_resource_ref() { + let ct = ContentType::ResourceRef; + let json = serde_json::to_string(&ct).unwrap(); + assert_eq!(json, "\"resource_ref\""); + } + + #[test] + fn test_content_type_prompt_variants() { + let req = ContentType::PromptRequest; + let res = ContentType::PromptResult; + assert_eq!(serde_json::to_string(&req).unwrap(), "\"prompt_request\""); + assert_eq!(serde_json::to_string(&res).unwrap(), "\"prompt_result\""); + } + + #[test] + fn test_resource_type_serde_roundtrip() { + let rt = ResourceType::Database; + let json = serde_json::to_string(&rt).unwrap(); + assert_eq!(json, "\"database\""); + let deserialized: ResourceType = serde_json::from_str(&json).unwrap(); + assert_eq!(deserialized, ResourceType::Database); + } + + #[test] + fn test_all_roles_deserialize() { + for (s, expected) in &[ + ("\"system\"", Role::System), + ("\"developer\"", Role::Developer), + ("\"user\"", Role::User), + ("\"assistant\"", Role::Assistant), + ("\"tool\"", Role::Tool), + ] { + let role: Role = serde_json::from_str(s).unwrap(); + assert_eq!(role, *expected); + } + } +} diff --git a/crates/cpex-payload/src/cmf/message.rs b/crates/cpex-payload/src/cmf/message.rs new file mode 100644 index 00000000..b2bad350 --- /dev/null +++ b/crates/cpex-payload/src/cmf/message.rs @@ -0,0 +1,460 @@ +// Location: ./crates/cpex-core/src/cmf/message.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// CMF Message — canonical message representation. +// +// A Message is the storage and wire format for a single turn in a +// conversation. It preserves structure exactly as the LLM or +// framework sent it. +// +// Extensions are NOT part of the Message. They are passed separately +// to handlers via the framework's Extensions type. This allows +// extensions to be shared across payload types and avoids copying +// the message when extensions change. +// +// Mirrors the Python Message in cpex/framework/cmf/message.py. + +use serde::{Deserialize, Serialize}; + +use super::content::*; +use super::enums::{Channel, Role}; +use crate::hooks::trait_def::PluginResult; + +// --------------------------------------------------------------------------- +// Message +// --------------------------------------------------------------------------- + +/// Canonical CMF message representing a single turn in a conversation. +/// +/// All content is carried as typed ContentPart variants. Extensions +/// (identity, security, HTTP, agent context) are passed separately +/// to handlers — not inside the message. +/// +/// Mirrors the Python `Message` in `cpex/framework/cmf/message.py`. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Message { + /// Message schema version. + #[serde(default = "default_schema_version")] + pub schema_version: String, + + /// Who is speaking. + pub role: Role, + + /// List of typed content parts (multimodal). + #[serde(default)] + pub content: Vec, + + /// Optional output classification. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub channel: Option, +} + +fn default_schema_version() -> String { + super::constants::SCHEMA_VERSION.to_string() +} + +impl Message { + /// Create a simple text message. + pub fn text(role: Role, text: impl Into) -> Self { + Self { + schema_version: super::constants::SCHEMA_VERSION.to_string(), + role, + content: vec![ContentPart::Text { text: text.into() }], + channel: None, + } + } + + /// Extract all text content from the message. + /// + /// Concatenates text from all `Text` content parts. + pub fn get_text_content(&self) -> String { + let mut texts = Vec::new(); + for part in &self.content { + if let ContentPart::Text { text } = part { + texts.push(text.as_str()); + } + } + texts.join("") + } + + /// Extract thinking/reasoning content if present. + pub fn get_thinking_content(&self) -> Option { + let mut texts = Vec::new(); + for part in &self.content { + if let ContentPart::Thinking { text } = part { + texts.push(text.as_str()); + } + } + if texts.is_empty() { + None + } else { + Some(texts.join("")) + } + } + + /// Get all tool calls in this message. + pub fn get_tool_calls(&self) -> Vec<&ToolCall> { + self.content + .iter() + .filter_map(|part| match part { + ContentPart::ToolCall { content } => Some(content), + _ => None, + }) + .collect() + } + + /// Get all tool results in this message. + pub fn get_tool_results(&self) -> Vec<&ToolResult> { + self.content + .iter() + .filter_map(|part| match part { + ContentPart::ToolResult { content } => Some(content), + _ => None, + }) + .collect() + } + + /// Whether this message contains any tool calls. + pub fn is_tool_call(&self) -> bool { + self.content + .iter() + .any(|p| matches!(p, ContentPart::ToolCall { .. })) + } + + /// Whether this message contains any tool results. + pub fn is_tool_result(&self) -> bool { + self.content + .iter() + .any(|p| matches!(p, ContentPart::ToolResult { .. })) + } + + /// Get all embedded resources in this message. + pub fn get_resources(&self) -> Vec<&Resource> { + self.content + .iter() + .filter_map(|part| match part { + ContentPart::Resource { content } => Some(content), + _ => None, + }) + .collect() + } + + /// Get all resource references in this message. + pub fn get_resource_refs(&self) -> Vec<&ResourceReference> { + self.content + .iter() + .filter_map(|part| match part { + ContentPart::ResourceRef { content } => Some(content), + _ => None, + }) + .collect() + } + + /// Get all resource URIs (both embedded and references). + pub fn get_all_resource_uris(&self) -> Vec<&str> { + self.content + .iter() + .filter_map(|part| match part { + ContentPart::Resource { content } => Some(content.uri.as_str()), + ContentPart::ResourceRef { content } => Some(content.uri.as_str()), + _ => None, + }) + .collect() + } + + /// Whether this message contains any resources or resource references. + pub fn has_resources(&self) -> bool { + self.content.iter().any(|p| { + matches!( + p, + ContentPart::Resource { .. } | ContentPart::ResourceRef { .. } + ) + }) + } + + /// Get all prompt requests in this message. + pub fn get_prompt_requests(&self) -> Vec<&PromptRequest> { + self.content + .iter() + .filter_map(|part| match part { + ContentPart::PromptRequest { content } => Some(content), + _ => None, + }) + .collect() + } + + /// Get all prompt results in this message. + pub fn get_prompt_results(&self) -> Vec<&PromptResult> { + self.content + .iter() + .filter_map(|part| match part { + ContentPart::PromptResult { content } => Some(content), + _ => None, + }) + .collect() + } +} + +// --------------------------------------------------------------------------- +// MessagePayload — PluginPayload wrapper +// --------------------------------------------------------------------------- + +/// CMF Message wrapped as a PluginPayload for hook dispatch. +/// +/// This is the payload type for all `cmf.*` hooks. Plugins that +/// handle CMF hooks implement `HookHandler` and receive +/// `&MessagePayload` in their handler. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MessagePayload { + /// The CMF message. + pub message: Message, +} + +crate::impl_plugin_payload!(MessagePayload); + +// --------------------------------------------------------------------------- +// CmfHook — Hook Type Definition +// --------------------------------------------------------------------------- + +crate::define_hook! { + /// CMF message evaluation hook. + /// + /// Plugins implement `HookHandler` and register under + /// one or more `cmf.*` hook names (e.g., `cmf.tool_pre_invoke`, + /// `cmf.llm_input`). The same handler covers all CMF hook points. + CmfHook, "cmf" => { + payload: MessagePayload, + result: PluginResult, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::hooks::payload::PluginPayload; + use crate::hooks::trait_def::HookTypeDef; + + #[test] + fn test_message_text_helper() { + let msg = Message::text(Role::User, "What is the weather?"); + assert_eq!(msg.get_text_content(), "What is the weather?"); + assert_eq!(msg.role, Role::User); + assert_eq!(msg.schema_version, "2.0"); + } + + #[test] + fn test_message_multi_part_text() { + let msg = Message { + schema_version: "2.0".into(), + role: Role::Assistant, + content: vec![ + ContentPart::Text { + text: "Hello ".into(), + }, + ContentPart::Text { + text: "world!".into(), + }, + ], + channel: None, + }; + assert_eq!(msg.get_text_content(), "Hello world!"); + } + + #[test] + fn test_message_thinking_content() { + let msg = Message { + schema_version: "2.0".into(), + role: Role::Assistant, + content: vec![ + ContentPart::Thinking { + text: "Let me think...".into(), + }, + ContentPart::Text { + text: "Here's my answer.".into(), + }, + ], + channel: Some(Channel::Final), + }; + assert_eq!( + msg.get_thinking_content(), + Some("Let me think...".to_string()) + ); + assert_eq!(msg.get_text_content(), "Here's my answer."); + } + + #[test] + fn test_message_tool_calls() { + let msg = Message { + schema_version: "2.0".into(), + role: Role::Assistant, + content: vec![ + ContentPart::Text { + text: "Let me check.".into(), + }, + ContentPart::ToolCall { + content: ToolCall { + tool_call_id: "tc_001".into(), + name: "get_weather".into(), + arguments: [("city".to_string(), serde_json::json!("London"))].into(), + namespace: None, + }, + }, + ContentPart::ToolCall { + content: ToolCall { + tool_call_id: "tc_002".into(), + name: "get_time".into(), + arguments: [("timezone".to_string(), serde_json::json!("UTC"))].into(), + namespace: None, + }, + }, + ], + channel: None, + }; + assert!(msg.is_tool_call()); + assert!(!msg.is_tool_result()); + let calls = msg.get_tool_calls(); + assert_eq!(calls.len(), 2); + assert_eq!(calls[0].name, "get_weather"); + assert_eq!(calls[1].name, "get_time"); + } + + #[test] + fn test_message_tool_results() { + let msg = Message { + schema_version: "2.0".into(), + role: Role::Tool, + content: vec![ContentPart::ToolResult { + content: ToolResult { + tool_call_id: "tc_001".into(), + tool_name: "get_weather".into(), + content: serde_json::json!({"temp": 20}), + is_error: false, + }, + }], + channel: None, + }; + assert!(msg.is_tool_result()); + assert!(!msg.is_tool_call()); + let results = msg.get_tool_results(); + assert_eq!(results.len(), 1); + assert_eq!(results[0].tool_name, "get_weather"); + } + + #[test] + fn test_message_resources() { + let msg = Message { + schema_version: "2.0".into(), + role: Role::Assistant, + content: vec![ + ContentPart::Resource { + content: Resource { + resource_request_id: "rr_001".into(), + uri: "file:///data.txt".into(), + name: Some("Data File".into()), + description: None, + resource_type: super::super::enums::ResourceType::File, + content: Some("file contents".into()), + blob: None, + mime_type: None, + size_bytes: None, + annotations: std::collections::HashMap::new(), + version: None, + }, + }, + ContentPart::ResourceRef { + content: ResourceReference { + resource_request_id: "rr_002".into(), + uri: "db://users/42".into(), + name: None, + resource_type: super::super::enums::ResourceType::Database, + range_start: None, + range_end: None, + selector: None, + }, + }, + ], + channel: None, + }; + assert!(msg.has_resources()); + assert_eq!(msg.get_resources().len(), 1); + assert_eq!(msg.get_resource_refs().len(), 1); + let uris = msg.get_all_resource_uris(); + assert_eq!(uris.len(), 2); + assert!(uris.contains(&"file:///data.txt")); + assert!(uris.contains(&"db://users/42")); + } + + #[test] + fn test_message_no_resources() { + let msg = Message::text(Role::User, "Hello"); + assert!(!msg.has_resources()); + assert!(msg.get_resources().is_empty()); + } + + #[test] + fn test_message_serde_roundtrip() { + let msg = Message { + schema_version: "2.0".into(), + role: Role::Assistant, + content: vec![ + ContentPart::Thinking { + text: "Analyzing...".into(), + }, + ContentPart::Text { + text: "Here's the answer.".into(), + }, + ContentPart::ToolCall { + content: ToolCall { + tool_call_id: "tc_001".into(), + name: "search".into(), + arguments: [("q".to_string(), serde_json::json!("rust"))].into(), + namespace: None, + }, + }, + ], + channel: Some(Channel::Final), + }; + + let json = serde_json::to_string(&msg).unwrap(); + let deserialized: Message = serde_json::from_str(&json).unwrap(); + + assert_eq!(deserialized.role, Role::Assistant); + assert_eq!(deserialized.schema_version, "2.0"); + assert_eq!(deserialized.channel, Some(Channel::Final)); + assert_eq!(deserialized.content.len(), 3); + assert_eq!(deserialized.get_text_content(), "Here's the answer."); + assert_eq!(deserialized.get_tool_calls().len(), 1); + } + + #[test] + fn test_message_payload_as_plugin_payload() { + let payload = MessagePayload { + message: Message::text(Role::User, "Hello"), + }; + + // Test clone_boxed + let boxed: Box = Box::new(payload.clone()); + let cloned = boxed.clone_boxed(); + + // Test as_any downcast + let downcasted = cloned + .as_any() + .downcast_ref::() + .expect("should downcast to MessagePayload"); + assert_eq!(downcasted.message.get_text_content(), "Hello"); + } + + #[test] + fn test_cmf_hook_type_def() { + assert_eq!(CmfHook::NAME, "cmf"); + } + + #[test] + fn test_message_default_schema_version() { + let json = r#"{"role":"user","content":[]}"#; + let msg: Message = serde_json::from_str(json).unwrap(); + assert_eq!(msg.schema_version, "2.0"); + } +} diff --git a/crates/cpex-payload/src/cmf/mod.rs b/crates/cpex-payload/src/cmf/mod.rs new file mode 100644 index 00000000..11ae76a4 --- /dev/null +++ b/crates/cpex-payload/src/cmf/mod.rs @@ -0,0 +1,100 @@ +// Location: ./crates/cpex-core/src/cmf/mod.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// ContextForge Message Format (CMF). +// +// Canonical message representation for interactions between users, +// agents, tools, and language models. All models mirror the Python +// CMF in cpex/framework/cmf/message.py. +// +// Extensions are NOT part of the Message — they are passed separately +// to handlers via the framework's Extensions type in hooks/payload.rs. +// This allows extensions to be shared across payload types and avoids +// copying the message when extensions change. +// +// # Hook Registration Patterns +// +// CMF supports two registration patterns for plugins: +// +// ## Pattern 1: One handler, multiple hook names (recommended) +// +// Use `CmfHook` as the hook type and register under multiple names. +// The plugin writes one handler that covers all CMF hooks. The host +// invokes via `invoke_by_name("cmf.tool_pre_invoke", ...)`. +// +// ```rust,ignore +// // Plugin implements one handler: +// impl HookHandler for MyPlugin { +// fn handle(&self, payload: &MessagePayload, ext: &Extensions, ctx: &mut PluginContext) +// -> PluginResult { ... } +// } +// +// // Factory registers under multiple names: +// PluginInstance { +// plugin: plugin.clone(), +// handlers: vec![ +// ("cmf.tool_pre_invoke", Arc::new(TypedHandlerAdapter::::new(plugin.clone()))), +// ("cmf.tool_post_invoke", Arc::new(TypedHandlerAdapter::::new(plugin))), +// ], +// } +// +// // Host invokes via invoke_named — compile-time payload type safety +// // plus runtime hook name routing: +// mgr.invoke_named::( +// "cmf.tool_pre_invoke", payload, ext, None, +// ).await; +// ``` +// +// `invoke_named::(hook_name, ...)` gives you both: +// - **Compile-time**: payload must be `MessagePayload` (from `CmfHook::Payload`) +// - **Runtime**: dispatches to plugins registered under the specific hook name +// +// This is the recommended approach for CMF hooks. Alternatively, use +// `invoke_by_name(hook_name, boxed_payload, ...)` for fully dynamic +// dispatch (no compile-time payload check). +// +// ## Pattern 2: Individual hook types (optional) +// +// For hosts that want per-hook marker types, define separate hook +// types. Each maps to one hook name. The plugin must implement a +// handler per type (more boilerplate). +// +// ```rust,ignore +// define_hook! { +// CmfToolPreInvoke, "cmf.tool_pre_invoke" => { +// payload: MessagePayload, +// result: PluginResult, +// } +// } +// +// // Plugin implements per-hook handlers: +// impl HookHandler for MyPlugin { ... } +// impl HookHandler for MyPlugin { ... } +// +// // Host uses typed invoke: +// mgr.invoke::(payload, ext, None).await; +// ``` +// +// Both patterns use the same executor, registry, and capabilities. +// Pattern 1 with `invoke_named` is recommended — one handler impl, +// compile-time payload safety, and explicit hook name routing. +// +// Available CMF hook names (defined in hooks/types.rs): +// cmf.tool_pre_invoke, cmf.tool_post_invoke, +// cmf.llm_input, cmf.llm_output, +// cmf.prompt_pre_fetch, cmf.prompt_post_fetch, +// cmf.resource_pre_fetch, cmf.resource_post_fetch + +pub mod constants; +pub mod content; +pub mod enums; +pub mod message; +pub mod view; + +// Re-export key types at the cmf module level +pub use content::*; +pub use enums::*; +pub use message::{CmfHook, Message, MessagePayload}; +pub use view::{MessageView, ViewAction, ViewKind}; diff --git a/crates/cpex-payload/src/cmf/view.rs b/crates/cpex-payload/src/cmf/view.rs new file mode 100644 index 00000000..2c92e76d --- /dev/null +++ b/crates/cpex-payload/src/cmf/view.rs @@ -0,0 +1,864 @@ +// Location: ./crates/cpex-core/src/cmf/view.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// MessageView — read-only projection for policy evaluation. +// +// Decomposes a Message into individually addressable views with a +// uniform interface regardless of content type. Zero-copy design — +// properties are computed on-demand by borrowing the underlying +// content part and extensions directly. +// +// Mirrors the Python MessageView in cpex/framework/cmf/view.py. + +use serde::{Deserialize, Serialize}; + +use super::content::*; +use super::enums::{ContentType, Role}; +use super::message::Message; +use crate::hooks::payload::Extensions; + +// --------------------------------------------------------------------------- +// Enums +// --------------------------------------------------------------------------- + +/// Type of content a view represents. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ViewKind { + Text, + Thinking, + ToolCall, + ToolResult, + Resource, + ResourceRef, + PromptRequest, + PromptResult, + Image, + Video, + Audio, + Document, +} + +/// The action this content represents in the data flow. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ViewAction { + Read, + Write, + Execute, + Invoke, + Send, + Receive, + Generate, +} + +impl ViewKind { + /// Map ContentType to ViewKind. + pub fn from_content_type(ct: ContentType) -> Self { + match ct { + ContentType::Text => ViewKind::Text, + ContentType::Thinking => ViewKind::Thinking, + ContentType::ToolCall => ViewKind::ToolCall, + ContentType::ToolResult => ViewKind::ToolResult, + ContentType::Resource => ViewKind::Resource, + ContentType::ResourceRef => ViewKind::ResourceRef, + ContentType::PromptRequest => ViewKind::PromptRequest, + ContentType::PromptResult => ViewKind::PromptResult, + ContentType::Image => ViewKind::Image, + ContentType::Video => ViewKind::Video, + ContentType::Audio => ViewKind::Audio, + ContentType::Document => ViewKind::Document, + } + } + + /// The default action for this kind of content. + pub fn default_action(&self, role: Role) -> ViewAction { + match self { + ViewKind::ToolCall => ViewAction::Execute, + ViewKind::ToolResult => ViewAction::Receive, + ViewKind::Resource | ViewKind::ResourceRef => ViewAction::Read, + ViewKind::PromptRequest => ViewAction::Invoke, + ViewKind::PromptResult => ViewAction::Receive, + // Direction-dependent kinds + ViewKind::Text + | ViewKind::Thinking + | ViewKind::Image + | ViewKind::Video + | ViewKind::Audio + | ViewKind::Document => match role { + Role::User => ViewAction::Send, + Role::Assistant => ViewAction::Generate, + Role::Tool => ViewAction::Receive, + Role::System | Role::Developer => ViewAction::Write, + }, + } + } + + /// Whether this is a tool-related kind. + pub fn is_tool(&self) -> bool { + matches!(self, ViewKind::ToolCall | ViewKind::ToolResult) + } + + /// Whether this is a resource-related kind. + pub fn is_resource(&self) -> bool { + matches!(self, ViewKind::Resource | ViewKind::ResourceRef) + } + + /// Whether this is a prompt-related kind. + pub fn is_prompt(&self) -> bool { + matches!(self, ViewKind::PromptRequest | ViewKind::PromptResult) + } + + /// Whether this is a media kind (image, video, audio, document). + pub fn is_media(&self) -> bool { + matches!( + self, + ViewKind::Image | ViewKind::Video | ViewKind::Audio | ViewKind::Document + ) + } + + /// Whether this is a text kind (text or thinking). + pub fn is_text(&self) -> bool { + matches!(self, ViewKind::Text | ViewKind::Thinking) + } +} + +// --------------------------------------------------------------------------- +// MessageView +// --------------------------------------------------------------------------- + +/// Read-only, zero-copy view over a single content part. +/// +/// Provides a uniform interface for policy evaluation regardless +/// of content type. Properties are computed on-demand by borrowing +/// the underlying content part and extensions. +/// +/// Produced by `Message::iter_views()` or the standalone `iter_views()`. +pub struct MessageView<'a> { + /// The underlying content part. + part: &'a ContentPart, + /// The kind of content. + kind: ViewKind, + /// The parent message role. + role: Role, + /// Optional hook location (e.g., "tool_pre_invoke"). + hook: Option<&'a str>, + /// Optional extensions (for security/http context). + extensions: Option<&'a Extensions>, +} + +impl<'a> MessageView<'a> { + /// Create a new view over a content part. + pub fn new( + part: &'a ContentPart, + role: Role, + hook: Option<&'a str>, + extensions: Option<&'a Extensions>, + ) -> Self { + let kind = match part { + ContentPart::Text { .. } => ViewKind::Text, + ContentPart::Thinking { .. } => ViewKind::Thinking, + ContentPart::ToolCall { .. } => ViewKind::ToolCall, + ContentPart::ToolResult { .. } => ViewKind::ToolResult, + ContentPart::Resource { .. } => ViewKind::Resource, + ContentPart::ResourceRef { .. } => ViewKind::ResourceRef, + ContentPart::PromptRequest { .. } => ViewKind::PromptRequest, + ContentPart::PromptResult { .. } => ViewKind::PromptResult, + ContentPart::Image { .. } => ViewKind::Image, + ContentPart::Video { .. } => ViewKind::Video, + ContentPart::Audio { .. } => ViewKind::Audio, + ContentPart::Document { .. } => ViewKind::Document, + }; + + Self { + part, + kind, + role, + hook, + extensions, + } + } + + // -- Core properties -- + + /// The kind of content this view represents. + pub fn kind(&self) -> ViewKind { + self.kind + } + + /// The role of the parent message. + pub fn role(&self) -> Role { + self.role + } + + /// The underlying content part. + pub fn raw(&self) -> &'a ContentPart { + self.part + } + + /// The hook location, if set. + pub fn hook(&self) -> Option<&str> { + self.hook + } + + /// The action this content represents. + pub fn action(&self) -> ViewAction { + self.kind.default_action(self.role) + } + + // -- Phase helpers -- + + /// Whether this is a pre-execution hook (tool_pre_invoke, prompt_pre_fetch, etc.). + pub fn is_pre(&self) -> bool { + self.hook.is_some_and(|h| h.contains("pre")) + } + + /// Whether this is a post-execution hook. + pub fn is_post(&self) -> bool { + self.hook.is_some_and(|h| h.contains("post")) + } + + // -- Universal properties -- + + /// Text content (for text, thinking, tool result content). + pub fn content(&self) -> Option<&str> { + match self.part { + ContentPart::Text { text } | ContentPart::Thinking { text } => Some(text), + ContentPart::ToolResult { content: tr } => { + tr.content.as_str().map(Some).unwrap_or(None) + } + ContentPart::Resource { content: r } => r.content.as_deref(), + ContentPart::PromptResult { content: pr } => pr.content.as_deref(), + _ => None, + } + } + + /// Entity name (tool name, resource URI, prompt name). + pub fn name(&self) -> Option<&str> { + match self.part { + ContentPart::ToolCall { content: tc } => Some(&tc.name), + ContentPart::ToolResult { content: tr } => Some(&tr.tool_name), + ContentPart::Resource { content: r } => r.name.as_deref().or(Some(&r.uri)), + ContentPart::ResourceRef { content: rr } => rr.name.as_deref().or(Some(&rr.uri)), + ContentPart::PromptRequest { content: pr } => Some(&pr.name), + ContentPart::PromptResult { content: pr } => Some(&pr.prompt_name), + _ => None, + } + } + + /// URI for the entity. + pub fn uri(&self) -> Option { + match self.part { + ContentPart::ToolCall { content: tc } => Some(format!("tool://_/{}", tc.name)), + ContentPart::Resource { content: r } => Some(r.uri.clone()), + ContentPart::ResourceRef { content: rr } => Some(rr.uri.clone()), + ContentPart::PromptRequest { content: pr } => Some(format!("prompt://_/{}", pr.name)), + _ => None, + } + } + + /// Arguments (for tool calls and prompt requests). + pub fn args(&self) -> Option<&std::collections::HashMap> { + match self.part { + ContentPart::ToolCall { content: tc } => Some(&tc.arguments), + ContentPart::PromptRequest { content: pr } => Some(&pr.arguments), + _ => None, + } + } + + /// Get a specific argument by name. + pub fn get_arg(&self, name: &str) -> Option<&serde_json::Value> { + self.args().and_then(|a| a.get(name)) + } + + /// Whether this content has arguments. + pub fn has_arg(&self, name: &str) -> bool { + self.get_arg(name).is_some() + } + + /// MIME type (for resources, media). + pub fn mime_type(&self) -> Option<&str> { + match self.part { + ContentPart::Resource { content: r } => r.mime_type.as_deref(), + ContentPart::Image { content: img } => img.media_type.as_deref(), + ContentPart::Video { content: vid } => vid.media_type.as_deref(), + ContentPart::Audio { content: aud } => aud.media_type.as_deref(), + ContentPart::Document { content: doc } => doc.media_type.as_deref(), + _ => None, + } + } + + /// Whether the result is an error (tool results, prompt results). + pub fn is_error(&self) -> bool { + match self.part { + ContentPart::ToolResult { content: tr } => tr.is_error, + ContentPart::PromptResult { content: pr } => pr.is_error, + _ => false, + } + } + + // -- Type helpers -- + + pub fn is_tool(&self) -> bool { + self.kind.is_tool() + } + pub fn is_resource(&self) -> bool { + self.kind.is_resource() + } + pub fn is_prompt(&self) -> bool { + self.kind.is_prompt() + } + pub fn is_media(&self) -> bool { + self.kind.is_media() + } + pub fn is_text(&self) -> bool { + self.kind.is_text() + } + + // -- Extension accessors -- + + /// Get the extensions, if provided. + pub fn extensions(&self) -> Option<&'a Extensions> { + self.extensions + } + + /// Check if a security label exists. + pub fn has_label(&self, label: &str) -> bool { + self.extensions + .and_then(|e| e.security.as_ref()) + .map(|s| s.has_label(label)) + .unwrap_or(false) + } + + /// Get an HTTP header value. + pub fn get_header(&self, name: &str) -> Option<&str> { + self.extensions + .and_then(|e| e.http.as_ref()) + .and_then(|h| h.get_header(name)) + } + + // -- Serialization -- + + /// Sensitive headers stripped during serialization. + const SENSITIVE_HEADERS: &'static [&'static str] = &["authorization", "cookie", "x-api-key"]; + + /// Serialize the view to a JSON-compatible map. + /// + /// Includes the view's properties, arguments, and optionally + /// text content and extension context. Sensitive headers + /// (Authorization, Cookie, X-API-Key) are stripped. + pub fn to_dict(&self, include_content: bool, include_context: bool) -> serde_json::Value { + use super::constants::*; + + let mut result = serde_json::Map::new(); + + // Core fields + result.insert(FIELD_KIND.into(), serde_json::json!(self.kind)); + result.insert(FIELD_ROLE.into(), serde_json::json!(self.role)); + result.insert(FIELD_IS_PRE.into(), serde_json::json!(self.is_pre())); + result.insert(FIELD_IS_POST.into(), serde_json::json!(self.is_post())); + result.insert(FIELD_ACTION.into(), serde_json::json!(self.action())); + + if let Some(hook) = self.hook { + result.insert(FIELD_HOOK.into(), serde_json::json!(hook)); + } + + if let Some(uri) = self.uri() { + result.insert(FIELD_URI.into(), serde_json::json!(uri)); + } + + if let Some(name) = self.name() { + result.insert(FIELD_NAME.into(), serde_json::json!(name)); + } + + // Content + if include_content { + if let Some(text) = self.content() { + result.insert(FIELD_SIZE_BYTES.into(), serde_json::json!(text.len())); + result.insert(FIELD_CONTENT.into(), serde_json::json!(text)); + } + } + + if let Some(mime) = self.mime_type() { + result.insert(FIELD_MIME_TYPE.into(), serde_json::json!(mime)); + } + + // Arguments + if let Some(args) = self.args() { + result.insert(FIELD_ARGUMENTS.into(), serde_json::json!(args)); + } + + // Extensions context + if include_context { + if let Some(ext) = self.extensions { + let mut ext_map = serde_json::Map::new(); + + // Subject + if let Some(ref sec) = ext.security { + if let Some(ref subject) = sec.subject { + let mut sub_map = serde_json::Map::new(); + if let Some(ref id) = subject.id { + sub_map.insert(FIELD_ID.into(), serde_json::json!(id)); + } + if let Some(ref st) = subject.subject_type { + sub_map.insert(FIELD_TYPE.into(), serde_json::json!(st)); + } + if !subject.roles.is_empty() { + let mut roles: Vec<&String> = subject.roles.iter().collect(); + roles.sort(); + sub_map.insert(FIELD_ROLES.into(), serde_json::json!(roles)); + } + if !subject.permissions.is_empty() { + let mut perms: Vec<&String> = subject.permissions.iter().collect(); + perms.sort(); + sub_map.insert(FIELD_PERMISSIONS.into(), serde_json::json!(perms)); + } + if !subject.teams.is_empty() { + let mut teams: Vec<&String> = subject.teams.iter().collect(); + teams.sort(); + sub_map.insert(FIELD_TEAMS.into(), serde_json::json!(teams)); + } + if !sub_map.is_empty() { + ext_map + .insert(FIELD_SUBJECT.into(), serde_json::Value::Object(sub_map)); + } + } + + // Labels + if !sec.labels.is_empty() { + let mut labels: Vec<&String> = sec.labels.iter().collect(); + labels.sort(); + ext_map.insert(FIELD_LABELS.into(), serde_json::json!(labels)); + } + } + + // Environment + if let Some(ref req) = ext.request { + if let Some(ref env) = req.environment { + ext_map.insert(FIELD_ENVIRONMENT.into(), serde_json::json!(env)); + } + } + + // Request headers (strip sensitive) + if let Some(ref http) = ext.http { + let safe: std::collections::HashMap<&String, &String> = http + .request_headers + .iter() + .filter(|(k, _)| { + !Self::SENSITIVE_HEADERS.contains(&k.to_lowercase().as_str()) + }) + .collect(); + if !safe.is_empty() { + ext_map.insert(FIELD_HEADERS.into(), serde_json::json!(safe)); + } + } + + // Agent context + if let Some(ref agent) = ext.agent { + let mut agent_map = serde_json::Map::new(); + if let Some(ref input) = agent.input { + agent_map.insert(FIELD_INPUT.into(), serde_json::json!(input)); + } + if let Some(ref sid) = agent.session_id { + agent_map.insert(FIELD_SESSION_ID.into(), serde_json::json!(sid)); + } + if let Some(ref cid) = agent.conversation_id { + agent_map.insert(FIELD_CONVERSATION_ID.into(), serde_json::json!(cid)); + } + if let Some(turn) = agent.turn { + agent_map.insert(FIELD_TURN.into(), serde_json::json!(turn)); + } + if let Some(ref aid) = agent.agent_id { + agent_map.insert(FIELD_AGENT_ID.into(), serde_json::json!(aid)); + } + if let Some(ref paid) = agent.parent_agent_id { + agent_map.insert(FIELD_PARENT_AGENT_ID.into(), serde_json::json!(paid)); + } + if !agent_map.is_empty() { + ext_map.insert(FIELD_AGENT.into(), serde_json::Value::Object(agent_map)); + } + } + + // Meta + if let Some(ref meta) = ext.meta { + let mut meta_map = serde_json::Map::new(); + if let Some(ref et) = meta.entity_type { + meta_map.insert(FIELD_ENTITY_TYPE.into(), serde_json::json!(et)); + } + if let Some(ref en) = meta.entity_name { + meta_map.insert(FIELD_ENTITY_NAME.into(), serde_json::json!(en)); + } + if !meta.tags.is_empty() { + let mut tags: Vec<&String> = meta.tags.iter().collect(); + tags.sort(); + meta_map.insert(FIELD_TAGS.into(), serde_json::json!(tags)); + } + if !meta_map.is_empty() { + ext_map.insert(FIELD_META.into(), serde_json::Value::Object(meta_map)); + } + } + + if !ext_map.is_empty() { + result.insert(FIELD_EXTENSIONS.into(), serde_json::Value::Object(ext_map)); + } + } + } + + serde_json::Value::Object(result) + } + + /// Serialize to OPA-compatible input format. + /// + /// Wraps the view in the standard OPA input envelope: + /// `{"input": {...view data...}}`. + pub fn to_opa_input(&self, include_content: bool) -> serde_json::Value { + use super::constants::FIELD_OPA_INPUT; + serde_json::json!({ + FIELD_OPA_INPUT: self.to_dict(include_content, true) + }) + } +} + +impl<'a> std::fmt::Debug for MessageView<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MessageView") + .field("kind", &self.kind) + .field("role", &self.role) + .field("name", &self.name()) + .field("hook", &self.hook) + .finish() + } +} + +// --------------------------------------------------------------------------- +// iter_views — decompose a Message into views +// --------------------------------------------------------------------------- + +/// Decompose a Message into individually addressable MessageViews. +/// +/// Yields one view per content part. Each view provides a uniform +/// interface for policy evaluation regardless of content type. +pub fn iter_views<'a>( + message: &'a Message, + hook: Option<&'a str>, + extensions: Option<&'a Extensions>, +) -> impl Iterator> { + message + .content + .iter() + .map(move |part| MessageView::new(part, message.role, hook, extensions)) +} + +// Also add iter_views to Message +impl Message { + /// Decompose this message into individually addressable MessageViews. + /// + /// Yields one view per content part. Each view provides a uniform + /// interface for policy evaluation regardless of content type. + pub fn iter_views<'a>( + &'a self, + hook: Option<&'a str>, + extensions: Option<&'a Extensions>, + ) -> impl Iterator> { + iter_views(self, hook, extensions) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cmf::enums::Role; + use crate::hooks::payload::MetaExtension; + + fn make_test_message() -> Message { + Message { + schema_version: "2.0".into(), + role: Role::Assistant, + content: vec![ + ContentPart::Thinking { + text: "Let me think...".into(), + }, + ContentPart::Text { + text: "Here's the answer.".into(), + }, + ContentPart::ToolCall { + content: ToolCall { + tool_call_id: "tc_001".into(), + name: "get_weather".into(), + arguments: [("city".to_string(), serde_json::json!("London"))].into(), + namespace: None, + }, + }, + ContentPart::Resource { + content: Resource { + resource_request_id: "rr_001".into(), + uri: "file:///data.csv".into(), + name: Some("Data File".into()), + resource_type: crate::cmf::enums::ResourceType::File, + content: Some("col1,col2".into()), + mime_type: Some("text/csv".into()), + ..Default::default() + }, + }, + ], + channel: None, + } + } + + #[test] + fn test_iter_views_count() { + let msg = make_test_message(); + let views: Vec<_> = msg.iter_views(None, None).collect(); + assert_eq!(views.len(), 4); + } + + #[test] + fn test_view_kinds() { + let msg = make_test_message(); + let views: Vec<_> = msg.iter_views(None, None).collect(); + assert_eq!(views[0].kind(), ViewKind::Thinking); + assert_eq!(views[1].kind(), ViewKind::Text); + assert_eq!(views[2].kind(), ViewKind::ToolCall); + assert_eq!(views[3].kind(), ViewKind::Resource); + } + + #[test] + fn test_view_content() { + let msg = make_test_message(); + let views: Vec<_> = msg.iter_views(None, None).collect(); + assert_eq!(views[0].content(), Some("Let me think...")); + assert_eq!(views[1].content(), Some("Here's the answer.")); + assert!(views[2].content().is_none()); // tool call has no text content + assert_eq!(views[3].content(), Some("col1,col2")); // resource has text content + } + + #[test] + fn test_view_name() { + let msg = make_test_message(); + let views: Vec<_> = msg.iter_views(None, None).collect(); + assert!(views[0].name().is_none()); // thinking has no name + assert!(views[1].name().is_none()); // text has no name + assert_eq!(views[2].name(), Some("get_weather")); + assert_eq!(views[3].name(), Some("Data File")); + } + + #[test] + fn test_view_uri() { + let msg = make_test_message(); + let views: Vec<_> = msg.iter_views(None, None).collect(); + assert_eq!(views[2].uri(), Some("tool://_/get_weather".to_string())); + assert_eq!(views[3].uri(), Some("file:///data.csv".to_string())); + } + + #[test] + fn test_view_args() { + let msg = make_test_message(); + let views: Vec<_> = msg.iter_views(None, None).collect(); + let tool_view = &views[2]; + assert!(tool_view.has_arg("city")); + assert_eq!(tool_view.get_arg("city").unwrap(), "London"); + assert!(!tool_view.has_arg("nonexistent")); + } + + #[test] + fn test_view_action() { + let msg = make_test_message(); + let views: Vec<_> = msg.iter_views(None, None).collect(); + assert_eq!(views[0].action(), ViewAction::Generate); // thinking from assistant + assert_eq!(views[1].action(), ViewAction::Generate); // text from assistant + assert_eq!(views[2].action(), ViewAction::Execute); // tool call + assert_eq!(views[3].action(), ViewAction::Read); // resource + } + + #[test] + fn test_view_action_user_role() { + let msg = Message::text(Role::User, "Hello"); + let views: Vec<_> = msg.iter_views(None, None).collect(); + assert_eq!(views[0].action(), ViewAction::Send); // text from user + } + + #[test] + fn test_view_hook_pre_post() { + let msg = make_test_message(); + let pre_views: Vec<_> = msg.iter_views(Some("tool_pre_invoke"), None).collect(); + assert!(pre_views[0].is_pre()); + assert!(!pre_views[0].is_post()); + + let post_views: Vec<_> = msg.iter_views(Some("tool_post_invoke"), None).collect(); + assert!(post_views[0].is_post()); + assert!(!post_views[0].is_pre()); + } + + #[test] + fn test_view_type_helpers() { + let msg = make_test_message(); + let views: Vec<_> = msg.iter_views(None, None).collect(); + assert!(views[0].is_text()); // thinking + assert!(views[1].is_text()); // text + assert!(views[2].is_tool()); // tool call + assert!(views[3].is_resource()); // resource + } + + #[test] + fn test_view_mime_type() { + let msg = make_test_message(); + let views: Vec<_> = msg.iter_views(None, None).collect(); + assert_eq!(views[3].mime_type(), Some("text/csv")); + } + + #[test] + fn test_view_with_extensions() { + use crate::extensions::{HttpExtension, SecurityExtension}; + use std::sync::Arc; + + let mut security = SecurityExtension::default(); + security.add_label("PII"); + + let mut http = HttpExtension::default(); + http.set_header("Authorization", "Bearer tok"); + + let ext = Extensions { + security: Some(Arc::new(security)), + http: Some(Arc::new(http)), + ..Default::default() + }; + + let msg = make_test_message(); + let views: Vec<_> = msg.iter_views(None, Some(&ext)).collect(); + + assert!(views[0].has_label("PII")); + assert!(!views[0].has_label("HIPAA")); + assert_eq!(views[0].get_header("Authorization"), Some("Bearer tok")); + } + + #[test] + fn test_to_dict_basic() { + let msg = Message::text(Role::User, "Hello world"); + let views: Vec<_> = msg.iter_views(Some("llm_input"), None).collect(); + let dict = views[0].to_dict(true, false); + + assert_eq!(dict["kind"], "text"); + assert_eq!(dict["role"], "user"); + assert_eq!(dict["action"], "send"); + assert_eq!(dict["hook"], "llm_input"); + assert_eq!(dict["content"], "Hello world"); + assert_eq!(dict["size_bytes"], 11); + assert_eq!(dict["is_pre"], false); + assert_eq!(dict["is_post"], false); + } + + #[test] + fn test_to_dict_tool_call() { + let msg = make_test_message(); + let views: Vec<_> = msg.iter_views(Some("tool_pre_invoke"), None).collect(); + let dict = views[2].to_dict(true, false); // tool call + + assert_eq!(dict["kind"], "tool_call"); + assert_eq!(dict["name"], "get_weather"); + assert_eq!(dict["uri"], "tool://_/get_weather"); + assert_eq!(dict["action"], "execute"); + assert_eq!(dict["is_pre"], true); + assert!(dict["arguments"].is_object()); + assert_eq!(dict["arguments"]["city"], "London"); + } + + #[test] + fn test_to_dict_without_content() { + let msg = Message::text(Role::User, "Secret message"); + let views: Vec<_> = msg.iter_views(None, None).collect(); + let dict = views[0].to_dict(false, false); + + assert!(dict.get("content").is_none()); + assert!(dict.get("size_bytes").is_none()); + } + + #[test] + fn test_to_dict_with_extensions() { + use crate::extensions::{ + AgentExtension, HttpExtension, RequestExtension, SecurityExtension, + }; + use std::sync::Arc; + + let mut security = SecurityExtension::default(); + security.add_label("PII"); + security.subject = Some(crate::extensions::security::SubjectExtension { + id: Some("alice".into()), + subject_type: Some(crate::extensions::security::SubjectType::User), + roles: ["admin".to_string()].into(), + ..Default::default() + }); + + let mut http = HttpExtension::default(); + http.set_header("Authorization", "Bearer secret"); + http.set_header("X-Request-ID", "req-123"); + + let ext = Extensions { + security: Some(Arc::new(security)), + http: Some(Arc::new(http)), + request: Some(Arc::new(RequestExtension { + environment: Some("production".into()), + ..Default::default() + })), + agent: Some(Arc::new(AgentExtension { + session_id: Some("sess-001".into()), + agent_id: Some("agent-x".into()), + ..Default::default() + })), + meta: Some(Arc::new(MetaExtension { + entity_type: Some("tool".into()), + entity_name: Some("get_compensation".into()), + tags: ["pii".to_string()].into(), + ..Default::default() + })), + ..Default::default() + }; + + let msg = Message::text(Role::User, "test"); + let views: Vec<_> = msg.iter_views(None, Some(&ext)).collect(); + let dict = views[0].to_dict(true, true); + + let extensions = &dict["extensions"]; + + // Subject visible + assert_eq!(extensions["subject"]["id"], "alice"); + assert!(extensions["subject"]["roles"] + .as_array() + .unwrap() + .contains(&serde_json::json!("admin"))); + + // Labels visible + assert!(extensions["labels"] + .as_array() + .unwrap() + .contains(&serde_json::json!("PII"))); + + // Environment visible + assert_eq!(extensions["environment"], "production"); + + // Headers visible — but Authorization stripped (sensitive) + assert!(extensions["headers"].get("Authorization").is_none()); + assert_eq!(extensions["headers"]["X-Request-ID"], "req-123"); + + // Agent context visible + assert_eq!(extensions["agent"]["session_id"], "sess-001"); + assert_eq!(extensions["agent"]["agent_id"], "agent-x"); + + // Meta visible + assert_eq!(extensions["meta"]["entity_type"], "tool"); + assert_eq!(extensions["meta"]["entity_name"], "get_compensation"); + } + + #[test] + fn test_to_opa_input() { + let msg = Message::text(Role::User, "Hello"); + let views: Vec<_> = msg.iter_views(None, None).collect(); + let opa = views[0].to_opa_input(true); + + assert!(opa.get("input").is_some()); + assert_eq!(opa["input"]["kind"], "text"); + assert_eq!(opa["input"]["role"], "user"); + assert_eq!(opa["input"]["content"], "Hello"); + } +} diff --git a/crates/cpex-payload/src/config.rs b/crates/cpex-payload/src/config.rs new file mode 100644 index 00000000..89d962a2 --- /dev/null +++ b/crates/cpex-payload/src/config.rs @@ -0,0 +1,1297 @@ +// Location: ./crates/cpex-core/src/config.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// Unified YAML configuration parsing. +// +// Parses the config format that combines global settings, plugin +// declarations, and per-entity routes into a single YAML document. +// +// Supports two modes controlled by `plugin_settings.routing_enabled`: +// - false (default, backward compatible): plugins declare their +// own conditions for when they fire. +// - true: per-entity routing rules determine which plugins fire, +// with plugin selection via policy groups and meta.tags. +// +// The two modes are mutually exclusive. When routing is disabled, +// the routes and global sections are ignored. When routing is +// enabled, conditions on individual plugins are ignored. + +use std::collections::{HashMap, HashSet}; +use std::path::Path; + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::error::PluginError; +use crate::plugin::PluginConfig; + +// --------------------------------------------------------------------------- +// Top-Level Config +// --------------------------------------------------------------------------- + +/// Top-level CPEX configuration. +/// +/// Parsed from a single YAML file. Plugin scoping mode is controlled +/// by `plugin_settings.routing_enabled` — if absent or false, plugins +/// use their own `conditions:` field (backward compatible). If true, +/// the `routes:` and `global:` sections take over. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct CpexConfig { + /// Global configuration — policies, defaults. + /// Only used when `plugin_settings.routing_enabled` is true. + #[serde(default)] + pub global: GlobalConfig, + + /// Directories to scan for plugin modules. + #[serde(default)] + pub plugin_dirs: Vec, + + /// Plugin declarations. + #[serde(default)] + pub plugins: Vec, + + /// Per-entity routing rules. + /// Only used when `plugin_settings.routing_enabled` is true. + #[serde(default)] + pub routes: Vec, + + /// Global plugin settings (timeout, error behavior, routing mode). + #[serde(default)] + pub plugin_settings: PluginSettings, +} + +impl CpexConfig { + /// Whether route-based plugin selection is enabled. + pub fn routing_enabled(&self) -> bool { + self.plugin_settings.routing_enabled + } +} + +// --------------------------------------------------------------------------- +// Plugin Settings +// --------------------------------------------------------------------------- + +/// Global plugin settings. +/// +/// Controls executor behavior and routing mode. All fields have +/// sensible defaults — a missing `plugin_settings:` section is valid. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PluginSettings { + /// Enable route-based plugin selection. + /// When false (default), plugins use their own `conditions:` field. + /// When true, the `routes:` and `global:` sections determine which + /// plugins fire per entity. + #[serde(default)] + pub routing_enabled: bool, + + /// Default timeout per plugin in seconds. + #[serde(default = "default_timeout")] + pub plugin_timeout: u64, + + /// Whether to halt on first deny in concurrent mode. + #[serde(default = "default_true")] + pub short_circuit_on_deny: bool, + + /// Whether plugins can execute in parallel within a mode band. + #[serde(default)] + pub parallel_execution_within_band: bool, + + /// Whether to halt the pipeline on any plugin error. + #[serde(default)] + pub fail_on_plugin_error: bool, + + /// Maximum number of entries in the routing cache. + /// + /// When the cache reaches this size, new resolutions are computed + /// normally but not memoized — the cache rejects further inserts + /// and emits a warning. This bounds memory growth from + /// attacker-controlled entity names without the reasoning hazards + /// of eviction (silently dropped entries, stale-vs-current + /// confusion). Operators see the warning and tune the cap or + /// investigate the entity-name growth. + #[serde(default = "default_route_cache_max_entries")] + pub route_cache_max_entries: usize, +} + +impl Default for PluginSettings { + fn default() -> Self { + Self { + routing_enabled: false, + plugin_timeout: 30, + short_circuit_on_deny: true, + parallel_execution_within_band: false, + fail_on_plugin_error: false, + route_cache_max_entries: default_route_cache_max_entries(), + } + } +} + +fn default_route_cache_max_entries() -> usize { + 10_000 +} + +fn default_timeout() -> u64 { + 30 +} + +fn default_true() -> bool { + true +} + +// --------------------------------------------------------------------------- +// Global Config +// --------------------------------------------------------------------------- + +/// Global configuration — applies across all routes. +/// +/// Only used when routing is enabled. Contains named policy groups +/// (including the reserved `all` group) and per-entity-type defaults. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct GlobalConfig { + /// Named policy groups. The reserved name `all` is applied to + /// every request unconditionally. Other groups are inherited + /// by routes via `meta.tags`. + #[serde(default)] + pub policies: HashMap, + + /// Per-entity-type default policy groups. + /// Keys are `tool`, `resource`, `prompt`, `llm`. + #[serde(default)] + pub defaults: HashMap, +} + +// --------------------------------------------------------------------------- +// Policy Group +// --------------------------------------------------------------------------- + +/// A named policy group — plugins to activate and optional metadata. +/// +/// The `all` group is reserved and always applied. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct PolicyGroup { + /// Human-readable description. + #[serde(default)] + pub description: Option, + + /// Arbitrary metadata for tooling and audit. + #[serde(default)] + pub metadata: HashMap, + + /// Plugin references to activate when this group matches. + #[serde(default)] + pub plugins: Vec, +} + +// --------------------------------------------------------------------------- +// Plugin Ref (route/group plugin reference) +// --------------------------------------------------------------------------- + +/// A reference to a plugin in a route or policy group. +/// +/// ```yaml +/// plugins: +/// - rate_limiter # bare name +/// - pii_scanner: # name with config overrides +/// config: +/// sensitivity: high +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum PluginRouteRef { + /// Just the name — activate the plugin with no config overrides. + Name(String), + /// Name with config overrides — single-key map. + WithOverrides(HashMap), +} + +impl PluginRouteRef { + /// Extract the plugin name from this reference. + pub fn name(&self) -> &str { + match self { + Self::Name(name) => name, + Self::WithOverrides(map) => map.keys().next().map(|s| s.as_str()).unwrap_or(""), + } + } + + /// Extract config overrides, if any. + pub fn overrides(&self) -> Option<&serde_json::Value> { + match self { + Self::Name(_) => None, + Self::WithOverrides(map) => map.values().next(), + } + } +} + +// --------------------------------------------------------------------------- +// Route Entry +// --------------------------------------------------------------------------- + +/// A per-entity routing rule. +/// +/// Matches one entity type (tool, resource, prompt, or LLM) and +/// determines which plugins fire. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct RouteEntry { + /// Match a tool by exact name, list, or glob. + #[serde(default)] + pub tool: Option, + + /// Match a resource by exact URI, list, or glob. + #[serde(default)] + pub resource: Option, + + /// Match a prompt by exact name, list, or glob. + #[serde(default)] + pub prompt: Option, + + /// Match an LLM by exact model name, list, or glob. + #[serde(default)] + pub llm: Option, + + /// Operational metadata — tags, scope, properties. + #[serde(default)] + pub meta: Option, + + /// Conditional match expression — carried but not evaluated + /// during static resolution. Evaluated at runtime when payload + /// data is available (future: APL evaluator). + #[serde(default)] + pub when: Option, + + /// Plugin references to activate for this route. + #[serde(default)] + pub plugins: Vec, +} + +// --------------------------------------------------------------------------- +// Route Meta +// --------------------------------------------------------------------------- + +/// Operational metadata on a route entry. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct RouteMeta { + /// Entity tags — drive policy group inheritance. + #[serde(default)] + pub tags: Vec, + + /// Host-defined grouping (virtual server ID, namespace, etc.). + /// Used for scope matching: route scope must match request scope. + #[serde(default)] + pub scope: Option, + + /// Arbitrary key-value metadata. + #[serde(default)] + pub properties: HashMap, +} + +// --------------------------------------------------------------------------- +// String or List (for tool matching) +// --------------------------------------------------------------------------- + +/// An entity-name pattern. Holds the original pattern string (for +/// serialization round-tripping and operator-facing diagnostics) plus a +/// `WildMatch` matcher pre-compiled at deserialize time so route resolution +/// doesn't re-parse the pattern on every request. Custom `Serialize` / +/// `Deserialize` make this transparent to YAML — it serializes as a plain +/// string, just like the previous `String` field did. +/// +/// Glob syntax (via `wildmatch`): +/// - `*` matches any sequence of characters (including empty). +/// - `?` matches any single character. +/// +/// The previous hand-rolled matcher only handled trailing-`*` correctly: +/// `*suffix` patterns silently matched almost nothing, and multi-star +/// patterns like `**` accidentally matched everything. Both shapes are +/// real security footguns for scope/tool restriction rules — switching to +/// `wildmatch` gives us full single-segment glob semantics. +#[derive(Debug, Clone)] +pub struct Pattern { + pattern: String, + matcher: wildmatch::WildMatch, +} + +impl Pattern { + /// Compile a pattern. Done once at config load; subsequent `matches()` + /// calls reuse the compiled `WildMatch`. + pub fn new(pattern: impl Into) -> Self { + let pattern = pattern.into(); + let matcher = wildmatch::WildMatch::new(&pattern); + Self { pattern, matcher } + } + + /// Match the given name against the compiled pattern. + pub fn matches(&self, name: &str) -> bool { + self.matcher.matches(name) + } + + /// The original pattern string (e.g., `"hr-*"`). + pub fn as_str(&self) -> &str { + &self.pattern + } +} + +impl Default for Pattern { + fn default() -> Self { + Self::new("") + } +} + +impl Serialize for Pattern { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(&self.pattern) + } +} + +impl<'de> Deserialize<'de> for Pattern { + fn deserialize>(deserializer: D) -> Result { + let s = String::deserialize(deserializer)?; + Ok(Pattern::new(s)) + } +} + +/// A tool matcher — single name, list of names, or glob pattern. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum StringOrList { + /// Single string (exact name or glob pattern). Pre-compiled at + /// deserialize time so the route-resolution slow path doesn't re-parse + /// on each request. + Single(Pattern), + /// List of exact names. + List(Vec), +} + +impl Default for StringOrList { + fn default() -> Self { + Self::Single(Pattern::default()) + } +} + +impl StringOrList { + /// Check if this matcher matches the given name. + pub fn matches(&self, name: &str) -> bool { + match self { + Self::Single(pattern) => pattern.matches(name), + Self::List(names) => names.iter().any(|n| n == name), + } + } +} + +// --------------------------------------------------------------------------- +// Config Loading +// --------------------------------------------------------------------------- + +/// Load and parse a CPEX config from a YAML file. +pub fn load_config(path: &Path) -> Result> { + let content = std::fs::read_to_string(path).map_err(|e| PluginError::Config { + message: format!("failed to read config file '{}': {}", path.display(), e), + })?; + parse_config(&content) +} + +/// Parse a CPEX config from a YAML string. +pub fn parse_config(yaml: &str) -> Result> { + let config: CpexConfig = serde_yaml::from_str(yaml).map_err(|e| PluginError::Config { + message: format!("failed to parse config YAML: {}", e), + })?; + validate_config(&config)?; + Ok(config) +} + +// --------------------------------------------------------------------------- +// Validation +// --------------------------------------------------------------------------- + +/// Validate a parsed config for structural correctness. +fn validate_config(config: &CpexConfig) -> Result<(), Box> { + let mut seen_names = HashSet::new(); + for plugin in &config.plugins { + if !seen_names.insert(&plugin.name) { + return Err(Box::new(PluginError::Config { + message: format!("duplicate plugin name: '{}'", plugin.name), + })); + } + } + + if config.routing_enabled() { + let plugin_names: HashSet<&str> = config.plugins.iter().map(|p| p.name.as_str()).collect(); + + for (i, route) in config.routes.iter().enumerate() { + let count = [ + route.tool.is_some(), + route.resource.is_some(), + route.prompt.is_some(), + route.llm.is_some(), + ] + .iter() + .filter(|&&m| m) + .count(); + + if count == 0 { + return Err(Box::new(PluginError::Config { + message: format!( + "route {} has no entity matcher (need tool, resource, prompt, or llm)", + i + ), + })); + } + if count > 1 { + return Err(Box::new(PluginError::Config { + message: format!( + "route {} has multiple entity matchers (need exactly one)", + i + ), + })); + } + + for plugin_ref in &route.plugins { + if !plugin_names.contains(plugin_ref.name()) { + return Err(Box::new(PluginError::Config { + message: format!( + "route {} references unknown plugin '{}'", + i, + plugin_ref.name() + ), + })); + } + } + } + + for (group_name, group) in &config.global.policies { + for plugin_ref in &group.plugins { + if !plugin_names.contains(plugin_ref.name()) { + return Err(Box::new(PluginError::Config { + message: format!( + "policy group '{}' references unknown plugin '{}'", + group_name, + plugin_ref.name() + ), + })); + } + } + } + } + + Ok(()) +} + +// --------------------------------------------------------------------------- +// Route Resolution +// --------------------------------------------------------------------------- + +/// Specificity scores for route matching. +const SPECIFICITY_EXACT_NAME: usize = 1000; +const SPECIFICITY_NAME_LIST: usize = 500; +const SPECIFICITY_GLOB: usize = 300; +const SPECIFICITY_WHEN_ONLY: usize = 10; +const SPECIFICITY_WILDCARD: usize = 0; + +/// Score a single entity matcher (tool / resource / prompt / llm) against +/// a request entity name, returning the specificity bucket if it matches +/// or `None` if it doesn't (or the matcher is absent). Replaces four +/// copy-pasted match arms in `resolve_plugins_for_entity`. +fn score_entity_match(matcher: Option<&StringOrList>, entity_name: &str) -> Option { + let matcher = matcher?; + if !matcher.matches(entity_name) { + return None; + } + let score = match matcher { + StringOrList::Single(p) if p.as_str() == "*" => SPECIFICITY_WILDCARD, + StringOrList::Single(p) if p.as_str().contains('*') => SPECIFICITY_GLOB, + StringOrList::List(_) => SPECIFICITY_NAME_LIST, + StringOrList::Single(_) => SPECIFICITY_EXACT_NAME, + }; + Some(score) +} + +/// Resolve which plugins should fire for a given entity. +/// +/// When routing is disabled, returns all plugin names. When enabled, +/// matches the entity against routes and collects plugins from the +/// `all` group, defaults, matching policy groups (via merged tags), +/// and the route itself. +/// +/// `request_scope` and `request_tags` come from the host's +/// `MetaExtension` on the request. +pub fn resolve_plugins_for_entity( + config: &CpexConfig, + entity_type: &str, + entity_name: &str, + request_scope: Option<&str>, + request_tags: &HashSet, +) -> Vec { + if !config.routing_enabled() { + return config + .plugins + .iter() + .map(|p| ResolvedPlugin { + name: p.name.clone(), + config_overrides: None, + when: None, + }) + .collect(); + } + + let mut resolved = Vec::new(); + + // 1. Always include plugins from the "all" policy group + if let Some(all_group) = config.global.policies.get("all") { + collect_plugin_refs(&all_group.plugins, &mut resolved, None); + } + + // 2. Include plugins from matching defaults + if let Some(default_group) = config.global.defaults.get(entity_type) { + collect_plugin_refs(&default_group.plugins, &mut resolved, None); + } + + // 3. Find matching route (with scope check) + if let Some(route) = find_matching_route(config, entity_type, entity_name, request_scope) { + // Merge tags: route's static tags + host's runtime tags + let mut merged_tags: HashSet = request_tags.clone(); + if let Some(meta) = &route.meta { + for tag in &meta.tags { + merged_tags.insert(tag.clone()); + } + } + + // Include plugins from all matching policy groups (merged tags) + for tag in &merged_tags { + if tag == "all" { + continue; // already handled above + } + if let Some(group) = config.global.policies.get(tag.as_str()) { + collect_plugin_refs(&group.plugins, &mut resolved, None); + } + } + + // Include route-level plugins, carrying the route's when clause + collect_plugin_refs(&route.plugins, &mut resolved, route.when.as_deref()); + } + + // Deduplicate by name, preserving order. Later overrides win. + let mut seen = HashSet::new(); + let mut deduped = Vec::new(); + for rp in resolved.into_iter().rev() { + if seen.insert(rp.name.clone()) { + deduped.push(rp); + } + } + deduped.reverse(); + deduped +} + +/// A resolved plugin with optional config overrides and when clause. +#[derive(Debug, Clone)] +pub struct ResolvedPlugin { + /// Plugin name. + pub name: String, + + /// Config overrides from the route. + pub config_overrides: Option, + + /// When clause from the route — carried but not evaluated here. + pub when: Option, +} + +/// Collect plugin refs into the resolved list. +fn collect_plugin_refs( + refs: &[PluginRouteRef], + resolved: &mut Vec, + route_when: Option<&str>, +) { + for plugin_ref in refs { + resolved.push(ResolvedPlugin { + name: plugin_ref.name().to_string(), + config_overrides: plugin_ref.overrides().cloned(), + when: route_when.map(String::from), + }); + } +} + +/// Find the best matching route for an entity by specificity. +/// +/// Scope matching: if a route declares a scope, the request must +/// have the same scope. No scope on the route matches any request. +fn find_matching_route<'a>( + config: &'a CpexConfig, + entity_type: &str, + entity_name: &str, + request_scope: Option<&str>, +) -> Option<&'a RouteEntry> { + let mut best: Option<(usize, &RouteEntry)> = None; + + for route in &config.routes { + // Check scope compatibility + let route_scope = route.meta.as_ref().and_then(|m| m.scope.as_deref()); + let scope_bonus = match (route_scope, request_scope) { + (None, _) => 0, // route is global + (Some(rs), Some(rq)) if rs == rq => 100, // scopes match + (Some(_), _) => continue, // scope mismatch — skip + }; + + let entity_matcher = match entity_type { + "tool" => route.tool.as_ref(), + "resource" => route.resource.as_ref(), + "prompt" => route.prompt.as_ref(), + "llm" => route.llm.as_ref(), + _ => continue, + }; + let base_specificity = match score_entity_match(entity_matcher, entity_name) { + Some(score) => score, + None => continue, + }; + + let when_bonus = if route.when.is_some() { + SPECIFICITY_WHEN_ONLY + } else { + 0 + }; + let total = base_specificity + scope_bonus + when_bonus; + + if best.is_none_or(|(s, _)| total > s) { + best = Some((total, route)); + } + } + + best.map(|(_, route)| route) +} + +#[cfg(test)] +mod tests { + use super::*; + + // Helper: empty tags for tests that don't need them + fn no_tags() -> HashSet { + HashSet::new() + } + + #[test] + fn test_parse_minimal_config() { + let yaml = r#" +plugins: + - name: rate_limiter + kind: builtin + hooks: [tool_pre_invoke] + mode: sequential + priority: 5 + config: + max_requests: 100 +"#; + let config = parse_config(yaml).unwrap(); + assert!(!config.routing_enabled()); + assert_eq!(config.plugins.len(), 1); + assert_eq!(config.plugins[0].name, "rate_limiter"); + } + + #[test] + fn test_no_plugin_settings_defaults_routing_disabled() { + let yaml = r#" +plugins: + - name: test + kind: builtin + hooks: [tool_pre_invoke] +"#; + let config = parse_config(yaml).unwrap(); + assert!(!config.routing_enabled()); + assert_eq!(config.plugin_settings.plugin_timeout, 30); + } + + #[test] + fn test_routing_enabled() { + let yaml = r#" +plugin_settings: + routing_enabled: true +global: + policies: + all: + plugins: [identity] +plugins: + - name: identity + kind: builtin + hooks: [identity_resolve] +routes: + - tool: get_compensation + meta: + tags: [pii] +"#; + let config = parse_config(yaml).unwrap(); + assert!(config.routing_enabled()); + } + + #[test] + fn test_duplicate_plugin_names_rejected() { + let yaml = r#" +plugins: + - name: dup + kind: builtin + hooks: [tool_pre_invoke] + - name: dup + kind: builtin + hooks: [tool_post_invoke] +"#; + assert!(parse_config(yaml) + .unwrap_err() + .to_string() + .contains("duplicate plugin name")); + } + + #[test] + fn test_route_requires_one_entity_matcher() { + let yaml = r#" +plugin_settings: + routing_enabled: true +plugins: [] +routes: + - meta: + tags: [pii] +"#; + assert!(parse_config(yaml) + .unwrap_err() + .to_string() + .contains("no entity matcher")); + } + + #[test] + fn test_route_rejects_multiple_entity_matchers() { + let yaml = r#" +plugin_settings: + routing_enabled: true +plugins: [] +routes: + - tool: get_compensation + resource: "hr://employees/*" +"#; + assert!(parse_config(yaml) + .unwrap_err() + .to_string() + .contains("multiple entity matchers")); + } + + #[test] + fn test_route_unknown_plugin_rejected() { + let yaml = r#" +plugin_settings: + routing_enabled: true +plugins: + - name: known + kind: builtin + hooks: [tool_pre_invoke] +routes: + - tool: get_compensation + plugins: + - unknown +"#; + assert!(parse_config(yaml) + .unwrap_err() + .to_string() + .contains("unknown plugin 'unknown'")); + } + + #[test] + fn test_policy_group_unknown_plugin_rejected() { + let yaml = r#" +plugin_settings: + routing_enabled: true +global: + policies: + all: + plugins: [nonexistent] +plugins: [] +routes: [] +"#; + assert!(parse_config(yaml) + .unwrap_err() + .to_string() + .contains("unknown plugin 'nonexistent'")); + } + + #[test] + fn test_resolve_conditions_mode_returns_all() { + let yaml = r#" +plugins: + - name: a + kind: builtin + hooks: [tool_pre_invoke] + - name: b + kind: builtin + hooks: [tool_post_invoke] +"#; + let config = parse_config(yaml).unwrap(); + let resolved = resolve_plugins_for_entity(&config, "tool", "anything", None, &no_tags()); + let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); + assert_eq!(names, vec!["a", "b"]); + } + + #[test] + fn test_resolve_routes_inherits_policy_groups() { + let yaml = r#" +plugin_settings: + routing_enabled: true +global: + policies: + all: + plugins: + - identity + pii: + plugins: + - apl_policy +plugins: + - name: identity + kind: builtin + hooks: [identity_resolve] + - name: apl_policy + kind: builtin + hooks: [cmf.tool_pre_invoke] +routes: + - tool: get_compensation + meta: + tags: [pii] +"#; + let config = parse_config(yaml).unwrap(); + let resolved = + resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &no_tags()); + let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); + assert!(names.contains(&"identity")); + assert!(names.contains(&"apl_policy")); + } + + #[test] + fn test_resolve_no_matching_route_gets_all_only() { + let yaml = r#" +plugin_settings: + routing_enabled: true +global: + policies: + all: + plugins: + - identity +plugins: + - name: identity + kind: builtin + hooks: [identity_resolve] +routes: + - tool: get_compensation +"#; + let config = parse_config(yaml).unwrap(); + let resolved = + resolve_plugins_for_entity(&config, "tool", "unknown_tool", None, &no_tags()); + let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); + assert_eq!(names, vec!["identity"]); + } + + #[test] + fn test_exact_match_beats_glob() { + let yaml = r#" +plugin_settings: + routing_enabled: true +plugins: + - name: specific + kind: builtin + hooks: [tool_pre_invoke] + - name: general + kind: builtin + hooks: [tool_pre_invoke] +routes: + - tool: "hr-*" + plugins: + - general + - tool: hr-compensation + plugins: + - specific +"#; + let config = parse_config(yaml).unwrap(); + let resolved = + resolve_plugins_for_entity(&config, "tool", "hr-compensation", None, &no_tags()); + let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); + assert!(names.contains(&"specific")); + assert!(!names.contains(&"general")); + } + + #[test] + fn test_plugin_ref_bare_name() { + let yaml = r#" +plugin_settings: + routing_enabled: true +plugins: + - name: rate_limiter + kind: builtin + hooks: [tool_pre_invoke] +routes: + - tool: get_compensation + plugins: + - rate_limiter +"#; + let config = parse_config(yaml).unwrap(); + let resolved = + resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &no_tags()); + assert_eq!(resolved[0].name, "rate_limiter"); + assert!(resolved[0].config_overrides.is_none()); + } + + #[test] + fn test_plugin_ref_with_overrides() { + let yaml = r#" +plugin_settings: + routing_enabled: true +plugins: + - name: rate_limiter + kind: builtin + hooks: [tool_pre_invoke] + config: + max_requests: 100 +routes: + - tool: get_compensation + plugins: + - rate_limiter: + config: + max_requests: 10 +"#; + let config = parse_config(yaml).unwrap(); + let resolved = + resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &no_tags()); + assert_eq!(resolved[0].name, "rate_limiter"); + assert!(resolved[0].config_overrides.is_some()); + let overrides = resolved[0].config_overrides.as_ref().unwrap(); + assert_eq!(overrides["config"]["max_requests"], 10); + } + + #[test] + fn test_plugin_ref_mixed_bare_and_overrides() { + let yaml = r#" +plugin_settings: + routing_enabled: true +plugins: + - name: rate_limiter + kind: builtin + hooks: [tool_pre_invoke] + - name: pii_scanner + kind: builtin + hooks: [tool_pre_invoke] +routes: + - tool: get_compensation + plugins: + - rate_limiter + - pii_scanner: + config: + sensitivity: high +"#; + let config = parse_config(yaml).unwrap(); + let resolved = + resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &no_tags()); + assert_eq!(resolved.len(), 2); + assert_eq!(resolved[0].name, "rate_limiter"); + assert!(resolved[0].config_overrides.is_none()); + assert_eq!(resolved[1].name, "pii_scanner"); + assert!(resolved[1].config_overrides.is_some()); + } + + #[test] + fn test_deduplication_preserves_order() { + let yaml = r#" +plugin_settings: + routing_enabled: true +global: + policies: + all: + plugins: [a, b] + pii: + plugins: [b, c] +plugins: + - name: a + kind: builtin + hooks: [tool_pre_invoke] + - name: b + kind: builtin + hooks: [tool_pre_invoke] + - name: c + kind: builtin + hooks: [tool_pre_invoke] +routes: + - tool: get_compensation + meta: + tags: [pii] +"#; + let config = parse_config(yaml).unwrap(); + let resolved = + resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &no_tags()); + let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); + assert_eq!(names, vec!["a", "b", "c"]); + } + + #[test] + fn test_glob_trailing_wildcard() { + let matcher = StringOrList::Single(Pattern::new("hr-*")); + assert!(matcher.matches("hr-compensation")); + assert!(matcher.matches("hr-benefits")); + assert!(matcher.matches("hr-")); // empty match for * + assert!(!matcher.matches("finance-report")); + assert!(!matcher.matches("hr")); + } + + #[test] + fn test_wildcard_matches_everything() { + let matcher = StringOrList::Single(Pattern::new("*")); + assert!(matcher.matches("anything")); + assert!(matcher.matches("")); + } + + /// Regression for the security footgun: `*suffix` patterns were + /// silently matching almost nothing because the previous matcher + /// looked for `"*suffix"` as a literal prefix. + #[test] + fn test_glob_leading_wildcard() { + let matcher = StringOrList::Single(Pattern::new("*-prod")); + assert!(matcher.matches("foo-prod")); + assert!(matcher.matches("-prod")); // empty match for * + assert!(!matcher.matches("foo-staging")); + assert!(!matcher.matches("prod")); + } + + /// Regression for `prefix*suffix` patterns also broken before. + #[test] + fn test_glob_mid_wildcard() { + let matcher = StringOrList::Single(Pattern::new("hr-*-v1")); + assert!(matcher.matches("hr-comp-v1")); + assert!(matcher.matches("hr--v1")); // empty match for * + assert!(!matcher.matches("hr-comp-v2")); + assert!(!matcher.matches("finance-comp-v1")); + } + + /// Multiple-wildcard patterns must work everywhere `*` appears. + #[test] + fn test_glob_multiple_wildcards() { + let matcher = StringOrList::Single(Pattern::new("*hr*comp*")); + assert!(matcher.matches("hr-comp")); + assert!(matcher.matches("xyz-hr-comp-foo")); + assert!(!matcher.matches("hr-only")); + assert!(!matcher.matches("comp-only")); + } + + /// Regression for the OTHER security footgun: multi-star patterns + /// like `**` were `trim_end_matches('*')`'d to `""` and then matched + /// every name via `starts_with("")`. With wildmatch this is a + /// degenerate-but-correct "match anything" pattern, equivalent to `*`. + #[test] + fn test_glob_multi_star_is_equivalent_to_single_star() { + for pattern in &["**", "***", "*****"] { + let matcher = StringOrList::Single(Pattern::new(*pattern)); + assert!( + matcher.matches("anything"), + "pattern {} should match", + pattern + ); + assert!( + matcher.matches(""), + "pattern {} should match empty", + pattern + ); + } + } + + /// `WildMatch` is built once at deserialize / `Pattern::new` time and + /// reused; this test just sanity-checks the round-trip through serde. + #[test] + fn test_pattern_round_trips_through_yaml() { + let yaml = "tool: '*-prod'"; + #[derive(Deserialize, Serialize)] + struct Wrap { + tool: StringOrList, + } + let parsed: Wrap = serde_yaml::from_str(yaml).unwrap(); + assert!(parsed.tool.matches("foo-prod")); + assert!(!parsed.tool.matches("foo-staging")); + let back = serde_yaml::to_string(&parsed).unwrap(); + assert!( + back.contains("*-prod"), + "serialized YAML should preserve pattern: {}", + back + ); + } + + #[test] + fn test_list_matches_any_member() { + let matcher = StringOrList::List(vec![ + "get_compensation".to_string(), + "get_benefits".to_string(), + ]); + assert!(matcher.matches("get_compensation")); + assert!(matcher.matches("get_benefits")); + assert!(!matcher.matches("send_email")); + } + + #[test] + fn test_validation_skipped_when_routing_disabled() { + let yaml = r#" +plugins: + - name: test + kind: builtin + hooks: [tool_pre_invoke] +routes: + - meta: + tags: [pii] +"#; + let config = parse_config(yaml); + assert!(config.is_ok()); + } + + // -- Scope matching tests -- + + #[test] + fn test_scope_match_selects_scoped_route() { + let yaml = r#" +plugin_settings: + routing_enabled: true +plugins: + - name: scoped_plugin + kind: builtin + hooks: [tool_pre_invoke] + - name: global_plugin + kind: builtin + hooks: [tool_pre_invoke] +routes: + - tool: get_compensation + meta: + scope: hr-services + plugins: + - scoped_plugin + - tool: get_compensation + plugins: + - global_plugin +"#; + let config = parse_config(yaml).unwrap(); + + // With matching scope — scoped route wins (more specific) + let resolved = resolve_plugins_for_entity( + &config, + "tool", + "get_compensation", + Some("hr-services"), + &no_tags(), + ); + let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); + assert!(names.contains(&"scoped_plugin")); + assert!(!names.contains(&"global_plugin")); + + // Without scope — global route matches + let resolved = + resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &no_tags()); + let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); + assert!(names.contains(&"global_plugin")); + assert!(!names.contains(&"scoped_plugin")); + + // With different scope — global route matches (scoped doesn't) + let resolved = resolve_plugins_for_entity( + &config, + "tool", + "get_compensation", + Some("billing"), + &no_tags(), + ); + let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); + assert!(names.contains(&"global_plugin")); + assert!(!names.contains(&"scoped_plugin")); + } + + // -- Tag merging tests -- + + #[test] + fn test_host_tags_merged_with_route_tags() { + let yaml = r#" +plugin_settings: + routing_enabled: true +global: + policies: + pii: + plugins: [pii_plugin] + runtime_tag: + plugins: [runtime_plugin] +plugins: + - name: pii_plugin + kind: builtin + hooks: [tool_pre_invoke] + - name: runtime_plugin + kind: builtin + hooks: [tool_pre_invoke] +routes: + - tool: get_compensation + meta: + tags: [pii] +"#; + let config = parse_config(yaml).unwrap(); + + // Host provides a runtime tag that matches a policy group + let mut host_tags = HashSet::new(); + host_tags.insert("runtime_tag".to_string()); + + let resolved = + resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &host_tags); + let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); + + // Both route's static tag (pii) and host's runtime tag activate their groups + assert!(names.contains(&"pii_plugin")); + assert!(names.contains(&"runtime_plugin")); + } + + // -- When clause carried tests -- + + #[test] + fn test_when_clause_carried_on_resolved_plugins() { + let yaml = r#" +plugin_settings: + routing_enabled: true +plugins: + - name: conditional_plugin + kind: builtin + hooks: [tool_pre_invoke] +routes: + - tool: get_compensation + when: "args.include_ssn == true" + plugins: + - conditional_plugin +"#; + let config = parse_config(yaml).unwrap(); + let resolved = + resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &no_tags()); + assert_eq!(resolved[0].name, "conditional_plugin"); + assert_eq!( + resolved[0].when.as_deref(), + Some("args.include_ssn == true") + ); + } + + #[test] + fn test_when_clause_not_on_policy_group_plugins() { + let yaml = r#" +plugin_settings: + routing_enabled: true +global: + policies: + all: + plugins: [global_plugin] +plugins: + - name: global_plugin + kind: builtin + hooks: [tool_pre_invoke] + - name: route_plugin + kind: builtin + hooks: [tool_pre_invoke] +routes: + - tool: get_compensation + when: "args.sensitive == true" + plugins: + - route_plugin +"#; + let config = parse_config(yaml).unwrap(); + let resolved = + resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &no_tags()); + + // global_plugin has no when clause (from all group) + let global = resolved.iter().find(|r| r.name == "global_plugin").unwrap(); + assert!(global.when.is_none()); + + // route_plugin carries the route's when clause + let route = resolved.iter().find(|r| r.name == "route_plugin").unwrap(); + assert_eq!(route.when.as_deref(), Some("args.sensitive == true")); + } +} diff --git a/crates/cpex-payload/src/context.rs b/crates/cpex-payload/src/context.rs new file mode 100644 index 00000000..2176c7bc --- /dev/null +++ b/crates/cpex-payload/src/context.rs @@ -0,0 +1,195 @@ +// Location: ./crates/cpex-core/src/context.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// Execution context types. +// +// Provides PluginContext — the per-plugin, per-invocation execution +// context carrying transient state (counters, caches, intermediate +// results). All data needed for policy evaluation comes from the +// payload's extensions (filtered by capabilities), not from context. +// +// PluginContext has two state maps: +// - local_state: private to this plugin, this invocation +// - global_state: shared across plugins in a pipeline +// +// Identity, request metadata, tenant scope, etc. live in extensions +// (MetaExtension, SecurityExtension), not in the context. +// +// Mirrors the spec's PluginContext in plugin-framework-spec-v2.md §8.1. + +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use uuid::Uuid; + +// --------------------------------------------------------------------------- +// Plugin Context +// --------------------------------------------------------------------------- + +/// Per-plugin, per-invocation execution context. +/// +/// Each plugin receives its own `PluginContext` with: +/// +/// - `local_state` — private to this plugin, this invocation. Fresh +/// each time. Used for per-plugin counters, caches, scratch data. +/// - `global_state` — shared across all plugins in a pipeline. The +/// executor merges changes back after serial phases so subsequent +/// plugins see contributions from earlier ones. +/// +/// All data needed for policy evaluation (identity, tenant, request +/// metadata) comes from the payload's extensions, capability-gated +/// per plugin. Context is purely for transient execution state. +/// +/// ```text +/// PluginContext +/// ├── local_state: HashMap # Per-plugin, per-request. Private. +/// └── global_state: HashMap # Shared across plugins. Use with care. +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PluginContext { + /// Plugin-local state. Private to this plugin, this invocation. + #[serde(default)] + pub local_state: HashMap, + + /// Shared state across all plugins in the pipeline. + /// The executor merges changes back after each serial-phase plugin. + #[serde(default)] + pub global_state: HashMap, +} + +impl PluginContext { + /// Create a new empty plugin context. + pub fn new() -> Self { + Self { + local_state: HashMap::new(), + global_state: HashMap::new(), + } + } + + /// Create a plugin context with pre-populated global state. + pub fn with_global_state(global_state: HashMap) -> Self { + Self { + local_state: HashMap::new(), + global_state, + } + } + + /// Get a value from local state. + pub fn get_local(&self, key: &str) -> Option<&Value> { + self.local_state.get(key) + } + + /// Set a value in local state. + pub fn set_local(&mut self, key: impl Into, value: Value) { + self.local_state.insert(key.into(), value); + } + + /// Get a value from global state. + pub fn get_global(&self, key: &str) -> Option<&Value> { + self.global_state.get(key) + } + + /// Set a value in global state. + pub fn set_global(&mut self, key: impl Into, value: Value) { + self.global_state.insert(key.into(), value); + } +} + +impl Default for PluginContext { + fn default() -> Self { + Self::new() + } +} + +// --------------------------------------------------------------------------- +// Plugin Context Table +// --------------------------------------------------------------------------- + +/// Threaded execution state carried from one hook invocation to the next +/// within a single request lifecycle (e.g., `pre_invoke` → `post_invoke`). +/// +/// The table holds the canonical pipeline state in two parts: +/// +/// - `global_state` — a single shared map across all plugins. The executor +/// clones this into each plugin's `PluginContext.global_state` at the +/// start of a run, then commits the plugin's possibly-modified copy back +/// when the run completes (last-writer-wins for serial phases). +/// - `local_states` — per-plugin private state, indexed by plugin ID. +/// Persists across hook invocations so a plugin's `pre_invoke` can stash +/// data its `post_invoke` will read. +/// +/// Storing `global_state` once (rather than copying it inside every per-plugin +/// `PluginContext`) makes the canonical state explicit and removes the +/// non-deterministic "pick an arbitrary plugin's snapshot" pattern that was +/// previously needed to recover it. +/// +/// Returned by the executor in `PipelineResult` and passed back into the +/// next hook call. On the first hook call pass `None` — the executor +/// creates a fresh table. +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct PluginContextTable { + /// Authoritative shared state across all plugins in the pipeline. + #[serde(default)] + pub global_state: HashMap, + + /// Per-plugin local state, indexed by plugin ID (`Uuid`). + #[serde(default)] + pub local_states: HashMap>, +} + +impl PluginContextTable { + /// Create an empty context table. + pub fn new() -> Self { + Self::default() + } + + /// Build a `PluginContext` for the given plugin, *removing* its stored + /// local_state from the table and seeding it with a fresh clone of the + /// canonical global_state. Use in serial phases where the plugin will + /// commit its local_state changes back via [`store_context`]. + /// + /// If the plugin has no stored local_state yet, its context starts + /// empty (first invocation in the request lifecycle). + pub fn take_context(&mut self, plugin_id: Uuid) -> PluginContext { + PluginContext { + local_state: self.local_states.remove(&plugin_id).unwrap_or_default(), + global_state: self.global_state.clone(), + } + } + + /// Build a `PluginContext` for the given plugin without mutating the + /// table — the local_state is *cloned* and the global_state is cloned. + /// Use in read-only phases (audit, concurrent, fire-and-forget) where + /// per-plugin mutations should not influence subsequent plugins. + pub fn snapshot_context(&self, plugin_id: Uuid) -> PluginContext { + PluginContext { + local_state: self + .local_states + .get(&plugin_id) + .cloned() + .unwrap_or_default(), + global_state: self.global_state.clone(), + } + } + + /// Commit a plugin's context back into the table after it ran. Replaces + /// the canonical global_state with the plugin's possibly-modified copy + /// (move, no clone) and stores the plugin's local_state for next time. + pub fn store_context(&mut self, plugin_id: Uuid, ctx: PluginContext) { + self.global_state = ctx.global_state; + self.local_states.insert(plugin_id, ctx.local_state); + } + + /// Number of plugins with stored local_state in the table. + pub fn len(&self) -> usize { + self.local_states.len() + } + + /// Whether the table holds no per-plugin local_state. + pub fn is_empty(&self) -> bool { + self.local_states.is_empty() + } +} diff --git a/crates/cpex-payload/src/error.rs b/crates/cpex-payload/src/error.rs new file mode 100644 index 00000000..576dc5bc --- /dev/null +++ b/crates/cpex-payload/src/error.rs @@ -0,0 +1,275 @@ +// Location: ./crates/cpex-core/src/error.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// Error types for the CPEX plugin framework. +// +// Provides structured error types for plugin execution failures, +// policy violations, timeouts, and configuration errors. Mirrors +// the Python framework's PluginError, PluginViolation, and +// PluginViolationError types. + +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +// --------------------------------------------------------------------------- +// Plugin Errors +// --------------------------------------------------------------------------- + +/// Top-level error type for the CPEX framework. +/// +/// Covers plugin execution failures, policy violations, timeouts, +/// and configuration issues. Each variant carries enough context +/// for the caller to log, report, or recover. +/// +/// Mirrors the Python framework's `PluginErrorModel` with: +/// - `code` — business-logic error code (e.g., `"rate_limit_exceeded"`) +/// - `details` — structured diagnostic data for logging +/// - `proto_error_code` — protocol-level error code for the host to +/// map back to the wire format (MCP JSON-RPC, HTTP status, etc.) +#[derive(Debug, Error)] +pub enum PluginError { + /// A plugin raised an execution error. + #[error("plugin '{plugin_name}' failed: {message}")] + Execution { + plugin_name: String, + message: String, + /// Business-logic error code (e.g., `"invalid_token"`). + #[source] + source: Option>, + /// Business-logic error code set by the plugin. + code: Option, + /// Structured diagnostic data for logging or debugging. + details: HashMap, + /// Protocol-level error code for the host to map to the wire + /// format. MCP: JSON-RPC codes (e.g., -32603). HTTP: status + /// codes. The host interprets this; CPEX just carries it. + proto_error_code: Option, + }, + + /// A plugin exceeded its execution timeout. + #[error("plugin '{plugin_name}' timed out after {timeout_ms}ms")] + Timeout { + plugin_name: String, + timeout_ms: u64, + /// Protocol-level error code for the host. + proto_error_code: Option, + }, + + /// A plugin returned a policy violation (deny). + #[error("plugin '{plugin_name}' denied: {}", violation.reason)] + Violation { + plugin_name: String, + violation: PluginViolation, + }, + + /// Configuration parsing or validation failed. + #[error("configuration error: {message}")] + Config { message: String }, + + /// A hook type was not found in the registry. + #[error("unknown hook type: {hook_type}")] + UnknownHook { hook_type: String }, +} + +impl PluginError { + /// Box this error for use in `Result>`. + /// + /// Public APIs return `Result>` rather than + /// `Result>` because the enum is large (~184 bytes + /// — `details: HashMap` and the `source: Box` push it + /// well past clippy's `result_large_err` threshold). Boxing keeps + /// `Result` pointer-sized on the success path; the + /// allocation only happens on the error path. + /// + /// `.boxed()` is sugar for `Box::new(...)` that reads better at + /// construction sites: `PluginError::Config { ... }.boxed()`. + /// `?` already calls `From::from`, and `From for Box` is + /// built into std, so existing `?` chains keep working. + pub fn boxed(self) -> Box { + Box::new(self) + } +} + +// --------------------------------------------------------------------------- +// Plugin Error Record +// --------------------------------------------------------------------------- + +/// A `Clone`-able, serialization-friendly snapshot of a `PluginError`. +/// +/// Used in `PipelineResult.errors` to surface execution failures from +/// `on_error: ignore` / `on_error: disable` plugins to the caller — +/// previously those errors were only logged via `tracing::warn!` and +/// were invisible to programmatic consumers (agents, dashboards, +/// retry logic). +/// +/// `PluginError` itself can't be `Clone` because of its +/// `Box` source field, and that +/// field doesn't survive serialization anyway. `PluginErrorRecord` +/// flattens the five enum variants into a single shape — the +/// `From<&PluginError>` impl handles the variant-to-fields mapping. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PluginErrorRecord { + pub plugin_name: String, + pub message: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub code: Option, + #[serde(default, skip_serializing_if = "HashMap::is_empty")] + pub details: HashMap, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub proto_error_code: Option, +} + +/// Forward `&Box` to the `&PluginError` impl. +/// +/// Public APIs return `Result>` (see +/// `PluginError::boxed`), which means error-handling code in the +/// pipeline (e.g., `Ok(Err(e))` inside `executor::run_*_phase`) holds +/// `e: Box`. This blanket forward keeps existing +/// `(&e).into()` call sites working without forcing every caller to +/// write `(&*e).into()` after the boxing migration. +impl From<&Box> for PluginErrorRecord { + fn from(e: &Box) -> Self { + PluginErrorRecord::from(e.as_ref()) + } +} + +impl From<&PluginError> for PluginErrorRecord { + fn from(e: &PluginError) -> Self { + match e { + PluginError::Execution { + plugin_name, + message, + code, + details, + proto_error_code, + .. + } => Self { + plugin_name: plugin_name.clone(), + message: message.clone(), + code: code.clone(), + details: details.clone(), + proto_error_code: *proto_error_code, + }, + PluginError::Timeout { + plugin_name, + timeout_ms, + proto_error_code, + } => Self { + plugin_name: plugin_name.clone(), + message: format!("plugin timed out after {}ms", timeout_ms), + code: Some("timeout".into()), + details: HashMap::new(), + proto_error_code: *proto_error_code, + }, + PluginError::Violation { + plugin_name, + violation, + } => Self { + plugin_name: plugin_name.clone(), + message: format!("plugin denied: {}", violation.reason), + code: Some(violation.code.clone()), + details: violation.details.clone(), + proto_error_code: violation.proto_error_code, + }, + PluginError::Config { message } => Self { + plugin_name: String::new(), + message: message.clone(), + code: Some("config".into()), + details: HashMap::new(), + proto_error_code: None, + }, + PluginError::UnknownHook { hook_type } => Self { + plugin_name: String::new(), + message: format!("unknown hook type: {}", hook_type), + code: Some("unknown_hook".into()), + details: HashMap::new(), + proto_error_code: None, + }, + } + } +} + +// --------------------------------------------------------------------------- +// Plugin Violations +// --------------------------------------------------------------------------- + +/// Structured policy violation returned by a plugin that denies execution. +/// +/// Carries a machine-readable code, human-readable reason, and optional +/// diagnostic details. Corresponds to the Python `PluginViolation` type. +/// +/// # Examples +/// +/// ``` +/// use cpex_core::error::PluginViolation; +/// +/// let v = PluginViolation::new("missing_permission", "User lacks pii_access"); +/// assert_eq!(v.code, "missing_permission"); +/// assert_eq!(v.reason, "User lacks pii_access"); +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PluginViolation { + /// Machine-readable violation identifier (e.g., `"missing_permission"`). + pub code: String, + + /// Short human-readable reason for the denial. + pub reason: String, + + /// Optional detailed explanation. + pub description: Option, + + /// Structured diagnostic data for logging or debugging. + pub details: HashMap, + + /// Name of the plugin that produced the violation. + /// Set by the framework after the plugin returns, not by the plugin itself. + pub plugin_name: Option, + + /// Protocol-level error code for the host to map to the wire format. + /// MCP: JSON-RPC codes (e.g., -32603). HTTP: status codes (e.g., 403). + /// Set by the plugin; the host interprets it. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub proto_error_code: Option, +} + +impl PluginViolation { + /// Create a new violation with a code and reason. + pub fn new(code: impl Into, reason: impl Into) -> Self { + Self { + code: code.into(), + reason: reason.into(), + description: None, + details: HashMap::new(), + plugin_name: None, + proto_error_code: None, + } + } + + /// Attach a detailed description. + pub fn with_description(mut self, description: impl Into) -> Self { + self.description = Some(description.into()); + self + } + + /// Attach structured diagnostic details. + pub fn with_details(mut self, details: HashMap) -> Self { + self.details = details; + self + } + + /// Attach a protocol-level error code. + pub fn with_proto_error_code(mut self, code: i64) -> Self { + self.proto_error_code = Some(code); + self + } +} + +impl std::fmt::Display for PluginViolation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[{}] {}", self.code, self.reason) + } +} diff --git a/crates/cpex-payload/src/extensions/agent.rs b/crates/cpex-payload/src/extensions/agent.rs new file mode 100644 index 00000000..f6eb9c3b --- /dev/null +++ b/crates/cpex-payload/src/extensions/agent.rs @@ -0,0 +1,60 @@ +// Location: ./crates/cpex-core/src/extensions/agent.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// AgentExtension — session, conversation, agent lineage. +// Mirrors cpex/framework/extensions/agent.py. + +use serde::{Deserialize, Serialize}; + +/// Conversation history context. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct ConversationContext { + /// Recent conversation history (lightweight summaries). + #[serde(default)] + pub history: Vec, + + /// LLM-generated summary of the conversation. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub summary: Option, + + /// Detected topics in the conversation. + #[serde(default)] + pub topics: Vec, +} + +/// Agent execution context extension. +/// +/// Carries session tracking, conversation context, multi-agent +/// lineage, and the original user/agent input. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct AgentExtension { + /// Original user/agent input that triggered this action. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub input: Option, + + /// Broad user/agent session identifier. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub session_id: Option, + + /// Specific dialogue/task identifier within a session. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub conversation_id: Option, + + /// Position within the conversation (0-indexed). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub turn: Option, + + /// Identifier of the agent that produced this message. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub agent_id: Option, + + /// If spawned by another agent, the parent's ID. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parent_agent_id: Option, + + /// Optional conversation context with history. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub conversation: Option, +} diff --git a/crates/cpex-payload/src/extensions/completion.rs b/crates/cpex-payload/src/extensions/completion.rs new file mode 100644 index 00000000..2c3ad14d --- /dev/null +++ b/crates/cpex-payload/src/extensions/completion.rs @@ -0,0 +1,71 @@ +// Location: ./crates/cpex-core/src/extensions/completion.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// CompletionExtension — LLM completion information. +// Mirrors cpex/framework/extensions/completion.py. + +use serde::{Deserialize, Serialize}; + +/// Why the model stopped generating. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum StopReason { + /// Natural end of message. + End, + /// Complete response (Harmony format). + Return, + /// Tool/function invocation. + Call, + /// Hit token limit. + MaxTokens, + /// Hit custom stop sequence. + StopSequence, +} + +/// Token usage statistics. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct TokenUsage { + /// Input tokens consumed. + #[serde(default)] + pub input_tokens: u32, + + /// Output tokens generated. + #[serde(default)] + pub output_tokens: u32, + + /// Total tokens (input + output). + #[serde(default)] + pub total_tokens: u32, +} + +/// LLM completion information. +/// +/// Immutable — set after the LLM responds. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct CompletionExtension { + /// Why the model stopped generating. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stop_reason: Option, + + /// Token usage statistics. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tokens: Option, + + /// Model identifier. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub model: Option, + + /// Raw response format (chatml, harmony, gemini, anthropic). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub raw_format: Option, + + /// Creation timestamp (ISO 8601). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub created_at: Option, + + /// Response latency in milliseconds. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub latency_ms: Option, +} diff --git a/crates/cpex-payload/src/extensions/container.rs b/crates/cpex-payload/src/extensions/container.rs new file mode 100644 index 00000000..6409bf43 --- /dev/null +++ b/crates/cpex-payload/src/extensions/container.rs @@ -0,0 +1,711 @@ +// Location: ./crates/cpex-core/src/extensions/container.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// Extensions and OwnedExtensions — typed containers for all +// extension data passed separately from the payload to handlers. +// +// Extensions is fully immutable (all Arc) — zero-copy shareable. +// OwnedExtensions is the plugin's writeable workspace, created by +// cow_copy(), returned in PluginResult::modify_extensions(). + +use std::collections::HashMap; +use std::sync::Arc; + +use serde::{Deserialize, Serialize}; + +use super::agent::AgentExtension; +use super::completion::CompletionExtension; +use super::delegation::DelegationExtension; +use super::framework::FrameworkExtension; +use super::guarded::{Guarded, WriteToken}; +use super::http::HttpExtension; +use super::llm::LLMExtension; +use super::mcp::MCPExtension; +use super::meta::MetaExtension; +use super::provenance::ProvenanceExtension; +use super::request::RequestExtension; +use super::security::SecurityExtension; + +// --------------------------------------------------------------------------- +// Extensions — all Arc, fully immutable, zero-copy shareable +// --------------------------------------------------------------------------- + +/// Typed container for all message extensions. +/// +/// All slots are `Arc` — fully immutable, zero-copy shareable. +/// Cloning is all refcount bumps. `filter_extensions()` creates a +/// filtered view by setting unwanted slots to `None` (still all Arc, +/// no deep copies). Plugins receive `&Extensions` (zero cost). +/// +/// To modify, plugins call `cow_copy()` which returns an +/// `OwnedExtensions` with mutable/monotonic/guarded slots cloned +/// out of Arc and write tokens propagated. +/// +/// Mirrors Python's `cpex.framework.extensions.Extensions`. +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct Extensions { + /// Execution environment and request tracing (immutable). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub request: Option>, + + /// Agent execution context — session, conversation, lineage (immutable). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub agent: Option>, + + /// HTTP headers (frozen as Arc — unfrozen in OwnedExtensions). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub http: Option>, + + /// Security — labels, classification, subject (frozen as Arc). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub security: Option>, + + /// Delegation chain (frozen as Arc). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub delegation: Option>, + + /// MCP entity metadata (immutable). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mcp: Option>, + + /// LLM completion information (immutable). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub completion: Option>, + + /// Origin and message threading (immutable). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub provenance: Option>, + + /// Model identity and capabilities (immutable). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub llm: Option>, + + /// Agentic framework context (immutable). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub framework: Option>, + + /// Host-provided operational metadata (immutable). + #[serde(default)] + pub meta: Option>, + + /// Custom extensions (frozen as Arc — unfrozen in OwnedExtensions). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub custom: Option>>, + + /// Write tokens — set by the executor per plugin, NOT serialized. + /// Used by `cow_copy()` to propagate write access to OwnedExtensions. + #[serde(skip)] + pub http_write_token: Option, + #[serde(skip)] + pub labels_write_token: Option, + #[serde(skip)] + pub delegation_write_token: Option, +} + +impl Clone for Extensions { + /// All Arc bumps — zero data copies. Write tokens are NOT cloned. + fn clone(&self) -> Self { + Self { + request: self.request.clone(), + agent: self.agent.clone(), + http: self.http.clone(), + security: self.security.clone(), + delegation: self.delegation.clone(), + mcp: self.mcp.clone(), + completion: self.completion.clone(), + provenance: self.provenance.clone(), + llm: self.llm.clone(), + framework: self.framework.clone(), + meta: self.meta.clone(), + custom: self.custom.clone(), + http_write_token: None, + labels_write_token: None, + delegation_write_token: None, + } + } +} + +impl Extensions { + /// Create a copy-on-write owned copy for modification. + /// + /// Immutable slots share the same `Arc` (refcount bump, ~1ns). + /// Mutable/monotonic/guarded slots are cloned out of Arc into + /// owned values — the plugin can modify them directly. + /// Write tokens are propagated from the original. + /// + /// # Usage + /// + /// ```ignore + /// fn handle(&self, payload: &P, ext: &Extensions, ctx: &mut PluginContext) -> PluginResult

{ + /// let mut owned = ext.cow_copy(); + /// owned.security.as_mut().unwrap().add_label("CHECKED"); + /// if let Some(ref token) = owned.http_write_token { + /// owned.http.as_mut().unwrap().write(token).set_header("X-Foo", "bar"); + /// } + /// PluginResult::modify_extensions(owned) + /// } + /// ``` + pub fn cow_copy(&self) -> OwnedExtensions { + OwnedExtensions { + // Immutable — same Arc pointers + request: self.request.clone(), + agent: self.agent.clone(), + mcp: self.mcp.clone(), + completion: self.completion.clone(), + provenance: self.provenance.clone(), + llm: self.llm.clone(), + framework: self.framework.clone(), + meta: self.meta.clone(), + + // Mutable/monotonic/guarded — cloned out of Arc into owned + http: self.http.as_ref().map(|arc| Guarded::new((**arc).clone())), + security: self.security.as_ref().map(|arc| (**arc).clone()), + delegation: self.delegation.as_ref().map(|arc| (**arc).clone()), + custom: self.custom.as_ref().map(|arc| (**arc).clone()), + + // Write tokens — propagated from the original + http_write_token: if self.http_write_token.is_some() { + Some(WriteToken::new()) + } else { + None + }, + labels_write_token: if self.labels_write_token.is_some() { + Some(WriteToken::new()) + } else { + None + }, + delegation_write_token: if self.delegation_write_token.is_some() { + Some(WriteToken::new()) + } else { + None + }, + } + } + + /// Validate that immutable slots were not tampered with. + /// + /// A slot that is `None` in modified (because capability filtering + /// hid it from the plugin) is always valid — the plugin never saw + /// it. Only flag as tampering when both are `Some` with different + /// Arc pointers, or when the original is `None` but modified is + /// `Some` (the plugin fabricated a slot it shouldn't have). + pub fn validate_immutable(&self, modified: &OwnedExtensions) -> bool { + fn ptr_eq_opt(a: &Option>, b: &Option>) -> bool { + match (a, b) { + (Some(a), Some(b)) => Arc::ptr_eq(a, b), + (None, None) => true, + (_, None) => true, // plugin never saw it — not tampering + (None, Some(_)) => false, // plugin fabricated a slot + } + } + + ptr_eq_opt(&self.request, &modified.request) + && ptr_eq_opt(&self.agent, &modified.agent) + && ptr_eq_opt(&self.mcp, &modified.mcp) + && ptr_eq_opt(&self.completion, &modified.completion) + && ptr_eq_opt(&self.provenance, &modified.provenance) + && ptr_eq_opt(&self.llm, &modified.llm) + && ptr_eq_opt(&self.framework, &modified.framework) + && ptr_eq_opt(&self.meta, &modified.meta) + } + + /// Merge an OwnedExtensions back into this Extensions. + pub fn merge_owned(&mut self, owned: OwnedExtensions) { + self.http = owned.http.map(|g| Arc::new(g.into_inner())); + self.security = owned.security.map(Arc::new); + self.delegation = owned.delegation.map(Arc::new); + self.custom = owned.custom.map(Arc::new); + } +} + +// --------------------------------------------------------------------------- +// OwnedExtensions — plugin's writeable workspace +// --------------------------------------------------------------------------- + +/// Owned copy of extensions for plugin modification. +/// +/// Returned by `Extensions::cow_copy()`. Immutable slots share +/// the same `Arc` pointers as the original (zero copy). Mutable, +/// monotonic, and guarded slots are cloned into owned values that +/// the plugin can modify directly. +/// +/// Plugins return this in `PluginResult::modify_extensions()`. +/// The executor validates (immutable unchanged, monotonic superset) +/// and merges back into the pipeline's `Extensions`. +/// +/// Hosts never see this type — the executor converts to `Extensions` +/// before building `PipelineResult`. +#[derive(Debug)] +pub struct OwnedExtensions { + // Immutable — same Arc pointers as original + pub request: Option>, + pub agent: Option>, + pub mcp: Option>, + pub completion: Option>, + pub provenance: Option>, + pub llm: Option>, + pub framework: Option>, + pub meta: Option>, + + // Mutable/monotonic/guarded — owned, modifiable + pub http: Option>, + pub security: Option, + pub delegation: Option, + pub custom: Option>, + + // Write tokens — propagated from executor + pub http_write_token: Option, + pub labels_write_token: Option, + pub delegation_write_token: Option, +} +#[cfg(test)] +mod tests { + use super::*; + use crate::extensions::{ + DelegationExtension, HttpExtension, RequestExtension, SecurityExtension, + }; + + fn make_extensions() -> Extensions { + let mut security = SecurityExtension::default(); + security.add_label("PII"); + + let mut http = HttpExtension::default(); + http.set_header("Authorization", "Bearer token"); + + Extensions { + request: Some(Arc::new(RequestExtension { + request_id: Some("req-001".into()), + ..Default::default() + })), + security: Some(Arc::new(security)), + http: Some(Arc::new(http)), + delegation: Some(Arc::new(DelegationExtension::default())), + meta: Some(Arc::new(MetaExtension { + entity_type: Some("tool".into()), + ..Default::default() + })), + ..Default::default() + } + } + + #[test] + fn test_cow_copy_shares_immutable_arcs() { + let ext = make_extensions(); + let cow = ext.cow_copy(); + + // Immutable slots share the same Arc — zero copy + assert!(Arc::ptr_eq( + ext.request.as_ref().unwrap(), + cow.request.as_ref().unwrap() + )); + assert!(Arc::ptr_eq( + ext.meta.as_ref().unwrap(), + cow.meta.as_ref().unwrap() + )); + } + + #[test] + fn test_cow_copy_deep_clones_mutable_slots() { + let ext = make_extensions(); + let cow = ext.cow_copy(); + + // Mutable/monotonic slots are deep cloned — independent copies + assert!(cow.security.is_some()); + assert!(cow.http.is_some()); + assert!(cow.delegation.is_some()); + + // Modifying the COW copy doesn't affect the original + cow.security.as_ref().unwrap().has_label("PII"); + } + + #[test] + fn test_cow_copy_propagates_write_tokens() { + let mut ext = make_extensions(); + + // No tokens on the original → no tokens on COW + let cow_no_tokens = ext.cow_copy(); + assert!(cow_no_tokens.http_write_token.is_none()); + assert!(cow_no_tokens.labels_write_token.is_none()); + assert!(cow_no_tokens.delegation_write_token.is_none()); + + // Executor sets tokens based on capabilities + ext.http_write_token = Some(WriteToken::new()); + ext.labels_write_token = Some(WriteToken::new()); + + // COW copy propagates only the tokens that exist + let cow_with_tokens = ext.cow_copy(); + assert!(cow_with_tokens.http_write_token.is_some()); + assert!(cow_with_tokens.labels_write_token.is_some()); + assert!(cow_with_tokens.delegation_write_token.is_none()); // wasn't set + } + + #[test] + fn test_cow_copy_write_token_enables_guarded_write() { + let mut ext = make_extensions(); + ext.http_write_token = Some(WriteToken::new()); + + let mut cow = ext.cow_copy(); + + // Can read without token + assert_eq!( + cow.http + .as_ref() + .unwrap() + .read() + .get_header("Authorization"), + Some("Bearer token") + ); + + // Can write with token from COW + let token = cow.http_write_token.as_ref().unwrap(); + cow.http + .as_mut() + .unwrap() + .write(token) + .set_header("X-Custom", "value"); + + assert_eq!( + cow.http.as_ref().unwrap().read().get_header("X-Custom"), + Some("value") + ); + + // Original unchanged + assert!(ext.http.as_ref().unwrap().get_header("X-Custom").is_none()); + } + + #[test] + fn test_cow_copy_monotonic_label_insert() { + let mut ext = make_extensions(); + ext.labels_write_token = Some(WriteToken::new()); + + let mut cow = ext.cow_copy(); + + // Can add labels on the COW copy + cow.security.as_mut().unwrap().add_label("HIPAA"); + assert!(cow.security.as_ref().unwrap().has_label("HIPAA")); + + // Original unchanged + assert!(!ext.security.as_ref().unwrap().has_label("HIPAA")); + } + + #[test] + fn test_validate_immutable_passes_for_cow() { + let ext = make_extensions(); + let cow = ext.cow_copy(); + + // COW copy shares immutable Arcs → validation passes + assert!(ext.validate_immutable(&cow)); + } + + #[test] + fn test_validate_immutable_fails_when_tampered() { + let ext = make_extensions(); + let mut cow = ext.cow_copy(); + + // Tamper with an immutable slot + cow.request = Some(Arc::new(RequestExtension { + request_id: Some("TAMPERED".into()), + ..Default::default() + })); + + // Validation fails — different Arc pointer + assert!(!ext.validate_immutable(&cow)); + } + + #[test] + fn test_validate_immutable_both_none_passes() { + let ext = Extensions::default(); + let cow = ext.cow_copy(); + assert!(ext.validate_immutable(&cow)); + } + + #[test] + fn test_clone_drops_write_tokens() { + let mut ext = make_extensions(); + ext.http_write_token = Some(WriteToken::new()); + ext.labels_write_token = Some(WriteToken::new()); + ext.delegation_write_token = Some(WriteToken::new()); + + // Regular clone drops all tokens + let cloned = ext.clone(); + assert!(cloned.http_write_token.is_none()); + assert!(cloned.labels_write_token.is_none()); + assert!(cloned.delegation_write_token.is_none()); + + // cow_copy propagates them + let cow = ext.cow_copy(); + assert!(cow.http_write_token.is_some()); + assert!(cow.labels_write_token.is_some()); + assert!(cow.delegation_write_token.is_some()); + } + + #[test] + fn test_cow_copy_modify_multiple_fields() { + use crate::extensions::delegation::DelegationHop; + use crate::extensions::DelegationExtension; + + // Build extensions with security, http, delegation, custom + let mut security = SecurityExtension::default(); + security.add_label("PII"); + + let mut http = HttpExtension::default(); + http.set_header("Authorization", "Bearer token"); + + let mut ext = Extensions { + security: Some(Arc::new(security)), + http: Some(Arc::new(http)), + delegation: Some(Arc::new(DelegationExtension::default())), + custom: Some(Arc::new( + [("existing".to_string(), serde_json::json!("value"))].into(), + )), + meta: Some(Arc::new(MetaExtension { + entity_type: Some("tool".into()), + ..Default::default() + })), + ..Default::default() + }; + + // Executor sets all write tokens + ext.http_write_token = Some(WriteToken::new()); + ext.labels_write_token = Some(WriteToken::new()); + ext.delegation_write_token = Some(WriteToken::new()); + + // Plugin does one cow_copy, modifies multiple fields + let mut cow = ext.cow_copy(); + + // 1. Add security labels (monotonic) + cow.security.as_mut().unwrap().add_label("CHECKED"); + cow.security.as_mut().unwrap().add_label("COMPLIANT"); + + // 2. Inject HTTP headers (guarded) + let token = cow.http_write_token.as_ref().unwrap(); + cow.http + .as_mut() + .unwrap() + .write(token) + .set_header("X-Checked", "true"); + cow.http + .as_mut() + .unwrap() + .write(token) + .set_header("X-Policy", "v2"); + + // 3. Append delegation hop (monotonic) + cow.delegation.as_mut().unwrap().append_hop(DelegationHop { + subject_id: "service-a".into(), + scopes_granted: vec!["read_hr".into()], + ..Default::default() + }); + + // 4. Add custom data (mutable, no token needed) + cow.custom + .as_mut() + .unwrap() + .insert("audit.timestamp".into(), serde_json::json!("2026-04-29")); + + // Verify COW copy has all modifications + let sec = cow.security.as_ref().unwrap(); + assert!(sec.has_label("PII")); // original + assert!(sec.has_label("CHECKED")); // added + assert!(sec.has_label("COMPLIANT")); // added + + let http = cow.http.as_ref().unwrap().read(); + assert_eq!(http.get_header("Authorization"), Some("Bearer token")); // original + assert_eq!(http.get_header("X-Checked"), Some("true")); // added + assert_eq!(http.get_header("X-Policy"), Some("v2")); // added + + assert_eq!(cow.delegation.as_ref().unwrap().chain.len(), 1); + assert_eq!( + cow.delegation.as_ref().unwrap().chain[0].subject_id, + "service-a" + ); + + assert_eq!( + cow.custom.as_ref().unwrap().get("existing").unwrap(), + "value" + ); + assert_eq!( + cow.custom.as_ref().unwrap().get("audit.timestamp").unwrap(), + "2026-04-29" + ); + + // Verify original is unchanged + assert!(!ext.security.as_ref().unwrap().has_label("CHECKED")); + assert!(ext.http.as_ref().unwrap().get_header("X-Checked").is_none()); + assert!(ext.delegation.as_ref().unwrap().chain.is_empty()); + assert!(!ext.custom.as_ref().unwrap().contains_key("audit.timestamp")); + + // Immutable slots still valid + assert!(ext.validate_immutable(&cow)); + } + + #[test] + fn test_validate_immutable_passes_when_slot_filtered_out() { + // Bug fix regression: when capability filtering hides a slot + // from the plugin (e.g., agent=None in owned because plugin + // lacks read_agent), validate_immutable must NOT treat that + // as tampering. + let ext = make_extensions(); + let mut cow = ext.cow_copy(); + + // Simulate capability filtering hiding the agent slot + cow.agent = None; + + // Validation should pass — plugin never saw the slot + assert!(ext.validate_immutable(&cow)); + } + + #[test] + fn test_validate_immutable_fails_when_slot_fabricated() { + // If the original has no agent but the plugin returns one, + // that's fabrication — should fail. + let ext = Extensions::default(); // no agent + let mut cow = ext.cow_copy(); + + cow.agent = Some(Arc::new(AgentExtension { + agent_id: Some("fabricated".into()), + ..Default::default() + })); + + assert!(!ext.validate_immutable(&cow)); + } + + #[test] + fn test_validate_immutable_passes_multiple_slots_filtered() { + // Multiple immutable slots filtered out — all should pass + let ext = make_extensions(); + let mut cow = ext.cow_copy(); + + cow.agent = None; + cow.mcp = None; + cow.completion = None; + cow.framework = None; + + assert!(ext.validate_immutable(&cow)); + } + + #[test] + fn test_merge_owned_preserves_http_response_headers() { + // Bug fix regression: merge_owned must preserve response + // headers written by a plugin through Guarded write access. + let mut http = HttpExtension::default(); + http.set_request_header("Authorization", "Bearer tok"); + + let mut ext = Extensions { + http: Some(Arc::new(http)), + ..Default::default() + }; + ext.http_write_token = Some(WriteToken::new()); + + let mut cow = ext.cow_copy(); + + // Plugin writes response headers through the guard + let token = cow.http_write_token.as_ref().unwrap(); + let h = cow.http.as_mut().unwrap().write(token); + h.set_response_header("X-Tool-Name", "get_compensation"); + h.set_response_header("X-Status", "success"); + + // Merge back + ext.merge_owned(cow); + + // Response headers must be present after merge + let merged_http = ext.http.as_ref().unwrap(); + assert_eq!( + merged_http.get_response_header("X-Tool-Name"), + Some("get_compensation") + ); + assert_eq!(merged_http.get_response_header("X-Status"), Some("success")); + // Original request headers preserved + assert_eq!( + merged_http.get_request_header("Authorization"), + Some("Bearer tok") + ); + } + + #[test] + fn test_merge_owned_with_filtered_security() { + // A plugin without read_labels gets empty labels in its + // filtered view. After cow_copy + merge_owned, the pipeline's + // security labels must be preserved (not overwritten with empty). + let mut security = SecurityExtension::default(); + security.add_label("PII"); + security.add_label("HR"); + + let ext = Extensions { + security: Some(Arc::new(security)), + ..Default::default() + }; + + // Simulate: plugin has no read_labels, so filtered security + // has empty labels. cow_copy of filtered would have empty labels. + let mut cow = ext.cow_copy(); + + // Plugin's owned security has the labels (from cow_copy of full ext) + // But in the real flow, it would be from the filtered ext. + // Simulate filtered: clear labels + cow.security.as_mut().unwrap().labels = crate::extensions::MonotonicSet::new(); + + // merge_owned replaces pipeline security with owned + let mut ext_mut = ext.clone(); + ext_mut.merge_owned(cow); + + // After merge, the security comes from the owned (which had empty labels) + // This is expected — the executor's monotonic check should prevent + // this case. merge_owned itself is just a field replacement. + let merged_sec = ext_mut.security.as_ref().unwrap(); + assert!(!merged_sec.has_label("PII")); // replaced by owned + } + + #[test] + fn test_merge_owned_none_http_preserves_pipeline() { + // If owned.http is None (plugin had no read_headers capability), + // merge_owned replaces with None. The executor should only call + // merge_owned when the plugin actually modified something. + let mut http = HttpExtension::default(); + http.set_request_header("X-Original", "value"); + + let mut ext = Extensions { + http: Some(Arc::new(http)), + ..Default::default() + }; + + let mut cow = ext.cow_copy(); + cow.http = None; // simulate filtered-out HTTP + + ext.merge_owned(cow); + + // HTTP is now None — this is the raw merge behavior. + // The executor guards against this by only calling merge_owned + // when the plugin returned modify_extensions. + assert!(ext.http.is_none()); + } + + #[test] + fn test_read_only_plugin_zero_cost() { + // Plugin that only reads — no cow_copy, no clone + let ext = make_extensions(); + + // Read security labels + let has_pii = ext + .security + .as_ref() + .map(|s| s.has_label("PII")) + .unwrap_or(false); + assert!(has_pii); + + // Read HTTP headers + let auth = ext + .http + .as_ref() + .and_then(|h| h.get_header("Authorization")); + assert_eq!(auth, Some("Bearer token")); + + // Read meta + let entity = ext.meta.as_ref().and_then(|m| m.entity_type.as_deref()); + assert_eq!(entity, Some("tool")); + + // No cow_copy called — zero allocations for read-only access + } +} diff --git a/crates/cpex-payload/src/extensions/delegation.rs b/crates/cpex-payload/src/extensions/delegation.rs new file mode 100644 index 00000000..e5f5ef50 --- /dev/null +++ b/crates/cpex-payload/src/extensions/delegation.rs @@ -0,0 +1,165 @@ +// Location: ./crates/cpex-core/src/extensions/delegation.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// DelegationExtension — token delegation chain. +// Mirrors cpex/framework/extensions/delegation.py. + +use serde::{Deserialize, Serialize}; + +/// A single hop in the delegation chain. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct DelegationHop { + /// Subject ID of the delegator. + pub subject_id: String, + + /// Subject type of the delegator. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub subject_type: Option, + + /// Target audience. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub audience: Option, + + /// Scopes granted in this delegation step. + #[serde(default)] + pub scopes_granted: Vec, + + /// Timestamp of delegation (ISO 8601). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timestamp: Option, + + /// Time-to-live in seconds. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ttl_seconds: Option, + + /// Delegation strategy used. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub strategy: Option, + + /// Whether this hop was resolved from cache. + #[serde(default)] + pub from_cache: bool, +} + +/// Delegation chain extension. +/// +/// Append-only — each hop narrows scope. A delegate cannot have +/// more permissions than the delegator. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct DelegationExtension { + /// Ordered delegation chain. + #[serde(default)] + pub chain: Vec, + + /// Chain depth (number of hops). + #[serde(default)] + pub depth: usize, + + /// Subject ID of the original delegator. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub origin_subject_id: Option, + + /// Subject ID of the current actor. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub actor_subject_id: Option, + + /// Whether delegation has occurred. + #[serde(default)] + pub delegated: bool, + + /// Age of the delegation chain in seconds. + #[serde(default)] + pub age_seconds: f64, +} + +impl DelegationExtension { + /// Append a delegation hop (monotonic — cannot remove). + pub fn append_hop(&mut self, hop: DelegationHop) { + self.chain.push(hop); + self.depth = self.chain.len(); + self.delegated = true; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_delegation_starts_empty() { + let del = DelegationExtension::default(); + assert!(del.chain.is_empty()); + assert_eq!(del.depth, 0); + assert!(!del.delegated); + } + + #[test] + fn test_append_hop() { + let mut del = DelegationExtension::default(); + del.append_hop(DelegationHop { + subject_id: "alice".into(), + scopes_granted: vec!["read_hr".into()], + ..Default::default() + }); + + assert_eq!(del.chain.len(), 1); + assert_eq!(del.depth, 1); + assert!(del.delegated); + assert_eq!(del.chain[0].subject_id, "alice"); + assert_eq!(del.chain[0].scopes_granted, vec!["read_hr"]); + } + + #[test] + fn test_append_multiple_hops() { + let mut del = DelegationExtension { + origin_subject_id: Some("alice".into()), + ..Default::default() + }; + + del.append_hop(DelegationHop { + subject_id: "alice".into(), + audience: Some("service-b".into()), + scopes_granted: vec!["read".into(), "write".into()], + strategy: Some("token_exchange".into()), + ..Default::default() + }); + + del.append_hop(DelegationHop { + subject_id: "service-b".into(), + audience: Some("service-c".into()), + scopes_granted: vec!["read".into()], // narrowed scope + ..Default::default() + }); + + assert_eq!(del.chain.len(), 2); + assert_eq!(del.depth, 2); + // Second hop has narrower scope + assert_eq!(del.chain[1].scopes_granted, vec!["read"]); + } + + #[test] + fn test_delegation_serde_roundtrip() { + let mut del = DelegationExtension { + origin_subject_id: Some("alice".into()), + actor_subject_id: Some("service-b".into()), + ..Default::default() + }; + del.append_hop(DelegationHop { + subject_id: "alice".into(), + subject_type: Some("user".into()), + scopes_granted: vec!["admin".into()], + from_cache: true, + ..Default::default() + }); + + let json = serde_json::to_string(&del).unwrap(); + let deserialized: DelegationExtension = serde_json::from_str(&json).unwrap(); + + assert_eq!(deserialized.depth, 1); + assert!(deserialized.delegated); + assert_eq!(deserialized.origin_subject_id.as_deref(), Some("alice")); + assert!(deserialized.chain[0].from_cache); + } +} diff --git a/crates/cpex-payload/src/extensions/filter.rs b/crates/cpex-payload/src/extensions/filter.rs new file mode 100644 index 00000000..1841164a --- /dev/null +++ b/crates/cpex-payload/src/extensions/filter.rs @@ -0,0 +1,547 @@ +// Location: ./crates/cpex-core/src/extensions/filter.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// Extension filtering — capability-gated visibility. +// +// Builds a Extensions from Extensions + declared capabilities. +// Secure by default: slots not explicitly included are None. +// +// Mirrors cpex/framework/extensions/tiers.py::filter_extensions(). + +use std::collections::HashSet; +use std::sync::Arc; + +use super::container::Extensions; + +use super::security::{SecurityExtension, SubjectExtension}; +use super::tiers::{AccessPolicy, Capability, MutabilityTier, SlotPolicy}; + +// --------------------------------------------------------------------------- +// Slot Registry — static policies per extension slot +// --------------------------------------------------------------------------- + +/// Extension slot identifiers. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum SlotName { + Request, + Agent, + Http, + Meta, + Delegation, + Custom, + Mcp, + Completion, + Provenance, + Llm, + Framework, + // Security sub-slots + SecurityLabels, + SecuritySubject, + SecuritySubjectRoles, + SecuritySubjectTeams, + SecuritySubjectClaims, + SecuritySubjectPermissions, + SecurityObjects, + SecurityData, +} + +/// Get the policy for a given slot. +pub fn slot_policy(slot: SlotName) -> SlotPolicy { + match slot { + // Unrestricted immutable — always visible + SlotName::Request => SlotPolicy { + tier: MutabilityTier::Immutable, + access: AccessPolicy::Unrestricted, + read_cap: None, + write_cap: None, + }, + SlotName::Provenance => SlotPolicy { + tier: MutabilityTier::Immutable, + access: AccessPolicy::Unrestricted, + read_cap: None, + write_cap: None, + }, + SlotName::Completion => SlotPolicy { + tier: MutabilityTier::Immutable, + access: AccessPolicy::Unrestricted, + read_cap: None, + write_cap: None, + }, + SlotName::Llm => SlotPolicy { + tier: MutabilityTier::Immutable, + access: AccessPolicy::Unrestricted, + read_cap: None, + write_cap: None, + }, + SlotName::Framework => SlotPolicy { + tier: MutabilityTier::Immutable, + access: AccessPolicy::Unrestricted, + read_cap: None, + write_cap: None, + }, + SlotName::Mcp => SlotPolicy { + tier: MutabilityTier::Immutable, + access: AccessPolicy::Unrestricted, + read_cap: None, + write_cap: None, + }, + SlotName::Meta => SlotPolicy { + tier: MutabilityTier::Immutable, + access: AccessPolicy::Unrestricted, + read_cap: None, + write_cap: None, + }, + SlotName::Custom => SlotPolicy { + tier: MutabilityTier::Mutable, + access: AccessPolicy::Unrestricted, + read_cap: None, + write_cap: None, + }, + // Capability-gated + SlotName::Agent => SlotPolicy { + tier: MutabilityTier::Immutable, + access: AccessPolicy::CapabilityGated, + read_cap: Some(Capability::ReadAgent), + write_cap: None, + }, + SlotName::Http => SlotPolicy { + tier: MutabilityTier::Mutable, + access: AccessPolicy::CapabilityGated, + read_cap: Some(Capability::ReadHeaders), + write_cap: Some(Capability::WriteHeaders), + }, + SlotName::Delegation => SlotPolicy { + tier: MutabilityTier::Monotonic, + access: AccessPolicy::CapabilityGated, + read_cap: Some(Capability::ReadDelegation), + write_cap: Some(Capability::AppendDelegation), + }, + // Security sub-slots + SlotName::SecurityLabels => SlotPolicy { + tier: MutabilityTier::Monotonic, + access: AccessPolicy::CapabilityGated, + read_cap: Some(Capability::ReadLabels), + write_cap: Some(Capability::AppendLabels), + }, + SlotName::SecuritySubject => SlotPolicy { + tier: MutabilityTier::Immutable, + access: AccessPolicy::CapabilityGated, + read_cap: Some(Capability::ReadSubject), + write_cap: None, + }, + SlotName::SecuritySubjectRoles => SlotPolicy { + tier: MutabilityTier::Immutable, + access: AccessPolicy::CapabilityGated, + read_cap: Some(Capability::ReadRoles), + write_cap: None, + }, + SlotName::SecuritySubjectTeams => SlotPolicy { + tier: MutabilityTier::Immutable, + access: AccessPolicy::CapabilityGated, + read_cap: Some(Capability::ReadTeams), + write_cap: None, + }, + SlotName::SecuritySubjectClaims => SlotPolicy { + tier: MutabilityTier::Immutable, + access: AccessPolicy::CapabilityGated, + read_cap: Some(Capability::ReadClaims), + write_cap: None, + }, + SlotName::SecuritySubjectPermissions => SlotPolicy { + tier: MutabilityTier::Immutable, + access: AccessPolicy::CapabilityGated, + read_cap: Some(Capability::ReadPermissions), + write_cap: None, + }, + SlotName::SecurityObjects => SlotPolicy { + tier: MutabilityTier::Immutable, + access: AccessPolicy::Unrestricted, + read_cap: None, + write_cap: None, + }, + SlotName::SecurityData => SlotPolicy { + tier: MutabilityTier::Immutable, + access: AccessPolicy::Unrestricted, + read_cap: None, + write_cap: None, + }, + } +} + +// --------------------------------------------------------------------------- +// Capability Checking +// --------------------------------------------------------------------------- + +/// Check if a set of capabilities grants read access to a slot. +fn has_read_access(policy: &SlotPolicy, capabilities: &HashSet) -> bool { + if policy.access == AccessPolicy::Unrestricted { + return true; + } + if let Some(read_cap) = &policy.read_cap { + let cap_str = serde_json::to_string(read_cap) + .unwrap_or_default() + .trim_matches('"') + .to_string(); + if capabilities.contains(&cap_str) { + return true; + } + } + // Check if any subject sub-field cap implies read_subject + if policy.read_cap == Some(Capability::ReadSubject) { + return has_any_subject_capability(capabilities); + } + false +} + +/// Check if capabilities include any subject-related capability. +fn has_any_subject_capability(capabilities: &HashSet) -> bool { + let subject_caps = [ + Capability::ReadSubject, + Capability::ReadRoles, + Capability::ReadTeams, + Capability::ReadClaims, + Capability::ReadPermissions, + ]; + for cap in &subject_caps { + let cap_str = serde_json::to_string(cap) + .unwrap_or_default() + .trim_matches('"') + .to_string(); + if capabilities.contains(&cap_str) { + return true; + } + } + false +} + +/// Helper: convert Capability to its string representation. +fn cap_str(cap: Capability) -> String { + serde_json::to_string(&cap) + .unwrap_or_default() + .trim_matches('"') + .to_string() +} + +// --------------------------------------------------------------------------- +// Filter Extensions +// --------------------------------------------------------------------------- + +/// Build a Extensions containing only slots the plugin can access. +/// +/// Starts from an empty Extensions and clones in only the +/// slots the plugin has read access to. Slots not explicitly included +/// are `None`. Secure by default — if a new slot is added to +/// Extensions but not registered here, it remains hidden. +/// +/// For the security extension, filtering is granular: unrestricted +/// sub-fields (objects, data, classification) are always included, +/// while labels and subject sub-fields are gated by capabilities. +pub fn filter_extensions(extensions: &Extensions, capabilities: &HashSet) -> Extensions { + // Build the unrestricted-immutable fields up front; capability-gated + // slots stay default and are filled in below. + let mut filtered = Extensions { + request: extensions.request.clone(), + provenance: extensions.provenance.clone(), + completion: extensions.completion.clone(), + llm: extensions.llm.clone(), + framework: extensions.framework.clone(), + mcp: extensions.mcp.clone(), + meta: extensions.meta.clone(), + custom: extensions.custom.clone(), + ..Default::default() + }; + + // Capability-gated: delegation + if extensions.delegation.is_some() { + let policy = slot_policy(SlotName::Delegation); + if has_read_access(&policy, capabilities) { + filtered.delegation = extensions.delegation.clone(); + } + } + + // Capability-gated: agent + if extensions.agent.is_some() { + let policy = slot_policy(SlotName::Agent); + if has_read_access(&policy, capabilities) { + filtered.agent = extensions.agent.clone(); + } + } + + // Capability-gated: http + if extensions.http.is_some() { + let policy = slot_policy(SlotName::Http); + if has_read_access(&policy, capabilities) { + filtered.http = extensions.http.clone(); + } + } + + // Security — granular sub-field filtering + if let Some(ref security) = extensions.security { + filtered.security = Some(Arc::new(build_filtered_security(security, capabilities))); + } + + filtered +} + +/// Build a filtered SecurityExtension containing only accessible fields. +/// +/// Unrestricted sub-fields (objects, data, classification) are always +/// included. Labels and subject sub-fields are gated by capabilities. +fn build_filtered_security( + security: &SecurityExtension, + capabilities: &HashSet, +) -> SecurityExtension { + let mut filtered = SecurityExtension { + // Unrestricted — always included + objects: security.objects.clone(), + data: security.data.clone(), + classification: security.classification.clone(), + // Agent identity and auth method — always included (host-set, immutable) + agent: security.agent.clone(), + auth_method: security.auth_method.clone(), + // Default empty for capability-gated fields + labels: super::MonotonicSet::new(), + subject: None, + }; + + // Labels — capability-gated + let labels_policy = slot_policy(SlotName::SecurityLabels); + if has_read_access(&labels_policy, capabilities) { + filtered.labels = security.labels.clone(); + } + + // Subject — granular capability-gated + if let Some(ref subject) = security.subject { + if has_any_subject_capability(capabilities) { + filtered.subject = Some(build_filtered_subject(subject, capabilities)); + } + } + + filtered +} + +/// Build a filtered SubjectExtension containing only accessible fields. +/// +/// Always includes id and type (base subject access). Individual +/// sub-fields are only populated if the plugin holds the capability. +fn build_filtered_subject( + subject: &SubjectExtension, + capabilities: &HashSet, +) -> SubjectExtension { + SubjectExtension { + // Always included with any subject access + id: subject.id.clone(), + subject_type: subject.subject_type, + // Capability-gated sub-fields + roles: if capabilities.contains(&cap_str(Capability::ReadRoles)) { + subject.roles.clone() + } else { + HashSet::new() + }, + permissions: if capabilities.contains(&cap_str(Capability::ReadPermissions)) { + subject.permissions.clone() + } else { + HashSet::new() + }, + teams: if capabilities.contains(&cap_str(Capability::ReadTeams)) { + subject.teams.clone() + } else { + HashSet::new() + }, + claims: if capabilities.contains(&cap_str(Capability::ReadClaims)) { + subject.claims.clone() + } else { + std::collections::HashMap::new() + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::extensions::meta::MetaExtension; + use crate::extensions::SecurityExtension; + + fn make_full_extensions() -> Extensions { + let mut security = SecurityExtension::default(); + security.add_label("PII"); + security.classification = Some("confidential".into()); + security.subject = Some(SubjectExtension { + id: Some("alice".into()), + subject_type: Some(super::super::security::SubjectType::User), + roles: ["admin".to_string()].into(), + permissions: ["read_all".to_string()].into(), + teams: ["engineering".to_string()].into(), + claims: [("iss".to_string(), "example.com".to_string())].into(), + }); + + let mut http = super::super::HttpExtension::default(); + http.set_header("Authorization", "Bearer token123"); + + Extensions { + request: Some(std::sync::Arc::new(super::super::RequestExtension { + request_id: Some("req-001".into()), + ..Default::default() + })), + security: Some(Arc::new(security)), + http: Some(std::sync::Arc::new(http)), + agent: Some(std::sync::Arc::new(super::super::AgentExtension { + agent_id: Some("agent-1".into()), + ..Default::default() + })), + delegation: Some(std::sync::Arc::new(super::super::DelegationExtension { + delegated: true, + ..Default::default() + })), + meta: Some(std::sync::Arc::new(MetaExtension { + entity_type: Some("tool".into()), + entity_name: Some("get_compensation".into()), + ..Default::default() + })), + custom: Some(Arc::new( + [("key".to_string(), serde_json::json!("value"))].into(), + )), + ..Default::default() + } + } + + #[test] + fn test_no_capabilities_sees_unrestricted_only() { + let ext = make_full_extensions(); + let caps = HashSet::new(); + let filtered = filter_extensions(&ext, &caps); + + // Unrestricted slots visible + assert!(filtered.request.is_some()); + assert!(filtered.meta.is_some()); + assert!(filtered.custom.is_some()); + + // Capability-gated slots hidden + assert!(filtered.http.is_none()); + assert!(filtered.agent.is_none()); + assert!(filtered.delegation.is_none()); + + // Security: objects/data/classification visible, labels/subject hidden + let sec = filtered.security.as_ref().unwrap(); + assert!(sec.labels.is_empty()); + assert!(sec.subject.is_none()); + assert_eq!(sec.classification, Some("confidential".into())); + } + + #[test] + fn test_read_headers_capability() { + let ext = make_full_extensions(); + let caps: HashSet = ["read_headers".to_string()].into(); + let filtered = filter_extensions(&ext, &caps); + + assert!(filtered.http.is_some()); + assert_eq!( + filtered.http.unwrap().get_header("Authorization"), + Some("Bearer token123") + ); + // Still no agent access + assert!(filtered.agent.is_none()); + } + + #[test] + fn test_read_agent_capability() { + let ext = make_full_extensions(); + let caps: HashSet = ["read_agent".to_string()].into(); + let filtered = filter_extensions(&ext, &caps); + + assert!(filtered.agent.is_some()); + assert_eq!(filtered.agent.unwrap().agent_id, Some("agent-1".into())); + assert!(filtered.http.is_none()); + } + + #[test] + fn test_read_labels_capability() { + let ext = make_full_extensions(); + let caps: HashSet = ["read_labels".to_string()].into(); + let filtered = filter_extensions(&ext, &caps); + + let sec = filtered.security.as_ref().unwrap(); + assert!(sec.has_label("PII")); + // No subject access — just label access + assert!(sec.subject.is_none()); + } + + #[test] + fn test_read_subject_sees_id_and_type_only() { + let ext = make_full_extensions(); + let caps: HashSet = ["read_subject".to_string()].into(); + let filtered = filter_extensions(&ext, &caps); + + let sec = filtered.security.as_ref().unwrap(); + let subject = sec.subject.as_ref().unwrap(); + assert_eq!(subject.id, Some("alice".into())); + // Sub-fields empty without specific capabilities + assert!(subject.roles.is_empty()); + assert!(subject.permissions.is_empty()); + assert!(subject.teams.is_empty()); + assert!(subject.claims.is_empty()); + } + + #[test] + fn test_read_roles_implies_subject_access() { + let ext = make_full_extensions(); + let caps: HashSet = ["read_roles".to_string()].into(); + let filtered = filter_extensions(&ext, &caps); + + let sec = filtered.security.as_ref().unwrap(); + let subject = sec.subject.as_ref().unwrap(); + // Has subject access (implied by read_roles) + assert_eq!(subject.id, Some("alice".into())); + // Has roles + assert!(subject.roles.contains("admin")); + // No other sub-fields + assert!(subject.permissions.is_empty()); + assert!(subject.teams.is_empty()); + } + + #[test] + fn test_full_capabilities() { + let ext = make_full_extensions(); + let caps: HashSet = [ + "read_headers", + "read_agent", + "read_delegation", + "read_labels", + "read_subject", + "read_roles", + "read_permissions", + "read_teams", + "read_claims", + ] + .into_iter() + .map(String::from) + .collect(); + + let filtered = filter_extensions(&ext, &caps); + + // Everything visible + assert!(filtered.http.is_some()); + assert!(filtered.agent.is_some()); + assert!(filtered.delegation.is_some()); + + let sec = filtered.security.as_ref().unwrap(); + assert!(sec.has_label("PII")); + let subject = sec.subject.as_ref().unwrap(); + assert!(subject.roles.contains("admin")); + assert!(subject.permissions.contains("read_all")); + assert!(subject.teams.contains("engineering")); + assert!(subject.claims.contains_key("iss")); + } + + #[test] + fn test_read_delegation_capability() { + let ext = make_full_extensions(); + let caps: HashSet = ["read_delegation".to_string()].into(); + let filtered = filter_extensions(&ext, &caps); + + assert!(filtered.delegation.is_some()); + assert!(filtered.delegation.unwrap().delegated); + } +} diff --git a/crates/cpex-payload/src/extensions/framework.rs b/crates/cpex-payload/src/extensions/framework.rs new file mode 100644 index 00000000..b4654055 --- /dev/null +++ b/crates/cpex-payload/src/extensions/framework.rs @@ -0,0 +1,38 @@ +// Location: ./crates/cpex-core/src/extensions/framework.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// FrameworkExtension — agentic framework context. +// Mirrors cpex/framework/extensions/framework.py. + +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +/// Agentic framework context. +/// +/// Carries framework identity and graph/workflow metadata. +/// Immutable — set by the host. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct FrameworkExtension { + /// Framework name (e.g., "langchain", "crewai", "autogen"). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub framework: Option, + + /// Framework version. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub framework_version: Option, + + /// Node ID in an agent graph/workflow. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub node_id: Option, + + /// Graph/workflow ID. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub graph_id: Option, + + /// Framework-specific metadata. + #[serde(default)] + pub metadata: HashMap, +} diff --git a/crates/cpex-payload/src/extensions/guarded.rs b/crates/cpex-payload/src/extensions/guarded.rs new file mode 100644 index 00000000..fb369a16 --- /dev/null +++ b/crates/cpex-payload/src/extensions/guarded.rs @@ -0,0 +1,144 @@ +// Location: ./crates/cpex-core/src/extensions/guarded.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// Guarded — capability-gated write access. +// +// A value that requires a WriteToken for mutable access. Read access +// is always available (if the plugin can see the extension at all). +// Write access requires the framework to issue a WriteToken based on +// the plugin's declared capabilities. +// +// Mirrors the spec in rust-implementation-spec.md §2.3. + +use serde::{Deserialize, Serialize}; + +/// A value that requires a WriteToken for mutable access. +/// +/// Read access via `.read()` is always available. Write access via +/// `.write(token)` requires a `WriteToken` proving the caller has +/// the capability. +/// +/// The framework issues write tokens only to plugins that declared +/// the corresponding write capability (e.g., `write_headers`). +/// Plugin code without a token cannot call `.write()`. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(transparent)] +pub struct Guarded { + inner: T, +} + +impl Guarded { + /// Wrap a value in a guard. + pub fn new(value: T) -> Self { + Self { inner: value } + } + + /// Read access — always available if the plugin can see this extension. + pub fn read(&self) -> &T { + &self.inner + } + + /// Write access — requires a WriteToken proving the caller has capability. + /// + /// The framework issues WriteTokens only to plugins that declared + /// the write capability in their config. Without the token, this + /// method is uncallable — the plugin can read but not write. + pub fn write(&mut self, _token: &WriteToken) -> &mut T { + &mut self.inner + } + + /// Consume the guard, returning the inner value. + pub fn into_inner(self) -> T { + self.inner + } +} + +impl Default for Guarded { + fn default() -> Self { + Self { + inner: T::default(), + } + } +} + +/// Opaque token for write access — only the framework can create one. +/// +/// `pub(crate)` constructor means plugin crates cannot mint tokens. +/// The executor creates tokens based on the plugin's declared +/// capabilities from `PluginConfig`. +pub struct WriteToken { + _private: (), +} + +impl WriteToken { + /// Only callable by the framework (pub(crate)). + /// Plugin crates cannot construct this. + pub(crate) fn new() -> Self { + Self { _private: () } + } +} + +// WriteToken is not Clone, not Copy — each plugin gets its own from the executor. +// It's also not Send/Sync by default (no auto-traits on zero-sized private fields). +// We explicitly mark it safe since it's just a capability proof with no data. +unsafe impl Send for WriteToken {} +unsafe impl Sync for WriteToken {} + +impl std::fmt::Debug for WriteToken { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("WriteToken") + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_guarded_read_without_token() { + let guarded = Guarded::new(42); + assert_eq!(*guarded.read(), 42); + } + + #[test] + fn test_guarded_write_with_token() { + let mut guarded = Guarded::new(42); + let token = WriteToken::new(); + *guarded.write(&token) = 100; + assert_eq!(*guarded.read(), 100); + } + + #[test] + fn test_guarded_serde_transparent() { + let guarded = Guarded::new("hello".to_string()); + let json = serde_json::to_string(&guarded).unwrap(); + assert_eq!(json, "\"hello\""); + let deserialized: Guarded = serde_json::from_str(&json).unwrap(); + assert_eq!(*deserialized.read(), "hello"); + } + + #[test] + fn test_guarded_with_struct() { + use std::collections::HashMap; + + #[derive(Clone, Debug, Default, Serialize, Deserialize)] + struct Headers { + map: HashMap, + } + + let mut guarded = Guarded::new(Headers::default()); + let token = WriteToken::new(); + + // Read — no token needed + assert!(guarded.read().map.is_empty()); + + // Write — token required + guarded + .write(&token) + .map + .insert("X-Auth".into(), "Bearer tok".into()); + assert_eq!(guarded.read().map.get("X-Auth").unwrap(), "Bearer tok"); + } +} diff --git a/crates/cpex-payload/src/extensions/http.rs b/crates/cpex-payload/src/extensions/http.rs new file mode 100644 index 00000000..3fa1157a --- /dev/null +++ b/crates/cpex-payload/src/extensions/http.rs @@ -0,0 +1,210 @@ +// Location: ./crates/cpex-core/src/extensions/http.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// HttpExtension — HTTP request and response headers. +// Mirrors cpex/framework/extensions/http.py. + +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +/// HTTP-related extensions. +/// +/// Carries both request and response headers separately. The host +/// populates what's available at each hook point: +/// - Pre-invoke: `request_headers` filled, `response_headers` empty +/// - Post-invoke: both filled (request from original, response from upstream) +/// +/// Capability-gated: requires `read_headers` to see, `write_headers` +/// to modify (both request and response). +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct HttpExtension { + /// HTTP request headers (inbound from caller). + #[serde(default)] + pub request_headers: HashMap, + + /// HTTP response headers (from upstream, populated post-invoke). + #[serde(default)] + pub response_headers: HashMap, +} + +impl HttpExtension { + // -- Request header helpers -- + + /// Set a request header (overwrites if exists). + pub fn set_request_header(&mut self, name: impl Into, value: impl Into) { + self.request_headers.insert(name.into(), value.into()); + } + + /// Get a request header value (case-insensitive lookup). + pub fn get_request_header(&self, name: &str) -> Option<&str> { + get_header_ci(&self.request_headers, name) + } + + /// Check if a request header exists (case-insensitive). + pub fn has_request_header(&self, name: &str) -> bool { + self.get_request_header(name).is_some() + } + + /// Add request header only if it doesn't exist. Returns true if added. + pub fn add_request_header( + &mut self, + name: impl Into, + value: impl Into, + ) -> bool { + let name = name.into(); + if self.has_request_header(&name) { + return false; + } + self.request_headers.insert(name, value.into()); + true + } + + /// Remove a request header by name. Returns the removed value. + pub fn remove_request_header(&mut self, name: &str) -> Option { + remove_header_ci(&mut self.request_headers, name) + } + + // -- Response header helpers -- + + /// Set a response header (overwrites if exists). + pub fn set_response_header(&mut self, name: impl Into, value: impl Into) { + self.response_headers.insert(name.into(), value.into()); + } + + /// Get a response header value (case-insensitive lookup). + pub fn get_response_header(&self, name: &str) -> Option<&str> { + get_header_ci(&self.response_headers, name) + } + + /// Check if a response header exists (case-insensitive). + pub fn has_response_header(&self, name: &str) -> bool { + self.get_response_header(name).is_some() + } + + // -- Convenience aliases (backward-compatible, default to request) -- + + /// Set a header on request headers (convenience alias). + pub fn set_header(&mut self, name: impl Into, value: impl Into) { + self.set_request_header(name, value); + } + + /// Get a header from request headers (convenience alias, case-insensitive). + pub fn get_header(&self, name: &str) -> Option<&str> { + self.get_request_header(name) + } + + /// Check if a request header exists (convenience alias). + pub fn has_header(&self, name: &str) -> bool { + self.has_request_header(name) + } +} + +// -- Internal helpers -- + +fn get_header_ci<'a>(headers: &'a HashMap, name: &str) -> Option<&'a str> { + let lower = name.to_lowercase(); + headers + .iter() + .find(|(k, _)| k.to_lowercase() == lower) + .map(|(_, v)| v.as_str()) +} + +fn remove_header_ci(headers: &mut HashMap, name: &str) -> Option { + let lower = name.to_lowercase(); + let key = headers.keys().find(|k| k.to_lowercase() == lower).cloned(); + key.and_then(|k| headers.remove(&k)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_request_header_set_and_get() { + let mut http = HttpExtension::default(); + http.set_request_header("Content-Type", "application/json"); + assert_eq!( + http.get_request_header("Content-Type"), + Some("application/json") + ); + } + + #[test] + fn test_request_header_case_insensitive() { + let mut http = HttpExtension::default(); + http.set_request_header("Authorization", "Bearer tok"); + assert_eq!(http.get_request_header("authorization"), Some("Bearer tok")); + assert_eq!(http.get_request_header("AUTHORIZATION"), Some("Bearer tok")); + } + + #[test] + fn test_response_header_set_and_get() { + let mut http = HttpExtension::default(); + http.set_response_header("Content-Type", "text/html"); + assert_eq!(http.get_response_header("Content-Type"), Some("text/html")); + assert!(http.has_response_header("content-type")); + } + + #[test] + fn test_request_and_response_independent() { + let mut http = HttpExtension::default(); + http.set_request_header("Authorization", "Bearer req-tok"); + http.set_response_header("X-Response-Time", "42ms"); + + // Request headers don't leak into response + assert!(http.get_response_header("Authorization").is_none()); + // Response headers don't leak into request + assert!(http.get_request_header("X-Response-Time").is_none()); + } + + #[test] + fn test_convenience_aliases_default_to_request() { + let mut http = HttpExtension::default(); + http.set_header("X-Custom", "value"); + assert_eq!(http.get_header("X-Custom"), Some("value")); + assert!(http.has_header("X-Custom")); + // Verify it went to request_headers + assert_eq!(http.get_request_header("X-Custom"), Some("value")); + } + + #[test] + fn test_add_request_header_only_if_absent() { + let mut http = HttpExtension::default(); + assert!(http.add_request_header("X-New", "first")); + assert!(!http.add_request_header("X-New", "second")); + assert_eq!(http.get_request_header("X-New"), Some("first")); + } + + #[test] + fn test_remove_request_header() { + let mut http = HttpExtension::default(); + http.set_request_header("X-Remove", "value"); + let removed = http.remove_request_header("x-remove"); + assert_eq!(removed, Some("value".to_string())); + assert!(!http.has_request_header("X-Remove")); + } + + #[test] + fn test_serde_roundtrip() { + let mut http = HttpExtension::default(); + http.set_request_header("Authorization", "Bearer tok"); + http.set_request_header("X-Request-ID", "req-123"); + http.set_response_header("Content-Type", "application/json"); + http.set_response_header("X-Response-Time", "15ms"); + + let json = serde_json::to_string(&http).unwrap(); + let deserialized: HttpExtension = serde_json::from_str(&json).unwrap(); + + assert_eq!( + deserialized.get_request_header("Authorization"), + Some("Bearer tok") + ); + assert_eq!( + deserialized.get_response_header("Content-Type"), + Some("application/json") + ); + } +} diff --git a/crates/cpex-payload/src/extensions/llm.rs b/crates/cpex-payload/src/extensions/llm.rs new file mode 100644 index 00000000..adc2225c --- /dev/null +++ b/crates/cpex-payload/src/extensions/llm.rs @@ -0,0 +1,27 @@ +// Location: ./crates/cpex-core/src/extensions/llm.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// LLMExtension — model identity and capabilities. +// Mirrors cpex/framework/extensions/llm.py. + +use serde::{Deserialize, Serialize}; + +/// Model identity and capabilities. +/// +/// Immutable — set by the host. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct LLMExtension { + /// Model identifier (e.g., "gpt-4o", "claude-sonnet-4-20250514"). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub model_id: Option, + + /// Provider name (e.g., "openai", "anthropic"). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub provider: Option, + + /// Model capabilities (e.g., "tool_use", "vision", "streaming"). + #[serde(default)] + pub capabilities: Vec, +} diff --git a/crates/cpex-payload/src/extensions/mcp.rs b/crates/cpex-payload/src/extensions/mcp.rs new file mode 100644 index 00000000..c5eed384 --- /dev/null +++ b/crates/cpex-payload/src/extensions/mcp.rs @@ -0,0 +1,115 @@ +// Location: ./crates/cpex-core/src/extensions/mcp.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// MCPExtension — tool, resource, or prompt metadata. +// Mirrors cpex/framework/extensions/mcp.py. + +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +/// MCP tool metadata. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct ToolMetadata { + /// Tool name. + pub name: String, + + /// Human-readable title. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub title: Option, + + /// Tool description. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + + /// Input JSON schema. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub input_schema: Option, + + /// Output JSON schema. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub output_schema: Option, + + /// Source server ID. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server_id: Option, + + /// Tool namespace. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, + + /// Tool annotations. + #[serde(default)] + pub annotations: HashMap, +} + +/// MCP resource metadata. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct ResourceMetadata { + /// Resource URI. + pub uri: String, + + /// Human-readable name. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, + + /// Resource description. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + + /// MIME type. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mime_type: Option, + + /// Source server ID. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server_id: Option, + + /// Resource annotations. + #[serde(default)] + pub annotations: HashMap, +} + +/// MCP prompt metadata. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct PromptMetadata { + /// Prompt name. + pub name: String, + + /// Prompt description. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + + /// Prompt arguments schema. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub arguments: Option>, + + /// Source server ID. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub server_id: Option, + + /// Prompt annotations. + #[serde(default)] + pub annotations: HashMap, +} + +/// MCP-specific metadata extension. +/// +/// Carries tool, resource, or prompt metadata for the entity +/// being processed. Immutable — set by the host. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct MCPExtension { + /// Tool metadata (if this message involves a tool). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tool: Option, + + /// Resource metadata (if this message involves a resource). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub resource: Option, + + /// Prompt metadata (if this message involves a prompt). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub prompt: Option, +} diff --git a/crates/cpex-payload/src/extensions/meta.rs b/crates/cpex-payload/src/extensions/meta.rs new file mode 100644 index 00000000..4ba55516 --- /dev/null +++ b/crates/cpex-payload/src/extensions/meta.rs @@ -0,0 +1,45 @@ +// Location: ./crates/cpex-core/src/extensions/meta.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// MetaExtension — host-provided operational metadata. +// Mirrors cpex/framework/extensions/meta.py. + +use std::collections::{HashMap, HashSet}; + +use serde::{Deserialize, Serialize}; + +/// Host-provided operational metadata. +/// +/// Carries entity identification (type + name) for route resolution, +/// operational tags for policy group inheritance, scope for +/// host-defined grouping, and arbitrary properties. +/// +/// Immutable — set by the host before invoking the hook. Plugins +/// can read but not modify. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct MetaExtension { + /// Entity type: "tool", "resource", "prompt", "llm". + /// Used by the manager for route resolution. + #[serde(default)] + pub entity_type: Option, + + /// Entity name: "get_compensation", "hr://employees/*", etc. + /// Used by the manager for route resolution. + #[serde(default)] + pub entity_name: Option, + + /// Operational tags — drive policy group inheritance. + /// Merged with static tags from the matching route's `meta.tags`. + #[serde(default)] + pub tags: HashSet, + + /// Host-defined grouping (virtual server ID, namespace, etc.). + #[serde(default)] + pub scope: Option, + + /// Arbitrary key-value metadata. + #[serde(default)] + pub properties: HashMap, +} diff --git a/crates/cpex-payload/src/extensions/mod.rs b/crates/cpex-payload/src/extensions/mod.rs new file mode 100644 index 00000000..d51aec62 --- /dev/null +++ b/crates/cpex-payload/src/extensions/mod.rs @@ -0,0 +1,52 @@ +// Location: ./crates/cpex-core/src/extensions/mod.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// Typed extension models for the CPEX framework. +// +// Each extension carries contextual metadata with an explicit +// mutability tier enforced by the processing pipeline. Extensions +// are always passed separately from the payload to handlers. +// +// Mirrors the Python extensions in cpex/framework/extensions/. + +pub mod agent; +pub mod completion; +pub mod container; +pub mod delegation; +pub mod filter; +pub mod framework; +pub mod guarded; +pub mod http; +pub mod llm; +pub mod mcp; +pub mod meta; +pub mod monotonic; +pub mod provenance; +pub mod request; +pub mod security; +pub mod tiers; + +// Re-export containers +pub use container::{Extensions, OwnedExtensions}; + +// Re-export all extension types +pub use agent::{AgentExtension, ConversationContext}; +pub use completion::{CompletionExtension, StopReason, TokenUsage}; +pub use delegation::{DelegationExtension, DelegationHop}; +pub use filter::{filter_extensions, SlotName}; +pub use framework::FrameworkExtension; +pub use guarded::{Guarded, WriteToken}; +pub use http::HttpExtension; +pub use llm::LLMExtension; +pub use mcp::{MCPExtension, PromptMetadata, ResourceMetadata, ToolMetadata}; +pub use meta::MetaExtension; +pub use monotonic::{DeclassifierToken, MonotonicSet}; +pub use provenance::ProvenanceExtension; +pub use request::RequestExtension; +pub use security::{ + AgentIdentity, DataPolicy, ObjectSecurityProfile, RetentionPolicy, SecurityExtension, + SubjectExtension, SubjectType, +}; +pub use tiers::{AccessPolicy, Capability, MutabilityTier, SlotPolicy}; diff --git a/crates/cpex-payload/src/extensions/monotonic.rs b/crates/cpex-payload/src/extensions/monotonic.rs new file mode 100644 index 00000000..82530199 --- /dev/null +++ b/crates/cpex-payload/src/extensions/monotonic.rs @@ -0,0 +1,179 @@ +// Location: ./crates/cpex-core/src/extensions/monotonic.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// MonotonicSet — add-only set enforced at the type level. +// +// Security labels can only grow. The type exposes insert() but not +// remove(). Declassification requires a DeclassifierToken that only +// the security subsystem can construct. +// +// Mirrors the spec in rust-implementation-spec.md §2.2. + +use std::collections::HashSet; +use std::hash::Hash; + +use serde::{Deserialize, Serialize}; + +/// A set that only allows additions. No remove() in the public API. +/// +/// Plugins can call `insert()` but not `remove()`. Declassification +/// (removal) requires a `DeclassifierToken` that only the security +/// subsystem can construct. +/// +/// This enforces the monotonic tier at compile time — a plugin that +/// tries to call `.remove()` gets a compile error. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(transparent)] +pub struct MonotonicSet { + inner: HashSet, +} + +impl MonotonicSet { + /// Create an empty monotonic set. + pub fn new() -> Self { + Self { + inner: HashSet::new(), + } + } + + /// Create from an existing HashSet. + pub fn from_set(set: HashSet) -> Self { + Self { inner: set } + } + + /// Add a value. Returns true if the value was newly inserted. + pub fn insert(&mut self, value: T) -> bool { + self.inner.insert(value) + } + + /// Check if the set contains a value. + pub fn contains(&self, value: &T) -> bool { + self.inner.contains(value) + } + + /// Iterate over the values. + pub fn iter(&self) -> impl Iterator { + self.inner.iter() + } + + /// Number of elements. + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Whether the set is empty. + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + /// Whether this set is a superset of another. + pub fn is_superset(&self, other: &MonotonicSet) -> bool { + self.inner.is_superset(&other.inner) + } + + /// Get a reference to the inner HashSet (read-only). + pub fn as_set(&self) -> &HashSet { + &self.inner + } + + /// Removal requires a DeclassifierToken — privileged, audited operation. + /// Only the security subsystem can construct the token. + pub fn remove_with_declassifier(&mut self, value: &T, _token: &DeclassifierToken) -> bool { + self.inner.remove(value) + } +} + +impl Default for MonotonicSet { + fn default() -> Self { + Self::new() + } +} + +/// Opaque token for declassification — only the security subsystem +/// can create one. Constructing this token is a privileged operation. +pub struct DeclassifierToken { + _private: (), +} + +impl DeclassifierToken { + /// Only callable by the framework/security subsystem. + #[allow(dead_code)] + pub(crate) fn new() -> Self { + Self { _private: () } + } +} + +/// Case-insensitive label lookup on MonotonicSet. +impl MonotonicSet { + /// Check if a label exists (case-insensitive). + pub fn has_label(&self, label: &str) -> bool { + let lower = label.to_lowercase(); + self.inner.iter().any(|l| l.to_lowercase() == lower) + } + + /// Add a label (case-preserving on insert). + pub fn add_label(&mut self, label: impl Into) { + self.inner.insert(label.into()); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_monotonic_insert_only() { + let mut set = MonotonicSet::new(); + set.insert("PII".to_string()); + set.insert("CONFIDENTIAL".to_string()); + assert!(set.contains(&"PII".to_string())); + assert_eq!(set.len(), 2); + // No remove() method available — this is the key guarantee + } + + #[test] + fn test_monotonic_superset() { + let mut before = MonotonicSet::new(); + before.insert("PII".to_string()); + + let mut after = before.clone(); + after.insert("HIPAA".to_string()); + + assert!(after.is_superset(&before)); + assert!(!before.is_superset(&after)); + } + + #[test] + fn test_monotonic_declassifier() { + let mut set = MonotonicSet::new(); + set.insert("PII".to_string()); + + // Only works with the token + let token = DeclassifierToken::new(); + assert!(set.remove_with_declassifier(&"PII".to_string(), &token)); + assert!(!set.contains(&"PII".to_string())); + } + + #[test] + fn test_monotonic_has_label_case_insensitive() { + let mut set = MonotonicSet::new(); + set.add_label("PII"); + assert!(set.has_label("pii")); + assert!(set.has_label("PII")); + assert!(set.has_label("Pii")); + } + + #[test] + fn test_monotonic_serde_roundtrip() { + let mut set = MonotonicSet::new(); + set.insert("PII".to_string()); + set.insert("HIPAA".to_string()); + + let json = serde_json::to_string(&set).unwrap(); + let deserialized: MonotonicSet = serde_json::from_str(&json).unwrap(); + assert!(deserialized.contains(&"PII".to_string())); + assert!(deserialized.contains(&"HIPAA".to_string())); + } +} diff --git a/crates/cpex-payload/src/extensions/provenance.rs b/crates/cpex-payload/src/extensions/provenance.rs new file mode 100644 index 00000000..1873f521 --- /dev/null +++ b/crates/cpex-payload/src/extensions/provenance.rs @@ -0,0 +1,27 @@ +// Location: ./crates/cpex-core/src/extensions/provenance.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// ProvenanceExtension — origin and message threading. +// Mirrors cpex/framework/extensions/provenance.py. + +use serde::{Deserialize, Serialize}; + +/// Origin and message threading. +/// +/// Immutable — set by the host. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct ProvenanceExtension { + /// Source system or service. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub source: Option, + + /// Unique message identifier. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub message_id: Option, + + /// Parent message ID (for threading). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parent_id: Option, +} diff --git a/crates/cpex-payload/src/extensions/request.rs b/crates/cpex-payload/src/extensions/request.rs new file mode 100644 index 00000000..435ab1fa --- /dev/null +++ b/crates/cpex-payload/src/extensions/request.rs @@ -0,0 +1,35 @@ +// Location: ./crates/cpex-core/src/extensions/request.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// RequestExtension — execution environment and tracing. +// Mirrors cpex/framework/extensions/request.py. + +use serde::{Deserialize, Serialize}; + +/// Execution environment and request tracing. +/// +/// Immutable — set by the host before invoking the hook. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct RequestExtension { + /// Deployment environment (e.g., "production", "staging"). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub environment: Option, + + /// Unique request identifier. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub request_id: Option, + + /// Request timestamp (ISO 8601). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timestamp: Option, + + /// Distributed trace ID. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub trace_id: Option, + + /// Span ID within the trace. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub span_id: Option, +} diff --git a/crates/cpex-payload/src/extensions/security.rs b/crates/cpex-payload/src/extensions/security.rs new file mode 100644 index 00000000..91d54c18 --- /dev/null +++ b/crates/cpex-payload/src/extensions/security.rs @@ -0,0 +1,348 @@ +// Location: ./crates/cpex-core/src/extensions/security.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// SecurityExtension — labels, classification, identity, data policy. +// Mirrors cpex/framework/extensions/security.py. + +use std::collections::{HashMap, HashSet}; + +use serde::{Deserialize, Serialize}; + +use super::monotonic::MonotonicSet; + +/// Subject type for identity classification. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum SubjectType { + User, + Agent, + Service, + System, +} + +/// Authenticated subject identity. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct SubjectExtension { + /// Subject identifier (e.g., JWT sub). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub id: Option, + + /// Subject type. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub subject_type: Option, + + /// Assigned roles. + #[serde(default)] + pub roles: HashSet, + + /// Granted permissions. + #[serde(default)] + pub permissions: HashSet, + + /// Team memberships. + #[serde(default)] + pub teams: HashSet, + + /// Raw claims (e.g., JWT claims). + #[serde(default)] + pub claims: HashMap, +} + +/// Security profile for a managed object. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct ObjectSecurityProfile { + /// Who manages this object. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub managed_by: Option, + + /// Required permissions. + #[serde(default)] + pub permissions: Vec, + + /// Trust domain. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub trust_domain: Option, + + /// Data scope. + #[serde(default)] + pub data_scope: Vec, +} + +/// Retention policy for data. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct RetentionPolicy { + /// Maximum age in seconds. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub max_age_seconds: Option, + + /// Policy name. + #[serde(default)] + pub policy: String, + + /// Deletion timestamp. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub delete_after: Option, +} + +/// Data policy for a named data element. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct DataPolicy { + /// Labels to apply. + #[serde(default)] + pub apply_labels: Vec, + + /// Allowed actions (None = all allowed). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub allowed_actions: Option>, + + /// Denied actions. + #[serde(default)] + pub denied_actions: Vec, + + /// Retention policy. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub retention: Option, +} + +/// This agent's own workload identity. +/// +/// Distinct from `SubjectExtension` which represents the *caller*. +/// `AgentIdentity` represents *this agent/service* — its own +/// workload identity, OAuth client_id, and trust domain. +/// +/// Populated by the host before the pipeline runs. Plugins can +/// make decisions based on both who is calling (Subject) and +/// which agent is processing (AgentIdentity). +/// +/// Maps to AuthBridge's `AgentIdentity` and the Go bindings' +/// `SecurityExtension.Agent`. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct AgentIdentity { + /// OAuth client_id of this agent. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub client_id: Option, + + /// Workload identity URI (SPIFFE, k8s service account, platform-specific). + /// e.g., `spiffe://example.com/ns/team1/sa/weather-tool` + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workload_id: Option, + + /// Trust domain of the workload identity. + /// e.g., `example.com` + #[serde(default, skip_serializing_if = "Option::is_none")] + pub trust_domain: Option, +} + +/// Security-related extensions. +/// +/// Carries security labels (monotonic add-only), classification, +/// authenticated caller identity (subject), this agent's own +/// workload identity (agent), object security profiles, and +/// data policies. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct SecurityExtension { + /// Security labels (monotonic — add-only via MonotonicSet). + /// No remove() method — enforced at compile time. + #[serde(default)] + pub labels: MonotonicSet, + + /// Data classification level. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub classification: Option, + + /// Authenticated caller identity (who is calling). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub subject: Option, + + /// This agent's own workload identity (who this agent is). + /// Populated by the host, not by plugins. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub agent: Option, + + /// Authentication method used (e.g., "jwt", "mtls", "spiffe", "api_key"). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub auth_method: Option, + + /// Object security profiles keyed by object name. + #[serde(default)] + pub objects: HashMap, + + /// Data policies keyed by data element name. + #[serde(default)] + pub data: HashMap, +} + +impl SecurityExtension { + /// Add a security label (monotonic — cannot remove). + pub fn add_label(&mut self, label: impl Into) { + self.labels.add_label(label); + } + + /// Check if a label exists (case-insensitive). + pub fn has_label(&self, label: &str) -> bool { + self.labels.has_label(label) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_security_labels_monotonic() { + let mut sec = SecurityExtension::default(); + sec.add_label("PII"); + sec.add_label("HIPAA"); + assert!(sec.has_label("PII")); + assert!(sec.has_label("pii")); // case-insensitive + assert!(sec.has_label("HIPAA")); + assert!(!sec.has_label("SOX")); + } + + #[test] + fn test_security_classification() { + let sec = SecurityExtension { + classification: Some("confidential".into()), + ..Default::default() + }; + assert_eq!(sec.classification.as_deref(), Some("confidential")); + } + + #[test] + fn test_subject_extension() { + let subject = SubjectExtension { + id: Some("alice".into()), + subject_type: Some(SubjectType::User), + roles: ["admin".to_string(), "hr".to_string()].into(), + permissions: ["read_all".to_string()].into(), + teams: ["engineering".to_string()].into(), + claims: [("iss".to_string(), "auth.example.com".to_string())].into(), + }; + assert_eq!(subject.id.as_deref(), Some("alice")); + assert_eq!(subject.subject_type, Some(SubjectType::User)); + assert!(subject.roles.contains("admin")); + assert!(subject.permissions.contains("read_all")); + assert!(subject.teams.contains("engineering")); + assert_eq!(subject.claims.get("iss").unwrap(), "auth.example.com"); + } + + #[test] + fn test_agent_identity() { + let agent = AgentIdentity { + client_id: Some("weather-agent".into()), + workload_id: Some("spiffe://example.com/ns/team1/sa/weather-tool".into()), + trust_domain: Some("example.com".into()), + }; + assert_eq!(agent.client_id.as_deref(), Some("weather-agent")); + assert_eq!( + agent.workload_id.as_deref(), + Some("spiffe://example.com/ns/team1/sa/weather-tool") + ); + assert_eq!(agent.trust_domain.as_deref(), Some("example.com")); + } + + #[test] + fn test_agent_identity_default() { + let agent = AgentIdentity::default(); + assert!(agent.client_id.is_none()); + assert!(agent.workload_id.is_none()); + assert!(agent.trust_domain.is_none()); + } + + #[test] + fn test_security_with_agent_and_subject() { + let sec = SecurityExtension { + labels: { + let mut l = super::super::MonotonicSet::new(); + l.add_label("PII"); + l + }, + classification: Some("confidential".into()), + subject: Some(SubjectExtension { + id: Some("alice".into()), + subject_type: Some(SubjectType::User), + ..Default::default() + }), + agent: Some(AgentIdentity { + client_id: Some("hr-agent".into()), + workload_id: Some("spiffe://corp.com/hr-agent".into()), + trust_domain: Some("corp.com".into()), + }), + auth_method: Some("jwt".into()), + ..Default::default() + }; + + // Caller identity + assert_eq!(sec.subject.as_ref().unwrap().id.as_deref(), Some("alice")); + // Agent identity (distinct from caller) + assert_eq!( + sec.agent.as_ref().unwrap().client_id.as_deref(), + Some("hr-agent") + ); + assert_eq!( + sec.agent.as_ref().unwrap().trust_domain.as_deref(), + Some("corp.com") + ); + // Auth method + assert_eq!(sec.auth_method.as_deref(), Some("jwt")); + // Labels + assert!(sec.has_label("PII")); + } + + #[test] + fn test_security_serde_roundtrip() { + let mut sec = SecurityExtension::default(); + sec.add_label("PII"); + sec.classification = Some("internal".into()); + sec.agent = Some(AgentIdentity { + client_id: Some("my-agent".into()), + ..Default::default() + }); + sec.auth_method = Some("mtls".into()); + + let json = serde_json::to_string(&sec).unwrap(); + let deserialized: SecurityExtension = serde_json::from_str(&json).unwrap(); + + assert!(deserialized.has_label("PII")); + assert_eq!(deserialized.classification.as_deref(), Some("internal")); + assert_eq!( + deserialized.agent.as_ref().unwrap().client_id.as_deref(), + Some("my-agent") + ); + assert_eq!(deserialized.auth_method.as_deref(), Some("mtls")); + } + + #[test] + fn test_object_security_profile() { + let profile = ObjectSecurityProfile { + managed_by: Some("hr-system".into()), + permissions: vec!["read".into(), "write".into()], + trust_domain: Some("corp.com".into()), + data_scope: vec!["employee_data".into()], + }; + assert_eq!(profile.managed_by.as_deref(), Some("hr-system")); + assert_eq!(profile.permissions.len(), 2); + } + + #[test] + fn test_data_policy() { + let policy = DataPolicy { + apply_labels: vec!["PII".into()], + allowed_actions: Some(vec!["read".into()]), + denied_actions: vec!["delete".into()], + retention: Some(RetentionPolicy { + max_age_seconds: Some(86400), + policy: "30-day".into(), + delete_after: Some("2026-05-01".into()), + }), + }; + assert_eq!(policy.apply_labels[0], "PII"); + assert!(policy.retention.is_some()); + assert_eq!( + policy.retention.as_ref().unwrap().max_age_seconds, + Some(86400) + ); + } +} diff --git a/crates/cpex-payload/src/extensions/tiers.rs b/crates/cpex-payload/src/extensions/tiers.rs new file mode 100644 index 00000000..a22406f9 --- /dev/null +++ b/crates/cpex-payload/src/extensions/tiers.rs @@ -0,0 +1,100 @@ +// Location: ./crates/cpex-core/src/extensions/tiers.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// Mutability tiers and capability definitions. +// +// Each extension slot has a mutability tier that controls how plugins +// can interact with it. Capabilities gate per-plugin access. +// +// Mirrors cpex/framework/extensions/tiers.py. + +use serde::{Deserialize, Serialize}; + +/// Mutability tier for an extension slot. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum MutabilityTier { + /// Cannot be modified after creation. + Immutable, + /// Can only grow (add-only sets, append-only chains). + Monotonic, + /// Can be freely modified by plugins with write capability. + Mutable, +} + +/// Declared permission that controls extension access. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum Capability { + /// Read the authenticated subject identity. + ReadSubject, + /// Read subject roles. + ReadRoles, + /// Read subject team memberships. + ReadTeams, + /// Read subject claims (e.g., JWT claims). + ReadClaims, + /// Read subject permissions. + ReadPermissions, + /// Read the agent execution context. + ReadAgent, + /// Read HTTP headers. + ReadHeaders, + /// Write (modify) HTTP headers. + WriteHeaders, + /// Read security labels. + ReadLabels, + /// Append security labels (monotonic add-only). + AppendLabels, + /// Read the delegation chain. + ReadDelegation, + /// Append to the delegation chain (monotonic). + AppendDelegation, +} + +/// Access policy for an extension slot. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum AccessPolicy { + /// All plugins can access. + Unrestricted, + /// Only plugins with the declared capability can access. + CapabilityGated, +} + +/// Policy for a single extension slot. +/// +/// Declares the mutability tier, access policy, and required +/// capabilities for reading and writing. +#[derive(Debug, Clone)] +pub struct SlotPolicy { + /// How the slot can be modified. + pub tier: MutabilityTier, + /// Whether access requires a capability. + pub access: AccessPolicy, + /// Capability required for reading (if capability-gated). + pub read_cap: Option, + /// Capability required for writing (if capability-gated). + pub write_cap: Option, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_tier_serde() { + let tier = MutabilityTier::Monotonic; + let json = serde_json::to_string(&tier).unwrap(); + assert_eq!(json, "\"monotonic\""); + } + + #[test] + fn test_capability_serde() { + let cap = Capability::AppendLabels; + let json = serde_json::to_string(&cap).unwrap(); + assert_eq!(json, "\"append_labels\""); + } +} diff --git a/crates/cpex-payload/src/hooks/macros.rs b/crates/cpex-payload/src/hooks/macros.rs new file mode 100644 index 00000000..80012acf --- /dev/null +++ b/crates/cpex-payload/src/hooks/macros.rs @@ -0,0 +1,70 @@ +// Location: ./crates/cpex-core/src/hooks/macros.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// define_hook! macro. +// +// Generates a HookTypeDef marker struct and trait implementation +// from a single declaration. This is the primary way to define new +// hooks — both built-in (CMF, tool, prompt) and custom (rate +// limiting, deployment gates, federation sync). +// +// Plugins implement the generic HookHandler trait (from +// trait_def.rs) for the generated marker struct. The handler +// receives a borrowed payload and returns the hook's result type. + +/// Generates a hook type definition and marker struct. +/// +/// # Usage +/// +/// ```rust,ignore +/// define_hook! { +/// /// Doc comment for the hook. +/// MyHook, "my_hook" => { +/// payload: MyPayload, +/// result: PluginResult, +/// } +/// } +/// ``` +/// +/// This generates a marker struct `MyHook` implementing `HookTypeDef`. +/// Plugins handle it by implementing `HookHandler`. +/// +/// # CMF Pattern (one handler, multiple hook names) +/// +/// For CMF hooks where one handler covers multiple hook names: +/// +/// ```rust,ignore +/// define_hook! { +/// /// CMF message evaluation hook. +/// CmfHook, "cmf" => { +/// payload: MessagePayload, +/// result: PluginResult, +/// } +/// } +/// +/// // Register the same handler for multiple names: +/// // manager.register_handler_for_names::(plugin, config, &[ +/// // "cmf.tool_pre_invoke", "cmf.llm_input", ... +/// // ]); +/// ``` +#[macro_export] +macro_rules! define_hook { + ( + $(#[$meta:meta])* + $name:ident, $hook_name:literal => { + payload: $payload:ty, + result: $result:ty $(,)? + } + ) => { + $(#[$meta])* + pub struct $name; + + impl $crate::hooks::trait_def::HookTypeDef for $name { + type Payload = $payload; + type Result = $result; + const NAME: &'static str = $hook_name; + } + }; +} diff --git a/crates/cpex-payload/src/hooks/mod.rs b/crates/cpex-payload/src/hooks/mod.rs new file mode 100644 index 00000000..1982fad1 --- /dev/null +++ b/crates/cpex-payload/src/hooks/mod.rs @@ -0,0 +1,27 @@ +// Location: ./crates/cpex-core/src/hooks/mod.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// Hook system. +// +// Provides the core abstractions for defining and dispatching hooks: +// +// - [`HookTypeDef`] — marker trait associating a typed payload + result with a hook name. +// - [`PluginPayload`] — base trait for all hook payloads (mirrors Python's PluginPayload). +// - [`PluginResult`] — result type with separate payload and extension modifications. +// - [`Extensions`] — capability-gated extension view passed to handlers. +// - [`define_hook!`] — macro for declaring new hook types with handler traits. +// - [`hook_names`] / [`cmf_hook_names`] — string constants for built-in hooks. +// +// Hook types are open — hosts define their own using define_hook! alongside the built-ins. + +pub mod macros; +pub mod payload; +pub mod trait_def; +pub mod types; + +// Re-export core types at the hooks level +pub use payload::{Extensions, PluginPayload}; +pub use trait_def::{HookHandler, HookTypeDef, PluginResult}; +pub use types::{builtin_hook_types, hook_type_from_str, HookType}; diff --git a/crates/cpex-payload/src/hooks/payload.rs b/crates/cpex-payload/src/hooks/payload.rs new file mode 100644 index 00000000..d284bf4c --- /dev/null +++ b/crates/cpex-payload/src/hooks/payload.rs @@ -0,0 +1,133 @@ +// Location: ./crates/cpex-core/src/hooks/payload.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// PluginPayload trait and Extensions stub. +// +// PluginPayload is the base trait for all hook payloads, mirroring +// Python's PluginPayload(BaseModel, frozen=True). All payloads in +// the framework implement this trait, giving the executor and +// registry a common bound for type safety. +// +// The trait is object-safe — the executor works with `Box` +// instead of `Box`, catching type errors at compile time. +// Downcasting to concrete types uses the `as_any()` method. +// +// Extensions is the typed container for all message extensions +// (security, delegation, HTTP, meta, etc.). It is always passed +// as a separate parameter to handlers — never inside the payload. +// This allows per-plugin capability filtering and independent +// modification without copying the payload. + +use std::any::Any; +use std::fmt; + +// Re-export Extensions and OwnedExtensions from the extensions module. +// These are the typed containers for all extension data. They live in +// extensions/container.rs but are re-exported here for backward +// compatibility with existing code that imports from hooks::payload. +pub use crate::extensions::{Extensions, Guarded, MetaExtension, OwnedExtensions, WriteToken}; + +// --------------------------------------------------------------------------- +// PluginPayload Trait +// --------------------------------------------------------------------------- + +/// Base trait for all hook payloads. +/// +/// Mirrors Python's `PluginPayload(BaseModel, frozen=True)`. Every +/// payload type in the framework implements this trait. The executor +/// and registry use `Box` (not `Box`) +/// for type-safe dispatch. +/// +/// The trait is **object-safe** — it can be used behind `Box`, `&`, +/// and `Arc` without knowing the concrete type. This is achieved by +/// providing `clone_boxed()` instead of requiring `Clone` directly +/// (which is not object-safe), and `as_any()` / `as_any_mut()` for +/// downcasting to the concrete type when needed. +/// +/// Payloads are: +/// - Cloneable via `clone_boxed()` — the executor uses this for COW +/// when a modifying plugin (Sequential or Transform) needs ownership. +/// - `Send + Sync` — payloads may be shared across threads for +/// Concurrent mode plugins. +/// - `'static` — payloads must be owned types (no borrowed references). +/// +/// Extensions are **not** part of the payload. They are passed as a +/// separate `&Extensions` parameter to handlers. +/// +/// # Examples +/// +/// ``` +/// use cpex_core::hooks::payload::PluginPayload; +/// +/// #[derive(Debug, Clone)] +/// struct RateLimitPayload { +/// client_id: String, +/// request_count: u64, +/// } +/// +/// impl PluginPayload for RateLimitPayload { +/// fn clone_boxed(&self) -> Box { +/// Box::new(self.clone()) +/// } +/// fn as_any(&self) -> &dyn std::any::Any { self } +/// fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self } +/// } +/// ``` +pub trait PluginPayload: Send + Sync + 'static { + /// Clone this payload into a new `Box`. + /// + /// Used by the executor for copy-on-write: read-only modes borrow + /// the payload, modifying modes receive a clone via this method. + fn clone_boxed(&self) -> Box; + + /// Downcast to a concrete type via `&dyn Any`. + /// + /// Used by typed handler wrappers to recover the concrete payload + /// type from `Box`. + fn as_any(&self) -> &dyn Any; + + /// Downcast to a concrete type via `&mut dyn Any`. + fn as_any_mut(&mut self) -> &mut dyn Any; +} + +impl fmt::Debug for dyn PluginPayload { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("dyn PluginPayload") + } +} + +// --------------------------------------------------------------------------- +// Blanket helper macro for implementing PluginPayload +// --------------------------------------------------------------------------- + +/// Implements `PluginPayload` for a type that is `Clone + Send + Sync + 'static`. +/// +/// Saves boilerplate — instead of writing the three methods manually, +/// just invoke this macro: +/// +/// ``` +/// use cpex_core::impl_plugin_payload; +/// +/// #[derive(Debug, Clone)] +/// struct MyPayload { value: i32 } +/// +/// impl_plugin_payload!(MyPayload); +/// ``` +#[macro_export] +macro_rules! impl_plugin_payload { + ($ty:ty) => { + impl $crate::hooks::payload::PluginPayload for $ty { + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + } + }; +} diff --git a/crates/cpex-payload/src/hooks/trait_def.rs b/crates/cpex-payload/src/hooks/trait_def.rs new file mode 100644 index 00000000..b75d2b8a --- /dev/null +++ b/crates/cpex-payload/src/hooks/trait_def.rs @@ -0,0 +1,322 @@ +// Location: ./crates/cpex-core/src/hooks/trait_def.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// HookTypeDef trait and PluginResult type. +// +// Every hook in the CPEX framework is defined by a marker type that +// implements HookTypeDef. This associates a typed PluginPayload and +// PluginResult with a string name used for registry lookup and config. +// +// The hook type does NOT declare an access pattern (read-only vs +// mutating). The plugin's mode (from PluginRef.trusted_config) +// determines scheduling and authority at runtime. Security invariants +// come from the types inside the payload (Arc, MonotonicSet, +// Guarded), not from borrow mechanics. +// +// Extensions are always a separate parameter — never part of the +// payload. This allows capability-filtered views per plugin and +// independent modification of extensions without copying the payload. + +use crate::context::PluginContext; +use crate::error::PluginViolation; +use crate::hooks::payload::{Extensions, PluginPayload}; +use crate::plugin::Plugin; + +// --------------------------------------------------------------------------- +// HookTypeDef Trait +// --------------------------------------------------------------------------- + +/// Defines a hook's contract: what goes in and what comes out. +/// +/// Each hook type is a zero-sized marker struct that implements this +/// trait. The framework uses the associated types for compile-time +/// dispatch and the NAME constant for registry lookup. +/// +/// The hook type does **not** declare an access pattern. The plugin's +/// mode (from `PluginRef.trusted_config`) determines whether the +/// executor passes a borrow or a clone: +/// +/// | Mode | Receives | Can Block? | Can Modify? | +/// |-----------------|-----------------|------------|-------------| +/// | Sequential | owned (clone) | Yes | Yes | +/// | Transform | owned (clone) | No | Yes | +/// | Audit | &Payload | No | No | +/// | Concurrent | &Payload | Yes | No | +/// | FireAndForget | &Payload | No | No | +/// +/// # Defining a Hook +/// +/// Use the [`define_hook!`] macro instead of implementing this trait +/// manually — the macro generates the marker struct, the trait impl, +/// and the handler trait in one declaration. +pub trait HookTypeDef: Send + Sync + 'static { + /// The typed payload that handlers receive. + /// Must implement [`PluginPayload`] (Clone + Send + Sync + 'static). + type Payload: PluginPayload; + + /// The typed result that handlers return. + type Result: Send + Sync; + + /// Hook name — used as the registry key and in config YAML. + /// + /// Multiple hook names can map to the same HookTypeDef (the CMF + /// pattern where one handler covers `cmf.tool_pre_invoke`, + /// `cmf.llm_input`, etc.). The primary NAME is used for + /// single-name registration; additional names are registered + /// via `register_for_names()`. + const NAME: &'static str; +} + +// --------------------------------------------------------------------------- +// Hook Handler Trait +// --------------------------------------------------------------------------- + +/// Typed handler for a specific hook type. +/// +/// Plugin authors implement this trait (alongside [`Plugin`]) to handle +/// a specific hook. The type parameter `H` ties the handler to a +/// `HookTypeDef`, ensuring the correct payload and result types at +/// compile time. The framework creates a type-erased adapter internally +/// when you register — you never touch `AnyHookHandler` directly. +/// +/// # Async by design +/// +/// `handle` is an `async fn`. Plugins that don't need to `.await` +/// anything still write `async fn handle(...)` and return synchronously +/// — the compiler emits a trivially-ready future and LLVM inlines it +/// at the adapter site, so there's no observable runtime cost over a +/// plain function. Plugins that *do* need to `.await` (fresh JWKS +/// fetch, RPC to an authz service, dynamic policy lookup) just use +/// `.await` inside the body. +/// +/// **Best practice:** even when async is available, prefer pre-loading +/// state in [`Plugin::initialize`] and reading from cache in `handle`. +/// Hot-path I/O is the most common source of latency regressions. +/// +/// # Native AFIT, not `#[async_trait]` +/// +/// The trait uses native `async fn` (return-position `impl Future`) +/// rather than `#[async_trait]`. This avoids a per-call heap +/// allocation: the returned future is monomorphized into the +/// [`TypedHandlerAdapter`] rather than boxed. The trait is therefore +/// **not object-safe** — you cannot have `Box>`. +/// We don't need that; type erasure happens one layer up at +/// [`AnyHookHandler`]. +/// +/// # Examples +/// +/// ```rust,ignore +/// // Synchronous plugin — no .await, no extra cost +/// impl HookHandler for AllowPlugin { +/// async fn handle( +/// &self, +/// _payload: &MessagePayload, +/// _extensions: &Extensions, +/// _ctx: &mut PluginContext, +/// ) -> PluginResult { +/// PluginResult::allow() +/// } +/// } +/// +/// // Async plugin — calls .await inside the body +/// impl HookHandler for AuthzPlugin { +/// async fn handle( +/// &self, +/// payload: &MyPayload, +/// _extensions: &Extensions, +/// _ctx: &mut PluginContext, +/// ) -> PluginResult { +/// match self.client.check(&payload.user).await { +/// Ok(true) => PluginResult::allow(), +/// _ => PluginResult::deny(/* ... */), +/// } +/// } +/// } +/// +/// // Registration is the same for both: +/// manager.register_handler::(plugin, config)?; +/// ``` +/// +/// [`PluginManager::register_handler`]: crate::manager::PluginManager::register_handler +/// [`AnyHookHandler`]: crate::registry::AnyHookHandler +/// [`TypedHandlerAdapter`]: crate::hooks::adapter::TypedHandlerAdapter +pub trait HookHandler: Plugin + Send + Sync { + /// Handle the hook invocation. + /// + /// Receives a **borrow** of the typed payload, capability-filtered + /// extensions, and per-invocation context. Returns a typed result. + /// + /// The payload is immutable — Rust's borrow checker prevents + /// modification through `&H::Payload`. To modify, the plugin + /// must `clone()` the payload (or the fields it needs) and return + /// the modified copy in `PluginResult::modify_payload()`. This + /// pushes the clone cost to the plugin that actually needs it — + /// read-only plugins (validators, auditors) never pay for a copy. + /// + /// Returns a `Send`-able future so the executor can drive it from + /// any worker thread (including the concurrent-phase `JoinSet`). + /// `H::Result` is already `Send + Sync` per the `HookTypeDef` + /// bound, so the `Send` constraint comes for free for typical + /// handlers. + fn handle( + &self, + payload: &H::Payload, + extensions: &Extensions, + ctx: &mut PluginContext, + ) -> impl std::future::Future + Send; +} + +// --------------------------------------------------------------------------- +// Plugin Result +// --------------------------------------------------------------------------- + +/// Result returned by a hook handler. +/// +/// Payload and extension modifications are **separate** — this is a +/// core design decision. Extension-only changes (add a label, set a +/// header) don't require copying the payload. The payload is only +/// present in `modified_payload` when message content actually changed. +/// +/// The executor interprets the result based on the plugin's mode: +/// - Sequential/Transform: `modified_payload` and `modified_extensions` are accepted. +/// - Audit/Concurrent/FireAndForget: modifications are discarded. +/// - Sequential/Concurrent: `continue_processing = false` halts the pipeline. +/// - Transform/Audit/FireAndForget: blocks are suppressed. +/// +/// Mirrors Python's `PluginResult[T]` with separate `modified_payload` +/// and `modified_extensions` fields. +/// +/// # Examples +/// +/// ``` +/// use cpex_core::hooks::{PluginPayload, PluginResult}; +/// use cpex_core::error::PluginViolation; +/// +/// // Define a simple payload +/// #[derive(Debug, Clone)] +/// struct TestPayload { value: i32 } +/// cpex_core::impl_plugin_payload!(TestPayload); +/// +/// // Allow — no changes +/// let result: PluginResult = PluginResult::allow(); +/// assert!(result.continue_processing); +/// assert!(result.modified_payload.is_none()); +/// +/// // Deny +/// let result: PluginResult = PluginResult::deny( +/// PluginViolation::new("forbidden", "not allowed") +/// ); +/// assert!(!result.continue_processing); +/// assert!(result.violation.is_some()); +/// ``` +#[derive(Debug)] +pub struct PluginResult { + /// Whether the pipeline should continue processing. + /// `false` halts the pipeline (deny). Only respected for + /// Sequential and Concurrent modes. + pub continue_processing: bool, + + /// Modified payload. `None` means no content modification. + /// Only accepted from Sequential and Transform mode plugins. + pub modified_payload: Option

, + + /// Modified extensions. `None` means no extension changes. + /// Return an `OwnedExtensions` from `extensions.cow_copy()`. + /// The executor validates (immutable unchanged, monotonic superset) + /// and merges back into the pipeline's `Extensions`. + pub modified_extensions: Option, + + /// Policy violation. Present when `continue_processing` is `false`. + pub violation: Option, + + /// Optional metadata from the plugin (telemetry, diagnostics). + /// Not used for scheduling or policy decisions. + pub metadata: Option, +} + +impl PluginResult

{ + /// Allow — payload continues unchanged, no extension changes. + pub fn allow() -> Self { + Self { + continue_processing: true, + modified_payload: None, + modified_extensions: None, + + violation: None, + metadata: None, + } + } + + /// Deny — pipeline halts with a violation. + pub fn deny(violation: PluginViolation) -> Self { + Self { + continue_processing: false, + modified_payload: None, + modified_extensions: None, + + violation: Some(violation), + metadata: None, + } + } + + /// Modify payload only — extensions unchanged. + pub fn modify_payload(payload: P) -> Self { + Self { + continue_processing: true, + modified_payload: Some(payload), + modified_extensions: None, + + violation: None, + metadata: None, + } + } + + /// Modify extensions only — payload unchanged. + /// Takes an `OwnedExtensions` from `extensions.cow_copy()`. + pub fn modify_extensions(extensions: crate::hooks::payload::OwnedExtensions) -> Self { + Self { + continue_processing: true, + modified_payload: None, + modified_extensions: Some(extensions), + + violation: None, + metadata: None, + } + } + + /// Modify both payload and extensions. + /// Takes an `OwnedExtensions` from `extensions.cow_copy()`. + pub fn modify(payload: P, extensions: crate::hooks::payload::OwnedExtensions) -> Self { + Self { + continue_processing: true, + modified_payload: Some(payload), + modified_extensions: Some(extensions), + + violation: None, + metadata: None, + } + } + + /// Whether this result represents a denial. + pub fn is_denied(&self) -> bool { + !self.continue_processing + } + + /// Whether this result carries a modified payload. + pub fn is_payload_modified(&self) -> bool { + self.modified_payload.is_some() + } + + /// Whether this result carries modified extensions. + pub fn is_extensions_modified(&self) -> bool { + self.modified_extensions.is_some() + } +} + +impl Default for PluginResult

{ + fn default() -> Self { + Self::allow() + } +} diff --git a/crates/cpex-payload/src/hooks/types.rs b/crates/cpex-payload/src/hooks/types.rs new file mode 100644 index 00000000..3295d1a6 --- /dev/null +++ b/crates/cpex-payload/src/hooks/types.rs @@ -0,0 +1,190 @@ +// Location: ./crates/cpex-core/src/hooks/types.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// Hook type definitions. +// +// Hook types are open strings — hosts define hook points appropriate +// to their execution lifecycle. This module provides a newtype wrapper +// for type safety and built-in constants for the common hook points. +// +// The framework does not prescribe a fixed set of hook points. Each +// host places `invoke_hook()` calls at sites appropriate to its +// processing pipeline. The constants below cover the standard +// MCP/CMF lifecycle but hosts may register additional types. + +use std::fmt; + +use serde::{Deserialize, Serialize}; + +// --------------------------------------------------------------------------- +// Hook Type +// --------------------------------------------------------------------------- + +/// A named hook point in the host's execution lifecycle. +/// +/// Wraps a string identifier. Hook types are open — hosts register +/// their own alongside the built-in constants. +/// +/// # Examples +/// +/// ``` +/// use cpex_core::hooks::HookType; +/// use cpex_core::hooks::types::hook_names; +/// +/// // Use a built-in name constant +/// let hook = HookType::new(hook_names::TOOL_PRE_INVOKE); +/// assert_eq!(hook.as_str(), "tool_pre_invoke"); +/// +/// // Define a custom hook +/// let custom = HookType::new("generation_pre_call"); +/// assert_eq!(custom.as_str(), "generation_pre_call"); +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(transparent)] +pub struct HookType(String); + +impl HookType { + /// Create a new hook type from a string. + pub fn new(name: impl Into) -> Self { + Self(name.into()) + } + + /// Return the hook type as a string slice. + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl fmt::Display for HookType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.0) + } +} + +impl From<&str> for HookType { + fn from(s: &str) -> Self { + Self::new(s) + } +} + +impl From for HookType { + fn from(s: String) -> Self { + Self(s) + } +} + +// --------------------------------------------------------------------------- +// Built-in Hook String Constants +// --------------------------------------------------------------------------- +// Canonical string names for built-in hooks. Use these with +// HookType::new() or pass them directly to APIs that accept &str. + +/// Legacy hook names — typed payloads (ToolPreInvokePayload, etc.). +pub mod hook_names { + // Tool lifecycle + pub const TOOL_PRE_INVOKE: &str = "tool_pre_invoke"; + pub const TOOL_POST_INVOKE: &str = "tool_post_invoke"; + + // Prompt lifecycle + pub const PROMPT_PRE_FETCH: &str = "prompt_pre_fetch"; + pub const PROMPT_POST_FETCH: &str = "prompt_post_fetch"; + + // Resource lifecycle + pub const RESOURCE_PRE_FETCH: &str = "resource_pre_fetch"; + pub const RESOURCE_POST_FETCH: &str = "resource_post_fetch"; + + // Identity and delegation + pub const IDENTITY_RESOLVE: &str = "identity_resolve"; + pub const TOKEN_DELEGATE: &str = "token_delegate"; +} + +/// CMF hook names — MessagePayload wrapping a CMF Message. +/// The `cmf.` prefix lets legacy and CMF plugins coexist at the +/// same interception point. The gateway fires both at each event. +pub mod cmf_hook_names { + // Tool lifecycle + pub const TOOL_PRE_INVOKE: &str = "cmf.tool_pre_invoke"; + pub const TOOL_POST_INVOKE: &str = "cmf.tool_post_invoke"; + + // LLM lifecycle (CMF only — no legacy equivalent) + pub const LLM_INPUT: &str = "cmf.llm_input"; + pub const LLM_OUTPUT: &str = "cmf.llm_output"; + + // Prompt lifecycle + pub const PROMPT_PRE_FETCH: &str = "cmf.prompt_pre_fetch"; + pub const PROMPT_POST_FETCH: &str = "cmf.prompt_post_fetch"; + + // Resource lifecycle + pub const RESOURCE_PRE_FETCH: &str = "cmf.resource_pre_fetch"; + pub const RESOURCE_POST_FETCH: &str = "cmf.resource_post_fetch"; +} + +// --------------------------------------------------------------------------- +// Built-in hook type helpers +// --------------------------------------------------------------------------- + +/// Returns all built-in hook types with their canonical string values. +/// +/// Called once during PluginManager initialization to populate the +/// hook registry. Hosts add their own hook types after this. +pub fn builtin_hook_types() -> Vec { + vec![ + // Legacy (typed payloads) + HookType::new("tool_pre_invoke"), + HookType::new("tool_post_invoke"), + HookType::new("prompt_pre_fetch"), + HookType::new("prompt_post_fetch"), + HookType::new("resource_pre_fetch"), + HookType::new("resource_post_fetch"), + HookType::new("identity_resolve"), + HookType::new("token_delegate"), + // CMF (MessagePayload) + HookType::new("cmf.tool_pre_invoke"), + HookType::new("cmf.tool_post_invoke"), + HookType::new("cmf.llm_input"), + HookType::new("cmf.llm_output"), + HookType::new("cmf.prompt_pre_fetch"), + HookType::new("cmf.prompt_post_fetch"), + HookType::new("cmf.resource_pre_fetch"), + HookType::new("cmf.resource_post_fetch"), + ] +} + +/// Look up a hook type by name. Returns the canonical instance if +/// it matches a built-in, otherwise creates a new custom HookType. +pub fn hook_type_from_str(name: &str) -> HookType { + HookType::new(name) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_hook_type_equality() { + let a = HookType::new("tool_pre_invoke"); + let b = HookType::new("tool_pre_invoke"); + assert_eq!(a, b); + } + + #[test] + fn test_hook_type_display() { + let h = HookType::new("cmf.llm_input"); + assert_eq!(h.to_string(), "cmf.llm_input"); + } + + #[test] + fn test_hook_type_from_str() { + let h: HookType = "custom_hook".into(); + assert_eq!(h.as_str(), "custom_hook"); + } + + #[test] + fn test_builtin_hook_types_count() { + let builtins = builtin_hook_types(); + // 8 legacy + 8 CMF + assert_eq!(builtins.len(), 16); + } +} diff --git a/crates/cpex-payload/src/lib.rs b/crates/cpex-payload/src/lib.rs new file mode 100644 index 00000000..eab7fc78 --- /dev/null +++ b/crates/cpex-payload/src/lib.rs @@ -0,0 +1,32 @@ +// Location: ./crates/cpex-core/src/lib.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// CPEX Core library root. +// +// Pure Rust plugin runtime with no FFI, WASM, or PyO3 dependencies. +// Provides the PluginManager, 5-phase executor, hook registry, +// unified config parser, and all core types. +// +// # Modules +// +// - [`plugin`] — Plugin trait, PluginRef, PluginMetadata, PluginConfig +// - [`hooks`] — HookType (open string registry), payload/result traits +// - [`executor`] — 5-phase execution engine (sequential → transform → audit → concurrent → fire_and_forget) +// - [`manager`] — PluginManager lifecycle and hook dispatch +// - [`registry`] — PluginInstanceRegistry and HookRegistry +// - [`config`] — Unified YAML configuration parsing +// - [`factory`] — Plugin factory registry for config-driven instantiation +// - [`context`] — PluginContext (local_state + global_state) +// - [`cmf`] — ContextForge Message Format (Message, ContentPart, enums) +// - [`error`] — Error types, violations, and result types + +pub mod cmf; +pub mod config; +pub mod context; +pub mod error; +pub mod extensions; +pub mod hooks; +pub mod plugin; +pub mod plugins; diff --git a/crates/cpex-payload/src/plugin.rs b/crates/cpex-payload/src/plugin.rs new file mode 100644 index 00000000..dd743a24 --- /dev/null +++ b/crates/cpex-payload/src/plugin.rs @@ -0,0 +1,553 @@ +// Location: ./crates/cpex-core/src/plugin.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Teryl Taylor +// +// Plugin trait and supporting types. +// +// Defines the core Plugin trait that all plugin implementations satisfy — +// native Rust, WASM hosts, Python bridge hosts, and dlopen'd shared +// libraries. Also defines PluginConfig (YAML-declared plugin settings), +// PluginMode (5-phase execution modes), and OnError (failure behavior). +// +// The Plugin trait handles lifecycle only (initialize, shutdown, config). +// Hook-specific logic is defined by handler traits generated by the +// define_hook! macro (see hooks/macros.rs). A plugin implements Plugin +// for lifecycle + one or more handler traits for the hooks it handles. +// +// The manager wraps each plugin in a PluginRef with an authoritative +// config from the config loader — the plugin's own config() is for +// the plugin's reading only, never used by the executor for scheduling. + +use std::collections::HashSet; +use std::fmt; + +use async_trait::async_trait; +use serde::{Deserialize, Serialize}; + +use crate::error::PluginError; + +// --------------------------------------------------------------------------- +// Plugin Trait +// --------------------------------------------------------------------------- + +/// Core plugin interface — lifecycle management only. +/// +/// Every plugin in the CPEX framework — regardless of language or +/// deployment model — implements this trait. It covers lifecycle +/// (initialize, shutdown) and identity (config). Hook-specific logic +/// is defined separately by handler traits generated by `define_hook!`. +/// +/// # Lifecycle +/// +/// 1. `initialize()` — called once after loading, before any hooks fire. +/// 2. Hook handlers — called on each hook invocation (defined by handler traits). +/// 3. `shutdown()` — called once during graceful teardown. +/// +/// # Hook Handlers +/// +/// A plugin implements one or more handler traits alongside Plugin: +/// +/// ```rust,ignore +/// impl Plugin for MyPlugin { +/// fn config(&self) -> &PluginConfig { &self.config } +/// async fn initialize(&self) -> Result<(), Box> { Ok(()) } +/// async fn shutdown(&self) -> Result<(), Box> { Ok(()) } +/// } +/// +/// impl CmfHookHandler for MyPlugin { +/// fn cmf_hook(&self, payload: MessagePayload, ext: &Extensions, ctx: &PluginContext) -> PluginResult { +/// PluginResult::allow() +/// } +/// } +/// ``` +/// +/// # Trust Model +/// +/// The manager wraps each plugin in a `PluginRef` with an authoritative +/// config from the config loader. The executor reads scheduling decisions +/// (mode, priority, hooks, capabilities) from the `PluginRef` — never +/// from `plugin.config()`. The plugin's own `config()` is available for +/// the plugin's reading during hook execution. +/// +/// # Implementors +/// +/// - Native Rust plugins (implement directly) +/// - `cpex-hosts::wasm` (bridges to WASM guest via wasmtime) +/// - `cpex-hosts::python` (bridges to Python plugin classes via PyO3) +/// - `cpex-hosts::native` (bridges to dlopen'd shared libraries) +#[async_trait] +pub trait Plugin: Send + Sync { + /// Returns the plugin's configuration. + /// + /// Available for the plugin's own reading during hook execution. + /// The manager/executor never reads this — they use the authoritative + /// config from `PluginRef.trusted_config()`. + fn config(&self) -> &PluginConfig; + + /// One-time initialization after loading. + /// + /// Called before any hook invocations. Use this to establish + /// connections, load resources, or validate configuration. + /// Default implementation does nothing. + async fn initialize(&self) -> Result<(), Box> { + Ok(()) + } + + /// Graceful shutdown. + /// + /// Called once during teardown. Use this to flush buffers, close + /// connections, or release resources. + /// Default implementation does nothing. + async fn shutdown(&self) -> Result<(), Box> { + Ok(()) + } +} + +// --------------------------------------------------------------------------- +// Plugin Configuration +// --------------------------------------------------------------------------- + +/// Declared plugin configuration from the unified YAML config. +/// +/// Controls how the framework loads, schedules, and gates the plugin. +/// Corresponds to a single entry in the `plugins:` list in config YAML. +/// +/// The manager holds the authoritative copy in `PluginRef.trusted_config`. +/// The plugin receives its own copy for reading via `Plugin::config()`. +/// +/// # Examples +/// +/// ```yaml +/// plugins: +/// - name: apl-policy +/// kind: builtin +/// hooks: [tool_pre_invoke, tool_post_invoke] +/// mode: sequential +/// priority: 10 +/// on_error: fail +/// capabilities: [read_security, append_labels] +/// config: +/// policy_file: apl/demo/hr_policy.yaml +/// ``` +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct PluginConfig { + /// Unique plugin name. + pub name: String, + + /// Plugin kind — determines how the framework loads it. + /// + /// - `"builtin"` — compiled into the runtime + /// - `"native://path/to/lib.so"` — dlopen'd shared library + /// - `"wasm://path/to/plugin.wasm"` — wasmtime sandbox + /// - `"python://module.path.ClassName"` — PyO3 bridge + /// - `"external"` — MCP/gRPC/Unix socket transport + pub kind: String, + + /// Human-readable description. + #[serde(default)] + pub description: Option, + + /// Plugin author or team. + #[serde(default)] + pub author: Option, + + /// Semantic version string. + #[serde(default)] + pub version: Option, + + /// Hook names this plugin handles. + #[serde(default)] + pub hooks: Vec, + + /// Execution mode — determines scheduling behavior and authority. + #[serde(default)] + pub mode: PluginMode, + + /// Execution priority — lower numbers execute first within each mode. + #[serde(default = "default_priority")] + pub priority: i32, + + /// Error handling behavior when the plugin fails. + #[serde(default)] + pub on_error: OnError, + + /// Declared capabilities for extension visibility gating. + /// + /// Controls which extensions the plugin can see and modify. + /// Extensions not covered by declared capabilities appear as + /// `None` in the filtered view. + #[serde(default)] + pub capabilities: HashSet, + + /// Tags for categorization and searchability. + #[serde(default)] + pub tags: Vec, + + /// Legacy conditions for when the plugin should execute. + /// + /// Each condition narrows the plugin's scope by server, tenant, + /// tool name, prompt name, etc. If any condition in the list + /// matches, the plugin runs. If the list is empty (default), + /// the plugin runs unconditionally. + /// + /// **Backward compatibility:** Conditions are the legacy mechanism + /// for scoping plugins. When the host uses the unified routing + /// system (`routes:` in config YAML), routing rules handle scope + /// matching and conditions on the plugin are ignored. The two + /// mechanisms should not be used together on the same plugin. + #[serde(default)] + pub conditions: Vec, + + /// Plugin-specific configuration (opaque to the framework). + #[serde(default)] + pub config: Option, +} + +impl PluginConfig { + /// Whether this plugin's `conditions` allow it to fire for the given + /// request `Extensions`. Used in legacy mode (`routing_enabled: false`) + /// to filter which plugins run per request — mirrors the Python + /// implementation's per-plugin condition filtering. + /// + /// Semantics: + /// - Empty `conditions` Vec → fire always (no restriction). + /// - Non-empty → fire if ANY condition matches (OR across the list, + /// AND within each individual condition). + /// + /// Field-source mapping (see project memory `project_conditions_field_mapping`): + /// - `server_ids` ← `extensions.mcp.{tool|resource|prompt}.server_id` + /// - `tenant_ids` ← `extensions.security.subject.claims["tenant"]` + /// - `tools|prompts|resources` ← `extensions.meta.entity_name` (when matching `entity_type`) + /// - `agents` ← `extensions.agent.agent_id` + /// - `user_patterns` ← `extensions.security.subject.id` (glob match) + /// - `content_types` ← `extensions.mcp.resource.mime_type` + pub fn passes_conditions(&self, extensions: &crate::hooks::payload::Extensions) -> bool { + if self.conditions.is_empty() { + return true; + } + + // Source values once from the extensions tree. + let server_id = extensions.mcp.as_ref().and_then(|m| { + m.tool + .as_ref() + .and_then(|t| t.server_id.as_deref()) + .or_else(|| m.resource.as_ref().and_then(|r| r.server_id.as_deref())) + .or_else(|| m.prompt.as_ref().and_then(|p| p.server_id.as_deref())) + }); + let tenant_id = extensions + .security + .as_ref() + .and_then(|s| s.subject.as_ref()) + .and_then(|sub| sub.claims.get("tenant")) + .map(|s| s.as_str()); + let entity_name = extensions + .meta + .as_ref() + .and_then(|m| m.entity_name.as_deref()); + let entity_type = extensions + .meta + .as_ref() + .and_then(|m| m.entity_type.as_deref()); + let (tool, prompt, resource) = match entity_type { + Some("tool") => (entity_name, None, None), + Some("prompt") => (None, entity_name, None), + Some("resource") => (None, None, entity_name), + _ => (None, None, None), + }; + let agent = extensions + .agent + .as_ref() + .and_then(|a| a.agent_id.as_deref()); + let user = extensions + .security + .as_ref() + .and_then(|s| s.subject.as_ref()) + .and_then(|sub| sub.id.as_deref()); + let content_type = extensions + .mcp + .as_ref() + .and_then(|m| m.resource.as_ref()) + .and_then(|r| r.mime_type.as_deref()); + + let ctx = MatchContext { + server_id, + tenant_id, + tool, + prompt, + resource, + agent, + user, + content_type, + }; + self.conditions.iter().any(|c| c.matches(&ctx)) + } +} + +fn default_priority() -> i32 { + 100 +} + +// --------------------------------------------------------------------------- +// Plugin Condition (legacy scoping) +// --------------------------------------------------------------------------- + +/// Condition for when a plugin should execute. +/// +/// Narrows plugin scope to specific servers, tenants, tools, prompts, +/// resources, or agents. All fields are optional — only specified +/// fields participate in matching. Within a field, any match suffices +/// (OR semantics). Across fields, all must match (AND semantics). +/// +/// This is the legacy scoping mechanism. The unified routing system +/// (`routes:` in config) supersedes this — when routes are used, +/// conditions are ignored. +/// +/// Mirrors Python's `PluginCondition` in `cpex/framework/models.py`. +/// +/// # Examples +/// +/// ``` +/// use cpex_core::plugin::PluginCondition; +/// +/// // Only run for specific tools on specific servers +/// let cond = PluginCondition { +/// server_ids: Some(vec!["server-1".into(), "server-2".into()].into_iter().collect()), +/// tools: Some(vec!["get_compensation".into()].into_iter().collect()), +/// ..Default::default() +/// }; +/// assert!(cond.server_ids.as_ref().unwrap().contains("server-1")); +/// assert!(cond.tools.as_ref().unwrap().contains("get_compensation")); +/// ``` +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct PluginCondition { + /// Set of server IDs — plugin runs only on these servers. + #[serde(default)] + pub server_ids: Option>, + + /// Set of tenant IDs — plugin runs only for these tenants. + #[serde(default)] + pub tenant_ids: Option>, + + /// Set of tool names — plugin runs only for these tools. + #[serde(default)] + pub tools: Option>, + + /// Set of prompt names — plugin runs only for these prompts. + #[serde(default)] + pub prompts: Option>, + + /// Set of resource identifiers — plugin runs only for these resources. + #[serde(default)] + pub resources: Option>, + + /// Set of agent identifiers — plugin runs only for these agents. + #[serde(default)] + pub agents: Option>, + + /// User patterns (glob or regex) — plugin runs only for matching users. + #[serde(default)] + pub user_patterns: Option>, + + /// Content types — plugin runs only for these content types. + #[serde(default)] + pub content_types: Option>, +} + +/// Bundle of optional context values used to evaluate a `PluginCondition`. +/// +/// Each field corresponds to one of the condition's gates. `None` means +/// "no value sourced from the extensions tree"; the condition then +/// rejects when the corresponding `Some(set)` is set on the condition +/// (i.e., the gate was specified but couldn't be evaluated). +/// +/// Replaces an 8-arg `matches(...)` call where every arg was +/// `Option<&str>` and could be misordered silently. +#[derive(Debug, Default, Clone, Copy)] +pub struct MatchContext<'a> { + pub server_id: Option<&'a str>, + pub tenant_id: Option<&'a str>, + pub tool: Option<&'a str>, + pub prompt: Option<&'a str>, + pub resource: Option<&'a str>, + pub agent: Option<&'a str>, + pub user: Option<&'a str>, + pub content_type: Option<&'a str>, +} + +impl PluginCondition { + /// Whether this condition matches the given context. + /// + /// A field that is `None` is treated as "any" (no restriction). + /// A `Some(set)` field matches if the given value is in the set + /// (exact match for ID-shaped fields; glob match via `wildmatch` + /// for `user_patterns`). + /// All specified fields must match — AND semantics within one condition. + pub fn matches(&self, ctx: &MatchContext<'_>) -> bool { + let MatchContext { + server_id, + tenant_id, + tool, + prompt, + resource, + agent, + user, + content_type, + } = *ctx; + let check_set = |field: &Option>, value: Option<&str>| -> bool { + match field { + None => true, // not specified — matches anything + Some(set) => match value { + Some(v) => set.contains(v), + None => false, // field required but no value provided + }, + } + }; + + // user_patterns: list of globs. Match if any pattern matches the user. + let check_patterns = |field: &Option>, value: Option<&str>| -> bool { + match field { + None => true, + Some(patterns) => match value { + Some(v) => patterns + .iter() + .any(|p| wildmatch::WildMatch::new(p).matches(v)), + None => false, + }, + } + }; + + // content_types: list of exact strings. + let check_list = |field: &Option>, value: Option<&str>| -> bool { + match field { + None => true, + Some(list) => match value { + Some(v) => list.iter().any(|s| s == v), + None => false, + }, + } + }; + + check_set(&self.server_ids, server_id) + && check_set(&self.tenant_ids, tenant_id) + && check_set(&self.tools, tool) + && check_set(&self.prompts, prompt) + && check_set(&self.resources, resource) + && check_set(&self.agents, agent) + && check_patterns(&self.user_patterns, user) + && check_list(&self.content_types, content_type) + } +} + +// --------------------------------------------------------------------------- +// Plugin Mode +// --------------------------------------------------------------------------- + +/// Execution mode — determines a plugin's scheduling behavior and authority. +/// +/// The 5-phase model defines both what a plugin *can do* (block, modify) +/// and *how it runs* (serial, parallel, background). Scheduling is derived +/// from mode; plugin authors don't control it directly. +/// +/// # Execution Order +/// +/// ```text +/// SEQUENTIAL → TRANSFORM → AUDIT → CONCURRENT → FIRE_AND_FORGET +/// ``` +/// +/// # Mode Capabilities +/// +/// | Mode | Can Block? | Can Modify? | Execution | +/// |----------------|------------|-------------|-----------------| +/// | Sequential | Yes | Yes | Serial, chained | +/// | Transform | No | Yes | Serial, chained | +/// | Audit | No | No | Serial | +/// | Concurrent | Yes | No | Parallel | +/// | FireAndForget | No | No | Background | +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)] +#[serde(rename_all = "snake_case")] +#[non_exhaustive] +pub enum PluginMode { + /// Policy enforcement + transformation. Serial, chained. Can block and modify. + #[default] + Sequential, + + /// Data shaping (PII redaction, normalization). Serial, chained. Can modify, cannot block. + Transform, + + /// Observation and logging. Serial, read-only. Cannot block or modify. + Audit, + + /// Independent policy gates. Parallel, fail-fast. Can block, cannot modify. + Concurrent, + + /// Telemetry and async side effects. Background tasks. Cannot block or modify. + FireAndForget, + + /// Plugin is disabled — skipped during execution. + Disabled, +} + +impl PluginMode { + /// Whether this mode allows the plugin to block the pipeline. + pub fn can_block(&self) -> bool { + matches!(self, Self::Sequential | Self::Concurrent) + } + + /// Whether this mode allows the plugin to modify the payload. + pub fn can_modify(&self) -> bool { + matches!(self, Self::Sequential | Self::Transform) + } + + /// Whether the framework waits for this plugin to complete. + pub fn is_awaited(&self) -> bool { + !matches!(self, Self::FireAndForget | Self::Disabled) + } +} + +impl fmt::Display for PluginMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Sequential => write!(f, "sequential"), + Self::Transform => write!(f, "transform"), + Self::Audit => write!(f, "audit"), + Self::Concurrent => write!(f, "concurrent"), + Self::FireAndForget => write!(f, "fire_and_forget"), + Self::Disabled => write!(f, "disabled"), + } + } +} + +// --------------------------------------------------------------------------- +// Error Handling Mode +// --------------------------------------------------------------------------- + +/// Error handling behavior when a plugin fails. +/// +/// Independent of [`PluginMode`] — any mode can use any error behavior. +/// Controls whether plugin failures halt the pipeline, are logged and +/// skipped, or cause the plugin to be auto-disabled. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)] +#[serde(rename_all = "snake_case")] +#[non_exhaustive] +pub enum OnError { + /// Pipeline halts and error propagates. Fail-safe enforcement. + #[default] + Fail, + + /// Error logged, pipeline continues. For non-critical plugins. + Ignore, + + /// Plugin auto-disabled after error. Prevents repeated failures. + Disable, +} + +impl fmt::Display for OnError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Fail => write!(f, "fail"), + Self::Ignore => write!(f, "ignore"), + Self::Disable => write!(f, "disable"), + } + } +} diff --git a/crates/cpex-payload/src/plugins/identity_checker.rs b/crates/cpex-payload/src/plugins/identity_checker.rs new file mode 100644 index 00000000..59e1450e --- /dev/null +++ b/crates/cpex-payload/src/plugins/identity_checker.rs @@ -0,0 +1,75 @@ +use crate::cmf::message::MessagePayload; +use crate::context::PluginContext; +use crate::error::PluginViolation; +use crate::extensions::Extensions; +use crate::hooks::PluginResult; + +pub fn identity_check(payload: &MessagePayload, extensions: &Extensions, _ctx: &PluginContext) -> PluginResult { + let is_result = payload.message.is_tool_result(); + + if is_result { + let tool_name = payload + .message + .get_tool_results() + .first() + .map(|tr| tr.tool_name.as_str()) + .unwrap_or("unknown"); + println!( + " [identity-checker] POST-INVOKE: verifying result from '{}'", + tool_name + ); + + if let Some(ref security) = extensions.security { + if let Some(ref subject) = security.subject { + println!( + " [identity-checker] Result authorized for subject: {:?}", + subject.id + ); + } + } + println!(" [identity-checker] POST-INVOKE ALLOWED"); + } else { + let tool_name = payload + .message + .get_tool_calls() + .first() + .map(|tc| tc.name.as_str()) + .unwrap_or("unknown"); + println!( + " [identity-checker] PRE-INVOKE: checking identity for '{}'", + tool_name + ); + + if let Some(ref security) = extensions.security { + let labels: Vec<&String> = security.labels.iter().collect(); + println!(" [identity-checker] Security labels: {:?}", labels); + + if let Some(ref subject) = security.subject { + println!( + " [identity-checker] Subject: {:?}, Roles: {:?}", + subject.id, + subject.roles.iter().collect::>() + ); + + if security.has_label("PII") && !subject.roles.contains("hr_admin") { + return PluginResult::deny(PluginViolation::new( + "insufficient_role", + &format!( + "Tool '{}' requires 'hr_admin' role for PII data", + tool_name + ), + )); + } + } + } + + if extensions.http.is_some() { + println!(" [identity-checker] WARNING: HTTP visible (unexpected!)"); + } else { + println!(" [identity-checker] HTTP: not visible (correct — no read_headers)"); + } + println!(" [identity-checker] PRE-INVOKE ALLOWED"); + } + + PluginResult::allow() +} diff --git a/crates/cpex-payload/src/plugins/mod.rs b/crates/cpex-payload/src/plugins/mod.rs new file mode 100644 index 00000000..7ec351ed --- /dev/null +++ b/crates/cpex-payload/src/plugins/mod.rs @@ -0,0 +1 @@ +pub mod identity_checker; \ No newline at end of file From 5beb25dee4d1ab8158d13a5804fca0c871b3909b Mon Sep 17 00:00:00 2001 From: Shriti Priya Date: Tue, 9 Jun 2026 14:47:02 -0400 Subject: [PATCH 3/7] docs: updated documentation on usage Signed-off-by: Shriti Priya --- crates/cpex-wasm-host/README.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/crates/cpex-wasm-host/README.md b/crates/cpex-wasm-host/README.md index 9753358d..f2fc15e9 100644 --- a/crates/cpex-wasm-host/README.md +++ b/crates/cpex-wasm-host/README.md @@ -177,20 +177,6 @@ cargo test ## Usage -### Direct (without PluginManager) - -```rust -use std::path::Path; -use cpex_wasm_host::policy_loader::SandboxPolicy; -use cpex_wasm_host::sandbox_manager::SandboxManager; - -let policy = SandboxPolicy::default(); // deny-all sandbox -let mut manager = SandboxManager::new()?; -manager.load_wasmplugin(Path::new("wasm/plugin.wasm"), Some(&policy)).await?; - -let result = manager.invoke(payload, extensions, ctx).await?; -``` - ### Via PluginManager ```rust From 62a41fe699d00122ddb652bc9fdf1d3c9372d1e1 Mon Sep 17 00:00:00 2001 From: Shriti Priya Date: Thu, 2 Jul 2026 17:14:00 -0400 Subject: [PATCH 4/7] feat: feature-gate cpex-core for WASM compilation Signed-off-by: Shriti Priya --- crates/cpex-core/Cargo.toml | 21 +++++++++++---------- crates/cpex-core/src/config.rs | 2 ++ crates/cpex-core/src/delegation/payload.rs | 2 ++ crates/cpex-core/src/hooks/mod.rs | 5 ++++- crates/cpex-core/src/identity/payload.rs | 2 ++ crates/cpex-core/src/lib.rs | 14 +++++++++++--- 6 files changed, 32 insertions(+), 14 deletions(-) diff --git a/crates/cpex-core/Cargo.toml b/crates/cpex-core/Cargo.toml index 1f0d90aa..62a10b94 100644 --- a/crates/cpex-core/Cargo.toml +++ b/crates/cpex-core/Cargo.toml @@ -20,9 +20,12 @@ keywords.workspace = true categories.workspace = true rust-version.workspace = true +[features] +default = ["runtime"] +runtime = ["dep:tokio", "dep:tokio-util", "dep:cpex-orchestration", "dep:arc-swap"] + [dependencies] -tokio = { workspace = true } -tokio-util = { workspace = true } +# --- Always-on dependencies (WASM-compatible) --- serde = { workspace = true } serde_yaml = { workspace = true } serde_json = { workspace = true } @@ -31,17 +34,15 @@ thiserror = { workspace = true } tracing = { workspace = true } uuid = { workspace = true } hashbrown = { workspace = true } -arc-swap = { workspace = true } wildmatch = { workspace = true } chrono = { workspace = true } -# Zeroizing wrapper for raw credential material in RawCredentialsExtension. -# `derive` feature pulls the proc-macro so we can `#[derive(Zeroize)]` on -# token-bearing structs in a future slice; for now only the -# `Zeroizing` wrapper is used directly. zeroize = { version = "1.8", features = ["zeroize_derive"] } -# Shared concurrency primitive used by `executor::run_concurrent_phase` -# (and apl-core's `Effect::Parallel`). Leaf crate, no cycles back here. -cpex-orchestration = { workspace = true } + +# --- Runtime-only dependencies (require tokio, not WASM-compatible) --- +tokio = { workspace = true, optional = true } +tokio-util = { workspace = true, optional = true } +arc-swap = { workspace = true, optional = true } +cpex-orchestration = { path = "../cpex-orchestration", optional = true } [lints] workspace = true diff --git a/crates/cpex-core/src/config.rs b/crates/cpex-core/src/config.rs index b4e87f3c..dc5466d1 100644 --- a/crates/cpex-core/src/config.rs +++ b/crates/cpex-core/src/config.rs @@ -19,6 +19,7 @@ // enabled, conditions on individual plugins are ignored. use std::collections::{HashMap, HashSet}; +#[cfg(feature = "runtime")] use std::path::Path; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -595,6 +596,7 @@ impl StringOrList { // Config Loading // --------------------------------------------------------------------------- +#[cfg(feature = "runtime")] /// Load and parse a CPEX config from a YAML file. pub fn load_config(path: &Path) -> Result> { let content = std::fs::read_to_string(path).map_err(|e| PluginError::Config { diff --git a/crates/cpex-core/src/delegation/payload.rs b/crates/cpex-core/src/delegation/payload.rs index f02d725d..d4f431de 100644 --- a/crates/cpex-core/src/delegation/payload.rs +++ b/crates/cpex-core/src/delegation/payload.rs @@ -56,6 +56,7 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use zeroize::Zeroizing; +#[cfg(feature = "runtime")] use crate::executor::PipelineResult; use crate::extensions::raw_credentials::DelegationMode; use crate::extensions::{ @@ -358,6 +359,7 @@ impl DelegationPayload { // -------- Host-side application helpers -------- + #[cfg(feature = "runtime")] /// Pull the resolved `DelegationPayload` out of a `PipelineResult` /// returned by `mgr.invoke_named::(...)`. /// Returns `None` when the pipeline was denied or when the result's diff --git a/crates/cpex-core/src/hooks/mod.rs b/crates/cpex-core/src/hooks/mod.rs index 8ad5115e..05b04e65 100644 --- a/crates/cpex-core/src/hooks/mod.rs +++ b/crates/cpex-core/src/hooks/mod.rs @@ -16,14 +16,17 @@ // // Hook types are open — hosts define their own using define_hook! alongside the built-ins. -pub mod adapter; pub mod macros; pub mod metadata; pub mod payload; pub mod trait_def; pub mod types; +#[cfg(feature = "runtime")] +pub mod adapter; + // Re-export core types at the hooks level +#[cfg(feature = "runtime")] pub use adapter::TypedHandlerAdapter; pub use metadata::{ lookup as lookup_hook_metadata, register_hook_metadata, HookMetadata, HookPhase, diff --git a/crates/cpex-core/src/identity/payload.rs b/crates/cpex-core/src/identity/payload.rs index 42c95221..ff3a63ed 100644 --- a/crates/cpex-core/src/identity/payload.rs +++ b/crates/cpex-core/src/identity/payload.rs @@ -63,6 +63,7 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use zeroize::Zeroizing; +#[cfg(feature = "runtime")] use crate::executor::PipelineResult; use crate::extensions::{ ClientExtension, DelegationExtension, Extensions, RawCredentialsExtension, SecurityExtension, @@ -285,6 +286,7 @@ impl IdentityPayload { // -------- Host-side application helpers -------- + #[cfg(feature = "runtime")] /// Pull the resolved `IdentityPayload` out of a `PipelineResult` /// returned by `mgr.invoke_named::(...)`. Returns /// `None` when the pipeline was denied (no `modified_payload`) diff --git a/crates/cpex-core/src/lib.rs b/crates/cpex-core/src/lib.rs index 12378bfd..66bf4550 100644 --- a/crates/cpex-core/src/lib.rs +++ b/crates/cpex-core/src/lib.rs @@ -31,12 +31,20 @@ pub mod config; pub mod context; pub mod delegation; pub mod error; -pub mod executor; pub mod extensions; -pub mod factory; pub mod hooks; pub mod identity; -pub mod manager; pub mod plugin; + +// Runtime-only modules — require tokio, task spawning, orchestration. +// Excluded when building for WASM targets (use `default-features = false`). +#[cfg(feature = "runtime")] +pub mod executor; +#[cfg(feature = "runtime")] +pub mod factory; +#[cfg(feature = "runtime")] +pub mod manager; +#[cfg(feature = "runtime")] pub mod registry; +#[cfg(feature = "runtime")] pub mod visitor; From f93c675937b0f0f76b6568fa6a3aafd4752e3a6e Mon Sep 17 00:00:00 2001 From: Shriti Priya Date: Thu, 2 Jul 2026 19:19:08 -0400 Subject: [PATCH 5/7] feat: removing cpex-payload, adding feature gating in cpex-core Signed-off-by: Shriti Priya --- crates/cpex-payload/Cargo.toml | 26 - crates/cpex-payload/src/cmf/constants.rs | 65 - crates/cpex-payload/src/cmf/content.rs | 486 ------ crates/cpex-payload/src/cmf/enums.rs | 176 --- crates/cpex-payload/src/cmf/message.rs | 460 ------ crates/cpex-payload/src/cmf/mod.rs | 100 -- crates/cpex-payload/src/cmf/view.rs | 864 ----------- crates/cpex-payload/src/config.rs | 1297 ----------------- crates/cpex-payload/src/context.rs | 195 --- crates/cpex-payload/src/error.rs | 275 ---- crates/cpex-payload/src/extensions/agent.rs | 60 - .../cpex-payload/src/extensions/completion.rs | 71 - .../cpex-payload/src/extensions/container.rs | 711 --------- .../cpex-payload/src/extensions/delegation.rs | 165 --- crates/cpex-payload/src/extensions/filter.rs | 547 ------- .../cpex-payload/src/extensions/framework.rs | 38 - crates/cpex-payload/src/extensions/guarded.rs | 144 -- crates/cpex-payload/src/extensions/http.rs | 210 --- crates/cpex-payload/src/extensions/llm.rs | 27 - crates/cpex-payload/src/extensions/mcp.rs | 115 -- crates/cpex-payload/src/extensions/meta.rs | 45 - crates/cpex-payload/src/extensions/mod.rs | 52 - .../cpex-payload/src/extensions/monotonic.rs | 179 --- .../cpex-payload/src/extensions/provenance.rs | 27 - crates/cpex-payload/src/extensions/request.rs | 35 - .../cpex-payload/src/extensions/security.rs | 348 ----- crates/cpex-payload/src/extensions/tiers.rs | 100 -- crates/cpex-payload/src/hooks/macros.rs | 70 - crates/cpex-payload/src/hooks/mod.rs | 27 - crates/cpex-payload/src/hooks/payload.rs | 133 -- crates/cpex-payload/src/hooks/trait_def.rs | 322 ---- crates/cpex-payload/src/hooks/types.rs | 190 --- crates/cpex-payload/src/lib.rs | 32 - crates/cpex-payload/src/plugin.rs | 553 ------- .../src/plugins/identity_checker.rs | 75 - crates/cpex-payload/src/plugins/mod.rs | 1 - 36 files changed, 8221 deletions(-) delete mode 100644 crates/cpex-payload/Cargo.toml delete mode 100644 crates/cpex-payload/src/cmf/constants.rs delete mode 100644 crates/cpex-payload/src/cmf/content.rs delete mode 100644 crates/cpex-payload/src/cmf/enums.rs delete mode 100644 crates/cpex-payload/src/cmf/message.rs delete mode 100644 crates/cpex-payload/src/cmf/mod.rs delete mode 100644 crates/cpex-payload/src/cmf/view.rs delete mode 100644 crates/cpex-payload/src/config.rs delete mode 100644 crates/cpex-payload/src/context.rs delete mode 100644 crates/cpex-payload/src/error.rs delete mode 100644 crates/cpex-payload/src/extensions/agent.rs delete mode 100644 crates/cpex-payload/src/extensions/completion.rs delete mode 100644 crates/cpex-payload/src/extensions/container.rs delete mode 100644 crates/cpex-payload/src/extensions/delegation.rs delete mode 100644 crates/cpex-payload/src/extensions/filter.rs delete mode 100644 crates/cpex-payload/src/extensions/framework.rs delete mode 100644 crates/cpex-payload/src/extensions/guarded.rs delete mode 100644 crates/cpex-payload/src/extensions/http.rs delete mode 100644 crates/cpex-payload/src/extensions/llm.rs delete mode 100644 crates/cpex-payload/src/extensions/mcp.rs delete mode 100644 crates/cpex-payload/src/extensions/meta.rs delete mode 100644 crates/cpex-payload/src/extensions/mod.rs delete mode 100644 crates/cpex-payload/src/extensions/monotonic.rs delete mode 100644 crates/cpex-payload/src/extensions/provenance.rs delete mode 100644 crates/cpex-payload/src/extensions/request.rs delete mode 100644 crates/cpex-payload/src/extensions/security.rs delete mode 100644 crates/cpex-payload/src/extensions/tiers.rs delete mode 100644 crates/cpex-payload/src/hooks/macros.rs delete mode 100644 crates/cpex-payload/src/hooks/mod.rs delete mode 100644 crates/cpex-payload/src/hooks/payload.rs delete mode 100644 crates/cpex-payload/src/hooks/trait_def.rs delete mode 100644 crates/cpex-payload/src/hooks/types.rs delete mode 100644 crates/cpex-payload/src/lib.rs delete mode 100644 crates/cpex-payload/src/plugin.rs delete mode 100644 crates/cpex-payload/src/plugins/identity_checker.rs delete mode 100644 crates/cpex-payload/src/plugins/mod.rs diff --git a/crates/cpex-payload/Cargo.toml b/crates/cpex-payload/Cargo.toml deleted file mode 100644 index 6f11b5e3..00000000 --- a/crates/cpex-payload/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -# Location: ./crates/cpex-payload/Cargo.toml -# Copyright 2025 -# SPDX-License-Identifier: Apache-2.0 -# Authors: Teryl Taylor -# -# CPEX Payload — minimal types for WASM plugins. - -[package] -name = "cpex-payload" -description = "CPEX payload types — minimal types for WASM plugins." -version.workspace = true -edition.workspace = true -license.workspace = true -authors.workspace = true - -[dependencies] -# WASM-compatible tokio features only -tokio = { version = "1", features = ["sync", "macros", "io-util", "rt", "time"] } -serde = { workspace = true } -serde_json = { workspace = true } -serde_yaml = { workspace = true } -async-trait = { workspace = true } -thiserror = { workspace = true } -tracing = { workspace = true } -uuid = { workspace = true } -wildmatch = { workspace = true } diff --git a/crates/cpex-payload/src/cmf/constants.rs b/crates/cpex-payload/src/cmf/constants.rs deleted file mode 100644 index 12a8ac5e..00000000 --- a/crates/cpex-payload/src/cmf/constants.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Location: ./crates/cpex-core/src/cmf/constants.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// CMF constants — schema version, serialization field names, and defaults. - -/// Current CMF message schema version. -pub const SCHEMA_VERSION: &str = "2.0"; - -// --------------------------------------------------------------------------- -// Serialization field names for MessageView::to_dict() / to_opa_input() -// --------------------------------------------------------------------------- - -// Core view fields -pub const FIELD_KIND: &str = "kind"; -pub const FIELD_ROLE: &str = "role"; -pub const FIELD_IS_PRE: &str = "is_pre"; -pub const FIELD_IS_POST: &str = "is_post"; -pub const FIELD_ACTION: &str = "action"; -pub const FIELD_HOOK: &str = "hook"; -pub const FIELD_URI: &str = "uri"; -pub const FIELD_NAME: &str = "name"; -pub const FIELD_CONTENT: &str = "content"; -pub const FIELD_SIZE_BYTES: &str = "size_bytes"; -pub const FIELD_MIME_TYPE: &str = "mime_type"; -pub const FIELD_ARGUMENTS: &str = "arguments"; - -// Extensions container -pub const FIELD_EXTENSIONS: &str = "extensions"; - -// Subject fields -pub const FIELD_SUBJECT: &str = "subject"; -pub const FIELD_ID: &str = "id"; -pub const FIELD_TYPE: &str = "type"; -pub const FIELD_ROLES: &str = "roles"; -pub const FIELD_PERMISSIONS: &str = "permissions"; -pub const FIELD_TEAMS: &str = "teams"; - -// Security fields -pub const FIELD_LABELS: &str = "labels"; - -// Request fields -pub const FIELD_ENVIRONMENT: &str = "environment"; - -// HTTP fields -pub const FIELD_HEADERS: &str = "headers"; - -// Agent fields -pub const FIELD_AGENT: &str = "agent"; -pub const FIELD_INPUT: &str = "input"; -pub const FIELD_SESSION_ID: &str = "session_id"; -pub const FIELD_CONVERSATION_ID: &str = "conversation_id"; -pub const FIELD_TURN: &str = "turn"; -pub const FIELD_AGENT_ID: &str = "agent_id"; -pub const FIELD_PARENT_AGENT_ID: &str = "parent_agent_id"; - -// Meta fields -pub const FIELD_META: &str = "meta"; -pub const FIELD_ENTITY_TYPE: &str = "entity_type"; -pub const FIELD_ENTITY_NAME: &str = "entity_name"; -pub const FIELD_TAGS: &str = "tags"; - -// OPA envelope -pub const FIELD_OPA_INPUT: &str = "input"; diff --git a/crates/cpex-payload/src/cmf/content.rs b/crates/cpex-payload/src/cmf/content.rs deleted file mode 100644 index 3cde9b65..00000000 --- a/crates/cpex-payload/src/cmf/content.rs +++ /dev/null @@ -1,486 +0,0 @@ -// Location: ./crates/cpex-core/src/cmf/content.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// CMF domain objects and ContentPart hierarchy. -// -// Domain objects (ToolCall, Resource, etc.) are standalone structs -// reusable outside of message content parts. ContentPart is a tagged -// enum that wraps them for message serialization. -// -// Mirrors the Python types in cpex/framework/cmf/message.py. - -use std::collections::HashMap; - -use serde::{Deserialize, Serialize}; - -use super::enums::ResourceType; -use super::message::Message; - -// --------------------------------------------------------------------------- -// Domain Objects -// --------------------------------------------------------------------------- - -/// Normalized tool/function invocation request. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ToolCall { - /// Unique request correlation ID. - pub tool_call_id: String, - /// Tool name. - pub name: String, - /// Arguments as a JSON-serializable map. - #[serde(default)] - pub arguments: HashMap, - /// Optional namespace for namespaced tools. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub namespace: Option, -} - -/// Result from tool execution. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ToolResult { - /// Correlation ID linking to the corresponding tool call. - pub tool_call_id: String, - /// Name of the tool that was executed. - pub tool_name: String, - /// Result content (any JSON-serializable value). - #[serde(default)] - pub content: serde_json::Value, - /// Whether the result represents an error. - #[serde(default)] - pub is_error: bool, -} - -/// Embedded resource with content (MCP). -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct Resource { - /// Unique request correlation ID. - pub resource_request_id: String, - /// Unique identifier in URI format. - pub uri: String, - /// Human-readable name. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub name: Option, - /// What this resource contains. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub description: Option, - /// The kind of resource. - pub resource_type: ResourceType, - /// Text content if embedded. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub content: Option, - /// Binary content if embedded (base64 in JSON). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub blob: Option>, - /// MIME type of content. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub mime_type: Option, - /// Size information. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub size_bytes: Option, - /// Metadata (classification, retention, etc.). - #[serde(default, skip_serializing_if = "HashMap::is_empty")] - pub annotations: HashMap, - /// Version tracking. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub version: Option, -} - -impl Resource { - /// Whether content or blob is embedded. - pub fn is_embedded(&self) -> bool { - self.content.is_some() || self.blob.is_some() - } - - /// Get text content if available. - pub fn get_text_content(&self) -> Option<&str> { - self.content.as_deref() - } -} - -/// Lightweight resource reference without embedded content. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ResourceReference { - /// Correlation ID linking to the originating resource request. - pub resource_request_id: String, - /// Resource URI. - pub uri: String, - /// Human-readable name. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub name: Option, - /// Type of resource. - pub resource_type: ResourceType, - /// Line number or byte offset for partial references. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub range_start: Option, - /// End of range. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub range_end: Option, - /// CSS/XPath/JSONPath selector. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub selector: Option, -} - -/// Prompt template invocation request (MCP). -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PromptRequest { - /// Request ID for correlation. - pub prompt_request_id: String, - /// Prompt template name. - pub name: String, - /// Arguments to pass to the template. - #[serde(default)] - pub arguments: HashMap, - /// Source server for multi-server scenarios. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub server_id: Option, -} - -/// Rendered prompt template result. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PromptResult { - /// ID of the corresponding prompt request. - pub prompt_request_id: String, - /// Name of the prompt that was rendered. - pub prompt_name: String, - /// Rendered messages (prompts produce messages). - #[serde(default)] - pub messages: Vec, - /// Single text result for simple prompts. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub content: Option, - /// Whether rendering failed. - #[serde(default)] - pub is_error: bool, - /// Error details if rendering failed. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub error_message: Option, -} - -// --------------------------------------------------------------------------- -// Media Source Types -// --------------------------------------------------------------------------- - -/// Image source data. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ImageSource { - /// Source type: "url" or "base64". - #[serde(rename = "type")] - pub source_type: String, - /// URL or base64-encoded string. - pub data: String, - /// MIME type (e.g., image/jpeg). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub media_type: Option, -} - -/// Video source data. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct VideoSource { - /// Source type: "url" or "base64". - #[serde(rename = "type")] - pub source_type: String, - /// URL or base64-encoded string. - pub data: String, - /// MIME type (e.g., video/mp4). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub media_type: Option, - /// Duration in milliseconds. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub duration_ms: Option, -} - -/// Audio source data. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct AudioSource { - /// Source type: "url" or "base64". - #[serde(rename = "type")] - pub source_type: String, - /// URL or base64-encoded string. - pub data: String, - /// MIME type (e.g., audio/mp3). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub media_type: Option, - /// Duration in milliseconds. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub duration_ms: Option, -} - -/// Document source data (PDF, Word, etc.). -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DocumentSource { - /// Source type: "url" or "base64". - #[serde(rename = "type")] - pub source_type: String, - /// URL or base64-encoded string. - pub data: String, - /// MIME type (e.g., application/pdf). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub media_type: Option, - /// Document title. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub title: Option, -} - -// --------------------------------------------------------------------------- -// ContentPart — Tagged Enum -// --------------------------------------------------------------------------- - -/// A typed content part in a CMF message. -/// -/// Discriminated by the `content_type` field. Each variant wraps -/// either a text string or a domain object. -/// -/// Mirrors the Python `ContentPartUnion` discriminated union in -/// `cpex/framework/cmf/message.py`. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "content_type")] -pub enum ContentPart { - /// Plain text content. - #[serde(rename = "text")] - Text { text: String }, - - /// Chain-of-thought reasoning. - #[serde(rename = "thinking")] - Thinking { text: String }, - - /// Tool/function invocation request. - #[serde(rename = "tool_call")] - ToolCall { content: ToolCall }, - - /// Result from tool execution. - #[serde(rename = "tool_result")] - ToolResult { content: ToolResult }, - - /// Embedded resource with content. - #[serde(rename = "resource")] - Resource { content: Resource }, - - /// Lightweight resource reference. - #[serde(rename = "resource_ref")] - ResourceRef { content: ResourceReference }, - - /// Prompt template invocation request. - #[serde(rename = "prompt_request")] - PromptRequest { content: PromptRequest }, - - /// Rendered prompt template result. - #[serde(rename = "prompt_result")] - PromptResult { content: PromptResult }, - - /// Image content. - #[serde(rename = "image")] - Image { content: ImageSource }, - - /// Video content. - #[serde(rename = "video")] - Video { content: VideoSource }, - - /// Audio content. - #[serde(rename = "audio")] - Audio { content: AudioSource }, - - /// Document content. - #[serde(rename = "document")] - Document { content: DocumentSource }, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_text_content_part_serde() { - let json = r#"{"content_type":"text","text":"Hello, world!"}"#; - let part: ContentPart = serde_json::from_str(json).unwrap(); - match &part { - ContentPart::Text { text } => assert_eq!(text, "Hello, world!"), - _ => panic!("expected Text variant"), - } - let roundtrip = serde_json::to_string(&part).unwrap(); - let part2: ContentPart = serde_json::from_str(&roundtrip).unwrap(); - match part2 { - ContentPart::Text { text } => assert_eq!(text, "Hello, world!"), - _ => panic!("expected Text variant"), - } - } - - #[test] - fn test_tool_call_content_part_serde() { - let json = r#"{ - "content_type": "tool_call", - "content": { - "tool_call_id": "tc_001", - "name": "get_weather", - "arguments": {"city": "London"} - } - }"#; - let part: ContentPart = serde_json::from_str(json).unwrap(); - match &part { - ContentPart::ToolCall { content } => { - assert_eq!(content.name, "get_weather"); - assert_eq!(content.tool_call_id, "tc_001"); - assert_eq!(content.arguments["city"], "London"); - } - _ => panic!("expected ToolCall variant"), - } - } - - #[test] - fn test_tool_result_content_part_serde() { - let json = r#"{ - "content_type": "tool_result", - "content": { - "tool_call_id": "tc_001", - "tool_name": "get_weather", - "content": {"temp": 20, "unit": "C"}, - "is_error": false - } - }"#; - let part: ContentPart = serde_json::from_str(json).unwrap(); - match &part { - ContentPart::ToolResult { content } => { - assert_eq!(content.tool_name, "get_weather"); - assert!(!content.is_error); - } - _ => panic!("expected ToolResult variant"), - } - } - - #[test] - fn test_resource_content_part_serde() { - let json = r#"{ - "content_type": "resource", - "content": { - "resource_request_id": "rr_001", - "uri": "file:///data.txt", - "resource_type": "file", - "content": "Hello from file" - } - }"#; - let part: ContentPart = serde_json::from_str(json).unwrap(); - match &part { - ContentPart::Resource { content } => { - assert_eq!(content.uri, "file:///data.txt"); - assert!(content.is_embedded()); - assert_eq!(content.get_text_content(), Some("Hello from file")); - } - _ => panic!("expected Resource variant"), - } - } - - #[test] - fn test_resource_ref_content_part_serde() { - let json = r#"{ - "content_type": "resource_ref", - "content": { - "resource_request_id": "rr_002", - "uri": "db://users/42", - "resource_type": "database" - } - }"#; - let part: ContentPart = serde_json::from_str(json).unwrap(); - match &part { - ContentPart::ResourceRef { content } => { - assert_eq!(content.uri, "db://users/42"); - assert_eq!(content.resource_type, ResourceType::Database); - } - _ => panic!("expected ResourceRef variant"), - } - } - - #[test] - fn test_image_content_part_serde() { - let json = r#"{ - "content_type": "image", - "content": { - "type": "url", - "data": "https://example.com/photo.jpg", - "media_type": "image/jpeg" - } - }"#; - let part: ContentPart = serde_json::from_str(json).unwrap(); - match &part { - ContentPart::Image { content } => { - assert_eq!(content.source_type, "url"); - assert_eq!(content.data, "https://example.com/photo.jpg"); - } - _ => panic!("expected Image variant"), - } - } - - #[test] - fn test_prompt_request_content_part_serde() { - let json = r#"{ - "content_type": "prompt_request", - "content": { - "prompt_request_id": "pr_001", - "name": "summarize", - "arguments": {"text": "Long document..."} - } - }"#; - let part: ContentPart = serde_json::from_str(json).unwrap(); - match &part { - ContentPart::PromptRequest { content } => { - assert_eq!(content.name, "summarize"); - } - _ => panic!("expected PromptRequest variant"), - } - } - - #[test] - fn test_thinking_content_part_serde() { - let json = r#"{"content_type":"thinking","text":"Let me analyze..."}"#; - let part: ContentPart = serde_json::from_str(json).unwrap(); - match &part { - ContentPart::Thinking { text } => assert_eq!(text, "Let me analyze..."), - _ => panic!("expected Thinking variant"), - } - } - - #[test] - fn test_tool_call_construction() { - let tc = ToolCall { - tool_call_id: "tc_001".into(), - name: "search".into(), - arguments: [("query".to_string(), serde_json::json!("rust"))].into(), - namespace: None, - }; - assert_eq!(tc.name, "search"); - assert_eq!(tc.arguments["query"], "rust"); - } - - #[test] - fn test_resource_is_embedded() { - let embedded = Resource { - resource_request_id: "rr_001".into(), - uri: "file:///data.txt".into(), - name: None, - description: None, - resource_type: ResourceType::File, - content: Some("data".into()), - blob: None, - mime_type: None, - size_bytes: None, - annotations: HashMap::new(), - version: None, - }; - assert!(embedded.is_embedded()); - - let not_embedded = Resource { - resource_request_id: "rr_002".into(), - uri: "file:///other.txt".into(), - name: None, - description: None, - resource_type: ResourceType::File, - content: None, - blob: None, - mime_type: None, - size_bytes: None, - annotations: HashMap::new(), - version: None, - }; - assert!(!not_embedded.is_embedded()); - } -} diff --git a/crates/cpex-payload/src/cmf/enums.rs b/crates/cpex-payload/src/cmf/enums.rs deleted file mode 100644 index 97f1dcc2..00000000 --- a/crates/cpex-payload/src/cmf/enums.rs +++ /dev/null @@ -1,176 +0,0 @@ -// Location: ./crates/cpex-core/src/cmf/enums.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// CMF enums — Role, Channel, ContentType, ResourceType. -// -// Mirrors the Python enums in cpex/framework/cmf/message.py. -// All use snake_case serialization to match Python string values. - -use serde::{Deserialize, Serialize}; - -// --------------------------------------------------------------------------- -// Role -// --------------------------------------------------------------------------- - -/// Identifies WHO is speaking in a conversation turn. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum Role { - /// System-level instructions. - System, - /// Developer-provided instructions. - Developer, - /// Human user input. - User, - /// LLM/agent response. - Assistant, - /// Tool execution result. - Tool, -} - -// --------------------------------------------------------------------------- -// Channel -// --------------------------------------------------------------------------- - -/// Classifies the kind of output a message represents. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum Channel { - /// Intermediate analytical output (chain-of-thought). - Analysis, - /// Meta-level observations about the task. - Commentary, - /// Terminal response intended for delivery. - Final, -} - -// --------------------------------------------------------------------------- -// ContentType -// --------------------------------------------------------------------------- - -/// Discriminator for the typed ContentPart hierarchy. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum ContentType { - /// Plain text content. - Text, - /// Chain-of-thought reasoning. - Thinking, - /// Tool/function invocation request. - ToolCall, - /// Result from tool execution. - ToolResult, - /// Embedded resource with content (MCP). - Resource, - /// Lightweight resource reference without embedded content. - ResourceRef, - /// Prompt template invocation request (MCP). - PromptRequest, - /// Rendered prompt template result. - PromptResult, - /// Image content (URL or base64). - Image, - /// Video content (URL or base64). - Video, - /// Audio content (URL or base64). - Audio, - /// Document content (PDF, Word, etc.). - Document, -} - -// --------------------------------------------------------------------------- -// ResourceType -// --------------------------------------------------------------------------- - -/// Type of resource being referenced. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum ResourceType { - /// File-system resource. - #[default] - File, - /// Binary large object. - Blob, - /// Generic URI-addressable resource. - Uri, - /// Database entity. - Database, - /// API endpoint. - Api, - /// In-memory or ephemeral resource. - Memory, - /// Produced artifact (generated output, build result). - Artifact, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_role_serde_roundtrip() { - let role = Role::Assistant; - let json = serde_json::to_string(&role).unwrap(); - assert_eq!(json, "\"assistant\""); - let deserialized: Role = serde_json::from_str(&json).unwrap(); - assert_eq!(deserialized, Role::Assistant); - } - - #[test] - fn test_channel_serde_roundtrip() { - let channel = Channel::Final; - let json = serde_json::to_string(&channel).unwrap(); - assert_eq!(json, "\"final\""); - let deserialized: Channel = serde_json::from_str(&json).unwrap(); - assert_eq!(deserialized, Channel::Final); - } - - #[test] - fn test_content_type_serde_roundtrip() { - let ct = ContentType::ToolCall; - let json = serde_json::to_string(&ct).unwrap(); - assert_eq!(json, "\"tool_call\""); - let deserialized: ContentType = serde_json::from_str(&json).unwrap(); - assert_eq!(deserialized, ContentType::ToolCall); - } - - #[test] - fn test_content_type_resource_ref() { - let ct = ContentType::ResourceRef; - let json = serde_json::to_string(&ct).unwrap(); - assert_eq!(json, "\"resource_ref\""); - } - - #[test] - fn test_content_type_prompt_variants() { - let req = ContentType::PromptRequest; - let res = ContentType::PromptResult; - assert_eq!(serde_json::to_string(&req).unwrap(), "\"prompt_request\""); - assert_eq!(serde_json::to_string(&res).unwrap(), "\"prompt_result\""); - } - - #[test] - fn test_resource_type_serde_roundtrip() { - let rt = ResourceType::Database; - let json = serde_json::to_string(&rt).unwrap(); - assert_eq!(json, "\"database\""); - let deserialized: ResourceType = serde_json::from_str(&json).unwrap(); - assert_eq!(deserialized, ResourceType::Database); - } - - #[test] - fn test_all_roles_deserialize() { - for (s, expected) in &[ - ("\"system\"", Role::System), - ("\"developer\"", Role::Developer), - ("\"user\"", Role::User), - ("\"assistant\"", Role::Assistant), - ("\"tool\"", Role::Tool), - ] { - let role: Role = serde_json::from_str(s).unwrap(); - assert_eq!(role, *expected); - } - } -} diff --git a/crates/cpex-payload/src/cmf/message.rs b/crates/cpex-payload/src/cmf/message.rs deleted file mode 100644 index b2bad350..00000000 --- a/crates/cpex-payload/src/cmf/message.rs +++ /dev/null @@ -1,460 +0,0 @@ -// Location: ./crates/cpex-core/src/cmf/message.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// CMF Message — canonical message representation. -// -// A Message is the storage and wire format for a single turn in a -// conversation. It preserves structure exactly as the LLM or -// framework sent it. -// -// Extensions are NOT part of the Message. They are passed separately -// to handlers via the framework's Extensions type. This allows -// extensions to be shared across payload types and avoids copying -// the message when extensions change. -// -// Mirrors the Python Message in cpex/framework/cmf/message.py. - -use serde::{Deserialize, Serialize}; - -use super::content::*; -use super::enums::{Channel, Role}; -use crate::hooks::trait_def::PluginResult; - -// --------------------------------------------------------------------------- -// Message -// --------------------------------------------------------------------------- - -/// Canonical CMF message representing a single turn in a conversation. -/// -/// All content is carried as typed ContentPart variants. Extensions -/// (identity, security, HTTP, agent context) are passed separately -/// to handlers — not inside the message. -/// -/// Mirrors the Python `Message` in `cpex/framework/cmf/message.py`. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Message { - /// Message schema version. - #[serde(default = "default_schema_version")] - pub schema_version: String, - - /// Who is speaking. - pub role: Role, - - /// List of typed content parts (multimodal). - #[serde(default)] - pub content: Vec, - - /// Optional output classification. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub channel: Option, -} - -fn default_schema_version() -> String { - super::constants::SCHEMA_VERSION.to_string() -} - -impl Message { - /// Create a simple text message. - pub fn text(role: Role, text: impl Into) -> Self { - Self { - schema_version: super::constants::SCHEMA_VERSION.to_string(), - role, - content: vec![ContentPart::Text { text: text.into() }], - channel: None, - } - } - - /// Extract all text content from the message. - /// - /// Concatenates text from all `Text` content parts. - pub fn get_text_content(&self) -> String { - let mut texts = Vec::new(); - for part in &self.content { - if let ContentPart::Text { text } = part { - texts.push(text.as_str()); - } - } - texts.join("") - } - - /// Extract thinking/reasoning content if present. - pub fn get_thinking_content(&self) -> Option { - let mut texts = Vec::new(); - for part in &self.content { - if let ContentPart::Thinking { text } = part { - texts.push(text.as_str()); - } - } - if texts.is_empty() { - None - } else { - Some(texts.join("")) - } - } - - /// Get all tool calls in this message. - pub fn get_tool_calls(&self) -> Vec<&ToolCall> { - self.content - .iter() - .filter_map(|part| match part { - ContentPart::ToolCall { content } => Some(content), - _ => None, - }) - .collect() - } - - /// Get all tool results in this message. - pub fn get_tool_results(&self) -> Vec<&ToolResult> { - self.content - .iter() - .filter_map(|part| match part { - ContentPart::ToolResult { content } => Some(content), - _ => None, - }) - .collect() - } - - /// Whether this message contains any tool calls. - pub fn is_tool_call(&self) -> bool { - self.content - .iter() - .any(|p| matches!(p, ContentPart::ToolCall { .. })) - } - - /// Whether this message contains any tool results. - pub fn is_tool_result(&self) -> bool { - self.content - .iter() - .any(|p| matches!(p, ContentPart::ToolResult { .. })) - } - - /// Get all embedded resources in this message. - pub fn get_resources(&self) -> Vec<&Resource> { - self.content - .iter() - .filter_map(|part| match part { - ContentPart::Resource { content } => Some(content), - _ => None, - }) - .collect() - } - - /// Get all resource references in this message. - pub fn get_resource_refs(&self) -> Vec<&ResourceReference> { - self.content - .iter() - .filter_map(|part| match part { - ContentPart::ResourceRef { content } => Some(content), - _ => None, - }) - .collect() - } - - /// Get all resource URIs (both embedded and references). - pub fn get_all_resource_uris(&self) -> Vec<&str> { - self.content - .iter() - .filter_map(|part| match part { - ContentPart::Resource { content } => Some(content.uri.as_str()), - ContentPart::ResourceRef { content } => Some(content.uri.as_str()), - _ => None, - }) - .collect() - } - - /// Whether this message contains any resources or resource references. - pub fn has_resources(&self) -> bool { - self.content.iter().any(|p| { - matches!( - p, - ContentPart::Resource { .. } | ContentPart::ResourceRef { .. } - ) - }) - } - - /// Get all prompt requests in this message. - pub fn get_prompt_requests(&self) -> Vec<&PromptRequest> { - self.content - .iter() - .filter_map(|part| match part { - ContentPart::PromptRequest { content } => Some(content), - _ => None, - }) - .collect() - } - - /// Get all prompt results in this message. - pub fn get_prompt_results(&self) -> Vec<&PromptResult> { - self.content - .iter() - .filter_map(|part| match part { - ContentPart::PromptResult { content } => Some(content), - _ => None, - }) - .collect() - } -} - -// --------------------------------------------------------------------------- -// MessagePayload — PluginPayload wrapper -// --------------------------------------------------------------------------- - -/// CMF Message wrapped as a PluginPayload for hook dispatch. -/// -/// This is the payload type for all `cmf.*` hooks. Plugins that -/// handle CMF hooks implement `HookHandler` and receive -/// `&MessagePayload` in their handler. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MessagePayload { - /// The CMF message. - pub message: Message, -} - -crate::impl_plugin_payload!(MessagePayload); - -// --------------------------------------------------------------------------- -// CmfHook — Hook Type Definition -// --------------------------------------------------------------------------- - -crate::define_hook! { - /// CMF message evaluation hook. - /// - /// Plugins implement `HookHandler` and register under - /// one or more `cmf.*` hook names (e.g., `cmf.tool_pre_invoke`, - /// `cmf.llm_input`). The same handler covers all CMF hook points. - CmfHook, "cmf" => { - payload: MessagePayload, - result: PluginResult, - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::hooks::payload::PluginPayload; - use crate::hooks::trait_def::HookTypeDef; - - #[test] - fn test_message_text_helper() { - let msg = Message::text(Role::User, "What is the weather?"); - assert_eq!(msg.get_text_content(), "What is the weather?"); - assert_eq!(msg.role, Role::User); - assert_eq!(msg.schema_version, "2.0"); - } - - #[test] - fn test_message_multi_part_text() { - let msg = Message { - schema_version: "2.0".into(), - role: Role::Assistant, - content: vec![ - ContentPart::Text { - text: "Hello ".into(), - }, - ContentPart::Text { - text: "world!".into(), - }, - ], - channel: None, - }; - assert_eq!(msg.get_text_content(), "Hello world!"); - } - - #[test] - fn test_message_thinking_content() { - let msg = Message { - schema_version: "2.0".into(), - role: Role::Assistant, - content: vec![ - ContentPart::Thinking { - text: "Let me think...".into(), - }, - ContentPart::Text { - text: "Here's my answer.".into(), - }, - ], - channel: Some(Channel::Final), - }; - assert_eq!( - msg.get_thinking_content(), - Some("Let me think...".to_string()) - ); - assert_eq!(msg.get_text_content(), "Here's my answer."); - } - - #[test] - fn test_message_tool_calls() { - let msg = Message { - schema_version: "2.0".into(), - role: Role::Assistant, - content: vec![ - ContentPart::Text { - text: "Let me check.".into(), - }, - ContentPart::ToolCall { - content: ToolCall { - tool_call_id: "tc_001".into(), - name: "get_weather".into(), - arguments: [("city".to_string(), serde_json::json!("London"))].into(), - namespace: None, - }, - }, - ContentPart::ToolCall { - content: ToolCall { - tool_call_id: "tc_002".into(), - name: "get_time".into(), - arguments: [("timezone".to_string(), serde_json::json!("UTC"))].into(), - namespace: None, - }, - }, - ], - channel: None, - }; - assert!(msg.is_tool_call()); - assert!(!msg.is_tool_result()); - let calls = msg.get_tool_calls(); - assert_eq!(calls.len(), 2); - assert_eq!(calls[0].name, "get_weather"); - assert_eq!(calls[1].name, "get_time"); - } - - #[test] - fn test_message_tool_results() { - let msg = Message { - schema_version: "2.0".into(), - role: Role::Tool, - content: vec![ContentPart::ToolResult { - content: ToolResult { - tool_call_id: "tc_001".into(), - tool_name: "get_weather".into(), - content: serde_json::json!({"temp": 20}), - is_error: false, - }, - }], - channel: None, - }; - assert!(msg.is_tool_result()); - assert!(!msg.is_tool_call()); - let results = msg.get_tool_results(); - assert_eq!(results.len(), 1); - assert_eq!(results[0].tool_name, "get_weather"); - } - - #[test] - fn test_message_resources() { - let msg = Message { - schema_version: "2.0".into(), - role: Role::Assistant, - content: vec![ - ContentPart::Resource { - content: Resource { - resource_request_id: "rr_001".into(), - uri: "file:///data.txt".into(), - name: Some("Data File".into()), - description: None, - resource_type: super::super::enums::ResourceType::File, - content: Some("file contents".into()), - blob: None, - mime_type: None, - size_bytes: None, - annotations: std::collections::HashMap::new(), - version: None, - }, - }, - ContentPart::ResourceRef { - content: ResourceReference { - resource_request_id: "rr_002".into(), - uri: "db://users/42".into(), - name: None, - resource_type: super::super::enums::ResourceType::Database, - range_start: None, - range_end: None, - selector: None, - }, - }, - ], - channel: None, - }; - assert!(msg.has_resources()); - assert_eq!(msg.get_resources().len(), 1); - assert_eq!(msg.get_resource_refs().len(), 1); - let uris = msg.get_all_resource_uris(); - assert_eq!(uris.len(), 2); - assert!(uris.contains(&"file:///data.txt")); - assert!(uris.contains(&"db://users/42")); - } - - #[test] - fn test_message_no_resources() { - let msg = Message::text(Role::User, "Hello"); - assert!(!msg.has_resources()); - assert!(msg.get_resources().is_empty()); - } - - #[test] - fn test_message_serde_roundtrip() { - let msg = Message { - schema_version: "2.0".into(), - role: Role::Assistant, - content: vec![ - ContentPart::Thinking { - text: "Analyzing...".into(), - }, - ContentPart::Text { - text: "Here's the answer.".into(), - }, - ContentPart::ToolCall { - content: ToolCall { - tool_call_id: "tc_001".into(), - name: "search".into(), - arguments: [("q".to_string(), serde_json::json!("rust"))].into(), - namespace: None, - }, - }, - ], - channel: Some(Channel::Final), - }; - - let json = serde_json::to_string(&msg).unwrap(); - let deserialized: Message = serde_json::from_str(&json).unwrap(); - - assert_eq!(deserialized.role, Role::Assistant); - assert_eq!(deserialized.schema_version, "2.0"); - assert_eq!(deserialized.channel, Some(Channel::Final)); - assert_eq!(deserialized.content.len(), 3); - assert_eq!(deserialized.get_text_content(), "Here's the answer."); - assert_eq!(deserialized.get_tool_calls().len(), 1); - } - - #[test] - fn test_message_payload_as_plugin_payload() { - let payload = MessagePayload { - message: Message::text(Role::User, "Hello"), - }; - - // Test clone_boxed - let boxed: Box = Box::new(payload.clone()); - let cloned = boxed.clone_boxed(); - - // Test as_any downcast - let downcasted = cloned - .as_any() - .downcast_ref::() - .expect("should downcast to MessagePayload"); - assert_eq!(downcasted.message.get_text_content(), "Hello"); - } - - #[test] - fn test_cmf_hook_type_def() { - assert_eq!(CmfHook::NAME, "cmf"); - } - - #[test] - fn test_message_default_schema_version() { - let json = r#"{"role":"user","content":[]}"#; - let msg: Message = serde_json::from_str(json).unwrap(); - assert_eq!(msg.schema_version, "2.0"); - } -} diff --git a/crates/cpex-payload/src/cmf/mod.rs b/crates/cpex-payload/src/cmf/mod.rs deleted file mode 100644 index 11ae76a4..00000000 --- a/crates/cpex-payload/src/cmf/mod.rs +++ /dev/null @@ -1,100 +0,0 @@ -// Location: ./crates/cpex-core/src/cmf/mod.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// ContextForge Message Format (CMF). -// -// Canonical message representation for interactions between users, -// agents, tools, and language models. All models mirror the Python -// CMF in cpex/framework/cmf/message.py. -// -// Extensions are NOT part of the Message — they are passed separately -// to handlers via the framework's Extensions type in hooks/payload.rs. -// This allows extensions to be shared across payload types and avoids -// copying the message when extensions change. -// -// # Hook Registration Patterns -// -// CMF supports two registration patterns for plugins: -// -// ## Pattern 1: One handler, multiple hook names (recommended) -// -// Use `CmfHook` as the hook type and register under multiple names. -// The plugin writes one handler that covers all CMF hooks. The host -// invokes via `invoke_by_name("cmf.tool_pre_invoke", ...)`. -// -// ```rust,ignore -// // Plugin implements one handler: -// impl HookHandler for MyPlugin { -// fn handle(&self, payload: &MessagePayload, ext: &Extensions, ctx: &mut PluginContext) -// -> PluginResult { ... } -// } -// -// // Factory registers under multiple names: -// PluginInstance { -// plugin: plugin.clone(), -// handlers: vec![ -// ("cmf.tool_pre_invoke", Arc::new(TypedHandlerAdapter::::new(plugin.clone()))), -// ("cmf.tool_post_invoke", Arc::new(TypedHandlerAdapter::::new(plugin))), -// ], -// } -// -// // Host invokes via invoke_named — compile-time payload type safety -// // plus runtime hook name routing: -// mgr.invoke_named::( -// "cmf.tool_pre_invoke", payload, ext, None, -// ).await; -// ``` -// -// `invoke_named::(hook_name, ...)` gives you both: -// - **Compile-time**: payload must be `MessagePayload` (from `CmfHook::Payload`) -// - **Runtime**: dispatches to plugins registered under the specific hook name -// -// This is the recommended approach for CMF hooks. Alternatively, use -// `invoke_by_name(hook_name, boxed_payload, ...)` for fully dynamic -// dispatch (no compile-time payload check). -// -// ## Pattern 2: Individual hook types (optional) -// -// For hosts that want per-hook marker types, define separate hook -// types. Each maps to one hook name. The plugin must implement a -// handler per type (more boilerplate). -// -// ```rust,ignore -// define_hook! { -// CmfToolPreInvoke, "cmf.tool_pre_invoke" => { -// payload: MessagePayload, -// result: PluginResult, -// } -// } -// -// // Plugin implements per-hook handlers: -// impl HookHandler for MyPlugin { ... } -// impl HookHandler for MyPlugin { ... } -// -// // Host uses typed invoke: -// mgr.invoke::(payload, ext, None).await; -// ``` -// -// Both patterns use the same executor, registry, and capabilities. -// Pattern 1 with `invoke_named` is recommended — one handler impl, -// compile-time payload safety, and explicit hook name routing. -// -// Available CMF hook names (defined in hooks/types.rs): -// cmf.tool_pre_invoke, cmf.tool_post_invoke, -// cmf.llm_input, cmf.llm_output, -// cmf.prompt_pre_fetch, cmf.prompt_post_fetch, -// cmf.resource_pre_fetch, cmf.resource_post_fetch - -pub mod constants; -pub mod content; -pub mod enums; -pub mod message; -pub mod view; - -// Re-export key types at the cmf module level -pub use content::*; -pub use enums::*; -pub use message::{CmfHook, Message, MessagePayload}; -pub use view::{MessageView, ViewAction, ViewKind}; diff --git a/crates/cpex-payload/src/cmf/view.rs b/crates/cpex-payload/src/cmf/view.rs deleted file mode 100644 index 2c92e76d..00000000 --- a/crates/cpex-payload/src/cmf/view.rs +++ /dev/null @@ -1,864 +0,0 @@ -// Location: ./crates/cpex-core/src/cmf/view.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// MessageView — read-only projection for policy evaluation. -// -// Decomposes a Message into individually addressable views with a -// uniform interface regardless of content type. Zero-copy design — -// properties are computed on-demand by borrowing the underlying -// content part and extensions directly. -// -// Mirrors the Python MessageView in cpex/framework/cmf/view.py. - -use serde::{Deserialize, Serialize}; - -use super::content::*; -use super::enums::{ContentType, Role}; -use super::message::Message; -use crate::hooks::payload::Extensions; - -// --------------------------------------------------------------------------- -// Enums -// --------------------------------------------------------------------------- - -/// Type of content a view represents. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum ViewKind { - Text, - Thinking, - ToolCall, - ToolResult, - Resource, - ResourceRef, - PromptRequest, - PromptResult, - Image, - Video, - Audio, - Document, -} - -/// The action this content represents in the data flow. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum ViewAction { - Read, - Write, - Execute, - Invoke, - Send, - Receive, - Generate, -} - -impl ViewKind { - /// Map ContentType to ViewKind. - pub fn from_content_type(ct: ContentType) -> Self { - match ct { - ContentType::Text => ViewKind::Text, - ContentType::Thinking => ViewKind::Thinking, - ContentType::ToolCall => ViewKind::ToolCall, - ContentType::ToolResult => ViewKind::ToolResult, - ContentType::Resource => ViewKind::Resource, - ContentType::ResourceRef => ViewKind::ResourceRef, - ContentType::PromptRequest => ViewKind::PromptRequest, - ContentType::PromptResult => ViewKind::PromptResult, - ContentType::Image => ViewKind::Image, - ContentType::Video => ViewKind::Video, - ContentType::Audio => ViewKind::Audio, - ContentType::Document => ViewKind::Document, - } - } - - /// The default action for this kind of content. - pub fn default_action(&self, role: Role) -> ViewAction { - match self { - ViewKind::ToolCall => ViewAction::Execute, - ViewKind::ToolResult => ViewAction::Receive, - ViewKind::Resource | ViewKind::ResourceRef => ViewAction::Read, - ViewKind::PromptRequest => ViewAction::Invoke, - ViewKind::PromptResult => ViewAction::Receive, - // Direction-dependent kinds - ViewKind::Text - | ViewKind::Thinking - | ViewKind::Image - | ViewKind::Video - | ViewKind::Audio - | ViewKind::Document => match role { - Role::User => ViewAction::Send, - Role::Assistant => ViewAction::Generate, - Role::Tool => ViewAction::Receive, - Role::System | Role::Developer => ViewAction::Write, - }, - } - } - - /// Whether this is a tool-related kind. - pub fn is_tool(&self) -> bool { - matches!(self, ViewKind::ToolCall | ViewKind::ToolResult) - } - - /// Whether this is a resource-related kind. - pub fn is_resource(&self) -> bool { - matches!(self, ViewKind::Resource | ViewKind::ResourceRef) - } - - /// Whether this is a prompt-related kind. - pub fn is_prompt(&self) -> bool { - matches!(self, ViewKind::PromptRequest | ViewKind::PromptResult) - } - - /// Whether this is a media kind (image, video, audio, document). - pub fn is_media(&self) -> bool { - matches!( - self, - ViewKind::Image | ViewKind::Video | ViewKind::Audio | ViewKind::Document - ) - } - - /// Whether this is a text kind (text or thinking). - pub fn is_text(&self) -> bool { - matches!(self, ViewKind::Text | ViewKind::Thinking) - } -} - -// --------------------------------------------------------------------------- -// MessageView -// --------------------------------------------------------------------------- - -/// Read-only, zero-copy view over a single content part. -/// -/// Provides a uniform interface for policy evaluation regardless -/// of content type. Properties are computed on-demand by borrowing -/// the underlying content part and extensions. -/// -/// Produced by `Message::iter_views()` or the standalone `iter_views()`. -pub struct MessageView<'a> { - /// The underlying content part. - part: &'a ContentPart, - /// The kind of content. - kind: ViewKind, - /// The parent message role. - role: Role, - /// Optional hook location (e.g., "tool_pre_invoke"). - hook: Option<&'a str>, - /// Optional extensions (for security/http context). - extensions: Option<&'a Extensions>, -} - -impl<'a> MessageView<'a> { - /// Create a new view over a content part. - pub fn new( - part: &'a ContentPart, - role: Role, - hook: Option<&'a str>, - extensions: Option<&'a Extensions>, - ) -> Self { - let kind = match part { - ContentPart::Text { .. } => ViewKind::Text, - ContentPart::Thinking { .. } => ViewKind::Thinking, - ContentPart::ToolCall { .. } => ViewKind::ToolCall, - ContentPart::ToolResult { .. } => ViewKind::ToolResult, - ContentPart::Resource { .. } => ViewKind::Resource, - ContentPart::ResourceRef { .. } => ViewKind::ResourceRef, - ContentPart::PromptRequest { .. } => ViewKind::PromptRequest, - ContentPart::PromptResult { .. } => ViewKind::PromptResult, - ContentPart::Image { .. } => ViewKind::Image, - ContentPart::Video { .. } => ViewKind::Video, - ContentPart::Audio { .. } => ViewKind::Audio, - ContentPart::Document { .. } => ViewKind::Document, - }; - - Self { - part, - kind, - role, - hook, - extensions, - } - } - - // -- Core properties -- - - /// The kind of content this view represents. - pub fn kind(&self) -> ViewKind { - self.kind - } - - /// The role of the parent message. - pub fn role(&self) -> Role { - self.role - } - - /// The underlying content part. - pub fn raw(&self) -> &'a ContentPart { - self.part - } - - /// The hook location, if set. - pub fn hook(&self) -> Option<&str> { - self.hook - } - - /// The action this content represents. - pub fn action(&self) -> ViewAction { - self.kind.default_action(self.role) - } - - // -- Phase helpers -- - - /// Whether this is a pre-execution hook (tool_pre_invoke, prompt_pre_fetch, etc.). - pub fn is_pre(&self) -> bool { - self.hook.is_some_and(|h| h.contains("pre")) - } - - /// Whether this is a post-execution hook. - pub fn is_post(&self) -> bool { - self.hook.is_some_and(|h| h.contains("post")) - } - - // -- Universal properties -- - - /// Text content (for text, thinking, tool result content). - pub fn content(&self) -> Option<&str> { - match self.part { - ContentPart::Text { text } | ContentPart::Thinking { text } => Some(text), - ContentPart::ToolResult { content: tr } => { - tr.content.as_str().map(Some).unwrap_or(None) - } - ContentPart::Resource { content: r } => r.content.as_deref(), - ContentPart::PromptResult { content: pr } => pr.content.as_deref(), - _ => None, - } - } - - /// Entity name (tool name, resource URI, prompt name). - pub fn name(&self) -> Option<&str> { - match self.part { - ContentPart::ToolCall { content: tc } => Some(&tc.name), - ContentPart::ToolResult { content: tr } => Some(&tr.tool_name), - ContentPart::Resource { content: r } => r.name.as_deref().or(Some(&r.uri)), - ContentPart::ResourceRef { content: rr } => rr.name.as_deref().or(Some(&rr.uri)), - ContentPart::PromptRequest { content: pr } => Some(&pr.name), - ContentPart::PromptResult { content: pr } => Some(&pr.prompt_name), - _ => None, - } - } - - /// URI for the entity. - pub fn uri(&self) -> Option { - match self.part { - ContentPart::ToolCall { content: tc } => Some(format!("tool://_/{}", tc.name)), - ContentPart::Resource { content: r } => Some(r.uri.clone()), - ContentPart::ResourceRef { content: rr } => Some(rr.uri.clone()), - ContentPart::PromptRequest { content: pr } => Some(format!("prompt://_/{}", pr.name)), - _ => None, - } - } - - /// Arguments (for tool calls and prompt requests). - pub fn args(&self) -> Option<&std::collections::HashMap> { - match self.part { - ContentPart::ToolCall { content: tc } => Some(&tc.arguments), - ContentPart::PromptRequest { content: pr } => Some(&pr.arguments), - _ => None, - } - } - - /// Get a specific argument by name. - pub fn get_arg(&self, name: &str) -> Option<&serde_json::Value> { - self.args().and_then(|a| a.get(name)) - } - - /// Whether this content has arguments. - pub fn has_arg(&self, name: &str) -> bool { - self.get_arg(name).is_some() - } - - /// MIME type (for resources, media). - pub fn mime_type(&self) -> Option<&str> { - match self.part { - ContentPart::Resource { content: r } => r.mime_type.as_deref(), - ContentPart::Image { content: img } => img.media_type.as_deref(), - ContentPart::Video { content: vid } => vid.media_type.as_deref(), - ContentPart::Audio { content: aud } => aud.media_type.as_deref(), - ContentPart::Document { content: doc } => doc.media_type.as_deref(), - _ => None, - } - } - - /// Whether the result is an error (tool results, prompt results). - pub fn is_error(&self) -> bool { - match self.part { - ContentPart::ToolResult { content: tr } => tr.is_error, - ContentPart::PromptResult { content: pr } => pr.is_error, - _ => false, - } - } - - // -- Type helpers -- - - pub fn is_tool(&self) -> bool { - self.kind.is_tool() - } - pub fn is_resource(&self) -> bool { - self.kind.is_resource() - } - pub fn is_prompt(&self) -> bool { - self.kind.is_prompt() - } - pub fn is_media(&self) -> bool { - self.kind.is_media() - } - pub fn is_text(&self) -> bool { - self.kind.is_text() - } - - // -- Extension accessors -- - - /// Get the extensions, if provided. - pub fn extensions(&self) -> Option<&'a Extensions> { - self.extensions - } - - /// Check if a security label exists. - pub fn has_label(&self, label: &str) -> bool { - self.extensions - .and_then(|e| e.security.as_ref()) - .map(|s| s.has_label(label)) - .unwrap_or(false) - } - - /// Get an HTTP header value. - pub fn get_header(&self, name: &str) -> Option<&str> { - self.extensions - .and_then(|e| e.http.as_ref()) - .and_then(|h| h.get_header(name)) - } - - // -- Serialization -- - - /// Sensitive headers stripped during serialization. - const SENSITIVE_HEADERS: &'static [&'static str] = &["authorization", "cookie", "x-api-key"]; - - /// Serialize the view to a JSON-compatible map. - /// - /// Includes the view's properties, arguments, and optionally - /// text content and extension context. Sensitive headers - /// (Authorization, Cookie, X-API-Key) are stripped. - pub fn to_dict(&self, include_content: bool, include_context: bool) -> serde_json::Value { - use super::constants::*; - - let mut result = serde_json::Map::new(); - - // Core fields - result.insert(FIELD_KIND.into(), serde_json::json!(self.kind)); - result.insert(FIELD_ROLE.into(), serde_json::json!(self.role)); - result.insert(FIELD_IS_PRE.into(), serde_json::json!(self.is_pre())); - result.insert(FIELD_IS_POST.into(), serde_json::json!(self.is_post())); - result.insert(FIELD_ACTION.into(), serde_json::json!(self.action())); - - if let Some(hook) = self.hook { - result.insert(FIELD_HOOK.into(), serde_json::json!(hook)); - } - - if let Some(uri) = self.uri() { - result.insert(FIELD_URI.into(), serde_json::json!(uri)); - } - - if let Some(name) = self.name() { - result.insert(FIELD_NAME.into(), serde_json::json!(name)); - } - - // Content - if include_content { - if let Some(text) = self.content() { - result.insert(FIELD_SIZE_BYTES.into(), serde_json::json!(text.len())); - result.insert(FIELD_CONTENT.into(), serde_json::json!(text)); - } - } - - if let Some(mime) = self.mime_type() { - result.insert(FIELD_MIME_TYPE.into(), serde_json::json!(mime)); - } - - // Arguments - if let Some(args) = self.args() { - result.insert(FIELD_ARGUMENTS.into(), serde_json::json!(args)); - } - - // Extensions context - if include_context { - if let Some(ext) = self.extensions { - let mut ext_map = serde_json::Map::new(); - - // Subject - if let Some(ref sec) = ext.security { - if let Some(ref subject) = sec.subject { - let mut sub_map = serde_json::Map::new(); - if let Some(ref id) = subject.id { - sub_map.insert(FIELD_ID.into(), serde_json::json!(id)); - } - if let Some(ref st) = subject.subject_type { - sub_map.insert(FIELD_TYPE.into(), serde_json::json!(st)); - } - if !subject.roles.is_empty() { - let mut roles: Vec<&String> = subject.roles.iter().collect(); - roles.sort(); - sub_map.insert(FIELD_ROLES.into(), serde_json::json!(roles)); - } - if !subject.permissions.is_empty() { - let mut perms: Vec<&String> = subject.permissions.iter().collect(); - perms.sort(); - sub_map.insert(FIELD_PERMISSIONS.into(), serde_json::json!(perms)); - } - if !subject.teams.is_empty() { - let mut teams: Vec<&String> = subject.teams.iter().collect(); - teams.sort(); - sub_map.insert(FIELD_TEAMS.into(), serde_json::json!(teams)); - } - if !sub_map.is_empty() { - ext_map - .insert(FIELD_SUBJECT.into(), serde_json::Value::Object(sub_map)); - } - } - - // Labels - if !sec.labels.is_empty() { - let mut labels: Vec<&String> = sec.labels.iter().collect(); - labels.sort(); - ext_map.insert(FIELD_LABELS.into(), serde_json::json!(labels)); - } - } - - // Environment - if let Some(ref req) = ext.request { - if let Some(ref env) = req.environment { - ext_map.insert(FIELD_ENVIRONMENT.into(), serde_json::json!(env)); - } - } - - // Request headers (strip sensitive) - if let Some(ref http) = ext.http { - let safe: std::collections::HashMap<&String, &String> = http - .request_headers - .iter() - .filter(|(k, _)| { - !Self::SENSITIVE_HEADERS.contains(&k.to_lowercase().as_str()) - }) - .collect(); - if !safe.is_empty() { - ext_map.insert(FIELD_HEADERS.into(), serde_json::json!(safe)); - } - } - - // Agent context - if let Some(ref agent) = ext.agent { - let mut agent_map = serde_json::Map::new(); - if let Some(ref input) = agent.input { - agent_map.insert(FIELD_INPUT.into(), serde_json::json!(input)); - } - if let Some(ref sid) = agent.session_id { - agent_map.insert(FIELD_SESSION_ID.into(), serde_json::json!(sid)); - } - if let Some(ref cid) = agent.conversation_id { - agent_map.insert(FIELD_CONVERSATION_ID.into(), serde_json::json!(cid)); - } - if let Some(turn) = agent.turn { - agent_map.insert(FIELD_TURN.into(), serde_json::json!(turn)); - } - if let Some(ref aid) = agent.agent_id { - agent_map.insert(FIELD_AGENT_ID.into(), serde_json::json!(aid)); - } - if let Some(ref paid) = agent.parent_agent_id { - agent_map.insert(FIELD_PARENT_AGENT_ID.into(), serde_json::json!(paid)); - } - if !agent_map.is_empty() { - ext_map.insert(FIELD_AGENT.into(), serde_json::Value::Object(agent_map)); - } - } - - // Meta - if let Some(ref meta) = ext.meta { - let mut meta_map = serde_json::Map::new(); - if let Some(ref et) = meta.entity_type { - meta_map.insert(FIELD_ENTITY_TYPE.into(), serde_json::json!(et)); - } - if let Some(ref en) = meta.entity_name { - meta_map.insert(FIELD_ENTITY_NAME.into(), serde_json::json!(en)); - } - if !meta.tags.is_empty() { - let mut tags: Vec<&String> = meta.tags.iter().collect(); - tags.sort(); - meta_map.insert(FIELD_TAGS.into(), serde_json::json!(tags)); - } - if !meta_map.is_empty() { - ext_map.insert(FIELD_META.into(), serde_json::Value::Object(meta_map)); - } - } - - if !ext_map.is_empty() { - result.insert(FIELD_EXTENSIONS.into(), serde_json::Value::Object(ext_map)); - } - } - } - - serde_json::Value::Object(result) - } - - /// Serialize to OPA-compatible input format. - /// - /// Wraps the view in the standard OPA input envelope: - /// `{"input": {...view data...}}`. - pub fn to_opa_input(&self, include_content: bool) -> serde_json::Value { - use super::constants::FIELD_OPA_INPUT; - serde_json::json!({ - FIELD_OPA_INPUT: self.to_dict(include_content, true) - }) - } -} - -impl<'a> std::fmt::Debug for MessageView<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("MessageView") - .field("kind", &self.kind) - .field("role", &self.role) - .field("name", &self.name()) - .field("hook", &self.hook) - .finish() - } -} - -// --------------------------------------------------------------------------- -// iter_views — decompose a Message into views -// --------------------------------------------------------------------------- - -/// Decompose a Message into individually addressable MessageViews. -/// -/// Yields one view per content part. Each view provides a uniform -/// interface for policy evaluation regardless of content type. -pub fn iter_views<'a>( - message: &'a Message, - hook: Option<&'a str>, - extensions: Option<&'a Extensions>, -) -> impl Iterator> { - message - .content - .iter() - .map(move |part| MessageView::new(part, message.role, hook, extensions)) -} - -// Also add iter_views to Message -impl Message { - /// Decompose this message into individually addressable MessageViews. - /// - /// Yields one view per content part. Each view provides a uniform - /// interface for policy evaluation regardless of content type. - pub fn iter_views<'a>( - &'a self, - hook: Option<&'a str>, - extensions: Option<&'a Extensions>, - ) -> impl Iterator> { - iter_views(self, hook, extensions) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::cmf::enums::Role; - use crate::hooks::payload::MetaExtension; - - fn make_test_message() -> Message { - Message { - schema_version: "2.0".into(), - role: Role::Assistant, - content: vec![ - ContentPart::Thinking { - text: "Let me think...".into(), - }, - ContentPart::Text { - text: "Here's the answer.".into(), - }, - ContentPart::ToolCall { - content: ToolCall { - tool_call_id: "tc_001".into(), - name: "get_weather".into(), - arguments: [("city".to_string(), serde_json::json!("London"))].into(), - namespace: None, - }, - }, - ContentPart::Resource { - content: Resource { - resource_request_id: "rr_001".into(), - uri: "file:///data.csv".into(), - name: Some("Data File".into()), - resource_type: crate::cmf::enums::ResourceType::File, - content: Some("col1,col2".into()), - mime_type: Some("text/csv".into()), - ..Default::default() - }, - }, - ], - channel: None, - } - } - - #[test] - fn test_iter_views_count() { - let msg = make_test_message(); - let views: Vec<_> = msg.iter_views(None, None).collect(); - assert_eq!(views.len(), 4); - } - - #[test] - fn test_view_kinds() { - let msg = make_test_message(); - let views: Vec<_> = msg.iter_views(None, None).collect(); - assert_eq!(views[0].kind(), ViewKind::Thinking); - assert_eq!(views[1].kind(), ViewKind::Text); - assert_eq!(views[2].kind(), ViewKind::ToolCall); - assert_eq!(views[3].kind(), ViewKind::Resource); - } - - #[test] - fn test_view_content() { - let msg = make_test_message(); - let views: Vec<_> = msg.iter_views(None, None).collect(); - assert_eq!(views[0].content(), Some("Let me think...")); - assert_eq!(views[1].content(), Some("Here's the answer.")); - assert!(views[2].content().is_none()); // tool call has no text content - assert_eq!(views[3].content(), Some("col1,col2")); // resource has text content - } - - #[test] - fn test_view_name() { - let msg = make_test_message(); - let views: Vec<_> = msg.iter_views(None, None).collect(); - assert!(views[0].name().is_none()); // thinking has no name - assert!(views[1].name().is_none()); // text has no name - assert_eq!(views[2].name(), Some("get_weather")); - assert_eq!(views[3].name(), Some("Data File")); - } - - #[test] - fn test_view_uri() { - let msg = make_test_message(); - let views: Vec<_> = msg.iter_views(None, None).collect(); - assert_eq!(views[2].uri(), Some("tool://_/get_weather".to_string())); - assert_eq!(views[3].uri(), Some("file:///data.csv".to_string())); - } - - #[test] - fn test_view_args() { - let msg = make_test_message(); - let views: Vec<_> = msg.iter_views(None, None).collect(); - let tool_view = &views[2]; - assert!(tool_view.has_arg("city")); - assert_eq!(tool_view.get_arg("city").unwrap(), "London"); - assert!(!tool_view.has_arg("nonexistent")); - } - - #[test] - fn test_view_action() { - let msg = make_test_message(); - let views: Vec<_> = msg.iter_views(None, None).collect(); - assert_eq!(views[0].action(), ViewAction::Generate); // thinking from assistant - assert_eq!(views[1].action(), ViewAction::Generate); // text from assistant - assert_eq!(views[2].action(), ViewAction::Execute); // tool call - assert_eq!(views[3].action(), ViewAction::Read); // resource - } - - #[test] - fn test_view_action_user_role() { - let msg = Message::text(Role::User, "Hello"); - let views: Vec<_> = msg.iter_views(None, None).collect(); - assert_eq!(views[0].action(), ViewAction::Send); // text from user - } - - #[test] - fn test_view_hook_pre_post() { - let msg = make_test_message(); - let pre_views: Vec<_> = msg.iter_views(Some("tool_pre_invoke"), None).collect(); - assert!(pre_views[0].is_pre()); - assert!(!pre_views[0].is_post()); - - let post_views: Vec<_> = msg.iter_views(Some("tool_post_invoke"), None).collect(); - assert!(post_views[0].is_post()); - assert!(!post_views[0].is_pre()); - } - - #[test] - fn test_view_type_helpers() { - let msg = make_test_message(); - let views: Vec<_> = msg.iter_views(None, None).collect(); - assert!(views[0].is_text()); // thinking - assert!(views[1].is_text()); // text - assert!(views[2].is_tool()); // tool call - assert!(views[3].is_resource()); // resource - } - - #[test] - fn test_view_mime_type() { - let msg = make_test_message(); - let views: Vec<_> = msg.iter_views(None, None).collect(); - assert_eq!(views[3].mime_type(), Some("text/csv")); - } - - #[test] - fn test_view_with_extensions() { - use crate::extensions::{HttpExtension, SecurityExtension}; - use std::sync::Arc; - - let mut security = SecurityExtension::default(); - security.add_label("PII"); - - let mut http = HttpExtension::default(); - http.set_header("Authorization", "Bearer tok"); - - let ext = Extensions { - security: Some(Arc::new(security)), - http: Some(Arc::new(http)), - ..Default::default() - }; - - let msg = make_test_message(); - let views: Vec<_> = msg.iter_views(None, Some(&ext)).collect(); - - assert!(views[0].has_label("PII")); - assert!(!views[0].has_label("HIPAA")); - assert_eq!(views[0].get_header("Authorization"), Some("Bearer tok")); - } - - #[test] - fn test_to_dict_basic() { - let msg = Message::text(Role::User, "Hello world"); - let views: Vec<_> = msg.iter_views(Some("llm_input"), None).collect(); - let dict = views[0].to_dict(true, false); - - assert_eq!(dict["kind"], "text"); - assert_eq!(dict["role"], "user"); - assert_eq!(dict["action"], "send"); - assert_eq!(dict["hook"], "llm_input"); - assert_eq!(dict["content"], "Hello world"); - assert_eq!(dict["size_bytes"], 11); - assert_eq!(dict["is_pre"], false); - assert_eq!(dict["is_post"], false); - } - - #[test] - fn test_to_dict_tool_call() { - let msg = make_test_message(); - let views: Vec<_> = msg.iter_views(Some("tool_pre_invoke"), None).collect(); - let dict = views[2].to_dict(true, false); // tool call - - assert_eq!(dict["kind"], "tool_call"); - assert_eq!(dict["name"], "get_weather"); - assert_eq!(dict["uri"], "tool://_/get_weather"); - assert_eq!(dict["action"], "execute"); - assert_eq!(dict["is_pre"], true); - assert!(dict["arguments"].is_object()); - assert_eq!(dict["arguments"]["city"], "London"); - } - - #[test] - fn test_to_dict_without_content() { - let msg = Message::text(Role::User, "Secret message"); - let views: Vec<_> = msg.iter_views(None, None).collect(); - let dict = views[0].to_dict(false, false); - - assert!(dict.get("content").is_none()); - assert!(dict.get("size_bytes").is_none()); - } - - #[test] - fn test_to_dict_with_extensions() { - use crate::extensions::{ - AgentExtension, HttpExtension, RequestExtension, SecurityExtension, - }; - use std::sync::Arc; - - let mut security = SecurityExtension::default(); - security.add_label("PII"); - security.subject = Some(crate::extensions::security::SubjectExtension { - id: Some("alice".into()), - subject_type: Some(crate::extensions::security::SubjectType::User), - roles: ["admin".to_string()].into(), - ..Default::default() - }); - - let mut http = HttpExtension::default(); - http.set_header("Authorization", "Bearer secret"); - http.set_header("X-Request-ID", "req-123"); - - let ext = Extensions { - security: Some(Arc::new(security)), - http: Some(Arc::new(http)), - request: Some(Arc::new(RequestExtension { - environment: Some("production".into()), - ..Default::default() - })), - agent: Some(Arc::new(AgentExtension { - session_id: Some("sess-001".into()), - agent_id: Some("agent-x".into()), - ..Default::default() - })), - meta: Some(Arc::new(MetaExtension { - entity_type: Some("tool".into()), - entity_name: Some("get_compensation".into()), - tags: ["pii".to_string()].into(), - ..Default::default() - })), - ..Default::default() - }; - - let msg = Message::text(Role::User, "test"); - let views: Vec<_> = msg.iter_views(None, Some(&ext)).collect(); - let dict = views[0].to_dict(true, true); - - let extensions = &dict["extensions"]; - - // Subject visible - assert_eq!(extensions["subject"]["id"], "alice"); - assert!(extensions["subject"]["roles"] - .as_array() - .unwrap() - .contains(&serde_json::json!("admin"))); - - // Labels visible - assert!(extensions["labels"] - .as_array() - .unwrap() - .contains(&serde_json::json!("PII"))); - - // Environment visible - assert_eq!(extensions["environment"], "production"); - - // Headers visible — but Authorization stripped (sensitive) - assert!(extensions["headers"].get("Authorization").is_none()); - assert_eq!(extensions["headers"]["X-Request-ID"], "req-123"); - - // Agent context visible - assert_eq!(extensions["agent"]["session_id"], "sess-001"); - assert_eq!(extensions["agent"]["agent_id"], "agent-x"); - - // Meta visible - assert_eq!(extensions["meta"]["entity_type"], "tool"); - assert_eq!(extensions["meta"]["entity_name"], "get_compensation"); - } - - #[test] - fn test_to_opa_input() { - let msg = Message::text(Role::User, "Hello"); - let views: Vec<_> = msg.iter_views(None, None).collect(); - let opa = views[0].to_opa_input(true); - - assert!(opa.get("input").is_some()); - assert_eq!(opa["input"]["kind"], "text"); - assert_eq!(opa["input"]["role"], "user"); - assert_eq!(opa["input"]["content"], "Hello"); - } -} diff --git a/crates/cpex-payload/src/config.rs b/crates/cpex-payload/src/config.rs deleted file mode 100644 index 89d962a2..00000000 --- a/crates/cpex-payload/src/config.rs +++ /dev/null @@ -1,1297 +0,0 @@ -// Location: ./crates/cpex-core/src/config.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// Unified YAML configuration parsing. -// -// Parses the config format that combines global settings, plugin -// declarations, and per-entity routes into a single YAML document. -// -// Supports two modes controlled by `plugin_settings.routing_enabled`: -// - false (default, backward compatible): plugins declare their -// own conditions for when they fire. -// - true: per-entity routing rules determine which plugins fire, -// with plugin selection via policy groups and meta.tags. -// -// The two modes are mutually exclusive. When routing is disabled, -// the routes and global sections are ignored. When routing is -// enabled, conditions on individual plugins are ignored. - -use std::collections::{HashMap, HashSet}; -use std::path::Path; - -use serde::{Deserialize, Deserializer, Serialize, Serializer}; - -use crate::error::PluginError; -use crate::plugin::PluginConfig; - -// --------------------------------------------------------------------------- -// Top-Level Config -// --------------------------------------------------------------------------- - -/// Top-level CPEX configuration. -/// -/// Parsed from a single YAML file. Plugin scoping mode is controlled -/// by `plugin_settings.routing_enabled` — if absent or false, plugins -/// use their own `conditions:` field (backward compatible). If true, -/// the `routes:` and `global:` sections take over. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct CpexConfig { - /// Global configuration — policies, defaults. - /// Only used when `plugin_settings.routing_enabled` is true. - #[serde(default)] - pub global: GlobalConfig, - - /// Directories to scan for plugin modules. - #[serde(default)] - pub plugin_dirs: Vec, - - /// Plugin declarations. - #[serde(default)] - pub plugins: Vec, - - /// Per-entity routing rules. - /// Only used when `plugin_settings.routing_enabled` is true. - #[serde(default)] - pub routes: Vec, - - /// Global plugin settings (timeout, error behavior, routing mode). - #[serde(default)] - pub plugin_settings: PluginSettings, -} - -impl CpexConfig { - /// Whether route-based plugin selection is enabled. - pub fn routing_enabled(&self) -> bool { - self.plugin_settings.routing_enabled - } -} - -// --------------------------------------------------------------------------- -// Plugin Settings -// --------------------------------------------------------------------------- - -/// Global plugin settings. -/// -/// Controls executor behavior and routing mode. All fields have -/// sensible defaults — a missing `plugin_settings:` section is valid. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PluginSettings { - /// Enable route-based plugin selection. - /// When false (default), plugins use their own `conditions:` field. - /// When true, the `routes:` and `global:` sections determine which - /// plugins fire per entity. - #[serde(default)] - pub routing_enabled: bool, - - /// Default timeout per plugin in seconds. - #[serde(default = "default_timeout")] - pub plugin_timeout: u64, - - /// Whether to halt on first deny in concurrent mode. - #[serde(default = "default_true")] - pub short_circuit_on_deny: bool, - - /// Whether plugins can execute in parallel within a mode band. - #[serde(default)] - pub parallel_execution_within_band: bool, - - /// Whether to halt the pipeline on any plugin error. - #[serde(default)] - pub fail_on_plugin_error: bool, - - /// Maximum number of entries in the routing cache. - /// - /// When the cache reaches this size, new resolutions are computed - /// normally but not memoized — the cache rejects further inserts - /// and emits a warning. This bounds memory growth from - /// attacker-controlled entity names without the reasoning hazards - /// of eviction (silently dropped entries, stale-vs-current - /// confusion). Operators see the warning and tune the cap or - /// investigate the entity-name growth. - #[serde(default = "default_route_cache_max_entries")] - pub route_cache_max_entries: usize, -} - -impl Default for PluginSettings { - fn default() -> Self { - Self { - routing_enabled: false, - plugin_timeout: 30, - short_circuit_on_deny: true, - parallel_execution_within_band: false, - fail_on_plugin_error: false, - route_cache_max_entries: default_route_cache_max_entries(), - } - } -} - -fn default_route_cache_max_entries() -> usize { - 10_000 -} - -fn default_timeout() -> u64 { - 30 -} - -fn default_true() -> bool { - true -} - -// --------------------------------------------------------------------------- -// Global Config -// --------------------------------------------------------------------------- - -/// Global configuration — applies across all routes. -/// -/// Only used when routing is enabled. Contains named policy groups -/// (including the reserved `all` group) and per-entity-type defaults. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct GlobalConfig { - /// Named policy groups. The reserved name `all` is applied to - /// every request unconditionally. Other groups are inherited - /// by routes via `meta.tags`. - #[serde(default)] - pub policies: HashMap, - - /// Per-entity-type default policy groups. - /// Keys are `tool`, `resource`, `prompt`, `llm`. - #[serde(default)] - pub defaults: HashMap, -} - -// --------------------------------------------------------------------------- -// Policy Group -// --------------------------------------------------------------------------- - -/// A named policy group — plugins to activate and optional metadata. -/// -/// The `all` group is reserved and always applied. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct PolicyGroup { - /// Human-readable description. - #[serde(default)] - pub description: Option, - - /// Arbitrary metadata for tooling and audit. - #[serde(default)] - pub metadata: HashMap, - - /// Plugin references to activate when this group matches. - #[serde(default)] - pub plugins: Vec, -} - -// --------------------------------------------------------------------------- -// Plugin Ref (route/group plugin reference) -// --------------------------------------------------------------------------- - -/// A reference to a plugin in a route or policy group. -/// -/// ```yaml -/// plugins: -/// - rate_limiter # bare name -/// - pii_scanner: # name with config overrides -/// config: -/// sensitivity: high -/// ``` -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(untagged)] -pub enum PluginRouteRef { - /// Just the name — activate the plugin with no config overrides. - Name(String), - /// Name with config overrides — single-key map. - WithOverrides(HashMap), -} - -impl PluginRouteRef { - /// Extract the plugin name from this reference. - pub fn name(&self) -> &str { - match self { - Self::Name(name) => name, - Self::WithOverrides(map) => map.keys().next().map(|s| s.as_str()).unwrap_or(""), - } - } - - /// Extract config overrides, if any. - pub fn overrides(&self) -> Option<&serde_json::Value> { - match self { - Self::Name(_) => None, - Self::WithOverrides(map) => map.values().next(), - } - } -} - -// --------------------------------------------------------------------------- -// Route Entry -// --------------------------------------------------------------------------- - -/// A per-entity routing rule. -/// -/// Matches one entity type (tool, resource, prompt, or LLM) and -/// determines which plugins fire. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct RouteEntry { - /// Match a tool by exact name, list, or glob. - #[serde(default)] - pub tool: Option, - - /// Match a resource by exact URI, list, or glob. - #[serde(default)] - pub resource: Option, - - /// Match a prompt by exact name, list, or glob. - #[serde(default)] - pub prompt: Option, - - /// Match an LLM by exact model name, list, or glob. - #[serde(default)] - pub llm: Option, - - /// Operational metadata — tags, scope, properties. - #[serde(default)] - pub meta: Option, - - /// Conditional match expression — carried but not evaluated - /// during static resolution. Evaluated at runtime when payload - /// data is available (future: APL evaluator). - #[serde(default)] - pub when: Option, - - /// Plugin references to activate for this route. - #[serde(default)] - pub plugins: Vec, -} - -// --------------------------------------------------------------------------- -// Route Meta -// --------------------------------------------------------------------------- - -/// Operational metadata on a route entry. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct RouteMeta { - /// Entity tags — drive policy group inheritance. - #[serde(default)] - pub tags: Vec, - - /// Host-defined grouping (virtual server ID, namespace, etc.). - /// Used for scope matching: route scope must match request scope. - #[serde(default)] - pub scope: Option, - - /// Arbitrary key-value metadata. - #[serde(default)] - pub properties: HashMap, -} - -// --------------------------------------------------------------------------- -// String or List (for tool matching) -// --------------------------------------------------------------------------- - -/// An entity-name pattern. Holds the original pattern string (for -/// serialization round-tripping and operator-facing diagnostics) plus a -/// `WildMatch` matcher pre-compiled at deserialize time so route resolution -/// doesn't re-parse the pattern on every request. Custom `Serialize` / -/// `Deserialize` make this transparent to YAML — it serializes as a plain -/// string, just like the previous `String` field did. -/// -/// Glob syntax (via `wildmatch`): -/// - `*` matches any sequence of characters (including empty). -/// - `?` matches any single character. -/// -/// The previous hand-rolled matcher only handled trailing-`*` correctly: -/// `*suffix` patterns silently matched almost nothing, and multi-star -/// patterns like `**` accidentally matched everything. Both shapes are -/// real security footguns for scope/tool restriction rules — switching to -/// `wildmatch` gives us full single-segment glob semantics. -#[derive(Debug, Clone)] -pub struct Pattern { - pattern: String, - matcher: wildmatch::WildMatch, -} - -impl Pattern { - /// Compile a pattern. Done once at config load; subsequent `matches()` - /// calls reuse the compiled `WildMatch`. - pub fn new(pattern: impl Into) -> Self { - let pattern = pattern.into(); - let matcher = wildmatch::WildMatch::new(&pattern); - Self { pattern, matcher } - } - - /// Match the given name against the compiled pattern. - pub fn matches(&self, name: &str) -> bool { - self.matcher.matches(name) - } - - /// The original pattern string (e.g., `"hr-*"`). - pub fn as_str(&self) -> &str { - &self.pattern - } -} - -impl Default for Pattern { - fn default() -> Self { - Self::new("") - } -} - -impl Serialize for Pattern { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_str(&self.pattern) - } -} - -impl<'de> Deserialize<'de> for Pattern { - fn deserialize>(deserializer: D) -> Result { - let s = String::deserialize(deserializer)?; - Ok(Pattern::new(s)) - } -} - -/// A tool matcher — single name, list of names, or glob pattern. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(untagged)] -pub enum StringOrList { - /// Single string (exact name or glob pattern). Pre-compiled at - /// deserialize time so the route-resolution slow path doesn't re-parse - /// on each request. - Single(Pattern), - /// List of exact names. - List(Vec), -} - -impl Default for StringOrList { - fn default() -> Self { - Self::Single(Pattern::default()) - } -} - -impl StringOrList { - /// Check if this matcher matches the given name. - pub fn matches(&self, name: &str) -> bool { - match self { - Self::Single(pattern) => pattern.matches(name), - Self::List(names) => names.iter().any(|n| n == name), - } - } -} - -// --------------------------------------------------------------------------- -// Config Loading -// --------------------------------------------------------------------------- - -/// Load and parse a CPEX config from a YAML file. -pub fn load_config(path: &Path) -> Result> { - let content = std::fs::read_to_string(path).map_err(|e| PluginError::Config { - message: format!("failed to read config file '{}': {}", path.display(), e), - })?; - parse_config(&content) -} - -/// Parse a CPEX config from a YAML string. -pub fn parse_config(yaml: &str) -> Result> { - let config: CpexConfig = serde_yaml::from_str(yaml).map_err(|e| PluginError::Config { - message: format!("failed to parse config YAML: {}", e), - })?; - validate_config(&config)?; - Ok(config) -} - -// --------------------------------------------------------------------------- -// Validation -// --------------------------------------------------------------------------- - -/// Validate a parsed config for structural correctness. -fn validate_config(config: &CpexConfig) -> Result<(), Box> { - let mut seen_names = HashSet::new(); - for plugin in &config.plugins { - if !seen_names.insert(&plugin.name) { - return Err(Box::new(PluginError::Config { - message: format!("duplicate plugin name: '{}'", plugin.name), - })); - } - } - - if config.routing_enabled() { - let plugin_names: HashSet<&str> = config.plugins.iter().map(|p| p.name.as_str()).collect(); - - for (i, route) in config.routes.iter().enumerate() { - let count = [ - route.tool.is_some(), - route.resource.is_some(), - route.prompt.is_some(), - route.llm.is_some(), - ] - .iter() - .filter(|&&m| m) - .count(); - - if count == 0 { - return Err(Box::new(PluginError::Config { - message: format!( - "route {} has no entity matcher (need tool, resource, prompt, or llm)", - i - ), - })); - } - if count > 1 { - return Err(Box::new(PluginError::Config { - message: format!( - "route {} has multiple entity matchers (need exactly one)", - i - ), - })); - } - - for plugin_ref in &route.plugins { - if !plugin_names.contains(plugin_ref.name()) { - return Err(Box::new(PluginError::Config { - message: format!( - "route {} references unknown plugin '{}'", - i, - plugin_ref.name() - ), - })); - } - } - } - - for (group_name, group) in &config.global.policies { - for plugin_ref in &group.plugins { - if !plugin_names.contains(plugin_ref.name()) { - return Err(Box::new(PluginError::Config { - message: format!( - "policy group '{}' references unknown plugin '{}'", - group_name, - plugin_ref.name() - ), - })); - } - } - } - } - - Ok(()) -} - -// --------------------------------------------------------------------------- -// Route Resolution -// --------------------------------------------------------------------------- - -/// Specificity scores for route matching. -const SPECIFICITY_EXACT_NAME: usize = 1000; -const SPECIFICITY_NAME_LIST: usize = 500; -const SPECIFICITY_GLOB: usize = 300; -const SPECIFICITY_WHEN_ONLY: usize = 10; -const SPECIFICITY_WILDCARD: usize = 0; - -/// Score a single entity matcher (tool / resource / prompt / llm) against -/// a request entity name, returning the specificity bucket if it matches -/// or `None` if it doesn't (or the matcher is absent). Replaces four -/// copy-pasted match arms in `resolve_plugins_for_entity`. -fn score_entity_match(matcher: Option<&StringOrList>, entity_name: &str) -> Option { - let matcher = matcher?; - if !matcher.matches(entity_name) { - return None; - } - let score = match matcher { - StringOrList::Single(p) if p.as_str() == "*" => SPECIFICITY_WILDCARD, - StringOrList::Single(p) if p.as_str().contains('*') => SPECIFICITY_GLOB, - StringOrList::List(_) => SPECIFICITY_NAME_LIST, - StringOrList::Single(_) => SPECIFICITY_EXACT_NAME, - }; - Some(score) -} - -/// Resolve which plugins should fire for a given entity. -/// -/// When routing is disabled, returns all plugin names. When enabled, -/// matches the entity against routes and collects plugins from the -/// `all` group, defaults, matching policy groups (via merged tags), -/// and the route itself. -/// -/// `request_scope` and `request_tags` come from the host's -/// `MetaExtension` on the request. -pub fn resolve_plugins_for_entity( - config: &CpexConfig, - entity_type: &str, - entity_name: &str, - request_scope: Option<&str>, - request_tags: &HashSet, -) -> Vec { - if !config.routing_enabled() { - return config - .plugins - .iter() - .map(|p| ResolvedPlugin { - name: p.name.clone(), - config_overrides: None, - when: None, - }) - .collect(); - } - - let mut resolved = Vec::new(); - - // 1. Always include plugins from the "all" policy group - if let Some(all_group) = config.global.policies.get("all") { - collect_plugin_refs(&all_group.plugins, &mut resolved, None); - } - - // 2. Include plugins from matching defaults - if let Some(default_group) = config.global.defaults.get(entity_type) { - collect_plugin_refs(&default_group.plugins, &mut resolved, None); - } - - // 3. Find matching route (with scope check) - if let Some(route) = find_matching_route(config, entity_type, entity_name, request_scope) { - // Merge tags: route's static tags + host's runtime tags - let mut merged_tags: HashSet = request_tags.clone(); - if let Some(meta) = &route.meta { - for tag in &meta.tags { - merged_tags.insert(tag.clone()); - } - } - - // Include plugins from all matching policy groups (merged tags) - for tag in &merged_tags { - if tag == "all" { - continue; // already handled above - } - if let Some(group) = config.global.policies.get(tag.as_str()) { - collect_plugin_refs(&group.plugins, &mut resolved, None); - } - } - - // Include route-level plugins, carrying the route's when clause - collect_plugin_refs(&route.plugins, &mut resolved, route.when.as_deref()); - } - - // Deduplicate by name, preserving order. Later overrides win. - let mut seen = HashSet::new(); - let mut deduped = Vec::new(); - for rp in resolved.into_iter().rev() { - if seen.insert(rp.name.clone()) { - deduped.push(rp); - } - } - deduped.reverse(); - deduped -} - -/// A resolved plugin with optional config overrides and when clause. -#[derive(Debug, Clone)] -pub struct ResolvedPlugin { - /// Plugin name. - pub name: String, - - /// Config overrides from the route. - pub config_overrides: Option, - - /// When clause from the route — carried but not evaluated here. - pub when: Option, -} - -/// Collect plugin refs into the resolved list. -fn collect_plugin_refs( - refs: &[PluginRouteRef], - resolved: &mut Vec, - route_when: Option<&str>, -) { - for plugin_ref in refs { - resolved.push(ResolvedPlugin { - name: plugin_ref.name().to_string(), - config_overrides: plugin_ref.overrides().cloned(), - when: route_when.map(String::from), - }); - } -} - -/// Find the best matching route for an entity by specificity. -/// -/// Scope matching: if a route declares a scope, the request must -/// have the same scope. No scope on the route matches any request. -fn find_matching_route<'a>( - config: &'a CpexConfig, - entity_type: &str, - entity_name: &str, - request_scope: Option<&str>, -) -> Option<&'a RouteEntry> { - let mut best: Option<(usize, &RouteEntry)> = None; - - for route in &config.routes { - // Check scope compatibility - let route_scope = route.meta.as_ref().and_then(|m| m.scope.as_deref()); - let scope_bonus = match (route_scope, request_scope) { - (None, _) => 0, // route is global - (Some(rs), Some(rq)) if rs == rq => 100, // scopes match - (Some(_), _) => continue, // scope mismatch — skip - }; - - let entity_matcher = match entity_type { - "tool" => route.tool.as_ref(), - "resource" => route.resource.as_ref(), - "prompt" => route.prompt.as_ref(), - "llm" => route.llm.as_ref(), - _ => continue, - }; - let base_specificity = match score_entity_match(entity_matcher, entity_name) { - Some(score) => score, - None => continue, - }; - - let when_bonus = if route.when.is_some() { - SPECIFICITY_WHEN_ONLY - } else { - 0 - }; - let total = base_specificity + scope_bonus + when_bonus; - - if best.is_none_or(|(s, _)| total > s) { - best = Some((total, route)); - } - } - - best.map(|(_, route)| route) -} - -#[cfg(test)] -mod tests { - use super::*; - - // Helper: empty tags for tests that don't need them - fn no_tags() -> HashSet { - HashSet::new() - } - - #[test] - fn test_parse_minimal_config() { - let yaml = r#" -plugins: - - name: rate_limiter - kind: builtin - hooks: [tool_pre_invoke] - mode: sequential - priority: 5 - config: - max_requests: 100 -"#; - let config = parse_config(yaml).unwrap(); - assert!(!config.routing_enabled()); - assert_eq!(config.plugins.len(), 1); - assert_eq!(config.plugins[0].name, "rate_limiter"); - } - - #[test] - fn test_no_plugin_settings_defaults_routing_disabled() { - let yaml = r#" -plugins: - - name: test - kind: builtin - hooks: [tool_pre_invoke] -"#; - let config = parse_config(yaml).unwrap(); - assert!(!config.routing_enabled()); - assert_eq!(config.plugin_settings.plugin_timeout, 30); - } - - #[test] - fn test_routing_enabled() { - let yaml = r#" -plugin_settings: - routing_enabled: true -global: - policies: - all: - plugins: [identity] -plugins: - - name: identity - kind: builtin - hooks: [identity_resolve] -routes: - - tool: get_compensation - meta: - tags: [pii] -"#; - let config = parse_config(yaml).unwrap(); - assert!(config.routing_enabled()); - } - - #[test] - fn test_duplicate_plugin_names_rejected() { - let yaml = r#" -plugins: - - name: dup - kind: builtin - hooks: [tool_pre_invoke] - - name: dup - kind: builtin - hooks: [tool_post_invoke] -"#; - assert!(parse_config(yaml) - .unwrap_err() - .to_string() - .contains("duplicate plugin name")); - } - - #[test] - fn test_route_requires_one_entity_matcher() { - let yaml = r#" -plugin_settings: - routing_enabled: true -plugins: [] -routes: - - meta: - tags: [pii] -"#; - assert!(parse_config(yaml) - .unwrap_err() - .to_string() - .contains("no entity matcher")); - } - - #[test] - fn test_route_rejects_multiple_entity_matchers() { - let yaml = r#" -plugin_settings: - routing_enabled: true -plugins: [] -routes: - - tool: get_compensation - resource: "hr://employees/*" -"#; - assert!(parse_config(yaml) - .unwrap_err() - .to_string() - .contains("multiple entity matchers")); - } - - #[test] - fn test_route_unknown_plugin_rejected() { - let yaml = r#" -plugin_settings: - routing_enabled: true -plugins: - - name: known - kind: builtin - hooks: [tool_pre_invoke] -routes: - - tool: get_compensation - plugins: - - unknown -"#; - assert!(parse_config(yaml) - .unwrap_err() - .to_string() - .contains("unknown plugin 'unknown'")); - } - - #[test] - fn test_policy_group_unknown_plugin_rejected() { - let yaml = r#" -plugin_settings: - routing_enabled: true -global: - policies: - all: - plugins: [nonexistent] -plugins: [] -routes: [] -"#; - assert!(parse_config(yaml) - .unwrap_err() - .to_string() - .contains("unknown plugin 'nonexistent'")); - } - - #[test] - fn test_resolve_conditions_mode_returns_all() { - let yaml = r#" -plugins: - - name: a - kind: builtin - hooks: [tool_pre_invoke] - - name: b - kind: builtin - hooks: [tool_post_invoke] -"#; - let config = parse_config(yaml).unwrap(); - let resolved = resolve_plugins_for_entity(&config, "tool", "anything", None, &no_tags()); - let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); - assert_eq!(names, vec!["a", "b"]); - } - - #[test] - fn test_resolve_routes_inherits_policy_groups() { - let yaml = r#" -plugin_settings: - routing_enabled: true -global: - policies: - all: - plugins: - - identity - pii: - plugins: - - apl_policy -plugins: - - name: identity - kind: builtin - hooks: [identity_resolve] - - name: apl_policy - kind: builtin - hooks: [cmf.tool_pre_invoke] -routes: - - tool: get_compensation - meta: - tags: [pii] -"#; - let config = parse_config(yaml).unwrap(); - let resolved = - resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &no_tags()); - let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); - assert!(names.contains(&"identity")); - assert!(names.contains(&"apl_policy")); - } - - #[test] - fn test_resolve_no_matching_route_gets_all_only() { - let yaml = r#" -plugin_settings: - routing_enabled: true -global: - policies: - all: - plugins: - - identity -plugins: - - name: identity - kind: builtin - hooks: [identity_resolve] -routes: - - tool: get_compensation -"#; - let config = parse_config(yaml).unwrap(); - let resolved = - resolve_plugins_for_entity(&config, "tool", "unknown_tool", None, &no_tags()); - let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); - assert_eq!(names, vec!["identity"]); - } - - #[test] - fn test_exact_match_beats_glob() { - let yaml = r#" -plugin_settings: - routing_enabled: true -plugins: - - name: specific - kind: builtin - hooks: [tool_pre_invoke] - - name: general - kind: builtin - hooks: [tool_pre_invoke] -routes: - - tool: "hr-*" - plugins: - - general - - tool: hr-compensation - plugins: - - specific -"#; - let config = parse_config(yaml).unwrap(); - let resolved = - resolve_plugins_for_entity(&config, "tool", "hr-compensation", None, &no_tags()); - let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); - assert!(names.contains(&"specific")); - assert!(!names.contains(&"general")); - } - - #[test] - fn test_plugin_ref_bare_name() { - let yaml = r#" -plugin_settings: - routing_enabled: true -plugins: - - name: rate_limiter - kind: builtin - hooks: [tool_pre_invoke] -routes: - - tool: get_compensation - plugins: - - rate_limiter -"#; - let config = parse_config(yaml).unwrap(); - let resolved = - resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &no_tags()); - assert_eq!(resolved[0].name, "rate_limiter"); - assert!(resolved[0].config_overrides.is_none()); - } - - #[test] - fn test_plugin_ref_with_overrides() { - let yaml = r#" -plugin_settings: - routing_enabled: true -plugins: - - name: rate_limiter - kind: builtin - hooks: [tool_pre_invoke] - config: - max_requests: 100 -routes: - - tool: get_compensation - plugins: - - rate_limiter: - config: - max_requests: 10 -"#; - let config = parse_config(yaml).unwrap(); - let resolved = - resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &no_tags()); - assert_eq!(resolved[0].name, "rate_limiter"); - assert!(resolved[0].config_overrides.is_some()); - let overrides = resolved[0].config_overrides.as_ref().unwrap(); - assert_eq!(overrides["config"]["max_requests"], 10); - } - - #[test] - fn test_plugin_ref_mixed_bare_and_overrides() { - let yaml = r#" -plugin_settings: - routing_enabled: true -plugins: - - name: rate_limiter - kind: builtin - hooks: [tool_pre_invoke] - - name: pii_scanner - kind: builtin - hooks: [tool_pre_invoke] -routes: - - tool: get_compensation - plugins: - - rate_limiter - - pii_scanner: - config: - sensitivity: high -"#; - let config = parse_config(yaml).unwrap(); - let resolved = - resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &no_tags()); - assert_eq!(resolved.len(), 2); - assert_eq!(resolved[0].name, "rate_limiter"); - assert!(resolved[0].config_overrides.is_none()); - assert_eq!(resolved[1].name, "pii_scanner"); - assert!(resolved[1].config_overrides.is_some()); - } - - #[test] - fn test_deduplication_preserves_order() { - let yaml = r#" -plugin_settings: - routing_enabled: true -global: - policies: - all: - plugins: [a, b] - pii: - plugins: [b, c] -plugins: - - name: a - kind: builtin - hooks: [tool_pre_invoke] - - name: b - kind: builtin - hooks: [tool_pre_invoke] - - name: c - kind: builtin - hooks: [tool_pre_invoke] -routes: - - tool: get_compensation - meta: - tags: [pii] -"#; - let config = parse_config(yaml).unwrap(); - let resolved = - resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &no_tags()); - let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); - assert_eq!(names, vec!["a", "b", "c"]); - } - - #[test] - fn test_glob_trailing_wildcard() { - let matcher = StringOrList::Single(Pattern::new("hr-*")); - assert!(matcher.matches("hr-compensation")); - assert!(matcher.matches("hr-benefits")); - assert!(matcher.matches("hr-")); // empty match for * - assert!(!matcher.matches("finance-report")); - assert!(!matcher.matches("hr")); - } - - #[test] - fn test_wildcard_matches_everything() { - let matcher = StringOrList::Single(Pattern::new("*")); - assert!(matcher.matches("anything")); - assert!(matcher.matches("")); - } - - /// Regression for the security footgun: `*suffix` patterns were - /// silently matching almost nothing because the previous matcher - /// looked for `"*suffix"` as a literal prefix. - #[test] - fn test_glob_leading_wildcard() { - let matcher = StringOrList::Single(Pattern::new("*-prod")); - assert!(matcher.matches("foo-prod")); - assert!(matcher.matches("-prod")); // empty match for * - assert!(!matcher.matches("foo-staging")); - assert!(!matcher.matches("prod")); - } - - /// Regression for `prefix*suffix` patterns also broken before. - #[test] - fn test_glob_mid_wildcard() { - let matcher = StringOrList::Single(Pattern::new("hr-*-v1")); - assert!(matcher.matches("hr-comp-v1")); - assert!(matcher.matches("hr--v1")); // empty match for * - assert!(!matcher.matches("hr-comp-v2")); - assert!(!matcher.matches("finance-comp-v1")); - } - - /// Multiple-wildcard patterns must work everywhere `*` appears. - #[test] - fn test_glob_multiple_wildcards() { - let matcher = StringOrList::Single(Pattern::new("*hr*comp*")); - assert!(matcher.matches("hr-comp")); - assert!(matcher.matches("xyz-hr-comp-foo")); - assert!(!matcher.matches("hr-only")); - assert!(!matcher.matches("comp-only")); - } - - /// Regression for the OTHER security footgun: multi-star patterns - /// like `**` were `trim_end_matches('*')`'d to `""` and then matched - /// every name via `starts_with("")`. With wildmatch this is a - /// degenerate-but-correct "match anything" pattern, equivalent to `*`. - #[test] - fn test_glob_multi_star_is_equivalent_to_single_star() { - for pattern in &["**", "***", "*****"] { - let matcher = StringOrList::Single(Pattern::new(*pattern)); - assert!( - matcher.matches("anything"), - "pattern {} should match", - pattern - ); - assert!( - matcher.matches(""), - "pattern {} should match empty", - pattern - ); - } - } - - /// `WildMatch` is built once at deserialize / `Pattern::new` time and - /// reused; this test just sanity-checks the round-trip through serde. - #[test] - fn test_pattern_round_trips_through_yaml() { - let yaml = "tool: '*-prod'"; - #[derive(Deserialize, Serialize)] - struct Wrap { - tool: StringOrList, - } - let parsed: Wrap = serde_yaml::from_str(yaml).unwrap(); - assert!(parsed.tool.matches("foo-prod")); - assert!(!parsed.tool.matches("foo-staging")); - let back = serde_yaml::to_string(&parsed).unwrap(); - assert!( - back.contains("*-prod"), - "serialized YAML should preserve pattern: {}", - back - ); - } - - #[test] - fn test_list_matches_any_member() { - let matcher = StringOrList::List(vec![ - "get_compensation".to_string(), - "get_benefits".to_string(), - ]); - assert!(matcher.matches("get_compensation")); - assert!(matcher.matches("get_benefits")); - assert!(!matcher.matches("send_email")); - } - - #[test] - fn test_validation_skipped_when_routing_disabled() { - let yaml = r#" -plugins: - - name: test - kind: builtin - hooks: [tool_pre_invoke] -routes: - - meta: - tags: [pii] -"#; - let config = parse_config(yaml); - assert!(config.is_ok()); - } - - // -- Scope matching tests -- - - #[test] - fn test_scope_match_selects_scoped_route() { - let yaml = r#" -plugin_settings: - routing_enabled: true -plugins: - - name: scoped_plugin - kind: builtin - hooks: [tool_pre_invoke] - - name: global_plugin - kind: builtin - hooks: [tool_pre_invoke] -routes: - - tool: get_compensation - meta: - scope: hr-services - plugins: - - scoped_plugin - - tool: get_compensation - plugins: - - global_plugin -"#; - let config = parse_config(yaml).unwrap(); - - // With matching scope — scoped route wins (more specific) - let resolved = resolve_plugins_for_entity( - &config, - "tool", - "get_compensation", - Some("hr-services"), - &no_tags(), - ); - let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); - assert!(names.contains(&"scoped_plugin")); - assert!(!names.contains(&"global_plugin")); - - // Without scope — global route matches - let resolved = - resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &no_tags()); - let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); - assert!(names.contains(&"global_plugin")); - assert!(!names.contains(&"scoped_plugin")); - - // With different scope — global route matches (scoped doesn't) - let resolved = resolve_plugins_for_entity( - &config, - "tool", - "get_compensation", - Some("billing"), - &no_tags(), - ); - let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); - assert!(names.contains(&"global_plugin")); - assert!(!names.contains(&"scoped_plugin")); - } - - // -- Tag merging tests -- - - #[test] - fn test_host_tags_merged_with_route_tags() { - let yaml = r#" -plugin_settings: - routing_enabled: true -global: - policies: - pii: - plugins: [pii_plugin] - runtime_tag: - plugins: [runtime_plugin] -plugins: - - name: pii_plugin - kind: builtin - hooks: [tool_pre_invoke] - - name: runtime_plugin - kind: builtin - hooks: [tool_pre_invoke] -routes: - - tool: get_compensation - meta: - tags: [pii] -"#; - let config = parse_config(yaml).unwrap(); - - // Host provides a runtime tag that matches a policy group - let mut host_tags = HashSet::new(); - host_tags.insert("runtime_tag".to_string()); - - let resolved = - resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &host_tags); - let names: Vec<&str> = resolved.iter().map(|r| r.name.as_str()).collect(); - - // Both route's static tag (pii) and host's runtime tag activate their groups - assert!(names.contains(&"pii_plugin")); - assert!(names.contains(&"runtime_plugin")); - } - - // -- When clause carried tests -- - - #[test] - fn test_when_clause_carried_on_resolved_plugins() { - let yaml = r#" -plugin_settings: - routing_enabled: true -plugins: - - name: conditional_plugin - kind: builtin - hooks: [tool_pre_invoke] -routes: - - tool: get_compensation - when: "args.include_ssn == true" - plugins: - - conditional_plugin -"#; - let config = parse_config(yaml).unwrap(); - let resolved = - resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &no_tags()); - assert_eq!(resolved[0].name, "conditional_plugin"); - assert_eq!( - resolved[0].when.as_deref(), - Some("args.include_ssn == true") - ); - } - - #[test] - fn test_when_clause_not_on_policy_group_plugins() { - let yaml = r#" -plugin_settings: - routing_enabled: true -global: - policies: - all: - plugins: [global_plugin] -plugins: - - name: global_plugin - kind: builtin - hooks: [tool_pre_invoke] - - name: route_plugin - kind: builtin - hooks: [tool_pre_invoke] -routes: - - tool: get_compensation - when: "args.sensitive == true" - plugins: - - route_plugin -"#; - let config = parse_config(yaml).unwrap(); - let resolved = - resolve_plugins_for_entity(&config, "tool", "get_compensation", None, &no_tags()); - - // global_plugin has no when clause (from all group) - let global = resolved.iter().find(|r| r.name == "global_plugin").unwrap(); - assert!(global.when.is_none()); - - // route_plugin carries the route's when clause - let route = resolved.iter().find(|r| r.name == "route_plugin").unwrap(); - assert_eq!(route.when.as_deref(), Some("args.sensitive == true")); - } -} diff --git a/crates/cpex-payload/src/context.rs b/crates/cpex-payload/src/context.rs deleted file mode 100644 index 2176c7bc..00000000 --- a/crates/cpex-payload/src/context.rs +++ /dev/null @@ -1,195 +0,0 @@ -// Location: ./crates/cpex-core/src/context.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// Execution context types. -// -// Provides PluginContext — the per-plugin, per-invocation execution -// context carrying transient state (counters, caches, intermediate -// results). All data needed for policy evaluation comes from the -// payload's extensions (filtered by capabilities), not from context. -// -// PluginContext has two state maps: -// - local_state: private to this plugin, this invocation -// - global_state: shared across plugins in a pipeline -// -// Identity, request metadata, tenant scope, etc. live in extensions -// (MetaExtension, SecurityExtension), not in the context. -// -// Mirrors the spec's PluginContext in plugin-framework-spec-v2.md §8.1. - -use std::collections::HashMap; - -use serde::{Deserialize, Serialize}; -use serde_json::Value; -use uuid::Uuid; - -// --------------------------------------------------------------------------- -// Plugin Context -// --------------------------------------------------------------------------- - -/// Per-plugin, per-invocation execution context. -/// -/// Each plugin receives its own `PluginContext` with: -/// -/// - `local_state` — private to this plugin, this invocation. Fresh -/// each time. Used for per-plugin counters, caches, scratch data. -/// - `global_state` — shared across all plugins in a pipeline. The -/// executor merges changes back after serial phases so subsequent -/// plugins see contributions from earlier ones. -/// -/// All data needed for policy evaluation (identity, tenant, request -/// metadata) comes from the payload's extensions, capability-gated -/// per plugin. Context is purely for transient execution state. -/// -/// ```text -/// PluginContext -/// ├── local_state: HashMap # Per-plugin, per-request. Private. -/// └── global_state: HashMap # Shared across plugins. Use with care. -/// ``` -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PluginContext { - /// Plugin-local state. Private to this plugin, this invocation. - #[serde(default)] - pub local_state: HashMap, - - /// Shared state across all plugins in the pipeline. - /// The executor merges changes back after each serial-phase plugin. - #[serde(default)] - pub global_state: HashMap, -} - -impl PluginContext { - /// Create a new empty plugin context. - pub fn new() -> Self { - Self { - local_state: HashMap::new(), - global_state: HashMap::new(), - } - } - - /// Create a plugin context with pre-populated global state. - pub fn with_global_state(global_state: HashMap) -> Self { - Self { - local_state: HashMap::new(), - global_state, - } - } - - /// Get a value from local state. - pub fn get_local(&self, key: &str) -> Option<&Value> { - self.local_state.get(key) - } - - /// Set a value in local state. - pub fn set_local(&mut self, key: impl Into, value: Value) { - self.local_state.insert(key.into(), value); - } - - /// Get a value from global state. - pub fn get_global(&self, key: &str) -> Option<&Value> { - self.global_state.get(key) - } - - /// Set a value in global state. - pub fn set_global(&mut self, key: impl Into, value: Value) { - self.global_state.insert(key.into(), value); - } -} - -impl Default for PluginContext { - fn default() -> Self { - Self::new() - } -} - -// --------------------------------------------------------------------------- -// Plugin Context Table -// --------------------------------------------------------------------------- - -/// Threaded execution state carried from one hook invocation to the next -/// within a single request lifecycle (e.g., `pre_invoke` → `post_invoke`). -/// -/// The table holds the canonical pipeline state in two parts: -/// -/// - `global_state` — a single shared map across all plugins. The executor -/// clones this into each plugin's `PluginContext.global_state` at the -/// start of a run, then commits the plugin's possibly-modified copy back -/// when the run completes (last-writer-wins for serial phases). -/// - `local_states` — per-plugin private state, indexed by plugin ID. -/// Persists across hook invocations so a plugin's `pre_invoke` can stash -/// data its `post_invoke` will read. -/// -/// Storing `global_state` once (rather than copying it inside every per-plugin -/// `PluginContext`) makes the canonical state explicit and removes the -/// non-deterministic "pick an arbitrary plugin's snapshot" pattern that was -/// previously needed to recover it. -/// -/// Returned by the executor in `PipelineResult` and passed back into the -/// next hook call. On the first hook call pass `None` — the executor -/// creates a fresh table. -#[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct PluginContextTable { - /// Authoritative shared state across all plugins in the pipeline. - #[serde(default)] - pub global_state: HashMap, - - /// Per-plugin local state, indexed by plugin ID (`Uuid`). - #[serde(default)] - pub local_states: HashMap>, -} - -impl PluginContextTable { - /// Create an empty context table. - pub fn new() -> Self { - Self::default() - } - - /// Build a `PluginContext` for the given plugin, *removing* its stored - /// local_state from the table and seeding it with a fresh clone of the - /// canonical global_state. Use in serial phases where the plugin will - /// commit its local_state changes back via [`store_context`]. - /// - /// If the plugin has no stored local_state yet, its context starts - /// empty (first invocation in the request lifecycle). - pub fn take_context(&mut self, plugin_id: Uuid) -> PluginContext { - PluginContext { - local_state: self.local_states.remove(&plugin_id).unwrap_or_default(), - global_state: self.global_state.clone(), - } - } - - /// Build a `PluginContext` for the given plugin without mutating the - /// table — the local_state is *cloned* and the global_state is cloned. - /// Use in read-only phases (audit, concurrent, fire-and-forget) where - /// per-plugin mutations should not influence subsequent plugins. - pub fn snapshot_context(&self, plugin_id: Uuid) -> PluginContext { - PluginContext { - local_state: self - .local_states - .get(&plugin_id) - .cloned() - .unwrap_or_default(), - global_state: self.global_state.clone(), - } - } - - /// Commit a plugin's context back into the table after it ran. Replaces - /// the canonical global_state with the plugin's possibly-modified copy - /// (move, no clone) and stores the plugin's local_state for next time. - pub fn store_context(&mut self, plugin_id: Uuid, ctx: PluginContext) { - self.global_state = ctx.global_state; - self.local_states.insert(plugin_id, ctx.local_state); - } - - /// Number of plugins with stored local_state in the table. - pub fn len(&self) -> usize { - self.local_states.len() - } - - /// Whether the table holds no per-plugin local_state. - pub fn is_empty(&self) -> bool { - self.local_states.is_empty() - } -} diff --git a/crates/cpex-payload/src/error.rs b/crates/cpex-payload/src/error.rs deleted file mode 100644 index 576dc5bc..00000000 --- a/crates/cpex-payload/src/error.rs +++ /dev/null @@ -1,275 +0,0 @@ -// Location: ./crates/cpex-core/src/error.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// Error types for the CPEX plugin framework. -// -// Provides structured error types for plugin execution failures, -// policy violations, timeouts, and configuration errors. Mirrors -// the Python framework's PluginError, PluginViolation, and -// PluginViolationError types. - -use std::collections::HashMap; - -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -// --------------------------------------------------------------------------- -// Plugin Errors -// --------------------------------------------------------------------------- - -/// Top-level error type for the CPEX framework. -/// -/// Covers plugin execution failures, policy violations, timeouts, -/// and configuration issues. Each variant carries enough context -/// for the caller to log, report, or recover. -/// -/// Mirrors the Python framework's `PluginErrorModel` with: -/// - `code` — business-logic error code (e.g., `"rate_limit_exceeded"`) -/// - `details` — structured diagnostic data for logging -/// - `proto_error_code` — protocol-level error code for the host to -/// map back to the wire format (MCP JSON-RPC, HTTP status, etc.) -#[derive(Debug, Error)] -pub enum PluginError { - /// A plugin raised an execution error. - #[error("plugin '{plugin_name}' failed: {message}")] - Execution { - plugin_name: String, - message: String, - /// Business-logic error code (e.g., `"invalid_token"`). - #[source] - source: Option>, - /// Business-logic error code set by the plugin. - code: Option, - /// Structured diagnostic data for logging or debugging. - details: HashMap, - /// Protocol-level error code for the host to map to the wire - /// format. MCP: JSON-RPC codes (e.g., -32603). HTTP: status - /// codes. The host interprets this; CPEX just carries it. - proto_error_code: Option, - }, - - /// A plugin exceeded its execution timeout. - #[error("plugin '{plugin_name}' timed out after {timeout_ms}ms")] - Timeout { - plugin_name: String, - timeout_ms: u64, - /// Protocol-level error code for the host. - proto_error_code: Option, - }, - - /// A plugin returned a policy violation (deny). - #[error("plugin '{plugin_name}' denied: {}", violation.reason)] - Violation { - plugin_name: String, - violation: PluginViolation, - }, - - /// Configuration parsing or validation failed. - #[error("configuration error: {message}")] - Config { message: String }, - - /// A hook type was not found in the registry. - #[error("unknown hook type: {hook_type}")] - UnknownHook { hook_type: String }, -} - -impl PluginError { - /// Box this error for use in `Result>`. - /// - /// Public APIs return `Result>` rather than - /// `Result>` because the enum is large (~184 bytes - /// — `details: HashMap` and the `source: Box` push it - /// well past clippy's `result_large_err` threshold). Boxing keeps - /// `Result` pointer-sized on the success path; the - /// allocation only happens on the error path. - /// - /// `.boxed()` is sugar for `Box::new(...)` that reads better at - /// construction sites: `PluginError::Config { ... }.boxed()`. - /// `?` already calls `From::from`, and `From for Box` is - /// built into std, so existing `?` chains keep working. - pub fn boxed(self) -> Box { - Box::new(self) - } -} - -// --------------------------------------------------------------------------- -// Plugin Error Record -// --------------------------------------------------------------------------- - -/// A `Clone`-able, serialization-friendly snapshot of a `PluginError`. -/// -/// Used in `PipelineResult.errors` to surface execution failures from -/// `on_error: ignore` / `on_error: disable` plugins to the caller — -/// previously those errors were only logged via `tracing::warn!` and -/// were invisible to programmatic consumers (agents, dashboards, -/// retry logic). -/// -/// `PluginError` itself can't be `Clone` because of its -/// `Box` source field, and that -/// field doesn't survive serialization anyway. `PluginErrorRecord` -/// flattens the five enum variants into a single shape — the -/// `From<&PluginError>` impl handles the variant-to-fields mapping. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PluginErrorRecord { - pub plugin_name: String, - pub message: String, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub code: Option, - #[serde(default, skip_serializing_if = "HashMap::is_empty")] - pub details: HashMap, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub proto_error_code: Option, -} - -/// Forward `&Box` to the `&PluginError` impl. -/// -/// Public APIs return `Result>` (see -/// `PluginError::boxed`), which means error-handling code in the -/// pipeline (e.g., `Ok(Err(e))` inside `executor::run_*_phase`) holds -/// `e: Box`. This blanket forward keeps existing -/// `(&e).into()` call sites working without forcing every caller to -/// write `(&*e).into()` after the boxing migration. -impl From<&Box> for PluginErrorRecord { - fn from(e: &Box) -> Self { - PluginErrorRecord::from(e.as_ref()) - } -} - -impl From<&PluginError> for PluginErrorRecord { - fn from(e: &PluginError) -> Self { - match e { - PluginError::Execution { - plugin_name, - message, - code, - details, - proto_error_code, - .. - } => Self { - plugin_name: plugin_name.clone(), - message: message.clone(), - code: code.clone(), - details: details.clone(), - proto_error_code: *proto_error_code, - }, - PluginError::Timeout { - plugin_name, - timeout_ms, - proto_error_code, - } => Self { - plugin_name: plugin_name.clone(), - message: format!("plugin timed out after {}ms", timeout_ms), - code: Some("timeout".into()), - details: HashMap::new(), - proto_error_code: *proto_error_code, - }, - PluginError::Violation { - plugin_name, - violation, - } => Self { - plugin_name: plugin_name.clone(), - message: format!("plugin denied: {}", violation.reason), - code: Some(violation.code.clone()), - details: violation.details.clone(), - proto_error_code: violation.proto_error_code, - }, - PluginError::Config { message } => Self { - plugin_name: String::new(), - message: message.clone(), - code: Some("config".into()), - details: HashMap::new(), - proto_error_code: None, - }, - PluginError::UnknownHook { hook_type } => Self { - plugin_name: String::new(), - message: format!("unknown hook type: {}", hook_type), - code: Some("unknown_hook".into()), - details: HashMap::new(), - proto_error_code: None, - }, - } - } -} - -// --------------------------------------------------------------------------- -// Plugin Violations -// --------------------------------------------------------------------------- - -/// Structured policy violation returned by a plugin that denies execution. -/// -/// Carries a machine-readable code, human-readable reason, and optional -/// diagnostic details. Corresponds to the Python `PluginViolation` type. -/// -/// # Examples -/// -/// ``` -/// use cpex_core::error::PluginViolation; -/// -/// let v = PluginViolation::new("missing_permission", "User lacks pii_access"); -/// assert_eq!(v.code, "missing_permission"); -/// assert_eq!(v.reason, "User lacks pii_access"); -/// ``` -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PluginViolation { - /// Machine-readable violation identifier (e.g., `"missing_permission"`). - pub code: String, - - /// Short human-readable reason for the denial. - pub reason: String, - - /// Optional detailed explanation. - pub description: Option, - - /// Structured diagnostic data for logging or debugging. - pub details: HashMap, - - /// Name of the plugin that produced the violation. - /// Set by the framework after the plugin returns, not by the plugin itself. - pub plugin_name: Option, - - /// Protocol-level error code for the host to map to the wire format. - /// MCP: JSON-RPC codes (e.g., -32603). HTTP: status codes (e.g., 403). - /// Set by the plugin; the host interprets it. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub proto_error_code: Option, -} - -impl PluginViolation { - /// Create a new violation with a code and reason. - pub fn new(code: impl Into, reason: impl Into) -> Self { - Self { - code: code.into(), - reason: reason.into(), - description: None, - details: HashMap::new(), - plugin_name: None, - proto_error_code: None, - } - } - - /// Attach a detailed description. - pub fn with_description(mut self, description: impl Into) -> Self { - self.description = Some(description.into()); - self - } - - /// Attach structured diagnostic details. - pub fn with_details(mut self, details: HashMap) -> Self { - self.details = details; - self - } - - /// Attach a protocol-level error code. - pub fn with_proto_error_code(mut self, code: i64) -> Self { - self.proto_error_code = Some(code); - self - } -} - -impl std::fmt::Display for PluginViolation { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "[{}] {}", self.code, self.reason) - } -} diff --git a/crates/cpex-payload/src/extensions/agent.rs b/crates/cpex-payload/src/extensions/agent.rs deleted file mode 100644 index f6eb9c3b..00000000 --- a/crates/cpex-payload/src/extensions/agent.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/agent.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// AgentExtension — session, conversation, agent lineage. -// Mirrors cpex/framework/extensions/agent.py. - -use serde::{Deserialize, Serialize}; - -/// Conversation history context. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct ConversationContext { - /// Recent conversation history (lightweight summaries). - #[serde(default)] - pub history: Vec, - - /// LLM-generated summary of the conversation. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub summary: Option, - - /// Detected topics in the conversation. - #[serde(default)] - pub topics: Vec, -} - -/// Agent execution context extension. -/// -/// Carries session tracking, conversation context, multi-agent -/// lineage, and the original user/agent input. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct AgentExtension { - /// Original user/agent input that triggered this action. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub input: Option, - - /// Broad user/agent session identifier. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub session_id: Option, - - /// Specific dialogue/task identifier within a session. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub conversation_id: Option, - - /// Position within the conversation (0-indexed). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub turn: Option, - - /// Identifier of the agent that produced this message. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub agent_id: Option, - - /// If spawned by another agent, the parent's ID. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub parent_agent_id: Option, - - /// Optional conversation context with history. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub conversation: Option, -} diff --git a/crates/cpex-payload/src/extensions/completion.rs b/crates/cpex-payload/src/extensions/completion.rs deleted file mode 100644 index 2c3ad14d..00000000 --- a/crates/cpex-payload/src/extensions/completion.rs +++ /dev/null @@ -1,71 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/completion.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// CompletionExtension — LLM completion information. -// Mirrors cpex/framework/extensions/completion.py. - -use serde::{Deserialize, Serialize}; - -/// Why the model stopped generating. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum StopReason { - /// Natural end of message. - End, - /// Complete response (Harmony format). - Return, - /// Tool/function invocation. - Call, - /// Hit token limit. - MaxTokens, - /// Hit custom stop sequence. - StopSequence, -} - -/// Token usage statistics. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct TokenUsage { - /// Input tokens consumed. - #[serde(default)] - pub input_tokens: u32, - - /// Output tokens generated. - #[serde(default)] - pub output_tokens: u32, - - /// Total tokens (input + output). - #[serde(default)] - pub total_tokens: u32, -} - -/// LLM completion information. -/// -/// Immutable — set after the LLM responds. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct CompletionExtension { - /// Why the model stopped generating. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub stop_reason: Option, - - /// Token usage statistics. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub tokens: Option, - - /// Model identifier. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub model: Option, - - /// Raw response format (chatml, harmony, gemini, anthropic). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub raw_format: Option, - - /// Creation timestamp (ISO 8601). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub created_at: Option, - - /// Response latency in milliseconds. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub latency_ms: Option, -} diff --git a/crates/cpex-payload/src/extensions/container.rs b/crates/cpex-payload/src/extensions/container.rs deleted file mode 100644 index 6409bf43..00000000 --- a/crates/cpex-payload/src/extensions/container.rs +++ /dev/null @@ -1,711 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/container.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// Extensions and OwnedExtensions — typed containers for all -// extension data passed separately from the payload to handlers. -// -// Extensions is fully immutable (all Arc) — zero-copy shareable. -// OwnedExtensions is the plugin's writeable workspace, created by -// cow_copy(), returned in PluginResult::modify_extensions(). - -use std::collections::HashMap; -use std::sync::Arc; - -use serde::{Deserialize, Serialize}; - -use super::agent::AgentExtension; -use super::completion::CompletionExtension; -use super::delegation::DelegationExtension; -use super::framework::FrameworkExtension; -use super::guarded::{Guarded, WriteToken}; -use super::http::HttpExtension; -use super::llm::LLMExtension; -use super::mcp::MCPExtension; -use super::meta::MetaExtension; -use super::provenance::ProvenanceExtension; -use super::request::RequestExtension; -use super::security::SecurityExtension; - -// --------------------------------------------------------------------------- -// Extensions — all Arc, fully immutable, zero-copy shareable -// --------------------------------------------------------------------------- - -/// Typed container for all message extensions. -/// -/// All slots are `Arc` — fully immutable, zero-copy shareable. -/// Cloning is all refcount bumps. `filter_extensions()` creates a -/// filtered view by setting unwanted slots to `None` (still all Arc, -/// no deep copies). Plugins receive `&Extensions` (zero cost). -/// -/// To modify, plugins call `cow_copy()` which returns an -/// `OwnedExtensions` with mutable/monotonic/guarded slots cloned -/// out of Arc and write tokens propagated. -/// -/// Mirrors Python's `cpex.framework.extensions.Extensions`. -#[derive(Debug, Default, Serialize, Deserialize)] -pub struct Extensions { - /// Execution environment and request tracing (immutable). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub request: Option>, - - /// Agent execution context — session, conversation, lineage (immutable). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub agent: Option>, - - /// HTTP headers (frozen as Arc — unfrozen in OwnedExtensions). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub http: Option>, - - /// Security — labels, classification, subject (frozen as Arc). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub security: Option>, - - /// Delegation chain (frozen as Arc). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub delegation: Option>, - - /// MCP entity metadata (immutable). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub mcp: Option>, - - /// LLM completion information (immutable). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub completion: Option>, - - /// Origin and message threading (immutable). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub provenance: Option>, - - /// Model identity and capabilities (immutable). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub llm: Option>, - - /// Agentic framework context (immutable). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub framework: Option>, - - /// Host-provided operational metadata (immutable). - #[serde(default)] - pub meta: Option>, - - /// Custom extensions (frozen as Arc — unfrozen in OwnedExtensions). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub custom: Option>>, - - /// Write tokens — set by the executor per plugin, NOT serialized. - /// Used by `cow_copy()` to propagate write access to OwnedExtensions. - #[serde(skip)] - pub http_write_token: Option, - #[serde(skip)] - pub labels_write_token: Option, - #[serde(skip)] - pub delegation_write_token: Option, -} - -impl Clone for Extensions { - /// All Arc bumps — zero data copies. Write tokens are NOT cloned. - fn clone(&self) -> Self { - Self { - request: self.request.clone(), - agent: self.agent.clone(), - http: self.http.clone(), - security: self.security.clone(), - delegation: self.delegation.clone(), - mcp: self.mcp.clone(), - completion: self.completion.clone(), - provenance: self.provenance.clone(), - llm: self.llm.clone(), - framework: self.framework.clone(), - meta: self.meta.clone(), - custom: self.custom.clone(), - http_write_token: None, - labels_write_token: None, - delegation_write_token: None, - } - } -} - -impl Extensions { - /// Create a copy-on-write owned copy for modification. - /// - /// Immutable slots share the same `Arc` (refcount bump, ~1ns). - /// Mutable/monotonic/guarded slots are cloned out of Arc into - /// owned values — the plugin can modify them directly. - /// Write tokens are propagated from the original. - /// - /// # Usage - /// - /// ```ignore - /// fn handle(&self, payload: &P, ext: &Extensions, ctx: &mut PluginContext) -> PluginResult

{ - /// let mut owned = ext.cow_copy(); - /// owned.security.as_mut().unwrap().add_label("CHECKED"); - /// if let Some(ref token) = owned.http_write_token { - /// owned.http.as_mut().unwrap().write(token).set_header("X-Foo", "bar"); - /// } - /// PluginResult::modify_extensions(owned) - /// } - /// ``` - pub fn cow_copy(&self) -> OwnedExtensions { - OwnedExtensions { - // Immutable — same Arc pointers - request: self.request.clone(), - agent: self.agent.clone(), - mcp: self.mcp.clone(), - completion: self.completion.clone(), - provenance: self.provenance.clone(), - llm: self.llm.clone(), - framework: self.framework.clone(), - meta: self.meta.clone(), - - // Mutable/monotonic/guarded — cloned out of Arc into owned - http: self.http.as_ref().map(|arc| Guarded::new((**arc).clone())), - security: self.security.as_ref().map(|arc| (**arc).clone()), - delegation: self.delegation.as_ref().map(|arc| (**arc).clone()), - custom: self.custom.as_ref().map(|arc| (**arc).clone()), - - // Write tokens — propagated from the original - http_write_token: if self.http_write_token.is_some() { - Some(WriteToken::new()) - } else { - None - }, - labels_write_token: if self.labels_write_token.is_some() { - Some(WriteToken::new()) - } else { - None - }, - delegation_write_token: if self.delegation_write_token.is_some() { - Some(WriteToken::new()) - } else { - None - }, - } - } - - /// Validate that immutable slots were not tampered with. - /// - /// A slot that is `None` in modified (because capability filtering - /// hid it from the plugin) is always valid — the plugin never saw - /// it. Only flag as tampering when both are `Some` with different - /// Arc pointers, or when the original is `None` but modified is - /// `Some` (the plugin fabricated a slot it shouldn't have). - pub fn validate_immutable(&self, modified: &OwnedExtensions) -> bool { - fn ptr_eq_opt(a: &Option>, b: &Option>) -> bool { - match (a, b) { - (Some(a), Some(b)) => Arc::ptr_eq(a, b), - (None, None) => true, - (_, None) => true, // plugin never saw it — not tampering - (None, Some(_)) => false, // plugin fabricated a slot - } - } - - ptr_eq_opt(&self.request, &modified.request) - && ptr_eq_opt(&self.agent, &modified.agent) - && ptr_eq_opt(&self.mcp, &modified.mcp) - && ptr_eq_opt(&self.completion, &modified.completion) - && ptr_eq_opt(&self.provenance, &modified.provenance) - && ptr_eq_opt(&self.llm, &modified.llm) - && ptr_eq_opt(&self.framework, &modified.framework) - && ptr_eq_opt(&self.meta, &modified.meta) - } - - /// Merge an OwnedExtensions back into this Extensions. - pub fn merge_owned(&mut self, owned: OwnedExtensions) { - self.http = owned.http.map(|g| Arc::new(g.into_inner())); - self.security = owned.security.map(Arc::new); - self.delegation = owned.delegation.map(Arc::new); - self.custom = owned.custom.map(Arc::new); - } -} - -// --------------------------------------------------------------------------- -// OwnedExtensions — plugin's writeable workspace -// --------------------------------------------------------------------------- - -/// Owned copy of extensions for plugin modification. -/// -/// Returned by `Extensions::cow_copy()`. Immutable slots share -/// the same `Arc` pointers as the original (zero copy). Mutable, -/// monotonic, and guarded slots are cloned into owned values that -/// the plugin can modify directly. -/// -/// Plugins return this in `PluginResult::modify_extensions()`. -/// The executor validates (immutable unchanged, monotonic superset) -/// and merges back into the pipeline's `Extensions`. -/// -/// Hosts never see this type — the executor converts to `Extensions` -/// before building `PipelineResult`. -#[derive(Debug)] -pub struct OwnedExtensions { - // Immutable — same Arc pointers as original - pub request: Option>, - pub agent: Option>, - pub mcp: Option>, - pub completion: Option>, - pub provenance: Option>, - pub llm: Option>, - pub framework: Option>, - pub meta: Option>, - - // Mutable/monotonic/guarded — owned, modifiable - pub http: Option>, - pub security: Option, - pub delegation: Option, - pub custom: Option>, - - // Write tokens — propagated from executor - pub http_write_token: Option, - pub labels_write_token: Option, - pub delegation_write_token: Option, -} -#[cfg(test)] -mod tests { - use super::*; - use crate::extensions::{ - DelegationExtension, HttpExtension, RequestExtension, SecurityExtension, - }; - - fn make_extensions() -> Extensions { - let mut security = SecurityExtension::default(); - security.add_label("PII"); - - let mut http = HttpExtension::default(); - http.set_header("Authorization", "Bearer token"); - - Extensions { - request: Some(Arc::new(RequestExtension { - request_id: Some("req-001".into()), - ..Default::default() - })), - security: Some(Arc::new(security)), - http: Some(Arc::new(http)), - delegation: Some(Arc::new(DelegationExtension::default())), - meta: Some(Arc::new(MetaExtension { - entity_type: Some("tool".into()), - ..Default::default() - })), - ..Default::default() - } - } - - #[test] - fn test_cow_copy_shares_immutable_arcs() { - let ext = make_extensions(); - let cow = ext.cow_copy(); - - // Immutable slots share the same Arc — zero copy - assert!(Arc::ptr_eq( - ext.request.as_ref().unwrap(), - cow.request.as_ref().unwrap() - )); - assert!(Arc::ptr_eq( - ext.meta.as_ref().unwrap(), - cow.meta.as_ref().unwrap() - )); - } - - #[test] - fn test_cow_copy_deep_clones_mutable_slots() { - let ext = make_extensions(); - let cow = ext.cow_copy(); - - // Mutable/monotonic slots are deep cloned — independent copies - assert!(cow.security.is_some()); - assert!(cow.http.is_some()); - assert!(cow.delegation.is_some()); - - // Modifying the COW copy doesn't affect the original - cow.security.as_ref().unwrap().has_label("PII"); - } - - #[test] - fn test_cow_copy_propagates_write_tokens() { - let mut ext = make_extensions(); - - // No tokens on the original → no tokens on COW - let cow_no_tokens = ext.cow_copy(); - assert!(cow_no_tokens.http_write_token.is_none()); - assert!(cow_no_tokens.labels_write_token.is_none()); - assert!(cow_no_tokens.delegation_write_token.is_none()); - - // Executor sets tokens based on capabilities - ext.http_write_token = Some(WriteToken::new()); - ext.labels_write_token = Some(WriteToken::new()); - - // COW copy propagates only the tokens that exist - let cow_with_tokens = ext.cow_copy(); - assert!(cow_with_tokens.http_write_token.is_some()); - assert!(cow_with_tokens.labels_write_token.is_some()); - assert!(cow_with_tokens.delegation_write_token.is_none()); // wasn't set - } - - #[test] - fn test_cow_copy_write_token_enables_guarded_write() { - let mut ext = make_extensions(); - ext.http_write_token = Some(WriteToken::new()); - - let mut cow = ext.cow_copy(); - - // Can read without token - assert_eq!( - cow.http - .as_ref() - .unwrap() - .read() - .get_header("Authorization"), - Some("Bearer token") - ); - - // Can write with token from COW - let token = cow.http_write_token.as_ref().unwrap(); - cow.http - .as_mut() - .unwrap() - .write(token) - .set_header("X-Custom", "value"); - - assert_eq!( - cow.http.as_ref().unwrap().read().get_header("X-Custom"), - Some("value") - ); - - // Original unchanged - assert!(ext.http.as_ref().unwrap().get_header("X-Custom").is_none()); - } - - #[test] - fn test_cow_copy_monotonic_label_insert() { - let mut ext = make_extensions(); - ext.labels_write_token = Some(WriteToken::new()); - - let mut cow = ext.cow_copy(); - - // Can add labels on the COW copy - cow.security.as_mut().unwrap().add_label("HIPAA"); - assert!(cow.security.as_ref().unwrap().has_label("HIPAA")); - - // Original unchanged - assert!(!ext.security.as_ref().unwrap().has_label("HIPAA")); - } - - #[test] - fn test_validate_immutable_passes_for_cow() { - let ext = make_extensions(); - let cow = ext.cow_copy(); - - // COW copy shares immutable Arcs → validation passes - assert!(ext.validate_immutable(&cow)); - } - - #[test] - fn test_validate_immutable_fails_when_tampered() { - let ext = make_extensions(); - let mut cow = ext.cow_copy(); - - // Tamper with an immutable slot - cow.request = Some(Arc::new(RequestExtension { - request_id: Some("TAMPERED".into()), - ..Default::default() - })); - - // Validation fails — different Arc pointer - assert!(!ext.validate_immutable(&cow)); - } - - #[test] - fn test_validate_immutable_both_none_passes() { - let ext = Extensions::default(); - let cow = ext.cow_copy(); - assert!(ext.validate_immutable(&cow)); - } - - #[test] - fn test_clone_drops_write_tokens() { - let mut ext = make_extensions(); - ext.http_write_token = Some(WriteToken::new()); - ext.labels_write_token = Some(WriteToken::new()); - ext.delegation_write_token = Some(WriteToken::new()); - - // Regular clone drops all tokens - let cloned = ext.clone(); - assert!(cloned.http_write_token.is_none()); - assert!(cloned.labels_write_token.is_none()); - assert!(cloned.delegation_write_token.is_none()); - - // cow_copy propagates them - let cow = ext.cow_copy(); - assert!(cow.http_write_token.is_some()); - assert!(cow.labels_write_token.is_some()); - assert!(cow.delegation_write_token.is_some()); - } - - #[test] - fn test_cow_copy_modify_multiple_fields() { - use crate::extensions::delegation::DelegationHop; - use crate::extensions::DelegationExtension; - - // Build extensions with security, http, delegation, custom - let mut security = SecurityExtension::default(); - security.add_label("PII"); - - let mut http = HttpExtension::default(); - http.set_header("Authorization", "Bearer token"); - - let mut ext = Extensions { - security: Some(Arc::new(security)), - http: Some(Arc::new(http)), - delegation: Some(Arc::new(DelegationExtension::default())), - custom: Some(Arc::new( - [("existing".to_string(), serde_json::json!("value"))].into(), - )), - meta: Some(Arc::new(MetaExtension { - entity_type: Some("tool".into()), - ..Default::default() - })), - ..Default::default() - }; - - // Executor sets all write tokens - ext.http_write_token = Some(WriteToken::new()); - ext.labels_write_token = Some(WriteToken::new()); - ext.delegation_write_token = Some(WriteToken::new()); - - // Plugin does one cow_copy, modifies multiple fields - let mut cow = ext.cow_copy(); - - // 1. Add security labels (monotonic) - cow.security.as_mut().unwrap().add_label("CHECKED"); - cow.security.as_mut().unwrap().add_label("COMPLIANT"); - - // 2. Inject HTTP headers (guarded) - let token = cow.http_write_token.as_ref().unwrap(); - cow.http - .as_mut() - .unwrap() - .write(token) - .set_header("X-Checked", "true"); - cow.http - .as_mut() - .unwrap() - .write(token) - .set_header("X-Policy", "v2"); - - // 3. Append delegation hop (monotonic) - cow.delegation.as_mut().unwrap().append_hop(DelegationHop { - subject_id: "service-a".into(), - scopes_granted: vec!["read_hr".into()], - ..Default::default() - }); - - // 4. Add custom data (mutable, no token needed) - cow.custom - .as_mut() - .unwrap() - .insert("audit.timestamp".into(), serde_json::json!("2026-04-29")); - - // Verify COW copy has all modifications - let sec = cow.security.as_ref().unwrap(); - assert!(sec.has_label("PII")); // original - assert!(sec.has_label("CHECKED")); // added - assert!(sec.has_label("COMPLIANT")); // added - - let http = cow.http.as_ref().unwrap().read(); - assert_eq!(http.get_header("Authorization"), Some("Bearer token")); // original - assert_eq!(http.get_header("X-Checked"), Some("true")); // added - assert_eq!(http.get_header("X-Policy"), Some("v2")); // added - - assert_eq!(cow.delegation.as_ref().unwrap().chain.len(), 1); - assert_eq!( - cow.delegation.as_ref().unwrap().chain[0].subject_id, - "service-a" - ); - - assert_eq!( - cow.custom.as_ref().unwrap().get("existing").unwrap(), - "value" - ); - assert_eq!( - cow.custom.as_ref().unwrap().get("audit.timestamp").unwrap(), - "2026-04-29" - ); - - // Verify original is unchanged - assert!(!ext.security.as_ref().unwrap().has_label("CHECKED")); - assert!(ext.http.as_ref().unwrap().get_header("X-Checked").is_none()); - assert!(ext.delegation.as_ref().unwrap().chain.is_empty()); - assert!(!ext.custom.as_ref().unwrap().contains_key("audit.timestamp")); - - // Immutable slots still valid - assert!(ext.validate_immutable(&cow)); - } - - #[test] - fn test_validate_immutable_passes_when_slot_filtered_out() { - // Bug fix regression: when capability filtering hides a slot - // from the plugin (e.g., agent=None in owned because plugin - // lacks read_agent), validate_immutable must NOT treat that - // as tampering. - let ext = make_extensions(); - let mut cow = ext.cow_copy(); - - // Simulate capability filtering hiding the agent slot - cow.agent = None; - - // Validation should pass — plugin never saw the slot - assert!(ext.validate_immutable(&cow)); - } - - #[test] - fn test_validate_immutable_fails_when_slot_fabricated() { - // If the original has no agent but the plugin returns one, - // that's fabrication — should fail. - let ext = Extensions::default(); // no agent - let mut cow = ext.cow_copy(); - - cow.agent = Some(Arc::new(AgentExtension { - agent_id: Some("fabricated".into()), - ..Default::default() - })); - - assert!(!ext.validate_immutable(&cow)); - } - - #[test] - fn test_validate_immutable_passes_multiple_slots_filtered() { - // Multiple immutable slots filtered out — all should pass - let ext = make_extensions(); - let mut cow = ext.cow_copy(); - - cow.agent = None; - cow.mcp = None; - cow.completion = None; - cow.framework = None; - - assert!(ext.validate_immutable(&cow)); - } - - #[test] - fn test_merge_owned_preserves_http_response_headers() { - // Bug fix regression: merge_owned must preserve response - // headers written by a plugin through Guarded write access. - let mut http = HttpExtension::default(); - http.set_request_header("Authorization", "Bearer tok"); - - let mut ext = Extensions { - http: Some(Arc::new(http)), - ..Default::default() - }; - ext.http_write_token = Some(WriteToken::new()); - - let mut cow = ext.cow_copy(); - - // Plugin writes response headers through the guard - let token = cow.http_write_token.as_ref().unwrap(); - let h = cow.http.as_mut().unwrap().write(token); - h.set_response_header("X-Tool-Name", "get_compensation"); - h.set_response_header("X-Status", "success"); - - // Merge back - ext.merge_owned(cow); - - // Response headers must be present after merge - let merged_http = ext.http.as_ref().unwrap(); - assert_eq!( - merged_http.get_response_header("X-Tool-Name"), - Some("get_compensation") - ); - assert_eq!(merged_http.get_response_header("X-Status"), Some("success")); - // Original request headers preserved - assert_eq!( - merged_http.get_request_header("Authorization"), - Some("Bearer tok") - ); - } - - #[test] - fn test_merge_owned_with_filtered_security() { - // A plugin without read_labels gets empty labels in its - // filtered view. After cow_copy + merge_owned, the pipeline's - // security labels must be preserved (not overwritten with empty). - let mut security = SecurityExtension::default(); - security.add_label("PII"); - security.add_label("HR"); - - let ext = Extensions { - security: Some(Arc::new(security)), - ..Default::default() - }; - - // Simulate: plugin has no read_labels, so filtered security - // has empty labels. cow_copy of filtered would have empty labels. - let mut cow = ext.cow_copy(); - - // Plugin's owned security has the labels (from cow_copy of full ext) - // But in the real flow, it would be from the filtered ext. - // Simulate filtered: clear labels - cow.security.as_mut().unwrap().labels = crate::extensions::MonotonicSet::new(); - - // merge_owned replaces pipeline security with owned - let mut ext_mut = ext.clone(); - ext_mut.merge_owned(cow); - - // After merge, the security comes from the owned (which had empty labels) - // This is expected — the executor's monotonic check should prevent - // this case. merge_owned itself is just a field replacement. - let merged_sec = ext_mut.security.as_ref().unwrap(); - assert!(!merged_sec.has_label("PII")); // replaced by owned - } - - #[test] - fn test_merge_owned_none_http_preserves_pipeline() { - // If owned.http is None (plugin had no read_headers capability), - // merge_owned replaces with None. The executor should only call - // merge_owned when the plugin actually modified something. - let mut http = HttpExtension::default(); - http.set_request_header("X-Original", "value"); - - let mut ext = Extensions { - http: Some(Arc::new(http)), - ..Default::default() - }; - - let mut cow = ext.cow_copy(); - cow.http = None; // simulate filtered-out HTTP - - ext.merge_owned(cow); - - // HTTP is now None — this is the raw merge behavior. - // The executor guards against this by only calling merge_owned - // when the plugin returned modify_extensions. - assert!(ext.http.is_none()); - } - - #[test] - fn test_read_only_plugin_zero_cost() { - // Plugin that only reads — no cow_copy, no clone - let ext = make_extensions(); - - // Read security labels - let has_pii = ext - .security - .as_ref() - .map(|s| s.has_label("PII")) - .unwrap_or(false); - assert!(has_pii); - - // Read HTTP headers - let auth = ext - .http - .as_ref() - .and_then(|h| h.get_header("Authorization")); - assert_eq!(auth, Some("Bearer token")); - - // Read meta - let entity = ext.meta.as_ref().and_then(|m| m.entity_type.as_deref()); - assert_eq!(entity, Some("tool")); - - // No cow_copy called — zero allocations for read-only access - } -} diff --git a/crates/cpex-payload/src/extensions/delegation.rs b/crates/cpex-payload/src/extensions/delegation.rs deleted file mode 100644 index e5f5ef50..00000000 --- a/crates/cpex-payload/src/extensions/delegation.rs +++ /dev/null @@ -1,165 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/delegation.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// DelegationExtension — token delegation chain. -// Mirrors cpex/framework/extensions/delegation.py. - -use serde::{Deserialize, Serialize}; - -/// A single hop in the delegation chain. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct DelegationHop { - /// Subject ID of the delegator. - pub subject_id: String, - - /// Subject type of the delegator. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub subject_type: Option, - - /// Target audience. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub audience: Option, - - /// Scopes granted in this delegation step. - #[serde(default)] - pub scopes_granted: Vec, - - /// Timestamp of delegation (ISO 8601). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub timestamp: Option, - - /// Time-to-live in seconds. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub ttl_seconds: Option, - - /// Delegation strategy used. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub strategy: Option, - - /// Whether this hop was resolved from cache. - #[serde(default)] - pub from_cache: bool, -} - -/// Delegation chain extension. -/// -/// Append-only — each hop narrows scope. A delegate cannot have -/// more permissions than the delegator. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct DelegationExtension { - /// Ordered delegation chain. - #[serde(default)] - pub chain: Vec, - - /// Chain depth (number of hops). - #[serde(default)] - pub depth: usize, - - /// Subject ID of the original delegator. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub origin_subject_id: Option, - - /// Subject ID of the current actor. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub actor_subject_id: Option, - - /// Whether delegation has occurred. - #[serde(default)] - pub delegated: bool, - - /// Age of the delegation chain in seconds. - #[serde(default)] - pub age_seconds: f64, -} - -impl DelegationExtension { - /// Append a delegation hop (monotonic — cannot remove). - pub fn append_hop(&mut self, hop: DelegationHop) { - self.chain.push(hop); - self.depth = self.chain.len(); - self.delegated = true; - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_delegation_starts_empty() { - let del = DelegationExtension::default(); - assert!(del.chain.is_empty()); - assert_eq!(del.depth, 0); - assert!(!del.delegated); - } - - #[test] - fn test_append_hop() { - let mut del = DelegationExtension::default(); - del.append_hop(DelegationHop { - subject_id: "alice".into(), - scopes_granted: vec!["read_hr".into()], - ..Default::default() - }); - - assert_eq!(del.chain.len(), 1); - assert_eq!(del.depth, 1); - assert!(del.delegated); - assert_eq!(del.chain[0].subject_id, "alice"); - assert_eq!(del.chain[0].scopes_granted, vec!["read_hr"]); - } - - #[test] - fn test_append_multiple_hops() { - let mut del = DelegationExtension { - origin_subject_id: Some("alice".into()), - ..Default::default() - }; - - del.append_hop(DelegationHop { - subject_id: "alice".into(), - audience: Some("service-b".into()), - scopes_granted: vec!["read".into(), "write".into()], - strategy: Some("token_exchange".into()), - ..Default::default() - }); - - del.append_hop(DelegationHop { - subject_id: "service-b".into(), - audience: Some("service-c".into()), - scopes_granted: vec!["read".into()], // narrowed scope - ..Default::default() - }); - - assert_eq!(del.chain.len(), 2); - assert_eq!(del.depth, 2); - // Second hop has narrower scope - assert_eq!(del.chain[1].scopes_granted, vec!["read"]); - } - - #[test] - fn test_delegation_serde_roundtrip() { - let mut del = DelegationExtension { - origin_subject_id: Some("alice".into()), - actor_subject_id: Some("service-b".into()), - ..Default::default() - }; - del.append_hop(DelegationHop { - subject_id: "alice".into(), - subject_type: Some("user".into()), - scopes_granted: vec!["admin".into()], - from_cache: true, - ..Default::default() - }); - - let json = serde_json::to_string(&del).unwrap(); - let deserialized: DelegationExtension = serde_json::from_str(&json).unwrap(); - - assert_eq!(deserialized.depth, 1); - assert!(deserialized.delegated); - assert_eq!(deserialized.origin_subject_id.as_deref(), Some("alice")); - assert!(deserialized.chain[0].from_cache); - } -} diff --git a/crates/cpex-payload/src/extensions/filter.rs b/crates/cpex-payload/src/extensions/filter.rs deleted file mode 100644 index 1841164a..00000000 --- a/crates/cpex-payload/src/extensions/filter.rs +++ /dev/null @@ -1,547 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/filter.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// Extension filtering — capability-gated visibility. -// -// Builds a Extensions from Extensions + declared capabilities. -// Secure by default: slots not explicitly included are None. -// -// Mirrors cpex/framework/extensions/tiers.py::filter_extensions(). - -use std::collections::HashSet; -use std::sync::Arc; - -use super::container::Extensions; - -use super::security::{SecurityExtension, SubjectExtension}; -use super::tiers::{AccessPolicy, Capability, MutabilityTier, SlotPolicy}; - -// --------------------------------------------------------------------------- -// Slot Registry — static policies per extension slot -// --------------------------------------------------------------------------- - -/// Extension slot identifiers. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum SlotName { - Request, - Agent, - Http, - Meta, - Delegation, - Custom, - Mcp, - Completion, - Provenance, - Llm, - Framework, - // Security sub-slots - SecurityLabels, - SecuritySubject, - SecuritySubjectRoles, - SecuritySubjectTeams, - SecuritySubjectClaims, - SecuritySubjectPermissions, - SecurityObjects, - SecurityData, -} - -/// Get the policy for a given slot. -pub fn slot_policy(slot: SlotName) -> SlotPolicy { - match slot { - // Unrestricted immutable — always visible - SlotName::Request => SlotPolicy { - tier: MutabilityTier::Immutable, - access: AccessPolicy::Unrestricted, - read_cap: None, - write_cap: None, - }, - SlotName::Provenance => SlotPolicy { - tier: MutabilityTier::Immutable, - access: AccessPolicy::Unrestricted, - read_cap: None, - write_cap: None, - }, - SlotName::Completion => SlotPolicy { - tier: MutabilityTier::Immutable, - access: AccessPolicy::Unrestricted, - read_cap: None, - write_cap: None, - }, - SlotName::Llm => SlotPolicy { - tier: MutabilityTier::Immutable, - access: AccessPolicy::Unrestricted, - read_cap: None, - write_cap: None, - }, - SlotName::Framework => SlotPolicy { - tier: MutabilityTier::Immutable, - access: AccessPolicy::Unrestricted, - read_cap: None, - write_cap: None, - }, - SlotName::Mcp => SlotPolicy { - tier: MutabilityTier::Immutable, - access: AccessPolicy::Unrestricted, - read_cap: None, - write_cap: None, - }, - SlotName::Meta => SlotPolicy { - tier: MutabilityTier::Immutable, - access: AccessPolicy::Unrestricted, - read_cap: None, - write_cap: None, - }, - SlotName::Custom => SlotPolicy { - tier: MutabilityTier::Mutable, - access: AccessPolicy::Unrestricted, - read_cap: None, - write_cap: None, - }, - // Capability-gated - SlotName::Agent => SlotPolicy { - tier: MutabilityTier::Immutable, - access: AccessPolicy::CapabilityGated, - read_cap: Some(Capability::ReadAgent), - write_cap: None, - }, - SlotName::Http => SlotPolicy { - tier: MutabilityTier::Mutable, - access: AccessPolicy::CapabilityGated, - read_cap: Some(Capability::ReadHeaders), - write_cap: Some(Capability::WriteHeaders), - }, - SlotName::Delegation => SlotPolicy { - tier: MutabilityTier::Monotonic, - access: AccessPolicy::CapabilityGated, - read_cap: Some(Capability::ReadDelegation), - write_cap: Some(Capability::AppendDelegation), - }, - // Security sub-slots - SlotName::SecurityLabels => SlotPolicy { - tier: MutabilityTier::Monotonic, - access: AccessPolicy::CapabilityGated, - read_cap: Some(Capability::ReadLabels), - write_cap: Some(Capability::AppendLabels), - }, - SlotName::SecuritySubject => SlotPolicy { - tier: MutabilityTier::Immutable, - access: AccessPolicy::CapabilityGated, - read_cap: Some(Capability::ReadSubject), - write_cap: None, - }, - SlotName::SecuritySubjectRoles => SlotPolicy { - tier: MutabilityTier::Immutable, - access: AccessPolicy::CapabilityGated, - read_cap: Some(Capability::ReadRoles), - write_cap: None, - }, - SlotName::SecuritySubjectTeams => SlotPolicy { - tier: MutabilityTier::Immutable, - access: AccessPolicy::CapabilityGated, - read_cap: Some(Capability::ReadTeams), - write_cap: None, - }, - SlotName::SecuritySubjectClaims => SlotPolicy { - tier: MutabilityTier::Immutable, - access: AccessPolicy::CapabilityGated, - read_cap: Some(Capability::ReadClaims), - write_cap: None, - }, - SlotName::SecuritySubjectPermissions => SlotPolicy { - tier: MutabilityTier::Immutable, - access: AccessPolicy::CapabilityGated, - read_cap: Some(Capability::ReadPermissions), - write_cap: None, - }, - SlotName::SecurityObjects => SlotPolicy { - tier: MutabilityTier::Immutable, - access: AccessPolicy::Unrestricted, - read_cap: None, - write_cap: None, - }, - SlotName::SecurityData => SlotPolicy { - tier: MutabilityTier::Immutable, - access: AccessPolicy::Unrestricted, - read_cap: None, - write_cap: None, - }, - } -} - -// --------------------------------------------------------------------------- -// Capability Checking -// --------------------------------------------------------------------------- - -/// Check if a set of capabilities grants read access to a slot. -fn has_read_access(policy: &SlotPolicy, capabilities: &HashSet) -> bool { - if policy.access == AccessPolicy::Unrestricted { - return true; - } - if let Some(read_cap) = &policy.read_cap { - let cap_str = serde_json::to_string(read_cap) - .unwrap_or_default() - .trim_matches('"') - .to_string(); - if capabilities.contains(&cap_str) { - return true; - } - } - // Check if any subject sub-field cap implies read_subject - if policy.read_cap == Some(Capability::ReadSubject) { - return has_any_subject_capability(capabilities); - } - false -} - -/// Check if capabilities include any subject-related capability. -fn has_any_subject_capability(capabilities: &HashSet) -> bool { - let subject_caps = [ - Capability::ReadSubject, - Capability::ReadRoles, - Capability::ReadTeams, - Capability::ReadClaims, - Capability::ReadPermissions, - ]; - for cap in &subject_caps { - let cap_str = serde_json::to_string(cap) - .unwrap_or_default() - .trim_matches('"') - .to_string(); - if capabilities.contains(&cap_str) { - return true; - } - } - false -} - -/// Helper: convert Capability to its string representation. -fn cap_str(cap: Capability) -> String { - serde_json::to_string(&cap) - .unwrap_or_default() - .trim_matches('"') - .to_string() -} - -// --------------------------------------------------------------------------- -// Filter Extensions -// --------------------------------------------------------------------------- - -/// Build a Extensions containing only slots the plugin can access. -/// -/// Starts from an empty Extensions and clones in only the -/// slots the plugin has read access to. Slots not explicitly included -/// are `None`. Secure by default — if a new slot is added to -/// Extensions but not registered here, it remains hidden. -/// -/// For the security extension, filtering is granular: unrestricted -/// sub-fields (objects, data, classification) are always included, -/// while labels and subject sub-fields are gated by capabilities. -pub fn filter_extensions(extensions: &Extensions, capabilities: &HashSet) -> Extensions { - // Build the unrestricted-immutable fields up front; capability-gated - // slots stay default and are filled in below. - let mut filtered = Extensions { - request: extensions.request.clone(), - provenance: extensions.provenance.clone(), - completion: extensions.completion.clone(), - llm: extensions.llm.clone(), - framework: extensions.framework.clone(), - mcp: extensions.mcp.clone(), - meta: extensions.meta.clone(), - custom: extensions.custom.clone(), - ..Default::default() - }; - - // Capability-gated: delegation - if extensions.delegation.is_some() { - let policy = slot_policy(SlotName::Delegation); - if has_read_access(&policy, capabilities) { - filtered.delegation = extensions.delegation.clone(); - } - } - - // Capability-gated: agent - if extensions.agent.is_some() { - let policy = slot_policy(SlotName::Agent); - if has_read_access(&policy, capabilities) { - filtered.agent = extensions.agent.clone(); - } - } - - // Capability-gated: http - if extensions.http.is_some() { - let policy = slot_policy(SlotName::Http); - if has_read_access(&policy, capabilities) { - filtered.http = extensions.http.clone(); - } - } - - // Security — granular sub-field filtering - if let Some(ref security) = extensions.security { - filtered.security = Some(Arc::new(build_filtered_security(security, capabilities))); - } - - filtered -} - -/// Build a filtered SecurityExtension containing only accessible fields. -/// -/// Unrestricted sub-fields (objects, data, classification) are always -/// included. Labels and subject sub-fields are gated by capabilities. -fn build_filtered_security( - security: &SecurityExtension, - capabilities: &HashSet, -) -> SecurityExtension { - let mut filtered = SecurityExtension { - // Unrestricted — always included - objects: security.objects.clone(), - data: security.data.clone(), - classification: security.classification.clone(), - // Agent identity and auth method — always included (host-set, immutable) - agent: security.agent.clone(), - auth_method: security.auth_method.clone(), - // Default empty for capability-gated fields - labels: super::MonotonicSet::new(), - subject: None, - }; - - // Labels — capability-gated - let labels_policy = slot_policy(SlotName::SecurityLabels); - if has_read_access(&labels_policy, capabilities) { - filtered.labels = security.labels.clone(); - } - - // Subject — granular capability-gated - if let Some(ref subject) = security.subject { - if has_any_subject_capability(capabilities) { - filtered.subject = Some(build_filtered_subject(subject, capabilities)); - } - } - - filtered -} - -/// Build a filtered SubjectExtension containing only accessible fields. -/// -/// Always includes id and type (base subject access). Individual -/// sub-fields are only populated if the plugin holds the capability. -fn build_filtered_subject( - subject: &SubjectExtension, - capabilities: &HashSet, -) -> SubjectExtension { - SubjectExtension { - // Always included with any subject access - id: subject.id.clone(), - subject_type: subject.subject_type, - // Capability-gated sub-fields - roles: if capabilities.contains(&cap_str(Capability::ReadRoles)) { - subject.roles.clone() - } else { - HashSet::new() - }, - permissions: if capabilities.contains(&cap_str(Capability::ReadPermissions)) { - subject.permissions.clone() - } else { - HashSet::new() - }, - teams: if capabilities.contains(&cap_str(Capability::ReadTeams)) { - subject.teams.clone() - } else { - HashSet::new() - }, - claims: if capabilities.contains(&cap_str(Capability::ReadClaims)) { - subject.claims.clone() - } else { - std::collections::HashMap::new() - }, - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::extensions::meta::MetaExtension; - use crate::extensions::SecurityExtension; - - fn make_full_extensions() -> Extensions { - let mut security = SecurityExtension::default(); - security.add_label("PII"); - security.classification = Some("confidential".into()); - security.subject = Some(SubjectExtension { - id: Some("alice".into()), - subject_type: Some(super::super::security::SubjectType::User), - roles: ["admin".to_string()].into(), - permissions: ["read_all".to_string()].into(), - teams: ["engineering".to_string()].into(), - claims: [("iss".to_string(), "example.com".to_string())].into(), - }); - - let mut http = super::super::HttpExtension::default(); - http.set_header("Authorization", "Bearer token123"); - - Extensions { - request: Some(std::sync::Arc::new(super::super::RequestExtension { - request_id: Some("req-001".into()), - ..Default::default() - })), - security: Some(Arc::new(security)), - http: Some(std::sync::Arc::new(http)), - agent: Some(std::sync::Arc::new(super::super::AgentExtension { - agent_id: Some("agent-1".into()), - ..Default::default() - })), - delegation: Some(std::sync::Arc::new(super::super::DelegationExtension { - delegated: true, - ..Default::default() - })), - meta: Some(std::sync::Arc::new(MetaExtension { - entity_type: Some("tool".into()), - entity_name: Some("get_compensation".into()), - ..Default::default() - })), - custom: Some(Arc::new( - [("key".to_string(), serde_json::json!("value"))].into(), - )), - ..Default::default() - } - } - - #[test] - fn test_no_capabilities_sees_unrestricted_only() { - let ext = make_full_extensions(); - let caps = HashSet::new(); - let filtered = filter_extensions(&ext, &caps); - - // Unrestricted slots visible - assert!(filtered.request.is_some()); - assert!(filtered.meta.is_some()); - assert!(filtered.custom.is_some()); - - // Capability-gated slots hidden - assert!(filtered.http.is_none()); - assert!(filtered.agent.is_none()); - assert!(filtered.delegation.is_none()); - - // Security: objects/data/classification visible, labels/subject hidden - let sec = filtered.security.as_ref().unwrap(); - assert!(sec.labels.is_empty()); - assert!(sec.subject.is_none()); - assert_eq!(sec.classification, Some("confidential".into())); - } - - #[test] - fn test_read_headers_capability() { - let ext = make_full_extensions(); - let caps: HashSet = ["read_headers".to_string()].into(); - let filtered = filter_extensions(&ext, &caps); - - assert!(filtered.http.is_some()); - assert_eq!( - filtered.http.unwrap().get_header("Authorization"), - Some("Bearer token123") - ); - // Still no agent access - assert!(filtered.agent.is_none()); - } - - #[test] - fn test_read_agent_capability() { - let ext = make_full_extensions(); - let caps: HashSet = ["read_agent".to_string()].into(); - let filtered = filter_extensions(&ext, &caps); - - assert!(filtered.agent.is_some()); - assert_eq!(filtered.agent.unwrap().agent_id, Some("agent-1".into())); - assert!(filtered.http.is_none()); - } - - #[test] - fn test_read_labels_capability() { - let ext = make_full_extensions(); - let caps: HashSet = ["read_labels".to_string()].into(); - let filtered = filter_extensions(&ext, &caps); - - let sec = filtered.security.as_ref().unwrap(); - assert!(sec.has_label("PII")); - // No subject access — just label access - assert!(sec.subject.is_none()); - } - - #[test] - fn test_read_subject_sees_id_and_type_only() { - let ext = make_full_extensions(); - let caps: HashSet = ["read_subject".to_string()].into(); - let filtered = filter_extensions(&ext, &caps); - - let sec = filtered.security.as_ref().unwrap(); - let subject = sec.subject.as_ref().unwrap(); - assert_eq!(subject.id, Some("alice".into())); - // Sub-fields empty without specific capabilities - assert!(subject.roles.is_empty()); - assert!(subject.permissions.is_empty()); - assert!(subject.teams.is_empty()); - assert!(subject.claims.is_empty()); - } - - #[test] - fn test_read_roles_implies_subject_access() { - let ext = make_full_extensions(); - let caps: HashSet = ["read_roles".to_string()].into(); - let filtered = filter_extensions(&ext, &caps); - - let sec = filtered.security.as_ref().unwrap(); - let subject = sec.subject.as_ref().unwrap(); - // Has subject access (implied by read_roles) - assert_eq!(subject.id, Some("alice".into())); - // Has roles - assert!(subject.roles.contains("admin")); - // No other sub-fields - assert!(subject.permissions.is_empty()); - assert!(subject.teams.is_empty()); - } - - #[test] - fn test_full_capabilities() { - let ext = make_full_extensions(); - let caps: HashSet = [ - "read_headers", - "read_agent", - "read_delegation", - "read_labels", - "read_subject", - "read_roles", - "read_permissions", - "read_teams", - "read_claims", - ] - .into_iter() - .map(String::from) - .collect(); - - let filtered = filter_extensions(&ext, &caps); - - // Everything visible - assert!(filtered.http.is_some()); - assert!(filtered.agent.is_some()); - assert!(filtered.delegation.is_some()); - - let sec = filtered.security.as_ref().unwrap(); - assert!(sec.has_label("PII")); - let subject = sec.subject.as_ref().unwrap(); - assert!(subject.roles.contains("admin")); - assert!(subject.permissions.contains("read_all")); - assert!(subject.teams.contains("engineering")); - assert!(subject.claims.contains_key("iss")); - } - - #[test] - fn test_read_delegation_capability() { - let ext = make_full_extensions(); - let caps: HashSet = ["read_delegation".to_string()].into(); - let filtered = filter_extensions(&ext, &caps); - - assert!(filtered.delegation.is_some()); - assert!(filtered.delegation.unwrap().delegated); - } -} diff --git a/crates/cpex-payload/src/extensions/framework.rs b/crates/cpex-payload/src/extensions/framework.rs deleted file mode 100644 index b4654055..00000000 --- a/crates/cpex-payload/src/extensions/framework.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/framework.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// FrameworkExtension — agentic framework context. -// Mirrors cpex/framework/extensions/framework.py. - -use std::collections::HashMap; - -use serde::{Deserialize, Serialize}; - -/// Agentic framework context. -/// -/// Carries framework identity and graph/workflow metadata. -/// Immutable — set by the host. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct FrameworkExtension { - /// Framework name (e.g., "langchain", "crewai", "autogen"). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub framework: Option, - - /// Framework version. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub framework_version: Option, - - /// Node ID in an agent graph/workflow. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub node_id: Option, - - /// Graph/workflow ID. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub graph_id: Option, - - /// Framework-specific metadata. - #[serde(default)] - pub metadata: HashMap, -} diff --git a/crates/cpex-payload/src/extensions/guarded.rs b/crates/cpex-payload/src/extensions/guarded.rs deleted file mode 100644 index fb369a16..00000000 --- a/crates/cpex-payload/src/extensions/guarded.rs +++ /dev/null @@ -1,144 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/guarded.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// Guarded — capability-gated write access. -// -// A value that requires a WriteToken for mutable access. Read access -// is always available (if the plugin can see the extension at all). -// Write access requires the framework to issue a WriteToken based on -// the plugin's declared capabilities. -// -// Mirrors the spec in rust-implementation-spec.md §2.3. - -use serde::{Deserialize, Serialize}; - -/// A value that requires a WriteToken for mutable access. -/// -/// Read access via `.read()` is always available. Write access via -/// `.write(token)` requires a `WriteToken` proving the caller has -/// the capability. -/// -/// The framework issues write tokens only to plugins that declared -/// the corresponding write capability (e.g., `write_headers`). -/// Plugin code without a token cannot call `.write()`. -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(transparent)] -pub struct Guarded { - inner: T, -} - -impl Guarded { - /// Wrap a value in a guard. - pub fn new(value: T) -> Self { - Self { inner: value } - } - - /// Read access — always available if the plugin can see this extension. - pub fn read(&self) -> &T { - &self.inner - } - - /// Write access — requires a WriteToken proving the caller has capability. - /// - /// The framework issues WriteTokens only to plugins that declared - /// the write capability in their config. Without the token, this - /// method is uncallable — the plugin can read but not write. - pub fn write(&mut self, _token: &WriteToken) -> &mut T { - &mut self.inner - } - - /// Consume the guard, returning the inner value. - pub fn into_inner(self) -> T { - self.inner - } -} - -impl Default for Guarded { - fn default() -> Self { - Self { - inner: T::default(), - } - } -} - -/// Opaque token for write access — only the framework can create one. -/// -/// `pub(crate)` constructor means plugin crates cannot mint tokens. -/// The executor creates tokens based on the plugin's declared -/// capabilities from `PluginConfig`. -pub struct WriteToken { - _private: (), -} - -impl WriteToken { - /// Only callable by the framework (pub(crate)). - /// Plugin crates cannot construct this. - pub(crate) fn new() -> Self { - Self { _private: () } - } -} - -// WriteToken is not Clone, not Copy — each plugin gets its own from the executor. -// It's also not Send/Sync by default (no auto-traits on zero-sized private fields). -// We explicitly mark it safe since it's just a capability proof with no data. -unsafe impl Send for WriteToken {} -unsafe impl Sync for WriteToken {} - -impl std::fmt::Debug for WriteToken { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("WriteToken") - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_guarded_read_without_token() { - let guarded = Guarded::new(42); - assert_eq!(*guarded.read(), 42); - } - - #[test] - fn test_guarded_write_with_token() { - let mut guarded = Guarded::new(42); - let token = WriteToken::new(); - *guarded.write(&token) = 100; - assert_eq!(*guarded.read(), 100); - } - - #[test] - fn test_guarded_serde_transparent() { - let guarded = Guarded::new("hello".to_string()); - let json = serde_json::to_string(&guarded).unwrap(); - assert_eq!(json, "\"hello\""); - let deserialized: Guarded = serde_json::from_str(&json).unwrap(); - assert_eq!(*deserialized.read(), "hello"); - } - - #[test] - fn test_guarded_with_struct() { - use std::collections::HashMap; - - #[derive(Clone, Debug, Default, Serialize, Deserialize)] - struct Headers { - map: HashMap, - } - - let mut guarded = Guarded::new(Headers::default()); - let token = WriteToken::new(); - - // Read — no token needed - assert!(guarded.read().map.is_empty()); - - // Write — token required - guarded - .write(&token) - .map - .insert("X-Auth".into(), "Bearer tok".into()); - assert_eq!(guarded.read().map.get("X-Auth").unwrap(), "Bearer tok"); - } -} diff --git a/crates/cpex-payload/src/extensions/http.rs b/crates/cpex-payload/src/extensions/http.rs deleted file mode 100644 index 3fa1157a..00000000 --- a/crates/cpex-payload/src/extensions/http.rs +++ /dev/null @@ -1,210 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/http.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// HttpExtension — HTTP request and response headers. -// Mirrors cpex/framework/extensions/http.py. - -use std::collections::HashMap; - -use serde::{Deserialize, Serialize}; - -/// HTTP-related extensions. -/// -/// Carries both request and response headers separately. The host -/// populates what's available at each hook point: -/// - Pre-invoke: `request_headers` filled, `response_headers` empty -/// - Post-invoke: both filled (request from original, response from upstream) -/// -/// Capability-gated: requires `read_headers` to see, `write_headers` -/// to modify (both request and response). -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct HttpExtension { - /// HTTP request headers (inbound from caller). - #[serde(default)] - pub request_headers: HashMap, - - /// HTTP response headers (from upstream, populated post-invoke). - #[serde(default)] - pub response_headers: HashMap, -} - -impl HttpExtension { - // -- Request header helpers -- - - /// Set a request header (overwrites if exists). - pub fn set_request_header(&mut self, name: impl Into, value: impl Into) { - self.request_headers.insert(name.into(), value.into()); - } - - /// Get a request header value (case-insensitive lookup). - pub fn get_request_header(&self, name: &str) -> Option<&str> { - get_header_ci(&self.request_headers, name) - } - - /// Check if a request header exists (case-insensitive). - pub fn has_request_header(&self, name: &str) -> bool { - self.get_request_header(name).is_some() - } - - /// Add request header only if it doesn't exist. Returns true if added. - pub fn add_request_header( - &mut self, - name: impl Into, - value: impl Into, - ) -> bool { - let name = name.into(); - if self.has_request_header(&name) { - return false; - } - self.request_headers.insert(name, value.into()); - true - } - - /// Remove a request header by name. Returns the removed value. - pub fn remove_request_header(&mut self, name: &str) -> Option { - remove_header_ci(&mut self.request_headers, name) - } - - // -- Response header helpers -- - - /// Set a response header (overwrites if exists). - pub fn set_response_header(&mut self, name: impl Into, value: impl Into) { - self.response_headers.insert(name.into(), value.into()); - } - - /// Get a response header value (case-insensitive lookup). - pub fn get_response_header(&self, name: &str) -> Option<&str> { - get_header_ci(&self.response_headers, name) - } - - /// Check if a response header exists (case-insensitive). - pub fn has_response_header(&self, name: &str) -> bool { - self.get_response_header(name).is_some() - } - - // -- Convenience aliases (backward-compatible, default to request) -- - - /// Set a header on request headers (convenience alias). - pub fn set_header(&mut self, name: impl Into, value: impl Into) { - self.set_request_header(name, value); - } - - /// Get a header from request headers (convenience alias, case-insensitive). - pub fn get_header(&self, name: &str) -> Option<&str> { - self.get_request_header(name) - } - - /// Check if a request header exists (convenience alias). - pub fn has_header(&self, name: &str) -> bool { - self.has_request_header(name) - } -} - -// -- Internal helpers -- - -fn get_header_ci<'a>(headers: &'a HashMap, name: &str) -> Option<&'a str> { - let lower = name.to_lowercase(); - headers - .iter() - .find(|(k, _)| k.to_lowercase() == lower) - .map(|(_, v)| v.as_str()) -} - -fn remove_header_ci(headers: &mut HashMap, name: &str) -> Option { - let lower = name.to_lowercase(); - let key = headers.keys().find(|k| k.to_lowercase() == lower).cloned(); - key.and_then(|k| headers.remove(&k)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_request_header_set_and_get() { - let mut http = HttpExtension::default(); - http.set_request_header("Content-Type", "application/json"); - assert_eq!( - http.get_request_header("Content-Type"), - Some("application/json") - ); - } - - #[test] - fn test_request_header_case_insensitive() { - let mut http = HttpExtension::default(); - http.set_request_header("Authorization", "Bearer tok"); - assert_eq!(http.get_request_header("authorization"), Some("Bearer tok")); - assert_eq!(http.get_request_header("AUTHORIZATION"), Some("Bearer tok")); - } - - #[test] - fn test_response_header_set_and_get() { - let mut http = HttpExtension::default(); - http.set_response_header("Content-Type", "text/html"); - assert_eq!(http.get_response_header("Content-Type"), Some("text/html")); - assert!(http.has_response_header("content-type")); - } - - #[test] - fn test_request_and_response_independent() { - let mut http = HttpExtension::default(); - http.set_request_header("Authorization", "Bearer req-tok"); - http.set_response_header("X-Response-Time", "42ms"); - - // Request headers don't leak into response - assert!(http.get_response_header("Authorization").is_none()); - // Response headers don't leak into request - assert!(http.get_request_header("X-Response-Time").is_none()); - } - - #[test] - fn test_convenience_aliases_default_to_request() { - let mut http = HttpExtension::default(); - http.set_header("X-Custom", "value"); - assert_eq!(http.get_header("X-Custom"), Some("value")); - assert!(http.has_header("X-Custom")); - // Verify it went to request_headers - assert_eq!(http.get_request_header("X-Custom"), Some("value")); - } - - #[test] - fn test_add_request_header_only_if_absent() { - let mut http = HttpExtension::default(); - assert!(http.add_request_header("X-New", "first")); - assert!(!http.add_request_header("X-New", "second")); - assert_eq!(http.get_request_header("X-New"), Some("first")); - } - - #[test] - fn test_remove_request_header() { - let mut http = HttpExtension::default(); - http.set_request_header("X-Remove", "value"); - let removed = http.remove_request_header("x-remove"); - assert_eq!(removed, Some("value".to_string())); - assert!(!http.has_request_header("X-Remove")); - } - - #[test] - fn test_serde_roundtrip() { - let mut http = HttpExtension::default(); - http.set_request_header("Authorization", "Bearer tok"); - http.set_request_header("X-Request-ID", "req-123"); - http.set_response_header("Content-Type", "application/json"); - http.set_response_header("X-Response-Time", "15ms"); - - let json = serde_json::to_string(&http).unwrap(); - let deserialized: HttpExtension = serde_json::from_str(&json).unwrap(); - - assert_eq!( - deserialized.get_request_header("Authorization"), - Some("Bearer tok") - ); - assert_eq!( - deserialized.get_response_header("Content-Type"), - Some("application/json") - ); - } -} diff --git a/crates/cpex-payload/src/extensions/llm.rs b/crates/cpex-payload/src/extensions/llm.rs deleted file mode 100644 index adc2225c..00000000 --- a/crates/cpex-payload/src/extensions/llm.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/llm.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// LLMExtension — model identity and capabilities. -// Mirrors cpex/framework/extensions/llm.py. - -use serde::{Deserialize, Serialize}; - -/// Model identity and capabilities. -/// -/// Immutable — set by the host. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct LLMExtension { - /// Model identifier (e.g., "gpt-4o", "claude-sonnet-4-20250514"). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub model_id: Option, - - /// Provider name (e.g., "openai", "anthropic"). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub provider: Option, - - /// Model capabilities (e.g., "tool_use", "vision", "streaming"). - #[serde(default)] - pub capabilities: Vec, -} diff --git a/crates/cpex-payload/src/extensions/mcp.rs b/crates/cpex-payload/src/extensions/mcp.rs deleted file mode 100644 index c5eed384..00000000 --- a/crates/cpex-payload/src/extensions/mcp.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/mcp.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// MCPExtension — tool, resource, or prompt metadata. -// Mirrors cpex/framework/extensions/mcp.py. - -use std::collections::HashMap; - -use serde::{Deserialize, Serialize}; - -/// MCP tool metadata. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct ToolMetadata { - /// Tool name. - pub name: String, - - /// Human-readable title. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub title: Option, - - /// Tool description. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub description: Option, - - /// Input JSON schema. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub input_schema: Option, - - /// Output JSON schema. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub output_schema: Option, - - /// Source server ID. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub server_id: Option, - - /// Tool namespace. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub namespace: Option, - - /// Tool annotations. - #[serde(default)] - pub annotations: HashMap, -} - -/// MCP resource metadata. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct ResourceMetadata { - /// Resource URI. - pub uri: String, - - /// Human-readable name. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub name: Option, - - /// Resource description. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub description: Option, - - /// MIME type. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub mime_type: Option, - - /// Source server ID. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub server_id: Option, - - /// Resource annotations. - #[serde(default)] - pub annotations: HashMap, -} - -/// MCP prompt metadata. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct PromptMetadata { - /// Prompt name. - pub name: String, - - /// Prompt description. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub description: Option, - - /// Prompt arguments schema. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub arguments: Option>, - - /// Source server ID. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub server_id: Option, - - /// Prompt annotations. - #[serde(default)] - pub annotations: HashMap, -} - -/// MCP-specific metadata extension. -/// -/// Carries tool, resource, or prompt metadata for the entity -/// being processed. Immutable — set by the host. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct MCPExtension { - /// Tool metadata (if this message involves a tool). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub tool: Option, - - /// Resource metadata (if this message involves a resource). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub resource: Option, - - /// Prompt metadata (if this message involves a prompt). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub prompt: Option, -} diff --git a/crates/cpex-payload/src/extensions/meta.rs b/crates/cpex-payload/src/extensions/meta.rs deleted file mode 100644 index 4ba55516..00000000 --- a/crates/cpex-payload/src/extensions/meta.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/meta.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// MetaExtension — host-provided operational metadata. -// Mirrors cpex/framework/extensions/meta.py. - -use std::collections::{HashMap, HashSet}; - -use serde::{Deserialize, Serialize}; - -/// Host-provided operational metadata. -/// -/// Carries entity identification (type + name) for route resolution, -/// operational tags for policy group inheritance, scope for -/// host-defined grouping, and arbitrary properties. -/// -/// Immutable — set by the host before invoking the hook. Plugins -/// can read but not modify. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct MetaExtension { - /// Entity type: "tool", "resource", "prompt", "llm". - /// Used by the manager for route resolution. - #[serde(default)] - pub entity_type: Option, - - /// Entity name: "get_compensation", "hr://employees/*", etc. - /// Used by the manager for route resolution. - #[serde(default)] - pub entity_name: Option, - - /// Operational tags — drive policy group inheritance. - /// Merged with static tags from the matching route's `meta.tags`. - #[serde(default)] - pub tags: HashSet, - - /// Host-defined grouping (virtual server ID, namespace, etc.). - #[serde(default)] - pub scope: Option, - - /// Arbitrary key-value metadata. - #[serde(default)] - pub properties: HashMap, -} diff --git a/crates/cpex-payload/src/extensions/mod.rs b/crates/cpex-payload/src/extensions/mod.rs deleted file mode 100644 index d51aec62..00000000 --- a/crates/cpex-payload/src/extensions/mod.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/mod.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// Typed extension models for the CPEX framework. -// -// Each extension carries contextual metadata with an explicit -// mutability tier enforced by the processing pipeline. Extensions -// are always passed separately from the payload to handlers. -// -// Mirrors the Python extensions in cpex/framework/extensions/. - -pub mod agent; -pub mod completion; -pub mod container; -pub mod delegation; -pub mod filter; -pub mod framework; -pub mod guarded; -pub mod http; -pub mod llm; -pub mod mcp; -pub mod meta; -pub mod monotonic; -pub mod provenance; -pub mod request; -pub mod security; -pub mod tiers; - -// Re-export containers -pub use container::{Extensions, OwnedExtensions}; - -// Re-export all extension types -pub use agent::{AgentExtension, ConversationContext}; -pub use completion::{CompletionExtension, StopReason, TokenUsage}; -pub use delegation::{DelegationExtension, DelegationHop}; -pub use filter::{filter_extensions, SlotName}; -pub use framework::FrameworkExtension; -pub use guarded::{Guarded, WriteToken}; -pub use http::HttpExtension; -pub use llm::LLMExtension; -pub use mcp::{MCPExtension, PromptMetadata, ResourceMetadata, ToolMetadata}; -pub use meta::MetaExtension; -pub use monotonic::{DeclassifierToken, MonotonicSet}; -pub use provenance::ProvenanceExtension; -pub use request::RequestExtension; -pub use security::{ - AgentIdentity, DataPolicy, ObjectSecurityProfile, RetentionPolicy, SecurityExtension, - SubjectExtension, SubjectType, -}; -pub use tiers::{AccessPolicy, Capability, MutabilityTier, SlotPolicy}; diff --git a/crates/cpex-payload/src/extensions/monotonic.rs b/crates/cpex-payload/src/extensions/monotonic.rs deleted file mode 100644 index 82530199..00000000 --- a/crates/cpex-payload/src/extensions/monotonic.rs +++ /dev/null @@ -1,179 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/monotonic.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// MonotonicSet — add-only set enforced at the type level. -// -// Security labels can only grow. The type exposes insert() but not -// remove(). Declassification requires a DeclassifierToken that only -// the security subsystem can construct. -// -// Mirrors the spec in rust-implementation-spec.md §2.2. - -use std::collections::HashSet; -use std::hash::Hash; - -use serde::{Deserialize, Serialize}; - -/// A set that only allows additions. No remove() in the public API. -/// -/// Plugins can call `insert()` but not `remove()`. Declassification -/// (removal) requires a `DeclassifierToken` that only the security -/// subsystem can construct. -/// -/// This enforces the monotonic tier at compile time — a plugin that -/// tries to call `.remove()` gets a compile error. -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(transparent)] -pub struct MonotonicSet { - inner: HashSet, -} - -impl MonotonicSet { - /// Create an empty monotonic set. - pub fn new() -> Self { - Self { - inner: HashSet::new(), - } - } - - /// Create from an existing HashSet. - pub fn from_set(set: HashSet) -> Self { - Self { inner: set } - } - - /// Add a value. Returns true if the value was newly inserted. - pub fn insert(&mut self, value: T) -> bool { - self.inner.insert(value) - } - - /// Check if the set contains a value. - pub fn contains(&self, value: &T) -> bool { - self.inner.contains(value) - } - - /// Iterate over the values. - pub fn iter(&self) -> impl Iterator { - self.inner.iter() - } - - /// Number of elements. - pub fn len(&self) -> usize { - self.inner.len() - } - - /// Whether the set is empty. - pub fn is_empty(&self) -> bool { - self.inner.is_empty() - } - - /// Whether this set is a superset of another. - pub fn is_superset(&self, other: &MonotonicSet) -> bool { - self.inner.is_superset(&other.inner) - } - - /// Get a reference to the inner HashSet (read-only). - pub fn as_set(&self) -> &HashSet { - &self.inner - } - - /// Removal requires a DeclassifierToken — privileged, audited operation. - /// Only the security subsystem can construct the token. - pub fn remove_with_declassifier(&mut self, value: &T, _token: &DeclassifierToken) -> bool { - self.inner.remove(value) - } -} - -impl Default for MonotonicSet { - fn default() -> Self { - Self::new() - } -} - -/// Opaque token for declassification — only the security subsystem -/// can create one. Constructing this token is a privileged operation. -pub struct DeclassifierToken { - _private: (), -} - -impl DeclassifierToken { - /// Only callable by the framework/security subsystem. - #[allow(dead_code)] - pub(crate) fn new() -> Self { - Self { _private: () } - } -} - -/// Case-insensitive label lookup on MonotonicSet. -impl MonotonicSet { - /// Check if a label exists (case-insensitive). - pub fn has_label(&self, label: &str) -> bool { - let lower = label.to_lowercase(); - self.inner.iter().any(|l| l.to_lowercase() == lower) - } - - /// Add a label (case-preserving on insert). - pub fn add_label(&mut self, label: impl Into) { - self.inner.insert(label.into()); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_monotonic_insert_only() { - let mut set = MonotonicSet::new(); - set.insert("PII".to_string()); - set.insert("CONFIDENTIAL".to_string()); - assert!(set.contains(&"PII".to_string())); - assert_eq!(set.len(), 2); - // No remove() method available — this is the key guarantee - } - - #[test] - fn test_monotonic_superset() { - let mut before = MonotonicSet::new(); - before.insert("PII".to_string()); - - let mut after = before.clone(); - after.insert("HIPAA".to_string()); - - assert!(after.is_superset(&before)); - assert!(!before.is_superset(&after)); - } - - #[test] - fn test_monotonic_declassifier() { - let mut set = MonotonicSet::new(); - set.insert("PII".to_string()); - - // Only works with the token - let token = DeclassifierToken::new(); - assert!(set.remove_with_declassifier(&"PII".to_string(), &token)); - assert!(!set.contains(&"PII".to_string())); - } - - #[test] - fn test_monotonic_has_label_case_insensitive() { - let mut set = MonotonicSet::new(); - set.add_label("PII"); - assert!(set.has_label("pii")); - assert!(set.has_label("PII")); - assert!(set.has_label("Pii")); - } - - #[test] - fn test_monotonic_serde_roundtrip() { - let mut set = MonotonicSet::new(); - set.insert("PII".to_string()); - set.insert("HIPAA".to_string()); - - let json = serde_json::to_string(&set).unwrap(); - let deserialized: MonotonicSet = serde_json::from_str(&json).unwrap(); - assert!(deserialized.contains(&"PII".to_string())); - assert!(deserialized.contains(&"HIPAA".to_string())); - } -} diff --git a/crates/cpex-payload/src/extensions/provenance.rs b/crates/cpex-payload/src/extensions/provenance.rs deleted file mode 100644 index 1873f521..00000000 --- a/crates/cpex-payload/src/extensions/provenance.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/provenance.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// ProvenanceExtension — origin and message threading. -// Mirrors cpex/framework/extensions/provenance.py. - -use serde::{Deserialize, Serialize}; - -/// Origin and message threading. -/// -/// Immutable — set by the host. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct ProvenanceExtension { - /// Source system or service. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub source: Option, - - /// Unique message identifier. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub message_id: Option, - - /// Parent message ID (for threading). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub parent_id: Option, -} diff --git a/crates/cpex-payload/src/extensions/request.rs b/crates/cpex-payload/src/extensions/request.rs deleted file mode 100644 index 435ab1fa..00000000 --- a/crates/cpex-payload/src/extensions/request.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/request.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// RequestExtension — execution environment and tracing. -// Mirrors cpex/framework/extensions/request.py. - -use serde::{Deserialize, Serialize}; - -/// Execution environment and request tracing. -/// -/// Immutable — set by the host before invoking the hook. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct RequestExtension { - /// Deployment environment (e.g., "production", "staging"). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub environment: Option, - - /// Unique request identifier. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub request_id: Option, - - /// Request timestamp (ISO 8601). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub timestamp: Option, - - /// Distributed trace ID. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub trace_id: Option, - - /// Span ID within the trace. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub span_id: Option, -} diff --git a/crates/cpex-payload/src/extensions/security.rs b/crates/cpex-payload/src/extensions/security.rs deleted file mode 100644 index 91d54c18..00000000 --- a/crates/cpex-payload/src/extensions/security.rs +++ /dev/null @@ -1,348 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/security.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// SecurityExtension — labels, classification, identity, data policy. -// Mirrors cpex/framework/extensions/security.py. - -use std::collections::{HashMap, HashSet}; - -use serde::{Deserialize, Serialize}; - -use super::monotonic::MonotonicSet; - -/// Subject type for identity classification. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum SubjectType { - User, - Agent, - Service, - System, -} - -/// Authenticated subject identity. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct SubjectExtension { - /// Subject identifier (e.g., JWT sub). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub id: Option, - - /// Subject type. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub subject_type: Option, - - /// Assigned roles. - #[serde(default)] - pub roles: HashSet, - - /// Granted permissions. - #[serde(default)] - pub permissions: HashSet, - - /// Team memberships. - #[serde(default)] - pub teams: HashSet, - - /// Raw claims (e.g., JWT claims). - #[serde(default)] - pub claims: HashMap, -} - -/// Security profile for a managed object. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct ObjectSecurityProfile { - /// Who manages this object. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub managed_by: Option, - - /// Required permissions. - #[serde(default)] - pub permissions: Vec, - - /// Trust domain. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub trust_domain: Option, - - /// Data scope. - #[serde(default)] - pub data_scope: Vec, -} - -/// Retention policy for data. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct RetentionPolicy { - /// Maximum age in seconds. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub max_age_seconds: Option, - - /// Policy name. - #[serde(default)] - pub policy: String, - - /// Deletion timestamp. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub delete_after: Option, -} - -/// Data policy for a named data element. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct DataPolicy { - /// Labels to apply. - #[serde(default)] - pub apply_labels: Vec, - - /// Allowed actions (None = all allowed). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub allowed_actions: Option>, - - /// Denied actions. - #[serde(default)] - pub denied_actions: Vec, - - /// Retention policy. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub retention: Option, -} - -/// This agent's own workload identity. -/// -/// Distinct from `SubjectExtension` which represents the *caller*. -/// `AgentIdentity` represents *this agent/service* — its own -/// workload identity, OAuth client_id, and trust domain. -/// -/// Populated by the host before the pipeline runs. Plugins can -/// make decisions based on both who is calling (Subject) and -/// which agent is processing (AgentIdentity). -/// -/// Maps to AuthBridge's `AgentIdentity` and the Go bindings' -/// `SecurityExtension.Agent`. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct AgentIdentity { - /// OAuth client_id of this agent. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub client_id: Option, - - /// Workload identity URI (SPIFFE, k8s service account, platform-specific). - /// e.g., `spiffe://example.com/ns/team1/sa/weather-tool` - #[serde(default, skip_serializing_if = "Option::is_none")] - pub workload_id: Option, - - /// Trust domain of the workload identity. - /// e.g., `example.com` - #[serde(default, skip_serializing_if = "Option::is_none")] - pub trust_domain: Option, -} - -/// Security-related extensions. -/// -/// Carries security labels (monotonic add-only), classification, -/// authenticated caller identity (subject), this agent's own -/// workload identity (agent), object security profiles, and -/// data policies. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct SecurityExtension { - /// Security labels (monotonic — add-only via MonotonicSet). - /// No remove() method — enforced at compile time. - #[serde(default)] - pub labels: MonotonicSet, - - /// Data classification level. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub classification: Option, - - /// Authenticated caller identity (who is calling). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub subject: Option, - - /// This agent's own workload identity (who this agent is). - /// Populated by the host, not by plugins. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub agent: Option, - - /// Authentication method used (e.g., "jwt", "mtls", "spiffe", "api_key"). - #[serde(default, skip_serializing_if = "Option::is_none")] - pub auth_method: Option, - - /// Object security profiles keyed by object name. - #[serde(default)] - pub objects: HashMap, - - /// Data policies keyed by data element name. - #[serde(default)] - pub data: HashMap, -} - -impl SecurityExtension { - /// Add a security label (monotonic — cannot remove). - pub fn add_label(&mut self, label: impl Into) { - self.labels.add_label(label); - } - - /// Check if a label exists (case-insensitive). - pub fn has_label(&self, label: &str) -> bool { - self.labels.has_label(label) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_security_labels_monotonic() { - let mut sec = SecurityExtension::default(); - sec.add_label("PII"); - sec.add_label("HIPAA"); - assert!(sec.has_label("PII")); - assert!(sec.has_label("pii")); // case-insensitive - assert!(sec.has_label("HIPAA")); - assert!(!sec.has_label("SOX")); - } - - #[test] - fn test_security_classification() { - let sec = SecurityExtension { - classification: Some("confidential".into()), - ..Default::default() - }; - assert_eq!(sec.classification.as_deref(), Some("confidential")); - } - - #[test] - fn test_subject_extension() { - let subject = SubjectExtension { - id: Some("alice".into()), - subject_type: Some(SubjectType::User), - roles: ["admin".to_string(), "hr".to_string()].into(), - permissions: ["read_all".to_string()].into(), - teams: ["engineering".to_string()].into(), - claims: [("iss".to_string(), "auth.example.com".to_string())].into(), - }; - assert_eq!(subject.id.as_deref(), Some("alice")); - assert_eq!(subject.subject_type, Some(SubjectType::User)); - assert!(subject.roles.contains("admin")); - assert!(subject.permissions.contains("read_all")); - assert!(subject.teams.contains("engineering")); - assert_eq!(subject.claims.get("iss").unwrap(), "auth.example.com"); - } - - #[test] - fn test_agent_identity() { - let agent = AgentIdentity { - client_id: Some("weather-agent".into()), - workload_id: Some("spiffe://example.com/ns/team1/sa/weather-tool".into()), - trust_domain: Some("example.com".into()), - }; - assert_eq!(agent.client_id.as_deref(), Some("weather-agent")); - assert_eq!( - agent.workload_id.as_deref(), - Some("spiffe://example.com/ns/team1/sa/weather-tool") - ); - assert_eq!(agent.trust_domain.as_deref(), Some("example.com")); - } - - #[test] - fn test_agent_identity_default() { - let agent = AgentIdentity::default(); - assert!(agent.client_id.is_none()); - assert!(agent.workload_id.is_none()); - assert!(agent.trust_domain.is_none()); - } - - #[test] - fn test_security_with_agent_and_subject() { - let sec = SecurityExtension { - labels: { - let mut l = super::super::MonotonicSet::new(); - l.add_label("PII"); - l - }, - classification: Some("confidential".into()), - subject: Some(SubjectExtension { - id: Some("alice".into()), - subject_type: Some(SubjectType::User), - ..Default::default() - }), - agent: Some(AgentIdentity { - client_id: Some("hr-agent".into()), - workload_id: Some("spiffe://corp.com/hr-agent".into()), - trust_domain: Some("corp.com".into()), - }), - auth_method: Some("jwt".into()), - ..Default::default() - }; - - // Caller identity - assert_eq!(sec.subject.as_ref().unwrap().id.as_deref(), Some("alice")); - // Agent identity (distinct from caller) - assert_eq!( - sec.agent.as_ref().unwrap().client_id.as_deref(), - Some("hr-agent") - ); - assert_eq!( - sec.agent.as_ref().unwrap().trust_domain.as_deref(), - Some("corp.com") - ); - // Auth method - assert_eq!(sec.auth_method.as_deref(), Some("jwt")); - // Labels - assert!(sec.has_label("PII")); - } - - #[test] - fn test_security_serde_roundtrip() { - let mut sec = SecurityExtension::default(); - sec.add_label("PII"); - sec.classification = Some("internal".into()); - sec.agent = Some(AgentIdentity { - client_id: Some("my-agent".into()), - ..Default::default() - }); - sec.auth_method = Some("mtls".into()); - - let json = serde_json::to_string(&sec).unwrap(); - let deserialized: SecurityExtension = serde_json::from_str(&json).unwrap(); - - assert!(deserialized.has_label("PII")); - assert_eq!(deserialized.classification.as_deref(), Some("internal")); - assert_eq!( - deserialized.agent.as_ref().unwrap().client_id.as_deref(), - Some("my-agent") - ); - assert_eq!(deserialized.auth_method.as_deref(), Some("mtls")); - } - - #[test] - fn test_object_security_profile() { - let profile = ObjectSecurityProfile { - managed_by: Some("hr-system".into()), - permissions: vec!["read".into(), "write".into()], - trust_domain: Some("corp.com".into()), - data_scope: vec!["employee_data".into()], - }; - assert_eq!(profile.managed_by.as_deref(), Some("hr-system")); - assert_eq!(profile.permissions.len(), 2); - } - - #[test] - fn test_data_policy() { - let policy = DataPolicy { - apply_labels: vec!["PII".into()], - allowed_actions: Some(vec!["read".into()]), - denied_actions: vec!["delete".into()], - retention: Some(RetentionPolicy { - max_age_seconds: Some(86400), - policy: "30-day".into(), - delete_after: Some("2026-05-01".into()), - }), - }; - assert_eq!(policy.apply_labels[0], "PII"); - assert!(policy.retention.is_some()); - assert_eq!( - policy.retention.as_ref().unwrap().max_age_seconds, - Some(86400) - ); - } -} diff --git a/crates/cpex-payload/src/extensions/tiers.rs b/crates/cpex-payload/src/extensions/tiers.rs deleted file mode 100644 index a22406f9..00000000 --- a/crates/cpex-payload/src/extensions/tiers.rs +++ /dev/null @@ -1,100 +0,0 @@ -// Location: ./crates/cpex-core/src/extensions/tiers.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// Mutability tiers and capability definitions. -// -// Each extension slot has a mutability tier that controls how plugins -// can interact with it. Capabilities gate per-plugin access. -// -// Mirrors cpex/framework/extensions/tiers.py. - -use serde::{Deserialize, Serialize}; - -/// Mutability tier for an extension slot. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum MutabilityTier { - /// Cannot be modified after creation. - Immutable, - /// Can only grow (add-only sets, append-only chains). - Monotonic, - /// Can be freely modified by plugins with write capability. - Mutable, -} - -/// Declared permission that controls extension access. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum Capability { - /// Read the authenticated subject identity. - ReadSubject, - /// Read subject roles. - ReadRoles, - /// Read subject team memberships. - ReadTeams, - /// Read subject claims (e.g., JWT claims). - ReadClaims, - /// Read subject permissions. - ReadPermissions, - /// Read the agent execution context. - ReadAgent, - /// Read HTTP headers. - ReadHeaders, - /// Write (modify) HTTP headers. - WriteHeaders, - /// Read security labels. - ReadLabels, - /// Append security labels (monotonic add-only). - AppendLabels, - /// Read the delegation chain. - ReadDelegation, - /// Append to the delegation chain (monotonic). - AppendDelegation, -} - -/// Access policy for an extension slot. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum AccessPolicy { - /// All plugins can access. - Unrestricted, - /// Only plugins with the declared capability can access. - CapabilityGated, -} - -/// Policy for a single extension slot. -/// -/// Declares the mutability tier, access policy, and required -/// capabilities for reading and writing. -#[derive(Debug, Clone)] -pub struct SlotPolicy { - /// How the slot can be modified. - pub tier: MutabilityTier, - /// Whether access requires a capability. - pub access: AccessPolicy, - /// Capability required for reading (if capability-gated). - pub read_cap: Option, - /// Capability required for writing (if capability-gated). - pub write_cap: Option, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_tier_serde() { - let tier = MutabilityTier::Monotonic; - let json = serde_json::to_string(&tier).unwrap(); - assert_eq!(json, "\"monotonic\""); - } - - #[test] - fn test_capability_serde() { - let cap = Capability::AppendLabels; - let json = serde_json::to_string(&cap).unwrap(); - assert_eq!(json, "\"append_labels\""); - } -} diff --git a/crates/cpex-payload/src/hooks/macros.rs b/crates/cpex-payload/src/hooks/macros.rs deleted file mode 100644 index 80012acf..00000000 --- a/crates/cpex-payload/src/hooks/macros.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Location: ./crates/cpex-core/src/hooks/macros.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// define_hook! macro. -// -// Generates a HookTypeDef marker struct and trait implementation -// from a single declaration. This is the primary way to define new -// hooks — both built-in (CMF, tool, prompt) and custom (rate -// limiting, deployment gates, federation sync). -// -// Plugins implement the generic HookHandler trait (from -// trait_def.rs) for the generated marker struct. The handler -// receives a borrowed payload and returns the hook's result type. - -/// Generates a hook type definition and marker struct. -/// -/// # Usage -/// -/// ```rust,ignore -/// define_hook! { -/// /// Doc comment for the hook. -/// MyHook, "my_hook" => { -/// payload: MyPayload, -/// result: PluginResult, -/// } -/// } -/// ``` -/// -/// This generates a marker struct `MyHook` implementing `HookTypeDef`. -/// Plugins handle it by implementing `HookHandler`. -/// -/// # CMF Pattern (one handler, multiple hook names) -/// -/// For CMF hooks where one handler covers multiple hook names: -/// -/// ```rust,ignore -/// define_hook! { -/// /// CMF message evaluation hook. -/// CmfHook, "cmf" => { -/// payload: MessagePayload, -/// result: PluginResult, -/// } -/// } -/// -/// // Register the same handler for multiple names: -/// // manager.register_handler_for_names::(plugin, config, &[ -/// // "cmf.tool_pre_invoke", "cmf.llm_input", ... -/// // ]); -/// ``` -#[macro_export] -macro_rules! define_hook { - ( - $(#[$meta:meta])* - $name:ident, $hook_name:literal => { - payload: $payload:ty, - result: $result:ty $(,)? - } - ) => { - $(#[$meta])* - pub struct $name; - - impl $crate::hooks::trait_def::HookTypeDef for $name { - type Payload = $payload; - type Result = $result; - const NAME: &'static str = $hook_name; - } - }; -} diff --git a/crates/cpex-payload/src/hooks/mod.rs b/crates/cpex-payload/src/hooks/mod.rs deleted file mode 100644 index 1982fad1..00000000 --- a/crates/cpex-payload/src/hooks/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Location: ./crates/cpex-core/src/hooks/mod.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// Hook system. -// -// Provides the core abstractions for defining and dispatching hooks: -// -// - [`HookTypeDef`] — marker trait associating a typed payload + result with a hook name. -// - [`PluginPayload`] — base trait for all hook payloads (mirrors Python's PluginPayload). -// - [`PluginResult`] — result type with separate payload and extension modifications. -// - [`Extensions`] — capability-gated extension view passed to handlers. -// - [`define_hook!`] — macro for declaring new hook types with handler traits. -// - [`hook_names`] / [`cmf_hook_names`] — string constants for built-in hooks. -// -// Hook types are open — hosts define their own using define_hook! alongside the built-ins. - -pub mod macros; -pub mod payload; -pub mod trait_def; -pub mod types; - -// Re-export core types at the hooks level -pub use payload::{Extensions, PluginPayload}; -pub use trait_def::{HookHandler, HookTypeDef, PluginResult}; -pub use types::{builtin_hook_types, hook_type_from_str, HookType}; diff --git a/crates/cpex-payload/src/hooks/payload.rs b/crates/cpex-payload/src/hooks/payload.rs deleted file mode 100644 index d284bf4c..00000000 --- a/crates/cpex-payload/src/hooks/payload.rs +++ /dev/null @@ -1,133 +0,0 @@ -// Location: ./crates/cpex-core/src/hooks/payload.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// PluginPayload trait and Extensions stub. -// -// PluginPayload is the base trait for all hook payloads, mirroring -// Python's PluginPayload(BaseModel, frozen=True). All payloads in -// the framework implement this trait, giving the executor and -// registry a common bound for type safety. -// -// The trait is object-safe — the executor works with `Box` -// instead of `Box`, catching type errors at compile time. -// Downcasting to concrete types uses the `as_any()` method. -// -// Extensions is the typed container for all message extensions -// (security, delegation, HTTP, meta, etc.). It is always passed -// as a separate parameter to handlers — never inside the payload. -// This allows per-plugin capability filtering and independent -// modification without copying the payload. - -use std::any::Any; -use std::fmt; - -// Re-export Extensions and OwnedExtensions from the extensions module. -// These are the typed containers for all extension data. They live in -// extensions/container.rs but are re-exported here for backward -// compatibility with existing code that imports from hooks::payload. -pub use crate::extensions::{Extensions, Guarded, MetaExtension, OwnedExtensions, WriteToken}; - -// --------------------------------------------------------------------------- -// PluginPayload Trait -// --------------------------------------------------------------------------- - -/// Base trait for all hook payloads. -/// -/// Mirrors Python's `PluginPayload(BaseModel, frozen=True)`. Every -/// payload type in the framework implements this trait. The executor -/// and registry use `Box` (not `Box`) -/// for type-safe dispatch. -/// -/// The trait is **object-safe** — it can be used behind `Box`, `&`, -/// and `Arc` without knowing the concrete type. This is achieved by -/// providing `clone_boxed()` instead of requiring `Clone` directly -/// (which is not object-safe), and `as_any()` / `as_any_mut()` for -/// downcasting to the concrete type when needed. -/// -/// Payloads are: -/// - Cloneable via `clone_boxed()` — the executor uses this for COW -/// when a modifying plugin (Sequential or Transform) needs ownership. -/// - `Send + Sync` — payloads may be shared across threads for -/// Concurrent mode plugins. -/// - `'static` — payloads must be owned types (no borrowed references). -/// -/// Extensions are **not** part of the payload. They are passed as a -/// separate `&Extensions` parameter to handlers. -/// -/// # Examples -/// -/// ``` -/// use cpex_core::hooks::payload::PluginPayload; -/// -/// #[derive(Debug, Clone)] -/// struct RateLimitPayload { -/// client_id: String, -/// request_count: u64, -/// } -/// -/// impl PluginPayload for RateLimitPayload { -/// fn clone_boxed(&self) -> Box { -/// Box::new(self.clone()) -/// } -/// fn as_any(&self) -> &dyn std::any::Any { self } -/// fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self } -/// } -/// ``` -pub trait PluginPayload: Send + Sync + 'static { - /// Clone this payload into a new `Box`. - /// - /// Used by the executor for copy-on-write: read-only modes borrow - /// the payload, modifying modes receive a clone via this method. - fn clone_boxed(&self) -> Box; - - /// Downcast to a concrete type via `&dyn Any`. - /// - /// Used by typed handler wrappers to recover the concrete payload - /// type from `Box`. - fn as_any(&self) -> &dyn Any; - - /// Downcast to a concrete type via `&mut dyn Any`. - fn as_any_mut(&mut self) -> &mut dyn Any; -} - -impl fmt::Debug for dyn PluginPayload { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("dyn PluginPayload") - } -} - -// --------------------------------------------------------------------------- -// Blanket helper macro for implementing PluginPayload -// --------------------------------------------------------------------------- - -/// Implements `PluginPayload` for a type that is `Clone + Send + Sync + 'static`. -/// -/// Saves boilerplate — instead of writing the three methods manually, -/// just invoke this macro: -/// -/// ``` -/// use cpex_core::impl_plugin_payload; -/// -/// #[derive(Debug, Clone)] -/// struct MyPayload { value: i32 } -/// -/// impl_plugin_payload!(MyPayload); -/// ``` -#[macro_export] -macro_rules! impl_plugin_payload { - ($ty:ty) => { - impl $crate::hooks::payload::PluginPayload for $ty { - fn clone_boxed(&self) -> Box { - Box::new(self.clone()) - } - fn as_any(&self) -> &dyn std::any::Any { - self - } - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - } - }; -} diff --git a/crates/cpex-payload/src/hooks/trait_def.rs b/crates/cpex-payload/src/hooks/trait_def.rs deleted file mode 100644 index b75d2b8a..00000000 --- a/crates/cpex-payload/src/hooks/trait_def.rs +++ /dev/null @@ -1,322 +0,0 @@ -// Location: ./crates/cpex-core/src/hooks/trait_def.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// HookTypeDef trait and PluginResult type. -// -// Every hook in the CPEX framework is defined by a marker type that -// implements HookTypeDef. This associates a typed PluginPayload and -// PluginResult with a string name used for registry lookup and config. -// -// The hook type does NOT declare an access pattern (read-only vs -// mutating). The plugin's mode (from PluginRef.trusted_config) -// determines scheduling and authority at runtime. Security invariants -// come from the types inside the payload (Arc, MonotonicSet, -// Guarded), not from borrow mechanics. -// -// Extensions are always a separate parameter — never part of the -// payload. This allows capability-filtered views per plugin and -// independent modification of extensions without copying the payload. - -use crate::context::PluginContext; -use crate::error::PluginViolation; -use crate::hooks::payload::{Extensions, PluginPayload}; -use crate::plugin::Plugin; - -// --------------------------------------------------------------------------- -// HookTypeDef Trait -// --------------------------------------------------------------------------- - -/// Defines a hook's contract: what goes in and what comes out. -/// -/// Each hook type is a zero-sized marker struct that implements this -/// trait. The framework uses the associated types for compile-time -/// dispatch and the NAME constant for registry lookup. -/// -/// The hook type does **not** declare an access pattern. The plugin's -/// mode (from `PluginRef.trusted_config`) determines whether the -/// executor passes a borrow or a clone: -/// -/// | Mode | Receives | Can Block? | Can Modify? | -/// |-----------------|-----------------|------------|-------------| -/// | Sequential | owned (clone) | Yes | Yes | -/// | Transform | owned (clone) | No | Yes | -/// | Audit | &Payload | No | No | -/// | Concurrent | &Payload | Yes | No | -/// | FireAndForget | &Payload | No | No | -/// -/// # Defining a Hook -/// -/// Use the [`define_hook!`] macro instead of implementing this trait -/// manually — the macro generates the marker struct, the trait impl, -/// and the handler trait in one declaration. -pub trait HookTypeDef: Send + Sync + 'static { - /// The typed payload that handlers receive. - /// Must implement [`PluginPayload`] (Clone + Send + Sync + 'static). - type Payload: PluginPayload; - - /// The typed result that handlers return. - type Result: Send + Sync; - - /// Hook name — used as the registry key and in config YAML. - /// - /// Multiple hook names can map to the same HookTypeDef (the CMF - /// pattern where one handler covers `cmf.tool_pre_invoke`, - /// `cmf.llm_input`, etc.). The primary NAME is used for - /// single-name registration; additional names are registered - /// via `register_for_names()`. - const NAME: &'static str; -} - -// --------------------------------------------------------------------------- -// Hook Handler Trait -// --------------------------------------------------------------------------- - -/// Typed handler for a specific hook type. -/// -/// Plugin authors implement this trait (alongside [`Plugin`]) to handle -/// a specific hook. The type parameter `H` ties the handler to a -/// `HookTypeDef`, ensuring the correct payload and result types at -/// compile time. The framework creates a type-erased adapter internally -/// when you register — you never touch `AnyHookHandler` directly. -/// -/// # Async by design -/// -/// `handle` is an `async fn`. Plugins that don't need to `.await` -/// anything still write `async fn handle(...)` and return synchronously -/// — the compiler emits a trivially-ready future and LLVM inlines it -/// at the adapter site, so there's no observable runtime cost over a -/// plain function. Plugins that *do* need to `.await` (fresh JWKS -/// fetch, RPC to an authz service, dynamic policy lookup) just use -/// `.await` inside the body. -/// -/// **Best practice:** even when async is available, prefer pre-loading -/// state in [`Plugin::initialize`] and reading from cache in `handle`. -/// Hot-path I/O is the most common source of latency regressions. -/// -/// # Native AFIT, not `#[async_trait]` -/// -/// The trait uses native `async fn` (return-position `impl Future`) -/// rather than `#[async_trait]`. This avoids a per-call heap -/// allocation: the returned future is monomorphized into the -/// [`TypedHandlerAdapter`] rather than boxed. The trait is therefore -/// **not object-safe** — you cannot have `Box>`. -/// We don't need that; type erasure happens one layer up at -/// [`AnyHookHandler`]. -/// -/// # Examples -/// -/// ```rust,ignore -/// // Synchronous plugin — no .await, no extra cost -/// impl HookHandler for AllowPlugin { -/// async fn handle( -/// &self, -/// _payload: &MessagePayload, -/// _extensions: &Extensions, -/// _ctx: &mut PluginContext, -/// ) -> PluginResult { -/// PluginResult::allow() -/// } -/// } -/// -/// // Async plugin — calls .await inside the body -/// impl HookHandler for AuthzPlugin { -/// async fn handle( -/// &self, -/// payload: &MyPayload, -/// _extensions: &Extensions, -/// _ctx: &mut PluginContext, -/// ) -> PluginResult { -/// match self.client.check(&payload.user).await { -/// Ok(true) => PluginResult::allow(), -/// _ => PluginResult::deny(/* ... */), -/// } -/// } -/// } -/// -/// // Registration is the same for both: -/// manager.register_handler::(plugin, config)?; -/// ``` -/// -/// [`PluginManager::register_handler`]: crate::manager::PluginManager::register_handler -/// [`AnyHookHandler`]: crate::registry::AnyHookHandler -/// [`TypedHandlerAdapter`]: crate::hooks::adapter::TypedHandlerAdapter -pub trait HookHandler: Plugin + Send + Sync { - /// Handle the hook invocation. - /// - /// Receives a **borrow** of the typed payload, capability-filtered - /// extensions, and per-invocation context. Returns a typed result. - /// - /// The payload is immutable — Rust's borrow checker prevents - /// modification through `&H::Payload`. To modify, the plugin - /// must `clone()` the payload (or the fields it needs) and return - /// the modified copy in `PluginResult::modify_payload()`. This - /// pushes the clone cost to the plugin that actually needs it — - /// read-only plugins (validators, auditors) never pay for a copy. - /// - /// Returns a `Send`-able future so the executor can drive it from - /// any worker thread (including the concurrent-phase `JoinSet`). - /// `H::Result` is already `Send + Sync` per the `HookTypeDef` - /// bound, so the `Send` constraint comes for free for typical - /// handlers. - fn handle( - &self, - payload: &H::Payload, - extensions: &Extensions, - ctx: &mut PluginContext, - ) -> impl std::future::Future + Send; -} - -// --------------------------------------------------------------------------- -// Plugin Result -// --------------------------------------------------------------------------- - -/// Result returned by a hook handler. -/// -/// Payload and extension modifications are **separate** — this is a -/// core design decision. Extension-only changes (add a label, set a -/// header) don't require copying the payload. The payload is only -/// present in `modified_payload` when message content actually changed. -/// -/// The executor interprets the result based on the plugin's mode: -/// - Sequential/Transform: `modified_payload` and `modified_extensions` are accepted. -/// - Audit/Concurrent/FireAndForget: modifications are discarded. -/// - Sequential/Concurrent: `continue_processing = false` halts the pipeline. -/// - Transform/Audit/FireAndForget: blocks are suppressed. -/// -/// Mirrors Python's `PluginResult[T]` with separate `modified_payload` -/// and `modified_extensions` fields. -/// -/// # Examples -/// -/// ``` -/// use cpex_core::hooks::{PluginPayload, PluginResult}; -/// use cpex_core::error::PluginViolation; -/// -/// // Define a simple payload -/// #[derive(Debug, Clone)] -/// struct TestPayload { value: i32 } -/// cpex_core::impl_plugin_payload!(TestPayload); -/// -/// // Allow — no changes -/// let result: PluginResult = PluginResult::allow(); -/// assert!(result.continue_processing); -/// assert!(result.modified_payload.is_none()); -/// -/// // Deny -/// let result: PluginResult = PluginResult::deny( -/// PluginViolation::new("forbidden", "not allowed") -/// ); -/// assert!(!result.continue_processing); -/// assert!(result.violation.is_some()); -/// ``` -#[derive(Debug)] -pub struct PluginResult { - /// Whether the pipeline should continue processing. - /// `false` halts the pipeline (deny). Only respected for - /// Sequential and Concurrent modes. - pub continue_processing: bool, - - /// Modified payload. `None` means no content modification. - /// Only accepted from Sequential and Transform mode plugins. - pub modified_payload: Option

, - - /// Modified extensions. `None` means no extension changes. - /// Return an `OwnedExtensions` from `extensions.cow_copy()`. - /// The executor validates (immutable unchanged, monotonic superset) - /// and merges back into the pipeline's `Extensions`. - pub modified_extensions: Option, - - /// Policy violation. Present when `continue_processing` is `false`. - pub violation: Option, - - /// Optional metadata from the plugin (telemetry, diagnostics). - /// Not used for scheduling or policy decisions. - pub metadata: Option, -} - -impl PluginResult

{ - /// Allow — payload continues unchanged, no extension changes. - pub fn allow() -> Self { - Self { - continue_processing: true, - modified_payload: None, - modified_extensions: None, - - violation: None, - metadata: None, - } - } - - /// Deny — pipeline halts with a violation. - pub fn deny(violation: PluginViolation) -> Self { - Self { - continue_processing: false, - modified_payload: None, - modified_extensions: None, - - violation: Some(violation), - metadata: None, - } - } - - /// Modify payload only — extensions unchanged. - pub fn modify_payload(payload: P) -> Self { - Self { - continue_processing: true, - modified_payload: Some(payload), - modified_extensions: None, - - violation: None, - metadata: None, - } - } - - /// Modify extensions only — payload unchanged. - /// Takes an `OwnedExtensions` from `extensions.cow_copy()`. - pub fn modify_extensions(extensions: crate::hooks::payload::OwnedExtensions) -> Self { - Self { - continue_processing: true, - modified_payload: None, - modified_extensions: Some(extensions), - - violation: None, - metadata: None, - } - } - - /// Modify both payload and extensions. - /// Takes an `OwnedExtensions` from `extensions.cow_copy()`. - pub fn modify(payload: P, extensions: crate::hooks::payload::OwnedExtensions) -> Self { - Self { - continue_processing: true, - modified_payload: Some(payload), - modified_extensions: Some(extensions), - - violation: None, - metadata: None, - } - } - - /// Whether this result represents a denial. - pub fn is_denied(&self) -> bool { - !self.continue_processing - } - - /// Whether this result carries a modified payload. - pub fn is_payload_modified(&self) -> bool { - self.modified_payload.is_some() - } - - /// Whether this result carries modified extensions. - pub fn is_extensions_modified(&self) -> bool { - self.modified_extensions.is_some() - } -} - -impl Default for PluginResult

{ - fn default() -> Self { - Self::allow() - } -} diff --git a/crates/cpex-payload/src/hooks/types.rs b/crates/cpex-payload/src/hooks/types.rs deleted file mode 100644 index 3295d1a6..00000000 --- a/crates/cpex-payload/src/hooks/types.rs +++ /dev/null @@ -1,190 +0,0 @@ -// Location: ./crates/cpex-core/src/hooks/types.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// Hook type definitions. -// -// Hook types are open strings — hosts define hook points appropriate -// to their execution lifecycle. This module provides a newtype wrapper -// for type safety and built-in constants for the common hook points. -// -// The framework does not prescribe a fixed set of hook points. Each -// host places `invoke_hook()` calls at sites appropriate to its -// processing pipeline. The constants below cover the standard -// MCP/CMF lifecycle but hosts may register additional types. - -use std::fmt; - -use serde::{Deserialize, Serialize}; - -// --------------------------------------------------------------------------- -// Hook Type -// --------------------------------------------------------------------------- - -/// A named hook point in the host's execution lifecycle. -/// -/// Wraps a string identifier. Hook types are open — hosts register -/// their own alongside the built-in constants. -/// -/// # Examples -/// -/// ``` -/// use cpex_core::hooks::HookType; -/// use cpex_core::hooks::types::hook_names; -/// -/// // Use a built-in name constant -/// let hook = HookType::new(hook_names::TOOL_PRE_INVOKE); -/// assert_eq!(hook.as_str(), "tool_pre_invoke"); -/// -/// // Define a custom hook -/// let custom = HookType::new("generation_pre_call"); -/// assert_eq!(custom.as_str(), "generation_pre_call"); -/// ``` -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(transparent)] -pub struct HookType(String); - -impl HookType { - /// Create a new hook type from a string. - pub fn new(name: impl Into) -> Self { - Self(name.into()) - } - - /// Return the hook type as a string slice. - pub fn as_str(&self) -> &str { - &self.0 - } -} - -impl fmt::Display for HookType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.0) - } -} - -impl From<&str> for HookType { - fn from(s: &str) -> Self { - Self::new(s) - } -} - -impl From for HookType { - fn from(s: String) -> Self { - Self(s) - } -} - -// --------------------------------------------------------------------------- -// Built-in Hook String Constants -// --------------------------------------------------------------------------- -// Canonical string names for built-in hooks. Use these with -// HookType::new() or pass them directly to APIs that accept &str. - -/// Legacy hook names — typed payloads (ToolPreInvokePayload, etc.). -pub mod hook_names { - // Tool lifecycle - pub const TOOL_PRE_INVOKE: &str = "tool_pre_invoke"; - pub const TOOL_POST_INVOKE: &str = "tool_post_invoke"; - - // Prompt lifecycle - pub const PROMPT_PRE_FETCH: &str = "prompt_pre_fetch"; - pub const PROMPT_POST_FETCH: &str = "prompt_post_fetch"; - - // Resource lifecycle - pub const RESOURCE_PRE_FETCH: &str = "resource_pre_fetch"; - pub const RESOURCE_POST_FETCH: &str = "resource_post_fetch"; - - // Identity and delegation - pub const IDENTITY_RESOLVE: &str = "identity_resolve"; - pub const TOKEN_DELEGATE: &str = "token_delegate"; -} - -/// CMF hook names — MessagePayload wrapping a CMF Message. -/// The `cmf.` prefix lets legacy and CMF plugins coexist at the -/// same interception point. The gateway fires both at each event. -pub mod cmf_hook_names { - // Tool lifecycle - pub const TOOL_PRE_INVOKE: &str = "cmf.tool_pre_invoke"; - pub const TOOL_POST_INVOKE: &str = "cmf.tool_post_invoke"; - - // LLM lifecycle (CMF only — no legacy equivalent) - pub const LLM_INPUT: &str = "cmf.llm_input"; - pub const LLM_OUTPUT: &str = "cmf.llm_output"; - - // Prompt lifecycle - pub const PROMPT_PRE_FETCH: &str = "cmf.prompt_pre_fetch"; - pub const PROMPT_POST_FETCH: &str = "cmf.prompt_post_fetch"; - - // Resource lifecycle - pub const RESOURCE_PRE_FETCH: &str = "cmf.resource_pre_fetch"; - pub const RESOURCE_POST_FETCH: &str = "cmf.resource_post_fetch"; -} - -// --------------------------------------------------------------------------- -// Built-in hook type helpers -// --------------------------------------------------------------------------- - -/// Returns all built-in hook types with their canonical string values. -/// -/// Called once during PluginManager initialization to populate the -/// hook registry. Hosts add their own hook types after this. -pub fn builtin_hook_types() -> Vec { - vec![ - // Legacy (typed payloads) - HookType::new("tool_pre_invoke"), - HookType::new("tool_post_invoke"), - HookType::new("prompt_pre_fetch"), - HookType::new("prompt_post_fetch"), - HookType::new("resource_pre_fetch"), - HookType::new("resource_post_fetch"), - HookType::new("identity_resolve"), - HookType::new("token_delegate"), - // CMF (MessagePayload) - HookType::new("cmf.tool_pre_invoke"), - HookType::new("cmf.tool_post_invoke"), - HookType::new("cmf.llm_input"), - HookType::new("cmf.llm_output"), - HookType::new("cmf.prompt_pre_fetch"), - HookType::new("cmf.prompt_post_fetch"), - HookType::new("cmf.resource_pre_fetch"), - HookType::new("cmf.resource_post_fetch"), - ] -} - -/// Look up a hook type by name. Returns the canonical instance if -/// it matches a built-in, otherwise creates a new custom HookType. -pub fn hook_type_from_str(name: &str) -> HookType { - HookType::new(name) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_hook_type_equality() { - let a = HookType::new("tool_pre_invoke"); - let b = HookType::new("tool_pre_invoke"); - assert_eq!(a, b); - } - - #[test] - fn test_hook_type_display() { - let h = HookType::new("cmf.llm_input"); - assert_eq!(h.to_string(), "cmf.llm_input"); - } - - #[test] - fn test_hook_type_from_str() { - let h: HookType = "custom_hook".into(); - assert_eq!(h.as_str(), "custom_hook"); - } - - #[test] - fn test_builtin_hook_types_count() { - let builtins = builtin_hook_types(); - // 8 legacy + 8 CMF - assert_eq!(builtins.len(), 16); - } -} diff --git a/crates/cpex-payload/src/lib.rs b/crates/cpex-payload/src/lib.rs deleted file mode 100644 index eab7fc78..00000000 --- a/crates/cpex-payload/src/lib.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Location: ./crates/cpex-core/src/lib.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// CPEX Core library root. -// -// Pure Rust plugin runtime with no FFI, WASM, or PyO3 dependencies. -// Provides the PluginManager, 5-phase executor, hook registry, -// unified config parser, and all core types. -// -// # Modules -// -// - [`plugin`] — Plugin trait, PluginRef, PluginMetadata, PluginConfig -// - [`hooks`] — HookType (open string registry), payload/result traits -// - [`executor`] — 5-phase execution engine (sequential → transform → audit → concurrent → fire_and_forget) -// - [`manager`] — PluginManager lifecycle and hook dispatch -// - [`registry`] — PluginInstanceRegistry and HookRegistry -// - [`config`] — Unified YAML configuration parsing -// - [`factory`] — Plugin factory registry for config-driven instantiation -// - [`context`] — PluginContext (local_state + global_state) -// - [`cmf`] — ContextForge Message Format (Message, ContentPart, enums) -// - [`error`] — Error types, violations, and result types - -pub mod cmf; -pub mod config; -pub mod context; -pub mod error; -pub mod extensions; -pub mod hooks; -pub mod plugin; -pub mod plugins; diff --git a/crates/cpex-payload/src/plugin.rs b/crates/cpex-payload/src/plugin.rs deleted file mode 100644 index dd743a24..00000000 --- a/crates/cpex-payload/src/plugin.rs +++ /dev/null @@ -1,553 +0,0 @@ -// Location: ./crates/cpex-core/src/plugin.rs -// Copyright 2025 -// SPDX-License-Identifier: Apache-2.0 -// Authors: Teryl Taylor -// -// Plugin trait and supporting types. -// -// Defines the core Plugin trait that all plugin implementations satisfy — -// native Rust, WASM hosts, Python bridge hosts, and dlopen'd shared -// libraries. Also defines PluginConfig (YAML-declared plugin settings), -// PluginMode (5-phase execution modes), and OnError (failure behavior). -// -// The Plugin trait handles lifecycle only (initialize, shutdown, config). -// Hook-specific logic is defined by handler traits generated by the -// define_hook! macro (see hooks/macros.rs). A plugin implements Plugin -// for lifecycle + one or more handler traits for the hooks it handles. -// -// The manager wraps each plugin in a PluginRef with an authoritative -// config from the config loader — the plugin's own config() is for -// the plugin's reading only, never used by the executor for scheduling. - -use std::collections::HashSet; -use std::fmt; - -use async_trait::async_trait; -use serde::{Deserialize, Serialize}; - -use crate::error::PluginError; - -// --------------------------------------------------------------------------- -// Plugin Trait -// --------------------------------------------------------------------------- - -/// Core plugin interface — lifecycle management only. -/// -/// Every plugin in the CPEX framework — regardless of language or -/// deployment model — implements this trait. It covers lifecycle -/// (initialize, shutdown) and identity (config). Hook-specific logic -/// is defined separately by handler traits generated by `define_hook!`. -/// -/// # Lifecycle -/// -/// 1. `initialize()` — called once after loading, before any hooks fire. -/// 2. Hook handlers — called on each hook invocation (defined by handler traits). -/// 3. `shutdown()` — called once during graceful teardown. -/// -/// # Hook Handlers -/// -/// A plugin implements one or more handler traits alongside Plugin: -/// -/// ```rust,ignore -/// impl Plugin for MyPlugin { -/// fn config(&self) -> &PluginConfig { &self.config } -/// async fn initialize(&self) -> Result<(), Box> { Ok(()) } -/// async fn shutdown(&self) -> Result<(), Box> { Ok(()) } -/// } -/// -/// impl CmfHookHandler for MyPlugin { -/// fn cmf_hook(&self, payload: MessagePayload, ext: &Extensions, ctx: &PluginContext) -> PluginResult { -/// PluginResult::allow() -/// } -/// } -/// ``` -/// -/// # Trust Model -/// -/// The manager wraps each plugin in a `PluginRef` with an authoritative -/// config from the config loader. The executor reads scheduling decisions -/// (mode, priority, hooks, capabilities) from the `PluginRef` — never -/// from `plugin.config()`. The plugin's own `config()` is available for -/// the plugin's reading during hook execution. -/// -/// # Implementors -/// -/// - Native Rust plugins (implement directly) -/// - `cpex-hosts::wasm` (bridges to WASM guest via wasmtime) -/// - `cpex-hosts::python` (bridges to Python plugin classes via PyO3) -/// - `cpex-hosts::native` (bridges to dlopen'd shared libraries) -#[async_trait] -pub trait Plugin: Send + Sync { - /// Returns the plugin's configuration. - /// - /// Available for the plugin's own reading during hook execution. - /// The manager/executor never reads this — they use the authoritative - /// config from `PluginRef.trusted_config()`. - fn config(&self) -> &PluginConfig; - - /// One-time initialization after loading. - /// - /// Called before any hook invocations. Use this to establish - /// connections, load resources, or validate configuration. - /// Default implementation does nothing. - async fn initialize(&self) -> Result<(), Box> { - Ok(()) - } - - /// Graceful shutdown. - /// - /// Called once during teardown. Use this to flush buffers, close - /// connections, or release resources. - /// Default implementation does nothing. - async fn shutdown(&self) -> Result<(), Box> { - Ok(()) - } -} - -// --------------------------------------------------------------------------- -// Plugin Configuration -// --------------------------------------------------------------------------- - -/// Declared plugin configuration from the unified YAML config. -/// -/// Controls how the framework loads, schedules, and gates the plugin. -/// Corresponds to a single entry in the `plugins:` list in config YAML. -/// -/// The manager holds the authoritative copy in `PluginRef.trusted_config`. -/// The plugin receives its own copy for reading via `Plugin::config()`. -/// -/// # Examples -/// -/// ```yaml -/// plugins: -/// - name: apl-policy -/// kind: builtin -/// hooks: [tool_pre_invoke, tool_post_invoke] -/// mode: sequential -/// priority: 10 -/// on_error: fail -/// capabilities: [read_security, append_labels] -/// config: -/// policy_file: apl/demo/hr_policy.yaml -/// ``` -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct PluginConfig { - /// Unique plugin name. - pub name: String, - - /// Plugin kind — determines how the framework loads it. - /// - /// - `"builtin"` — compiled into the runtime - /// - `"native://path/to/lib.so"` — dlopen'd shared library - /// - `"wasm://path/to/plugin.wasm"` — wasmtime sandbox - /// - `"python://module.path.ClassName"` — PyO3 bridge - /// - `"external"` — MCP/gRPC/Unix socket transport - pub kind: String, - - /// Human-readable description. - #[serde(default)] - pub description: Option, - - /// Plugin author or team. - #[serde(default)] - pub author: Option, - - /// Semantic version string. - #[serde(default)] - pub version: Option, - - /// Hook names this plugin handles. - #[serde(default)] - pub hooks: Vec, - - /// Execution mode — determines scheduling behavior and authority. - #[serde(default)] - pub mode: PluginMode, - - /// Execution priority — lower numbers execute first within each mode. - #[serde(default = "default_priority")] - pub priority: i32, - - /// Error handling behavior when the plugin fails. - #[serde(default)] - pub on_error: OnError, - - /// Declared capabilities for extension visibility gating. - /// - /// Controls which extensions the plugin can see and modify. - /// Extensions not covered by declared capabilities appear as - /// `None` in the filtered view. - #[serde(default)] - pub capabilities: HashSet, - - /// Tags for categorization and searchability. - #[serde(default)] - pub tags: Vec, - - /// Legacy conditions for when the plugin should execute. - /// - /// Each condition narrows the plugin's scope by server, tenant, - /// tool name, prompt name, etc. If any condition in the list - /// matches, the plugin runs. If the list is empty (default), - /// the plugin runs unconditionally. - /// - /// **Backward compatibility:** Conditions are the legacy mechanism - /// for scoping plugins. When the host uses the unified routing - /// system (`routes:` in config YAML), routing rules handle scope - /// matching and conditions on the plugin are ignored. The two - /// mechanisms should not be used together on the same plugin. - #[serde(default)] - pub conditions: Vec, - - /// Plugin-specific configuration (opaque to the framework). - #[serde(default)] - pub config: Option, -} - -impl PluginConfig { - /// Whether this plugin's `conditions` allow it to fire for the given - /// request `Extensions`. Used in legacy mode (`routing_enabled: false`) - /// to filter which plugins run per request — mirrors the Python - /// implementation's per-plugin condition filtering. - /// - /// Semantics: - /// - Empty `conditions` Vec → fire always (no restriction). - /// - Non-empty → fire if ANY condition matches (OR across the list, - /// AND within each individual condition). - /// - /// Field-source mapping (see project memory `project_conditions_field_mapping`): - /// - `server_ids` ← `extensions.mcp.{tool|resource|prompt}.server_id` - /// - `tenant_ids` ← `extensions.security.subject.claims["tenant"]` - /// - `tools|prompts|resources` ← `extensions.meta.entity_name` (when matching `entity_type`) - /// - `agents` ← `extensions.agent.agent_id` - /// - `user_patterns` ← `extensions.security.subject.id` (glob match) - /// - `content_types` ← `extensions.mcp.resource.mime_type` - pub fn passes_conditions(&self, extensions: &crate::hooks::payload::Extensions) -> bool { - if self.conditions.is_empty() { - return true; - } - - // Source values once from the extensions tree. - let server_id = extensions.mcp.as_ref().and_then(|m| { - m.tool - .as_ref() - .and_then(|t| t.server_id.as_deref()) - .or_else(|| m.resource.as_ref().and_then(|r| r.server_id.as_deref())) - .or_else(|| m.prompt.as_ref().and_then(|p| p.server_id.as_deref())) - }); - let tenant_id = extensions - .security - .as_ref() - .and_then(|s| s.subject.as_ref()) - .and_then(|sub| sub.claims.get("tenant")) - .map(|s| s.as_str()); - let entity_name = extensions - .meta - .as_ref() - .and_then(|m| m.entity_name.as_deref()); - let entity_type = extensions - .meta - .as_ref() - .and_then(|m| m.entity_type.as_deref()); - let (tool, prompt, resource) = match entity_type { - Some("tool") => (entity_name, None, None), - Some("prompt") => (None, entity_name, None), - Some("resource") => (None, None, entity_name), - _ => (None, None, None), - }; - let agent = extensions - .agent - .as_ref() - .and_then(|a| a.agent_id.as_deref()); - let user = extensions - .security - .as_ref() - .and_then(|s| s.subject.as_ref()) - .and_then(|sub| sub.id.as_deref()); - let content_type = extensions - .mcp - .as_ref() - .and_then(|m| m.resource.as_ref()) - .and_then(|r| r.mime_type.as_deref()); - - let ctx = MatchContext { - server_id, - tenant_id, - tool, - prompt, - resource, - agent, - user, - content_type, - }; - self.conditions.iter().any(|c| c.matches(&ctx)) - } -} - -fn default_priority() -> i32 { - 100 -} - -// --------------------------------------------------------------------------- -// Plugin Condition (legacy scoping) -// --------------------------------------------------------------------------- - -/// Condition for when a plugin should execute. -/// -/// Narrows plugin scope to specific servers, tenants, tools, prompts, -/// resources, or agents. All fields are optional — only specified -/// fields participate in matching. Within a field, any match suffices -/// (OR semantics). Across fields, all must match (AND semantics). -/// -/// This is the legacy scoping mechanism. The unified routing system -/// (`routes:` in config) supersedes this — when routes are used, -/// conditions are ignored. -/// -/// Mirrors Python's `PluginCondition` in `cpex/framework/models.py`. -/// -/// # Examples -/// -/// ``` -/// use cpex_core::plugin::PluginCondition; -/// -/// // Only run for specific tools on specific servers -/// let cond = PluginCondition { -/// server_ids: Some(vec!["server-1".into(), "server-2".into()].into_iter().collect()), -/// tools: Some(vec!["get_compensation".into()].into_iter().collect()), -/// ..Default::default() -/// }; -/// assert!(cond.server_ids.as_ref().unwrap().contains("server-1")); -/// assert!(cond.tools.as_ref().unwrap().contains("get_compensation")); -/// ``` -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct PluginCondition { - /// Set of server IDs — plugin runs only on these servers. - #[serde(default)] - pub server_ids: Option>, - - /// Set of tenant IDs — plugin runs only for these tenants. - #[serde(default)] - pub tenant_ids: Option>, - - /// Set of tool names — plugin runs only for these tools. - #[serde(default)] - pub tools: Option>, - - /// Set of prompt names — plugin runs only for these prompts. - #[serde(default)] - pub prompts: Option>, - - /// Set of resource identifiers — plugin runs only for these resources. - #[serde(default)] - pub resources: Option>, - - /// Set of agent identifiers — plugin runs only for these agents. - #[serde(default)] - pub agents: Option>, - - /// User patterns (glob or regex) — plugin runs only for matching users. - #[serde(default)] - pub user_patterns: Option>, - - /// Content types — plugin runs only for these content types. - #[serde(default)] - pub content_types: Option>, -} - -/// Bundle of optional context values used to evaluate a `PluginCondition`. -/// -/// Each field corresponds to one of the condition's gates. `None` means -/// "no value sourced from the extensions tree"; the condition then -/// rejects when the corresponding `Some(set)` is set on the condition -/// (i.e., the gate was specified but couldn't be evaluated). -/// -/// Replaces an 8-arg `matches(...)` call where every arg was -/// `Option<&str>` and could be misordered silently. -#[derive(Debug, Default, Clone, Copy)] -pub struct MatchContext<'a> { - pub server_id: Option<&'a str>, - pub tenant_id: Option<&'a str>, - pub tool: Option<&'a str>, - pub prompt: Option<&'a str>, - pub resource: Option<&'a str>, - pub agent: Option<&'a str>, - pub user: Option<&'a str>, - pub content_type: Option<&'a str>, -} - -impl PluginCondition { - /// Whether this condition matches the given context. - /// - /// A field that is `None` is treated as "any" (no restriction). - /// A `Some(set)` field matches if the given value is in the set - /// (exact match for ID-shaped fields; glob match via `wildmatch` - /// for `user_patterns`). - /// All specified fields must match — AND semantics within one condition. - pub fn matches(&self, ctx: &MatchContext<'_>) -> bool { - let MatchContext { - server_id, - tenant_id, - tool, - prompt, - resource, - agent, - user, - content_type, - } = *ctx; - let check_set = |field: &Option>, value: Option<&str>| -> bool { - match field { - None => true, // not specified — matches anything - Some(set) => match value { - Some(v) => set.contains(v), - None => false, // field required but no value provided - }, - } - }; - - // user_patterns: list of globs. Match if any pattern matches the user. - let check_patterns = |field: &Option>, value: Option<&str>| -> bool { - match field { - None => true, - Some(patterns) => match value { - Some(v) => patterns - .iter() - .any(|p| wildmatch::WildMatch::new(p).matches(v)), - None => false, - }, - } - }; - - // content_types: list of exact strings. - let check_list = |field: &Option>, value: Option<&str>| -> bool { - match field { - None => true, - Some(list) => match value { - Some(v) => list.iter().any(|s| s == v), - None => false, - }, - } - }; - - check_set(&self.server_ids, server_id) - && check_set(&self.tenant_ids, tenant_id) - && check_set(&self.tools, tool) - && check_set(&self.prompts, prompt) - && check_set(&self.resources, resource) - && check_set(&self.agents, agent) - && check_patterns(&self.user_patterns, user) - && check_list(&self.content_types, content_type) - } -} - -// --------------------------------------------------------------------------- -// Plugin Mode -// --------------------------------------------------------------------------- - -/// Execution mode — determines a plugin's scheduling behavior and authority. -/// -/// The 5-phase model defines both what a plugin *can do* (block, modify) -/// and *how it runs* (serial, parallel, background). Scheduling is derived -/// from mode; plugin authors don't control it directly. -/// -/// # Execution Order -/// -/// ```text -/// SEQUENTIAL → TRANSFORM → AUDIT → CONCURRENT → FIRE_AND_FORGET -/// ``` -/// -/// # Mode Capabilities -/// -/// | Mode | Can Block? | Can Modify? | Execution | -/// |----------------|------------|-------------|-----------------| -/// | Sequential | Yes | Yes | Serial, chained | -/// | Transform | No | Yes | Serial, chained | -/// | Audit | No | No | Serial | -/// | Concurrent | Yes | No | Parallel | -/// | FireAndForget | No | No | Background | -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)] -#[serde(rename_all = "snake_case")] -#[non_exhaustive] -pub enum PluginMode { - /// Policy enforcement + transformation. Serial, chained. Can block and modify. - #[default] - Sequential, - - /// Data shaping (PII redaction, normalization). Serial, chained. Can modify, cannot block. - Transform, - - /// Observation and logging. Serial, read-only. Cannot block or modify. - Audit, - - /// Independent policy gates. Parallel, fail-fast. Can block, cannot modify. - Concurrent, - - /// Telemetry and async side effects. Background tasks. Cannot block or modify. - FireAndForget, - - /// Plugin is disabled — skipped during execution. - Disabled, -} - -impl PluginMode { - /// Whether this mode allows the plugin to block the pipeline. - pub fn can_block(&self) -> bool { - matches!(self, Self::Sequential | Self::Concurrent) - } - - /// Whether this mode allows the plugin to modify the payload. - pub fn can_modify(&self) -> bool { - matches!(self, Self::Sequential | Self::Transform) - } - - /// Whether the framework waits for this plugin to complete. - pub fn is_awaited(&self) -> bool { - !matches!(self, Self::FireAndForget | Self::Disabled) - } -} - -impl fmt::Display for PluginMode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Sequential => write!(f, "sequential"), - Self::Transform => write!(f, "transform"), - Self::Audit => write!(f, "audit"), - Self::Concurrent => write!(f, "concurrent"), - Self::FireAndForget => write!(f, "fire_and_forget"), - Self::Disabled => write!(f, "disabled"), - } - } -} - -// --------------------------------------------------------------------------- -// Error Handling Mode -// --------------------------------------------------------------------------- - -/// Error handling behavior when a plugin fails. -/// -/// Independent of [`PluginMode`] — any mode can use any error behavior. -/// Controls whether plugin failures halt the pipeline, are logged and -/// skipped, or cause the plugin to be auto-disabled. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)] -#[serde(rename_all = "snake_case")] -#[non_exhaustive] -pub enum OnError { - /// Pipeline halts and error propagates. Fail-safe enforcement. - #[default] - Fail, - - /// Error logged, pipeline continues. For non-critical plugins. - Ignore, - - /// Plugin auto-disabled after error. Prevents repeated failures. - Disable, -} - -impl fmt::Display for OnError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Fail => write!(f, "fail"), - Self::Ignore => write!(f, "ignore"), - Self::Disable => write!(f, "disable"), - } - } -} diff --git a/crates/cpex-payload/src/plugins/identity_checker.rs b/crates/cpex-payload/src/plugins/identity_checker.rs deleted file mode 100644 index 59e1450e..00000000 --- a/crates/cpex-payload/src/plugins/identity_checker.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::cmf::message::MessagePayload; -use crate::context::PluginContext; -use crate::error::PluginViolation; -use crate::extensions::Extensions; -use crate::hooks::PluginResult; - -pub fn identity_check(payload: &MessagePayload, extensions: &Extensions, _ctx: &PluginContext) -> PluginResult { - let is_result = payload.message.is_tool_result(); - - if is_result { - let tool_name = payload - .message - .get_tool_results() - .first() - .map(|tr| tr.tool_name.as_str()) - .unwrap_or("unknown"); - println!( - " [identity-checker] POST-INVOKE: verifying result from '{}'", - tool_name - ); - - if let Some(ref security) = extensions.security { - if let Some(ref subject) = security.subject { - println!( - " [identity-checker] Result authorized for subject: {:?}", - subject.id - ); - } - } - println!(" [identity-checker] POST-INVOKE ALLOWED"); - } else { - let tool_name = payload - .message - .get_tool_calls() - .first() - .map(|tc| tc.name.as_str()) - .unwrap_or("unknown"); - println!( - " [identity-checker] PRE-INVOKE: checking identity for '{}'", - tool_name - ); - - if let Some(ref security) = extensions.security { - let labels: Vec<&String> = security.labels.iter().collect(); - println!(" [identity-checker] Security labels: {:?}", labels); - - if let Some(ref subject) = security.subject { - println!( - " [identity-checker] Subject: {:?}, Roles: {:?}", - subject.id, - subject.roles.iter().collect::>() - ); - - if security.has_label("PII") && !subject.roles.contains("hr_admin") { - return PluginResult::deny(PluginViolation::new( - "insufficient_role", - &format!( - "Tool '{}' requires 'hr_admin' role for PII data", - tool_name - ), - )); - } - } - } - - if extensions.http.is_some() { - println!(" [identity-checker] WARNING: HTTP visible (unexpected!)"); - } else { - println!(" [identity-checker] HTTP: not visible (correct — no read_headers)"); - } - println!(" [identity-checker] PRE-INVOKE ALLOWED"); - } - - PluginResult::allow() -} diff --git a/crates/cpex-payload/src/plugins/mod.rs b/crates/cpex-payload/src/plugins/mod.rs deleted file mode 100644 index 7ec351ed..00000000 --- a/crates/cpex-payload/src/plugins/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod identity_checker; \ No newline at end of file From d7fa41604f7b1f012df661dca2ad1d3c9ae69045 Mon Sep 17 00:00:00 2001 From: Shriti Priya Date: Thu, 2 Jul 2026 19:21:28 -0400 Subject: [PATCH 6/7] feat: wit interface redesign and implementation Signed-off-by: Shriti Priya --- Cargo.lock | 16 +- Cargo.toml | 1 - crates/cpex-core/src/cmf/message.rs | 1 + crates/cpex-core/src/hooks/mod.rs | 2 +- crates/cpex-core/src/hooks/payload.rs | 78 ++ crates/cpex-wasm-host/Cargo.toml | 1 + crates/cpex-wasm-host/config/config.yaml | 2 +- .../examples/wasm_generic_payload_demo.rs | 166 ++++ .../examples/wasm_plugin_demo.rs | 192 ++--- crates/cpex-wasm-host/src/conversions.rs | 752 +++++++++++++--- crates/cpex-wasm-host/src/factory.rs | 78 +- crates/cpex-wasm-host/src/lib.rs | 1 + crates/cpex-wasm-host/src/payload_registry.rs | 122 +++ crates/cpex-wasm-host/src/sandbox_manager.rs | 7 +- crates/cpex-wasm-host/wasm/plugin.wasm | Bin 604424 -> 660721 bytes crates/cpex-wasm-host/wit/world.wit | 336 +++++++- crates/cpex-wasm-plugin/Cargo.lock | 274 +++++- crates/cpex-wasm-plugin/Cargo.toml | 4 +- crates/cpex-wasm-plugin/README.md | 260 +++--- crates/cpex-wasm-plugin/plugin.wasm | Bin 604424 -> 660721 bytes .../specs/current-implementation-issues.md | 254 ++++++ .../specs/generalized-payload-interface.md | 683 +++++++++++++++ .../specs/implementation_phase.md | 274 ++++++ crates/cpex-wasm-plugin/specs/limitations.md | 160 ++++ crates/cpex-wasm-plugin/src/conversions.rs | 814 ++++++++++-------- crates/cpex-wasm-plugin/src/lib.rs | 168 ++-- crates/cpex-wasm-plugin/src/plugin.rs | 110 +++ crates/cpex-wasm-plugin/wit/world.wit | 334 ++++++- 28 files changed, 4242 insertions(+), 848 deletions(-) create mode 100644 crates/cpex-wasm-host/examples/wasm_generic_payload_demo.rs create mode 100644 crates/cpex-wasm-host/src/payload_registry.rs create mode 100644 crates/cpex-wasm-plugin/specs/current-implementation-issues.md create mode 100644 crates/cpex-wasm-plugin/specs/generalized-payload-interface.md create mode 100644 crates/cpex-wasm-plugin/specs/implementation_phase.md create mode 100644 crates/cpex-wasm-plugin/specs/limitations.md create mode 100644 crates/cpex-wasm-plugin/src/plugin.rs diff --git a/Cargo.lock b/Cargo.lock index 40036aa6..5046e360 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,21 +937,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "cpex-payload" -version = "0.1.0" -dependencies = [ - "async-trait", - "serde", - "serde_json", - "serde_yaml", - "thiserror 2.0.18", - "tokio", - "tracing", - "uuid", - "wildmatch", -] - [[package]] name = "cpex-sdk" version = "0.1.0" @@ -968,6 +953,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "chrono", "cpex-core", "hyper", "serde", diff --git a/Cargo.toml b/Cargo.toml index ac3c978c..6098dc22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ members = [ "crates/cpex-sdk", "crates/cpex-builtins", "crates/cpex-ffi", - "crates/cpex-payload", "crates/cpex-wasm-host", "crates/apl-core", "crates/apl-cmf", diff --git a/crates/cpex-core/src/cmf/message.rs b/crates/cpex-core/src/cmf/message.rs index 6a13a2ec..3846fecd 100644 --- a/crates/cpex-core/src/cmf/message.rs +++ b/crates/cpex-core/src/cmf/message.rs @@ -227,6 +227,7 @@ pub struct MessagePayload { } crate::impl_plugin_payload!(MessagePayload); +crate::impl_wasm_payload!(MessagePayload, "cmf.message"); // --------------------------------------------------------------------------- // CmfHook — Hook Type Definition diff --git a/crates/cpex-core/src/hooks/mod.rs b/crates/cpex-core/src/hooks/mod.rs index 05b04e65..499ee3c9 100644 --- a/crates/cpex-core/src/hooks/mod.rs +++ b/crates/cpex-core/src/hooks/mod.rs @@ -31,6 +31,6 @@ pub use adapter::TypedHandlerAdapter; pub use metadata::{ lookup as lookup_hook_metadata, register_hook_metadata, HookMetadata, HookPhase, }; -pub use payload::{Extensions, PluginPayload}; +pub use payload::{Extensions, PluginPayload, WasmSerializablePayload}; pub use trait_def::{HookHandler, HookTypeDef, PluginResult}; pub use types::{builtin_hook_types, hook_type_from_str, HookType}; diff --git a/crates/cpex-core/src/hooks/payload.rs b/crates/cpex-core/src/hooks/payload.rs index d284bf4c..9dca9d2b 100644 --- a/crates/cpex-core/src/hooks/payload.rs +++ b/crates/cpex-core/src/hooks/payload.rs @@ -131,3 +131,81 @@ macro_rules! impl_plugin_payload { } }; } + +// --------------------------------------------------------------------------- +// WasmSerializablePayload — opt-in WASM transport trait +// --------------------------------------------------------------------------- + +/// Opt-in trait for payload types that can cross the WASM serialization boundary. +/// +/// Implement this (via [`impl_wasm_payload!`]) for any payload type that WASM +/// plugins should be able to receive or return. The type discriminator string +/// is embedded in the WIT `generic-payload` record so the host and guest can +/// agree on which concrete type to deserialize. +/// +/// # Example +/// +/// ``` +/// use serde::{Deserialize, Serialize}; +/// use cpex_core::{impl_plugin_payload, impl_wasm_payload}; +/// +/// #[derive(Debug, Clone, Serialize, Deserialize)] +/// struct ToolInvokePayload { +/// tool_name: String, +/// user: String, +/// } +/// +/// impl_plugin_payload!(ToolInvokePayload); +/// impl_wasm_payload!(ToolInvokePayload, "cpex.tool_invoke"); +/// ``` +pub trait WasmSerializablePayload: PluginPayload { + /// Type discriminator used in the WIT `generic-payload` record. + /// + /// Must be unique across all payload types registered with the host. + /// Convention: `"."` (e.g. `"cmf.message"`, `"cpex.tool_invoke"`). + fn payload_type_name() -> &'static str + where + Self: Sized; + + /// Serialize this payload to JSON bytes for WASM transport. + fn to_wasm_bytes(&self) -> Result, serde_json::Error>; + + /// Deserialize a payload from JSON bytes received from WASM. + fn from_wasm_bytes(bytes: &[u8]) -> Result + where + Self: Sized; +} + +/// Implements [`WasmSerializablePayload`] for a type that is `Serialize + Deserialize`. +/// +/// The type must already implement [`PluginPayload`] (via [`impl_plugin_payload!`]). +/// +/// ``` +/// use serde::{Deserialize, Serialize}; +/// use cpex_core::{impl_plugin_payload, impl_wasm_payload}; +/// +/// #[derive(Debug, Clone, Serialize, Deserialize)] +/// struct ToolInvokePayload { +/// tool_name: String, +/// user: String, +/// } +/// +/// impl_plugin_payload!(ToolInvokePayload); +/// impl_wasm_payload!(ToolInvokePayload, "cpex.tool_invoke"); +/// ``` +#[macro_export] +macro_rules! impl_wasm_payload { + ($ty:ty, $name:literal) => { + impl $crate::hooks::payload::WasmSerializablePayload for $ty { + fn payload_type_name() -> &'static str { + $name + } + fn to_wasm_bytes(&self) -> Result, serde_json::Error> { + serde_json::to_vec(self) + } + fn from_wasm_bytes(bytes: &[u8]) -> Result { + serde_json::from_slice(bytes) + } + } + }; +} diff --git a/crates/cpex-wasm-host/Cargo.toml b/crates/cpex-wasm-host/Cargo.toml index 376d998f..61f3d2c2 100644 --- a/crates/cpex-wasm-host/Cargo.toml +++ b/crates/cpex-wasm-host/Cargo.toml @@ -15,3 +15,4 @@ serde_yaml = "0.9" serde_json = { workspace = true } cpex-core = { path = "../cpex-core" } async-trait = "0.1" +chrono = { version = "0.4", features = ["serde"] } diff --git a/crates/cpex-wasm-host/config/config.yaml b/crates/cpex-wasm-host/config/config.yaml index 818b488e..f3e9854d 100644 --- a/crates/cpex-wasm-host/config/config.yaml +++ b/crates/cpex-wasm-host/config/config.yaml @@ -15,7 +15,7 @@ global: plugins: - name: identity-checker kind: wasm://plugin.wasm - hooks: [cmf.tool_pre_invoke, cmf.tool_post_invoke] + hooks: [cmf.tool_pre_invoke, cmf.tool_post_invoke, tool_pre_invoke] mode: sequential priority: 10 on_error: fail diff --git a/crates/cpex-wasm-host/examples/wasm_generic_payload_demo.rs b/crates/cpex-wasm-host/examples/wasm_generic_payload_demo.rs new file mode 100644 index 00000000..8bfb92b0 --- /dev/null +++ b/crates/cpex-wasm-host/examples/wasm_generic_payload_demo.rs @@ -0,0 +1,166 @@ +// Location: ./crates/cpex-wasm-host/examples/wasm_generic_payload_demo.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Shriti Priya +// +// Generic payload WASM plugin demo. +// +// Demonstrates a custom payload type (ToolInvokePayload) crossing the WASM +// boundary via HookPayload::Generic. This covers the path beyond CMF: +// +// host: ToolInvokePayload → PayloadSerializerRegistry.serialize() +// → HookPayload::Generic { payload_type: "cpex.tool_invoke", bytes } +// → SandboxManager.invoke() +// WASM guest: receives Generic variant, logs receipt, returns allow() +// host: PayloadSerializerRegistry.deserialize() on any modified payload +// +// The guest currently returns allow() for all Generic payloads (full typed +// dispatch requires Phase 5+ generic handler registration in the guest). +// This demo validates the host-side infrastructure and the wire format. +// +// Prerequisites: build the WASM plugin first: +// cargo build -p cpex-wasm-plugin --target wasm32-wasip2 +// +// Run from the workspace root: +// cargo run -p cpex-wasm-host --example wasm_generic_payload_demo + +use std::path::PathBuf; +use std::sync::Arc; + +use serde::{Deserialize, Serialize}; + +use cpex_core::config::parse_config; +use cpex_core::extensions::container::Extensions; +use cpex_core::extensions::security::{SecurityExtension, SubjectExtension, SubjectType}; +use cpex_core::hooks::payload::WasmSerializablePayload; +use cpex_core::hooks::trait_def::PluginResult; +use cpex_core::manager::PluginManager; +use cpex_core::{impl_plugin_payload, impl_wasm_payload}; + +use cpex_wasm_host::factory::WasmPluginFactory; +use cpex_wasm_host::payload_registry::PayloadSerializerRegistry; + +// --------------------------------------------------------------------------- +// Custom payload type — defined on the host, shared with WASM guests +// --------------------------------------------------------------------------- + +/// A structured tool-invocation payload carrying explicit user/tool identity. +/// Distinct from CMF's MessagePayload: models the invocation itself rather +/// than the conversation turn containing the tool call. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ToolInvokePayload { + /// Name of the tool being invoked. + tool_name: String, + /// Invoking user's identity. + user: String, + /// Serialized tool arguments (JSON). + arguments: String, +} + +// Register with cpex-core's PluginPayload trait system. +impl_plugin_payload!(ToolInvokePayload); + +// Register for WASM transport: type discriminator + JSON serialization. +impl_wasm_payload!(ToolInvokePayload, "cpex.tool_invoke"); + +// --------------------------------------------------------------------------- +// Hook type definition for tool pre-invoke +// --------------------------------------------------------------------------- + +cpex_core::define_hook! { + /// Hook fired before a tool is invoked. Payload carries explicit user/tool identity. + ToolPreInvoke, "tool_pre_invoke" => { + payload: ToolInvokePayload, + result: PluginResult, + } +} + +// --------------------------------------------------------------------------- +// Demo +// --------------------------------------------------------------------------- + +#[tokio::main] +async fn main() { + println!("=== WASM Plugin Demo — Generic Payload (ToolInvokePayload) ===\n"); + + let crate_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let config_path = crate_dir.join("config/config.yaml"); + + println!("Loading config: {}", config_path.display()); + let yaml = std::fs::read_to_string(&config_path) + .unwrap_or_else(|e| panic!("failed to read {}: {}", config_path.display(), e)); + let cpex_config = parse_config(&yaml).unwrap(); + + // Build a registry with both MessagePayload (CMF fast-path) and + // ToolInvokePayload (generic path) registered. + let mut registry = PayloadSerializerRegistry::new(); + registry.register::(); + registry.register::(); + + println!( + "PayloadSerializerRegistry: registered 'cmf.message' and '{}'\n", + ToolInvokePayload::payload_type_name() + ); + + let mgr = PluginManager::default(); + mgr.register_factory( + "wasm://plugin.wasm", + Box::new(WasmPluginFactory::new(crate_dir.join("wasm"), Arc::new(registry))), + ); + mgr.load_config(cpex_config).unwrap(); + mgr.initialize().await.unwrap(); + + // Build the custom payload. + let payload = ToolInvokePayload { + tool_name: "get_compensation".into(), + user: "alice".into(), + arguments: r#"{"employee_id": 42}"#.into(), + }; + + println!("Payload: {:?}", payload); + println!( + "Wire type: '{}' ({} bytes when serialized)\n", + ToolInvokePayload::payload_type_name(), + payload.to_wasm_bytes().unwrap().len() + ); + + // Build extensions with identity context. + let ext = build_extensions(); + + // --- Invoke through the WASM pipeline --- + println!("=== tool_pre_invoke via WASM (Generic path) ==="); + + let (result, bg) = mgr + .invoke_named::("tool_pre_invoke", payload, ext, None) + .await; + + if result.continue_processing { + println!("Result: ALLOWED"); + println!(" (guest received Generic payload, logged receipt, returned allow())"); + } else { + let reason = result.violation.as_ref().map(|v| v.reason.as_str()).unwrap_or("unknown"); + println!("Result: DENIED — {}", reason); + } + + bg.wait_for_background_tasks().await; + + println!("\n=== Demo complete ==="); + println!("\nNote: full typed dispatch inside the guest (ToolInvokePayload → HookHandler)"); + println!("is the next milestone — the host-side generic path and wire format are validated here."); +} + +fn build_extensions() -> Extensions { + let mut security = SecurityExtension::default(); + security.subject = Some(SubjectExtension { + id: Some("alice".into()), + subject_type: Some(SubjectType::User), + roles: ["tool_user".to_string()].into(), + permissions: ["invoke_tools".to_string()].into(), + ..Default::default() + }); + + Extensions { + security: Some(Arc::new(security)), + ..Default::default() + } +} diff --git a/crates/cpex-wasm-host/examples/wasm_plugin_demo.rs b/crates/cpex-wasm-host/examples/wasm_plugin_demo.rs index a61a1104..8b8ae5cc 100644 --- a/crates/cpex-wasm-host/examples/wasm_plugin_demo.rs +++ b/crates/cpex-wasm-host/examples/wasm_plugin_demo.rs @@ -3,52 +3,63 @@ // SPDX-License-Identifier: Apache-2.0 // Authors: Shriti Priya // -// Demonstrates invoking a WASM plugin through the PluginManager pipeline. -// Reads plugin configuration from config/config.yaml. +// CMF payload WASM plugin demo. +// +// Shows the end-to-end CMF path: a MessagePayload crosses the WASM boundary, +// the guest IdentityCheckerPlugin (written with HookHandler — identical +// to a native plugin) runs the identity check, and the result flows back. +// +// Prerequisites: build the WASM plugin first: +// cargo build -p cpex-wasm-plugin --target wasm32-wasip2 +// cargo run --example wasm_plugin_demo +// +// Run from the workspace root: +// cargo run -p cpex-wasm-host --example wasm_plugin_demo use std::path::PathBuf; use std::sync::Arc; -use cpex_core::cmf::{CmfHook, ContentPart, Message, MessagePayload, Role, ToolCall}; +use cpex_core::cmf::{CmfHook, ContentPart, Message, MessagePayload, Role, ToolCall, ToolResult}; use cpex_core::config::parse_config; -use cpex_core::extensions::{HttpExtension, RequestExtension, SecurityExtension}; -use cpex_core::hooks::payload::{Extensions, MetaExtension}; -use cpex_core::extensions::security::SubjectExtension; +use cpex_core::extensions::container::Extensions; +use cpex_core::extensions::http::HttpExtension; +use cpex_core::extensions::meta::MetaExtension; +use cpex_core::extensions::request::RequestExtension; +use cpex_core::extensions::security::{SecurityExtension, SubjectExtension, SubjectType}; use cpex_core::manager::PluginManager; use cpex_wasm_host::factory::WasmPluginFactory; #[tokio::main] async fn main() { - println!("=== WASM Plugin Demo (via PluginManager) ===\n"); + println!("=== WASM Plugin Demo — CMF MessagePayload ===\n"); - // 1. Create plugin manager and register wasm factory under the exact kind string. - // Each plugin gets its own isolated SandboxManager instance. let crate_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let config_path = crate_dir.join("config/config.yaml"); - println!("--- Loading config from {} ---\n", config_path.display()); + + println!("Loading config: {}", config_path.display()); let yaml = std::fs::read_to_string(&config_path) - .unwrap_or_else(|e| panic!("Failed to read {}: {}", config_path.display(), e)); + .unwrap_or_else(|e| panic!("failed to read {}: {}", config_path.display(), e)); let cpex_config = parse_config(&yaml).unwrap(); + // WasmPluginFactory::with_cmf_only registers MessagePayload in the + // PayloadSerializerRegistry — the CMF fast-path uses HookPayload::Cmf + // directly rather than going through the generic serialization path. let mgr = PluginManager::default(); mgr.register_factory( "wasm://plugin.wasm", - Box::new(WasmPluginFactory::new(crate_dir.join("wasm"))), + Box::new(WasmPluginFactory::with_cmf_only(crate_dir.join("wasm"))), ); - mgr.load_config(cpex_config).unwrap(); mgr.initialize().await.unwrap(); - // 3. Build a test payload (assistant requesting a tool call) - let payload = MessagePayload { + // Build a pre-invoke payload: assistant requesting a tool call. + let pre_payload = MessagePayload { message: Message { schema_version: cpex_core::cmf::constants::SCHEMA_VERSION.into(), role: Role::Assistant, content: vec![ - ContentPart::Text { - text: "Looking up compensation.".into(), - }, + ContentPart::Text { text: "Looking up compensation data.".into() }, ContentPart::ToolCall { content: ToolCall { tool_call_id: "tc_001".into(), @@ -62,89 +73,36 @@ async fn main() { }, }; - // 4. Build extensions with security context - let mut security = SecurityExtension::default(); - security.add_label("PII"); - security.add_label("HR_DATA"); - security.classification = Some("confidential".into()); - security.subject = Some(SubjectExtension { - id: Some("alice".into()), - subject_type: Some(cpex_core::extensions::security::SubjectType::User), - roles: ["hr_admin".to_string()].into(), - permissions: ["read_compensation".to_string()].into(), - ..Default::default() - }); - - let mut http = HttpExtension::default(); - http.set_header("Authorization", "Bearer eyJ..."); - http.set_header("X-Request-ID", "req-abc-123"); - - let ext = Extensions { - request: Some(Arc::new(RequestExtension { - environment: Some("production".into()), - request_id: Some("req-abc-123".into()), - ..Default::default() - })), - security: Some(Arc::new(security)), - http: Some(Arc::new(http)), - meta: Some(Arc::new(MetaExtension { - entity_type: Some("tool".into()), - entity_name: Some("get_compensation".into()), - tags: ["pii".to_string(), "hr".to_string()].into(), - ..Default::default() - })), - ..Default::default() - }; + let pre_ext = build_extensions("PII"); - // --- Pre-invoke: type-safe dispatch via invoke_named --- - println!("=== Phase 1: cmf.tool_pre_invoke ===\n"); - let (pre_result, bg) = mgr - .invoke_named::( - "cmf.tool_pre_invoke", - payload, - ext, - None, // first hook — no context table - ) + // --- Phase 1: pre-invoke --- + println!("=== cmf.tool_pre_invoke ==="); + let (pre_result, pre_bg) = mgr + .invoke_named::("cmf.tool_pre_invoke", pre_payload, pre_ext, None) .await; - println!(); - - println!(); if pre_result.continue_processing { - println!("Pre-invoke result: ALLOWED"); - if let Some(ref modified_ext) = pre_result.modified_extensions { - if let Some(ref sec) = modified_ext.security { - let labels: Vec<&String> = sec.labels.iter().collect(); - println!(" Labels after pre-invoke: {:?}", labels); - } - if let Some(ref http) = modified_ext.http { - println!(" Headers after pre-invoke: {:?}", http.request_headers); - } - } + println!("Pre-invoke: ALLOWED"); } else { - println!( - "Pre-invoke result: DENIED — {}", - pre_result.violation.as_ref().unwrap().reason - ); - bg.wait_for_background_tasks().await; + let reason = pre_result.violation.as_ref().map(|v| v.reason.as_str()).unwrap_or("unknown"); + println!("Pre-invoke: DENIED — {}", reason); + pre_bg.wait_for_background_tasks().await; println!("\n=== Demo complete ==="); return; } - bg.wait_for_background_tasks().await; - - println!("\n--- Tool 'get_compensation' executes... ---"); - println!(" Result: {{\"salary\": 150000, \"currency\": \"USD\"}}\n"); + pre_bg.wait_for_background_tasks().await; - // --- Post-invoke: different CMF message with tool result --- - println!("=== Phase 2: cmf.tool_post_invoke ===\n"); + println!("\n [tool executes: {{\"salary\": 150000, \"currency\": \"USD\"}}]\n"); + // --- Phase 2: post-invoke with tool result --- + println!("=== cmf.tool_post_invoke ==="); let post_payload = MessagePayload { message: Message { schema_version: cpex_core::cmf::constants::SCHEMA_VERSION.into(), role: Role::Tool, content: vec![ContentPart::ToolResult { - content: cpex_core::cmf::ToolResult { + content: ToolResult { tool_call_id: "tc_001".into(), tool_name: "get_compensation".into(), content: serde_json::json!({"salary": 150000, "currency": "USD"}), @@ -155,24 +113,10 @@ async fn main() { }, }; - // Build post-invoke extensions — carry forward any modifications - // from pre-invoke via the context table - let post_ext = pre_result.modified_extensions.unwrap_or_else(|| { - // Rebuild if no modifications - let mut security = SecurityExtension::default(); - security.add_label("PII"); - Extensions { - security: Some(Arc::new(security)), - meta: Some(Arc::new(MetaExtension { - entity_type: Some("tool".into()), - entity_name: Some("get_compensation".into()), - ..Default::default() - })), - ..Default::default() - } - }); + // Carry forward any modified extensions from pre-invoke; rebuild if none. + let post_ext = pre_result.modified_extensions.unwrap_or_else(|| build_extensions("PII")); - let (post_result, post_bg) = mgr + let (post_result, post_bg) = mgr .invoke_named::( "cmf.tool_post_invoke", post_payload, @@ -181,16 +125,48 @@ async fn main() { ) .await; - println!(); if post_result.continue_processing { - println!("Post-invoke result: ALLOWED"); + println!("Post-invoke: ALLOWED"); } else { - println!( - "Post-invoke result: DENIED — {}", - post_result.violation.as_ref().unwrap().reason - ); + let reason = post_result.violation.as_ref().map(|v| v.reason.as_str()).unwrap_or("unknown"); + println!("Post-invoke: DENIED — {}", reason); } post_bg.wait_for_background_tasks().await; println!("\n=== Demo complete ==="); } + +fn build_extensions(security_label: &str) -> Extensions { + let mut security = SecurityExtension::default(); + security.add_label(security_label); + security.add_label("HR_DATA"); + security.classification = Some("confidential".into()); + security.subject = Some(SubjectExtension { + id: Some("alice".into()), + subject_type: Some(SubjectType::User), + roles: ["hr_admin".to_string()].into(), + permissions: ["read_compensation".to_string()].into(), + ..Default::default() + }); + + let mut http = HttpExtension::default(); + http.set_header("Authorization", "Bearer eyJ..."); + http.set_header("X-Request-ID", "req-abc-123"); + + Extensions { + request: Some(Arc::new(RequestExtension { + environment: Some("production".into()), + request_id: Some("req-abc-123".into()), + ..Default::default() + })), + security: Some(Arc::new(security)), + http: Some(Arc::new(http)), + meta: Some(Arc::new(MetaExtension { + entity_type: Some("tool".into()), + entity_name: Some("get_compensation".into()), + tags: ["pii".to_string(), "hr".to_string()].into(), + ..Default::default() + })), + ..Default::default() + } +} diff --git a/crates/cpex-wasm-host/src/conversions.rs b/crates/cpex-wasm-host/src/conversions.rs index 1aea5bf3..2f8035fa 100644 --- a/crates/cpex-wasm-host/src/conversions.rs +++ b/crates/cpex-wasm-host/src/conversions.rs @@ -7,34 +7,56 @@ // Used by WasmBridgeHandler to translate between the PluginManager's native // types and the WIT types that the WASM sandbox expects. -use std::collections::HashMap; +use std::sync::Arc; + +use chrono::DateTime; use cpex_core::cmf::content as native_content; use cpex_core::cmf::enums as native_enums; use cpex_core::cmf::message as native_msg; use cpex_core::context::PluginContext as NativePluginContext; use cpex_core::error::PluginViolation as NativePluginViolation; -use cpex_core::extensions::container::Extensions as NativeExtensions; +use cpex_core::extensions::agent::AgentExtension as NativeAgentExtension; +use cpex_core::extensions::authorization::AuthorizationDetail as NativeAuthDetail; +use cpex_core::extensions::completion::{ + CompletionExtension as NativeCompletionExtension, StopReason as NativeStopReason, + TokenUsage as NativeTokenUsage, +}; +use cpex_core::extensions::container::{ + Extensions as NativeExtensions, OwnedExtensions as NativeOwnedExtensions, +}; +use cpex_core::extensions::delegation::{ + DelegationExtension as NativeDelegationExtension, DelegationHop as NativeDelegationHop, + DelegationStrategy as NativeDelegationStrategy, +}; +use cpex_core::extensions::framework::FrameworkExtension as NativeFrameworkExtension; use cpex_core::extensions::http::HttpExtension as NativeHttpExtension; +use cpex_core::extensions::llm::LLMExtension as NativeLLMExtension; +use cpex_core::extensions::mcp::{ + MCPExtension as NativeMCPExtension, PromptMetadata as NativePromptMetadata, + ResourceMetadata as NativeResourceMetadata, ToolMetadata as NativeToolMetadata, +}; use cpex_core::extensions::meta::MetaExtension as NativeMetaExtension; +use cpex_core::extensions::provenance::ProvenanceExtension as NativeProvenanceExtension; use cpex_core::extensions::request::RequestExtension as NativeRequestExtension; use cpex_core::extensions::security::{ - SecurityExtension as NativeSecurityExtension, SubjectExtension as NativeSubjectExtension, - SubjectType as NativeSubjectType, + ClientExtension as NativeClientExtension, ClientTrustLevel as NativeClientTrustLevel, + DataPolicy as NativeDataPolicy, ObjectSecurityProfile as NativeObjectSecurityProfile, + RetentionPolicy as NativeRetentionPolicy, SecurityExtension as NativeSecurityExtension, + SubjectExtension as NativeSubjectExtension, SubjectType as NativeSubjectType, + WorkloadIdentity as NativeWorkloadIdentity, }; use cpex_core::hooks::trait_def::PluginResult as NativePluginResult; +use crate::payload_registry::PayloadSerializerRegistry; use crate::sandbox_manager::types::*; // --------------------------------------------------------------------------- // Native → WIT: MessagePayload // --------------------------------------------------------------------------- -/// Converts native cpex-core MessagePayload to WIT MessagePayload for the sandbox. pub fn native_payload_to_wit(payload: &native_msg::MessagePayload) -> MessagePayload { - MessagePayload { - message: native_message_to_wit(&payload.message), - } + MessagePayload { message: native_message_to_wit(&payload.message) } } fn native_message_to_wit(msg: &native_msg::Message) -> Message { @@ -196,14 +218,20 @@ fn native_prompt_result_to_wit(pr: &native_content::PromptResult) -> PromptResul // Native → WIT: Extensions // --------------------------------------------------------------------------- -/// Converts native cpex-core Extensions to WIT Extensions. -/// Only maps fields that the WIT interface supports (request, security, http, meta). pub fn native_extensions_to_wit(ext: &NativeExtensions) -> Extensions { Extensions { request: ext.request.as_ref().map(|r| native_request_to_wit(r)), security: ext.security.as_ref().map(|s| native_security_to_wit(s)), http: ext.http.as_ref().map(|h| native_http_to_wit(h)), meta: ext.meta.as_ref().map(|m| native_meta_to_wit(m)), + agent: ext.agent.as_ref().map(|a| native_agent_to_wit(a)), + mcp: ext.mcp.as_ref().map(|m| native_mcp_to_wit(m)), + completion: ext.completion.as_ref().map(|c| native_completion_to_wit(c)), + provenance: ext.provenance.as_ref().map(|p| native_provenance_to_wit(p)), + llm: ext.llm.as_ref().map(|l| native_llm_to_wit(l)), + framework: ext.framework.as_ref().map(|f| native_framework_to_wit(f)), + delegation: ext.delegation.as_ref().map(|d| native_delegation_to_wit(d)), + custom: ext.custom.as_ref().and_then(|c| serde_json::to_string(c.as_ref()).ok()), } } @@ -222,7 +250,16 @@ fn native_security_to_wit(s: &NativeSecurityExtension) -> SecurityExtension { labels: s.labels.iter().cloned().collect(), classification: s.classification.clone(), subject: s.subject.as_ref().map(native_subject_to_wit), + client: s.client.as_ref().map(native_client_to_wit), + caller_workload: s.caller_workload.as_ref().map(native_workload_to_wit), + this_workload: s.this_workload.as_ref().map(native_workload_to_wit), auth_method: s.auth_method.clone(), + objects: s.objects.iter() + .map(|(k, v)| (k.clone(), native_object_profile_to_wit(v))) + .collect(), + data: s.data.iter() + .map(|(k, v)| (k.clone(), native_data_policy_to_wit(v))) + .collect(), } } @@ -246,6 +283,63 @@ fn native_subject_type_to_wit(st: &NativeSubjectType) -> SubjectType { } } +fn native_client_to_wit(c: &NativeClientExtension) -> ClientExtension { + let (trust_level, trust_level_custom) = match &c.trust_level { + NativeClientTrustLevel::FirstParty => (ClientTrustLevel::FirstParty, None), + NativeClientTrustLevel::ThirdParty => (ClientTrustLevel::ThirdParty, None), + NativeClientTrustLevel::Internal => (ClientTrustLevel::Internal, None), + NativeClientTrustLevel::Custom(s) => (ClientTrustLevel::ThirdParty, Some(s.clone())), + _ => (ClientTrustLevel::ThirdParty, None), + }; + ClientExtension { + client_id: c.client_id.clone(), + client_name: c.client_name.clone(), + trust_level, + trust_level_custom, + authorized_scopes: c.authorized_scopes.clone(), + authorized_audiences: c.authorized_audiences.clone(), + roles: c.roles.clone(), + permissions: c.permissions.clone(), + teams: c.teams.clone(), + claims: c.claims.iter() + .map(|(k, v)| (k.clone(), serde_json::to_string(v).unwrap_or_default())) + .collect(), + } +} + +fn native_workload_to_wit(w: &NativeWorkloadIdentity) -> WorkloadIdentity { + WorkloadIdentity { + spiffe_id: w.spiffe_id.clone(), + trust_domain: w.trust_domain.clone(), + attested_at: w.attested_at.map(|dt| dt.to_rfc3339()), + attestor: w.attestor.clone(), + selectors: w.selectors.clone(), + client_id: w.client_id.clone(), + } +} + +fn native_object_profile_to_wit(o: &NativeObjectSecurityProfile) -> ObjectSecurityProfile { + ObjectSecurityProfile { + managed_by: o.managed_by.clone(), + permissions: o.permissions.clone(), + trust_domain: o.trust_domain.clone(), + data_scope: o.data_scope.clone(), + } +} + +fn native_data_policy_to_wit(d: &NativeDataPolicy) -> DataPolicy { + DataPolicy { + apply_labels: d.apply_labels.clone(), + allowed_actions: d.allowed_actions.clone(), + denied_actions: d.denied_actions.clone(), + retention: d.retention.as_ref().map(|r| RetentionPolicy { + max_age_seconds: r.max_age_seconds, + policy: r.policy.clone(), + delete_after: r.delete_after.clone(), + }), + } +} + fn native_http_to_wit(h: &NativeHttpExtension) -> HttpExtension { HttpExtension { request_headers: h.request_headers.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), @@ -263,31 +357,232 @@ fn native_meta_to_wit(m: &NativeMetaExtension) -> MetaExtension { } } +fn native_agent_to_wit(a: &NativeAgentExtension) -> AgentExtension { + AgentExtension { + input: a.input.clone(), + session_id: a.session_id.clone(), + conversation_id: a.conversation_id.clone(), + turn: a.turn, + agent_id: a.agent_id.clone(), + parent_agent_id: a.parent_agent_id.clone(), + conversation: a.conversation.as_ref().map(|c| ConversationContext { + history: c.history.iter() + .map(|v| serde_json::to_string(v).unwrap_or_default()) + .collect(), + summary: c.summary.clone(), + topics: c.topics.clone(), + }), + } +} + +fn native_mcp_to_wit(m: &NativeMCPExtension) -> McpExtension { + McpExtension { + tool: m.tool.as_ref().map(native_tool_metadata_to_wit), + resource_info: m.resource.as_ref().map(native_resource_metadata_to_wit), + prompt: m.prompt.as_ref().map(native_prompt_metadata_to_wit), + } +} + +fn native_tool_metadata_to_wit(t: &NativeToolMetadata) -> ToolMetadata { + ToolMetadata { + name: t.name.clone(), + title: t.title.clone(), + description: t.description.clone(), + input_schema: t.input_schema.as_ref().and_then(|v| serde_json::to_string(v).ok()), + output_schema: t.output_schema.as_ref().and_then(|v| serde_json::to_string(v).ok()), + server_id: t.server_id.clone(), + namespace: t.namespace.clone(), + annotations: t.annotations.iter() + .map(|(k, v)| (k.clone(), serde_json::to_string(v).unwrap_or_default())) + .collect(), + } +} + +fn native_resource_metadata_to_wit(r: &NativeResourceMetadata) -> ResourceMetadata { + ResourceMetadata { + uri: r.uri.clone(), + name: r.name.clone(), + description: r.description.clone(), + mime_type: r.mime_type.clone(), + server_id: r.server_id.clone(), + annotations: r.annotations.iter() + .map(|(k, v)| (k.clone(), serde_json::to_string(v).unwrap_or_default())) + .collect(), + } +} + +fn native_prompt_metadata_to_wit(p: &NativePromptMetadata) -> PromptMetadata { + PromptMetadata { + name: p.name.clone(), + description: p.description.clone(), + arguments: p.arguments.as_ref().and_then(|v| serde_json::to_string(v).ok()), + server_id: p.server_id.clone(), + annotations: p.annotations.iter() + .map(|(k, v)| (k.clone(), serde_json::to_string(v).unwrap_or_default())) + .collect(), + } +} + +fn native_completion_to_wit(c: &NativeCompletionExtension) -> CompletionExtension { + CompletionExtension { + stop_reason: c.stop_reason.map(|r| match r { + NativeStopReason::End => StopReason::End, + NativeStopReason::Return => StopReason::ReturnComplete, + NativeStopReason::Call => StopReason::Call, + NativeStopReason::MaxTokens => StopReason::MaxTokens, + NativeStopReason::StopSequence => StopReason::StopSequence, + }), + tokens: c.tokens.as_ref().map(|t| TokenUsage { + input_tokens: t.input_tokens, + output_tokens: t.output_tokens, + total_tokens: t.total_tokens, + }), + model: c.model.clone(), + raw_format: c.raw_format.clone(), + created_at: c.created_at.clone(), + latency_ms: c.latency_ms, + } +} + +fn native_provenance_to_wit(p: &NativeProvenanceExtension) -> ProvenanceExtension { + ProvenanceExtension { + source: p.source.clone(), + message_id: p.message_id.clone(), + parent_id: p.parent_id.clone(), + } +} + +fn native_llm_to_wit(l: &NativeLLMExtension) -> LlmExtension { + LlmExtension { + model_id: l.model_id.clone(), + provider: l.provider.clone(), + capabilities: l.capabilities.clone(), + } +} + +fn native_framework_to_wit(f: &NativeFrameworkExtension) -> FrameworkExtension { + FrameworkExtension { + framework: f.framework.clone(), + framework_version: f.framework_version.clone(), + node_id: f.node_id.clone(), + graph_id: f.graph_id.clone(), + metadata: if f.metadata.is_empty() { + None + } else { + serde_json::to_string(&f.metadata).ok() + }, + } +} + +fn native_delegation_to_wit(d: &NativeDelegationExtension) -> DelegationExtension { + DelegationExtension { + chain: d.chain.iter().map(native_delegation_hop_to_wit).collect(), + depth: d.depth, + origin_subject_id: d.origin_subject_id.clone(), + actor_subject_id: d.actor_subject_id.clone(), + delegated: d.delegated, + age_seconds: d.age_seconds.to_string(), + } +} + +fn native_delegation_hop_to_wit(hop: &NativeDelegationHop) -> DelegationHop { + let (strategy, strategy_custom) = match &hop.strategy { + None => (None, None), + Some(NativeDelegationStrategy::TokenExchange) => (Some(DelegationStrategy::TokenExchange), None), + Some(NativeDelegationStrategy::ClientCredentials) => (Some(DelegationStrategy::ClientCredentials), None), + Some(NativeDelegationStrategy::SpiffeSvid) => (Some(DelegationStrategy::SpiffeSvid), None), + Some(NativeDelegationStrategy::Passthrough) => (Some(DelegationStrategy::Passthrough), None), + Some(NativeDelegationStrategy::Ucan) => (Some(DelegationStrategy::Ucan), None), + Some(NativeDelegationStrategy::TransactionToken) => (Some(DelegationStrategy::TransactionToken), None), + Some(NativeDelegationStrategy::Custom(s)) => (None, Some(s.clone())), + Some(_) => (None, None), + }; + DelegationHop { + subject_id: hop.subject_id.clone(), + subject_type: hop.subject_type.as_ref().map(native_subject_type_to_wit), + audience: hop.audience.clone(), + scopes_granted: hop.scopes_granted.clone(), + authorization_details: hop.authorization_details.iter() + .map(native_auth_detail_to_wit) + .collect(), + timestamp: hop.timestamp.to_rfc3339(), + ttl_seconds: hop.ttl_seconds, + strategy, + strategy_custom, + from_cache: hop.from_cache, + } +} + +fn native_auth_detail_to_wit(a: &NativeAuthDetail) -> AuthorizationDetail { + AuthorizationDetail { + detail_type: a.detail_type.clone(), + locations: a.locations.clone(), + actions: a.actions.clone(), + datatypes: a.datatypes.clone(), + identifier: a.identifier.clone(), + privileges: a.privileges.clone(), + extra: if a.extra.is_empty() { + None + } else { + serde_json::to_string(&a.extra).ok() + }, + } +} + // --------------------------------------------------------------------------- // Native → WIT: PluginContext // --------------------------------------------------------------------------- -/// Converts native PluginContext (HashMaps) to WIT PluginContext (JSON strings). pub fn native_context_to_wit(ctx: &NativePluginContext) -> PluginContext { PluginContext { - local_state: serde_json::to_string(&ctx.local_state).unwrap_or_else(|_| "{}".to_string()), - global_state: serde_json::to_string(&ctx.global_state).unwrap_or_else(|_| "{}".to_string()), + local_state: ctx.local_state.iter() + .map(|(k, v)| ContextEntry { + key: k.clone(), + value: serde_json::to_string(v).unwrap_or_default(), + }) + .collect(), + global_state: ctx.global_state.iter() + .map(|(k, v)| ContextEntry { + key: k.clone(), + value: serde_json::to_string(v).unwrap_or_default(), + }) + .collect(), } } // --------------------------------------------------------------------------- -// WIT → Native: PluginResult +// WIT → Native: HookResult // --------------------------------------------------------------------------- -/// Converts a WIT PluginResult from the sandbox back to native PluginResult. -pub fn wit_result_to_native(result: crate::sandbox_manager::types::PluginResult) -> NativePluginResult { - NativePluginResult { +pub fn wit_hook_result_to_native( + result: crate::sandbox_manager::types::HookResult, + registry: &PayloadSerializerRegistry, +) -> (NativePluginResult, Option) { + let modified_payload = result.modified_payload.and_then(|hp| match hp { + HookPayload::Cmf(mp) => Some(wit_cmf_payload_to_native(mp)), + HookPayload::Generic(gp) => { + match registry.deserialize(&gp.payload_type, &gp.payload_data) { + Ok(boxed) => boxed.as_any().downcast_ref::().cloned(), + Err(e) => { + eprintln!("[HOST] generic payload writeback failed for '{}': {}", gp.payload_type, e); + None + } + } + } + }); + + let modified_extensions = result.modified_extensions.map(wit_extensions_to_owned); + + let native_result = NativePluginResult { continue_processing: result.continue_processing, - modified_payload: result.modified_payload.map(wit_payload_to_native), - modified_extensions: None, // WIT modified_extensions would require OwnedExtensions conversion + modified_payload, + modified_extensions, violation: result.violation.map(wit_violation_to_native), metadata: result.metadata.and_then(|s| serde_json::from_str(&s).ok()), - } + }; + + let modified_ctx = result.modified_context.map(wit_context_to_native); + (native_result, modified_ctx) } fn wit_violation_to_native(v: PluginViolation) -> NativePluginViolation { @@ -296,19 +591,28 @@ fn wit_violation_to_native(v: PluginViolation) -> NativePluginViolation { reason: v.reason, description: v.description, details: serde_json::from_str(&v.details).unwrap_or_default(), - plugin_name: None, + plugin_name: v.plugin_name, proto_error_code: v.proto_error_code, } } +pub fn wit_context_to_native(ctx: PluginContext) -> NativePluginContext { + NativePluginContext { + local_state: ctx.local_state.into_iter() + .map(|e| (e.key, serde_json::from_str(&e.value).unwrap_or(serde_json::Value::String(e.value)))) + .collect(), + global_state: ctx.global_state.into_iter() + .map(|e| (e.key, serde_json::from_str(&e.value).unwrap_or(serde_json::Value::String(e.value)))) + .collect(), + } +} + // --------------------------------------------------------------------------- // WIT → Native: MessagePayload (for modified_payload in results) // --------------------------------------------------------------------------- -fn wit_payload_to_native(payload: MessagePayload) -> native_msg::MessagePayload { - native_msg::MessagePayload { - message: wit_message_to_native(payload.message), - } +pub fn wit_cmf_payload_to_native(payload: MessagePayload) -> native_msg::MessagePayload { + native_msg::MessagePayload { message: wit_message_to_native(payload.message) } } fn wit_message_to_native(msg: Message) -> native_msg::Message { @@ -343,22 +647,65 @@ fn wit_content_part_to_native(part: ContentPart) -> native_content::ContentPart ContentPart::Text(text) => native_content::ContentPart::Text { text }, ContentPart::Thinking(text) => native_content::ContentPart::Thinking { text }, ContentPart::ToolCall(tc) => native_content::ContentPart::ToolCall { - content: wit_tool_call_to_native(tc), + content: native_content::ToolCall { + tool_call_id: tc.tool_call_id, + name: tc.name, + arguments: serde_json::from_str(&tc.arguments).unwrap_or_default(), + namespace: tc.namespace, + }, }, ContentPart::ToolResult(tr) => native_content::ContentPart::ToolResult { - content: wit_tool_result_to_native(tr), + content: native_content::ToolResult { + tool_call_id: tr.tool_call_id, + tool_name: tr.tool_name, + content: serde_json::from_str(&tr.content) + .unwrap_or(serde_json::Value::String(tr.content)), + is_error: tr.is_error, + }, }, ContentPart::CmfResource(r) => native_content::ContentPart::Resource { - content: wit_resource_to_native(r), + content: native_content::Resource { + resource_request_id: r.resource_request_id, + uri: r.uri, + name: r.name, + description: r.description, + resource_type: wit_resource_type_to_native(r.resource_type), + content: r.content, + blob: r.blob, + mime_type: r.mime_type, + size_bytes: r.size_bytes, + annotations: serde_json::from_str(&r.annotations).unwrap_or_default(), + version: r.version, + }, }, ContentPart::ResourceRef(rr) => native_content::ContentPart::ResourceRef { - content: wit_resource_ref_to_native(rr), + content: native_content::ResourceReference { + resource_request_id: rr.resource_request_id, + uri: rr.uri, + name: rr.name, + resource_type: wit_resource_type_to_native(rr.resource_type), + range_start: rr.range_start, + range_end: rr.range_end, + selector: rr.selector, + }, }, ContentPart::PromptRequest(pr) => native_content::ContentPart::PromptRequest { - content: wit_prompt_request_to_native(pr), + content: native_content::PromptRequest { + prompt_request_id: pr.prompt_request_id, + name: pr.name, + arguments: serde_json::from_str(&pr.arguments).unwrap_or_default(), + server_id: pr.server_id, + }, }, ContentPart::PromptResult(pr) => native_content::ContentPart::PromptResult { - content: wit_prompt_result_to_native(pr), + content: native_content::PromptResult { + prompt_request_id: pr.prompt_request_id, + prompt_name: pr.prompt_name, + messages: serde_json::from_str(&pr.messages).unwrap_or_default(), + content: pr.content, + is_error: pr.is_error, + error_message: pr.error_message, + }, }, ContentPart::Image(img) => native_content::ContentPart::Image { content: native_content::ImageSource { @@ -394,46 +741,6 @@ fn wit_content_part_to_native(part: ContentPart) -> native_content::ContentPart } } -fn wit_tool_call_to_native(tc: ToolCall) -> native_content::ToolCall { - let arguments: HashMap = - serde_json::from_str(&tc.arguments).unwrap_or_default(); - native_content::ToolCall { - tool_call_id: tc.tool_call_id, - name: tc.name, - arguments, - namespace: tc.namespace, - } -} - -fn wit_tool_result_to_native(tr: ToolResult) -> native_content::ToolResult { - let content: serde_json::Value = - serde_json::from_str(&tr.content).unwrap_or(serde_json::Value::String(tr.content.clone())); - native_content::ToolResult { - tool_call_id: tr.tool_call_id, - tool_name: tr.tool_name, - content, - is_error: tr.is_error, - } -} - -fn wit_resource_to_native(r: CmfResource) -> native_content::Resource { - let annotations: HashMap = - serde_json::from_str(&r.annotations).unwrap_or_default(); - native_content::Resource { - resource_request_id: r.resource_request_id, - uri: r.uri, - name: r.name, - description: r.description, - resource_type: wit_resource_type_to_native(r.resource_type), - content: r.content, - blob: r.blob, - mime_type: r.mime_type, - size_bytes: r.size_bytes, - annotations, - version: r.version, - } -} - fn wit_resource_type_to_native(rt: ResourceType) -> native_enums::ResourceType { match rt { ResourceType::File => native_enums::ResourceType::File, @@ -446,38 +753,287 @@ fn wit_resource_type_to_native(rt: ResourceType) -> native_enums::ResourceType { } } -fn wit_resource_ref_to_native(rr: ResourceReference) -> native_content::ResourceReference { - native_content::ResourceReference { - resource_request_id: rr.resource_request_id, - uri: rr.uri, - name: rr.name, - resource_type: wit_resource_type_to_native(rr.resource_type), - range_start: rr.range_start, - range_end: rr.range_end, - selector: rr.selector, +// --------------------------------------------------------------------------- +// WIT → Native: Extensions (writeback from guest) +// --------------------------------------------------------------------------- + +fn wit_extensions_to_owned(ext: Extensions) -> NativeOwnedExtensions { + use cpex_core::extensions::guarded::Guarded; + + let request = ext.request.map(|r| Arc::new(NativeRequestExtension { + environment: r.environment, + request_id: r.request_id, + timestamp: r.timestamp, + trace_id: r.trace_id, + span_id: r.span_id, + })); + + let security = ext.security.map(|s| wit_security_to_native(s)); + let http = ext.http.map(|h| Guarded::new(NativeHttpExtension { + request_headers: h.request_headers.into_iter().collect(), + response_headers: h.response_headers.into_iter().collect(), + })); + let meta = ext.meta.map(|m| Arc::new(NativeMetaExtension { + entity_type: m.entity_type, + entity_name: m.entity_name, + tags: m.tags.into_iter().collect(), + scope: m.scope, + properties: m.properties.into_iter().collect(), + })); + let agent = ext.agent.map(|a| Arc::new(wit_agent_to_native(a))); + let mcp = ext.mcp.map(|m| Arc::new(wit_mcp_to_native(m))); + let completion = ext.completion.map(|c| Arc::new(wit_completion_to_native(c))); + let provenance = ext.provenance.map(|p| Arc::new(NativeProvenanceExtension { + source: p.source, + message_id: p.message_id, + parent_id: p.parent_id, + })); + let llm = ext.llm.map(|l| Arc::new(NativeLLMExtension { + model_id: l.model_id, + provider: l.provider, + capabilities: l.capabilities, + })); + let framework = ext.framework.map(|f| Arc::new(wit_framework_to_native(f))); + let delegation = ext.delegation.map(wit_delegation_to_native); + let custom = ext.custom.and_then(|s| serde_json::from_str(&s).ok()); + + NativeOwnedExtensions { + request, + agent, + mcp, + completion, + provenance, + llm, + framework, + meta, + raw_credentials: None, + http, + security, + delegation, + custom, + http_write_token: None, + labels_write_token: None, + delegation_write_token: None, } } -fn wit_prompt_request_to_native(pr: PromptRequest) -> native_content::PromptRequest { - let arguments: HashMap = - serde_json::from_str(&pr.arguments).unwrap_or_default(); - native_content::PromptRequest { - prompt_request_id: pr.prompt_request_id, - name: pr.name, - arguments, - server_id: pr.server_id, +fn wit_security_to_native(s: SecurityExtension) -> NativeSecurityExtension { + NativeSecurityExtension { + labels: cpex_core::extensions::monotonic::MonotonicSet::from_set( + s.labels.into_iter().collect(), + ), + classification: s.classification, + subject: s.subject.map(|sub| NativeSubjectExtension { + id: sub.id, + subject_type: sub.subject_type.map(|st| match st { + SubjectType::User => NativeSubjectType::User, + SubjectType::Agent => NativeSubjectType::Agent, + SubjectType::Service => NativeSubjectType::Service, + SubjectType::System => NativeSubjectType::System, + }), + roles: sub.roles.into_iter().collect(), + permissions: sub.permissions.into_iter().collect(), + teams: sub.teams.into_iter().collect(), + claims: sub.claims.into_iter().collect(), + }), + client: s.client.map(|c| wit_client_to_native(c)), + caller_workload: s.caller_workload.map(|w| wit_workload_to_native(w)), + this_workload: s.this_workload.map(|w| wit_workload_to_native(w)), + auth_method: s.auth_method, + objects: s.objects.into_iter() + .map(|(k, v)| (k, NativeObjectSecurityProfile { + managed_by: v.managed_by, + permissions: v.permissions, + trust_domain: v.trust_domain, + data_scope: v.data_scope, + })) + .collect(), + data: s.data.into_iter() + .map(|(k, v)| (k, NativeDataPolicy { + apply_labels: v.apply_labels, + allowed_actions: v.allowed_actions, + denied_actions: v.denied_actions, + retention: v.retention.map(|r| NativeRetentionPolicy { + max_age_seconds: r.max_age_seconds, + policy: r.policy, + delete_after: r.delete_after, + }), + })) + .collect(), } } -fn wit_prompt_result_to_native(pr: PromptResult) -> native_content::PromptResult { - let messages: Vec = - serde_json::from_str(&pr.messages).unwrap_or_default(); - native_content::PromptResult { - prompt_request_id: pr.prompt_request_id, - prompt_name: pr.prompt_name, - messages, - content: pr.content, - is_error: pr.is_error, - error_message: pr.error_message, +fn wit_client_to_native(c: ClientExtension) -> NativeClientExtension { + let trust_level = match c.trust_level_custom { + Some(s) => NativeClientTrustLevel::Custom(s), + None => match c.trust_level { + ClientTrustLevel::FirstParty => NativeClientTrustLevel::FirstParty, + ClientTrustLevel::ThirdParty => NativeClientTrustLevel::ThirdParty, + ClientTrustLevel::Internal => NativeClientTrustLevel::Internal, + }, + }; + NativeClientExtension { + client_id: c.client_id, + client_name: c.client_name, + trust_level, + authorized_scopes: c.authorized_scopes, + authorized_audiences: c.authorized_audiences, + roles: c.roles, + permissions: c.permissions, + teams: c.teams, + claims: c.claims.into_iter() + .map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v)))) + .collect(), + } +} + +fn wit_workload_to_native(w: WorkloadIdentity) -> NativeWorkloadIdentity { + NativeWorkloadIdentity { + spiffe_id: w.spiffe_id, + trust_domain: w.trust_domain, + attested_at: w.attested_at.as_deref() + .and_then(|s| DateTime::parse_from_rfc3339(s).ok()) + .map(|dt| dt.with_timezone(&chrono::Utc)), + attestor: w.attestor, + selectors: w.selectors, + client_id: w.client_id, + } +} + +fn wit_agent_to_native(a: AgentExtension) -> NativeAgentExtension { + use cpex_core::extensions::agent::ConversationContext; + NativeAgentExtension { + input: a.input, + session_id: a.session_id, + conversation_id: a.conversation_id, + turn: a.turn, + agent_id: a.agent_id, + parent_agent_id: a.parent_agent_id, + conversation: a.conversation.map(|c| ConversationContext { + history: c.history.iter() + .map(|s| serde_json::from_str(s).unwrap_or(serde_json::Value::String(s.clone()))) + .collect(), + summary: c.summary, + topics: c.topics, + }), + } +} + +fn wit_mcp_to_native(m: McpExtension) -> NativeMCPExtension { + NativeMCPExtension { + tool: m.tool.map(|t| NativeToolMetadata { + name: t.name, + title: t.title, + description: t.description, + input_schema: t.input_schema.and_then(|s| serde_json::from_str(&s).ok()), + output_schema: t.output_schema.and_then(|s| serde_json::from_str(&s).ok()), + server_id: t.server_id, + namespace: t.namespace, + annotations: t.annotations.into_iter() + .map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v)))) + .collect(), + }), + resource: m.resource_info.map(|r| NativeResourceMetadata { + uri: r.uri, + name: r.name, + description: r.description, + mime_type: r.mime_type, + server_id: r.server_id, + annotations: r.annotations.into_iter() + .map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v)))) + .collect(), + }), + prompt: m.prompt.map(|p| NativePromptMetadata { + name: p.name, + description: p.description, + arguments: p.arguments.and_then(|s| serde_json::from_str(&s).ok()), + server_id: p.server_id, + annotations: p.annotations.into_iter() + .map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v)))) + .collect(), + }), + } +} + +fn wit_completion_to_native(c: CompletionExtension) -> NativeCompletionExtension { + NativeCompletionExtension { + stop_reason: c.stop_reason.map(|r| match r { + StopReason::End => NativeStopReason::End, + StopReason::ReturnComplete => NativeStopReason::Return, + StopReason::Call => NativeStopReason::Call, + StopReason::MaxTokens => NativeStopReason::MaxTokens, + StopReason::StopSequence => NativeStopReason::StopSequence, + }), + tokens: c.tokens.map(|t| NativeTokenUsage { + input_tokens: t.input_tokens, + output_tokens: t.output_tokens, + total_tokens: t.total_tokens, + }), + model: c.model, + raw_format: c.raw_format, + created_at: c.created_at, + latency_ms: c.latency_ms, + } +} + +fn wit_framework_to_native(f: FrameworkExtension) -> NativeFrameworkExtension { + NativeFrameworkExtension { + framework: f.framework, + framework_version: f.framework_version, + node_id: f.node_id, + graph_id: f.graph_id, + metadata: f.metadata.and_then(|s| serde_json::from_str(&s).ok()).unwrap_or_default(), + } +} + +fn wit_delegation_to_native(d: DelegationExtension) -> NativeDelegationExtension { + NativeDelegationExtension { + chain: d.chain.into_iter().map(|hop| { + let strategy = match (hop.strategy, hop.strategy_custom) { + (Some(DelegationStrategy::TokenExchange), _) => Some(NativeDelegationStrategy::TokenExchange), + (Some(DelegationStrategy::ClientCredentials), _) => Some(NativeDelegationStrategy::ClientCredentials), + (Some(DelegationStrategy::SpiffeSvid), _) => Some(NativeDelegationStrategy::SpiffeSvid), + (Some(DelegationStrategy::Passthrough), _) => Some(NativeDelegationStrategy::Passthrough), + (Some(DelegationStrategy::Ucan), _) => Some(NativeDelegationStrategy::Ucan), + (Some(DelegationStrategy::TransactionToken), _) => Some(NativeDelegationStrategy::TransactionToken), + (None, Some(s)) => Some(NativeDelegationStrategy::Custom(s)), + (None, None) => None, + }; + NativeDelegationHop { + subject_id: hop.subject_id, + subject_type: hop.subject_type.map(|st| match st { + SubjectType::User => NativeSubjectType::User, + SubjectType::Agent => NativeSubjectType::Agent, + SubjectType::Service => NativeSubjectType::Service, + SubjectType::System => NativeSubjectType::System, + }), + audience: hop.audience, + scopes_granted: hop.scopes_granted, + authorization_details: hop.authorization_details.into_iter() + .map(|a| NativeAuthDetail { + detail_type: a.detail_type, + locations: a.locations, + actions: a.actions, + datatypes: a.datatypes, + identifier: a.identifier, + privileges: a.privileges, + extra: a.extra + .and_then(|s| serde_json::from_str(&s).ok()) + .unwrap_or_default(), + }) + .collect(), + timestamp: DateTime::parse_from_rfc3339(&hop.timestamp) + .map(|dt| dt.with_timezone(&chrono::Utc)) + .unwrap_or_else(|_| chrono::Utc::now()), + ttl_seconds: hop.ttl_seconds, + strategy, + from_cache: hop.from_cache, + } + }).collect(), + depth: d.depth, + origin_subject_id: d.origin_subject_id, + actor_subject_id: d.actor_subject_id, + delegated: d.delegated, + age_seconds: d.age_seconds.parse().unwrap_or(0.0), } } diff --git a/crates/cpex-wasm-host/src/factory.rs b/crates/cpex-wasm-host/src/factory.rs index e9334410..4275d430 100644 --- a/crates/cpex-wasm-host/src/factory.rs +++ b/crates/cpex-wasm-host/src/factory.rs @@ -23,7 +23,8 @@ use cpex_core::hooks::payload::PluginPayload; use cpex_core::plugin::{Plugin, PluginConfig}; use cpex_core::registry::AnyHookHandler; -use crate::conversions::{native_context_to_wit, native_extensions_to_wit, native_payload_to_wit, wit_result_to_native}; +use crate::conversions::{native_context_to_wit, native_extensions_to_wit, native_payload_to_wit, wit_hook_result_to_native}; +use crate::payload_registry::PayloadSerializerRegistry; use crate::sandbox_manager::SandboxManager; // --------------------------------------------------------------------------- @@ -34,11 +35,22 @@ use crate::sandbox_manager::SandboxManager; /// Every plugin gets an isolated wasmtime engine and store — no contention between plugins. pub struct WasmPluginFactory { wasm_dir: PathBuf, + registry: Arc, } impl WasmPluginFactory { - pub fn new(wasm_dir: PathBuf) -> Self { - Self { wasm_dir } + /// Create a factory with a pre-built payload registry. + /// Register all payload types the host wants to route through WASM before + /// calling this, then pass the registry here. + pub fn new(wasm_dir: PathBuf, registry: Arc) -> Self { + Self { wasm_dir, registry } + } + + /// Convenience constructor that pre-registers `MessagePayload` only. + pub fn with_cmf_only(wasm_dir: PathBuf) -> Self { + let mut registry = PayloadSerializerRegistry::new(); + registry.register::(); + Self::new(wasm_dir, Arc::new(registry)) } } @@ -90,18 +102,19 @@ impl PluginFactory for WasmPluginFactory { config: config.clone(), }); - let handler: Arc = Arc::new(WasmBridgeHandler { - plugin_name: config.name.clone(), - sandbox, - }); - - // Register handler for each hook the plugin declares + // Register a separate handler per hook so each carries its own hook_name let hooks: Vec<(&'static str, Arc)> = config .hooks .iter() .map(|hook_name| { let leaked: &'static str = Box::leak(hook_name.clone().into_boxed_str()); - (leaked, handler.clone()) + let handler: Arc = Arc::new(WasmBridgeHandler { + plugin_name: config.name.clone(), + hook_name: hook_name.clone(), + sandbox: sandbox.clone(), + registry: self.registry.clone(), + }); + (leaked, handler) }) .collect(); @@ -145,7 +158,9 @@ impl Plugin for WasmBridgePlugin { /// Each handler owns its own SandboxManager — no contention with other plugins. struct WasmBridgeHandler { plugin_name: String, + hook_name: String, sandbox: Arc>, + registry: Arc, } #[async_trait] @@ -156,28 +171,37 @@ impl AnyHookHandler for WasmBridgeHandler { extensions: &Extensions, ctx: &mut PluginContext, ) -> Result, Box> { - // Downcast the type-erased payload to MessagePayload - let native_payload = payload - .as_any() - .downcast_ref::() - .ok_or_else(|| { + // Build the WIT payload: CMF fast-path, generic fallback via registry + let wit_hook_payload = if let Some(cmf) = payload.as_any().downcast_ref::() { + crate::sandbox_manager::types::HookPayload::Cmf(native_payload_to_wit(cmf)) + } else if self.registry.contains_type_id(payload.as_any().type_id()) { + let (type_name, bytes) = self.registry.serialize(payload).map_err(|e| { Box::new(PluginError::Config { - message: format!( - "plugin '{}': payload type mismatch, expected MessagePayload", - self.plugin_name - ), + message: format!("plugin '{}': payload serialization failed: {}", self.plugin_name, e), }) })?; + crate::sandbox_manager::types::HookPayload::Generic( + crate::sandbox_manager::types::GenericPayload { + payload_type: type_name.to_string(), + payload_data: bytes, + }, + ) + } else { + return Err(Box::new(PluginError::Config { + message: format!( + "plugin '{}': payload type not registered in PayloadSerializerRegistry", + self.plugin_name, + ), + })); + }; - // Convert native types → WIT types - let wit_payload = native_payload_to_wit(native_payload); let wit_extensions = native_extensions_to_wit(extensions); let wit_ctx = native_context_to_wit(ctx); // Invoke the WASM plugin through its dedicated sandbox let wit_result = { let mut mgr = self.sandbox.lock().await; - mgr.invoke(wit_payload, wit_extensions, wit_ctx) + mgr.invoke(&self.hook_name, wit_hook_payload, wit_extensions, wit_ctx) .await .map_err(|e| { Box::new(PluginError::Config { @@ -189,8 +213,14 @@ impl AnyHookHandler for WasmBridgeHandler { })? }; - // Convert WIT result → native PluginResult, then erase for the executor - let native_result = wit_result_to_native(wit_result); + // Convert WIT HookResult → native PluginResult + optional context writeback + let (native_result, modified_ctx) = wit_hook_result_to_native(wit_result, &self.registry); + + if let Some(new_ctx) = modified_ctx { + ctx.local_state = new_ctx.local_state; + ctx.global_state = new_ctx.global_state; + } + Ok(cpex_core::executor::erase_result(native_result)) } diff --git a/crates/cpex-wasm-host/src/lib.rs b/crates/cpex-wasm-host/src/lib.rs index 974fd41b..1ce81118 100644 --- a/crates/cpex-wasm-host/src/lib.rs +++ b/crates/cpex-wasm-host/src/lib.rs @@ -5,5 +5,6 @@ pub mod conversions; pub mod factory; +pub mod payload_registry; pub mod policy_loader; pub mod sandbox_manager; diff --git a/crates/cpex-wasm-host/src/payload_registry.rs b/crates/cpex-wasm-host/src/payload_registry.rs new file mode 100644 index 00000000..b8ccdac6 --- /dev/null +++ b/crates/cpex-wasm-host/src/payload_registry.rs @@ -0,0 +1,122 @@ +// Location: ./crates/cpex-wasm-host/src/payload_registry.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Shriti Priya +// +// PayloadSerializerRegistry — maps payload TypeId ↔ (type_name, serialize, deserialize). +// +// The host registers every payload type it wants to route through the WASM +// boundary here. WasmBridgeHandler calls serialize() to build HookPayload::Generic +// before invocation, and deserialize() to reconstruct a Box +// from the guest's returned generic payload. + +use std::any::TypeId; +use std::collections::HashMap; +use std::sync::Arc; + +use anyhow::{anyhow, Result}; + +use cpex_core::hooks::payload::{PluginPayload, WasmSerializablePayload}; + +// --------------------------------------------------------------------------- +// Internal codec — per-type serialize + deserialize closures +// --------------------------------------------------------------------------- + +struct PayloadCodec { + type_name: &'static str, + serialize: Arc Result> + Send + Sync>, + deserialize: Arc Result> + Send + Sync>, +} + +// --------------------------------------------------------------------------- +// PayloadSerializerRegistry +// --------------------------------------------------------------------------- + +/// Registry that maps payload types to their WASM serialization codecs. +/// +/// Register every payload type that WASM plugins should be able to receive +/// or return. The registry is built once (at factory creation time) and then +/// shared read-only across all handler invocations via `Arc`. +/// +/// # Example +/// +/// ```ignore +/// let mut registry = PayloadSerializerRegistry::new(); +/// registry.register::(); +/// registry.register::(); +/// let registry = Arc::new(registry); +/// ``` +#[derive(Default)] +pub struct PayloadSerializerRegistry { + by_type_id: HashMap, + by_type_name: HashMap<&'static str, TypeId>, +} + +impl PayloadSerializerRegistry { + pub fn new() -> Self { + Self::default() + } + + /// Register a payload type. `T` must implement both `PluginPayload` and + /// `WasmSerializablePayload`. Calling `register` twice for the same type + /// is idempotent — the second call overwrites the first. + pub fn register(&mut self) + where + T: WasmSerializablePayload + 'static, + { + let type_id = TypeId::of::(); + let type_name = T::payload_type_name(); + + let codec = PayloadCodec { + type_name, + serialize: Arc::new(|payload| { + let concrete = payload + .as_any() + .downcast_ref::() + .ok_or_else(|| anyhow!("serialize: TypeId matched but downcast failed"))?; + concrete.to_wasm_bytes().map_err(|e| anyhow!(e)) + }), + deserialize: Arc::new(|bytes| { + let value = T::from_wasm_bytes(bytes).map_err(|e| anyhow!(e))?; + Ok(Box::new(value) as Box) + }), + }; + + self.by_type_name.insert(type_name, type_id); + self.by_type_id.insert(type_id, codec); + } + + /// Serialize a type-erased payload to `(type_name, json_bytes)`. + /// + /// Returns an error if the payload's concrete type has not been registered. + pub fn serialize(&self, payload: &dyn PluginPayload) -> Result<(&'static str, Vec)> { + let type_id = payload.as_any().type_id(); + let codec = self + .by_type_id + .get(&type_id) + .ok_or_else(|| anyhow!("payload type not registered in PayloadSerializerRegistry"))?; + let bytes = (codec.serialize)(payload)?; + Ok((codec.type_name, bytes)) + } + + /// Deserialize a payload from `(type_name, json_bytes)` back to a + /// `Box`. + /// + /// Returns an error if `type_name` has not been registered. + pub fn deserialize(&self, type_name: &str, bytes: &[u8]) -> Result> { + let type_id = self + .by_type_name + .get(type_name) + .ok_or_else(|| anyhow!("unknown payload type '{}' in PayloadSerializerRegistry", type_name))?; + let codec = self + .by_type_id + .get(type_id) + .ok_or_else(|| anyhow!("codec missing for type '{}'", type_name))?; + (codec.deserialize)(bytes) + } + + /// Returns true if the given `TypeId` has a registered codec. + pub fn contains_type_id(&self, type_id: TypeId) -> bool { + self.by_type_id.contains_key(&type_id) + } +} diff --git a/crates/cpex-wasm-host/src/sandbox_manager.rs b/crates/cpex-wasm-host/src/sandbox_manager.rs index a011e508..f9a46ba1 100644 --- a/crates/cpex-wasm-host/src/sandbox_manager.rs +++ b/crates/cpex-wasm-host/src/sandbox_manager.rs @@ -219,10 +219,11 @@ impl SandboxManager { /// Epoch deadline is per-invocation (reset each call so no single call hangs). pub async fn invoke( &mut self, - payload: types::MessagePayload, + hook_name: &str, + payload: types::HookPayload, extensions: types::Extensions, ctx: types::PluginContext, - ) -> Result { + ) -> Result { eprintln!("[SANDBOX] invoke() called, plugin loaded: {}", self.is_loaded()); let instance = self .instance @@ -235,7 +236,7 @@ impl SandboxManager { eprintln!("[SANDBOX] Calling handle_hook on WASM component..."); let result = instance .plugin - .call_handle_hook(&mut instance.store, &payload, &extensions, &ctx) + .call_handle_hook(&mut instance.store, hook_name, &payload, &extensions, &ctx) .await; match &result { diff --git a/crates/cpex-wasm-host/wasm/plugin.wasm b/crates/cpex-wasm-host/wasm/plugin.wasm index 55472f0bc32b560e3fbc1b61ccc94b25d0a5c14c..e201bac6e3bc2df54c4eb47eee888bbeced3c691 100644 GIT binary patch literal 660721 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 lMvR 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%0D*9O9SWVV5ykYu@Kj;(~Vx%f* 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

i{7aL|GV79@=mDSS$0ptE!i2mlB2$``GHze zae)6JS1_OX%HnEcvfOl~@605#ZEiJF@|w8q!m_g_^ucTWXH$j(DZ0L7`4$gS_Nm2k z{+FU^GIR*4K4$k6PRoAFyE^w%mSe>9JZ!rp7;3g(K&SOu8ZZ2JcEKO)#fqX;kBm}s zbS4A}Z#q<0WfS@TWK;#0i?xNXFc%M-y4%>fP(4>{^TVfpRlb!6R4kb^OF|X6nFQ7& z13?By#VA2wVYBxiF{Q{4#j7^BWIlWaav@ViyD39DJq3{K=yjA z!MDduDbrcyAQtyn;WrxM_!}b@N%)e-PANs1`XJ83 zF?moJv~wLR6a2U-<>R|sdMB;AQe80)`}^I62$Ci<=sbQZr=jbu#z_(3dWWAdr9yWz zHokI>s>Z0gb3m$Fvfezg7?czS)=^8|nJ2AW4JXeDk&PB+)pUD1+J{WzX0oqpvkNn= z?5cV4RL(Jv@6EVsJXy?+tDGD!TkxcBBh`10KW(ZMr?JW^d{xDDD8};HefpHLQ0DAfbuG+`40@1c zTPH`cf|W#P`A{YsR!zG)djejJuFWJV8i2#?wEv7Ln ze(Y|mG<85&E;g+jnd%2k37}Rlm{OshD^+Kgsx$Pgf)PgWg;Od3b4@kbrA28elB)BM zB#AGY%B9}rhdIH+Ay_U}5ksH5cN^#h0DYkgYytJTz3uEaq>s&x0 ztL=^)*6$Xe$WlOi*_5&+9PD|^9P1J@Bnk2`GxYMQtJIDe$7|?0U#WC&im!Y{!E#j5 zTY9Yf87I0}qJ@EAN-0$i_Ld3GiPRVuohn|HZD#3`GUmSO%=c{6gNG6s1AtHaKKc13 zW!!{WlgE6qHXgisnX%@cwYo{vBy3KOr16?58G~ER`&v;lGb7<{RVd303d8s~9=*~O zk;Jbp`kr~3EMbUL;cQK<9jXk$a=8+M$CwxCe3@x|-D>)tD3G$e@2_93LY_t*!I*2`+ z>-8mmuv~mNY^2G!eH5mmCE@Sinq7%&tSyg<%4!Q_cCuDFhY796T*YsjDk%o4CU@NB z4H9n|e_ApMZ_n;W*}ck{Dhq~aIY6&2P>yLNOuFw_p)UouKO#TqnIs}}#r3>Gd-~4B zsjBSDn7S;fb#1Y9ovxB5%A&}-rmo2j?U>^~PbmeUJpaqj)oQgnx`pwe z*A8cnw-lfAo=iYkYxTMI!O!_%U3>483N~GZ!0nC*+@sFILjw2va(Xa+@cCJka(3nt zUp~w4&w8~PqFGsM`hkK($bf;$JyVbf*(C;i!#qeHIf?k-RPGEtUhI&}l>$G^$g?KB zt9MG<57a9Ak1NyyeX#4G%tLGD$9C<_{M& zWX_N))^PKZ8a`57yN8*pDyqQ(%9sd(wXc@^tDL!yW_R8Sw}4cu6t{pZ1^xe6rdBwp ztWDL;m55Y4m`iC-$*0o22GI;2$5SF2&p$q;&`=Kcj*K5b#y6CA-Y1GJQh1S$y+!gZ zQocn#Srjf}{>K(>GY9Oia^X*9YLG?1fXc#|#*-PKc)8!7E}G8RxiJe?y8X&q`!hx1 z!au#TX<0b$ce(IqGfK3g!p%Qk#@-oH4d^vcSHQN3~5;R7Agi28_A@IVPhT5{3zT3?#N!VP`y zx6L!YdHY|Uz)uI;Iet38BuH~{?JoD*dth3s)uz>V%*WFdNbvQ7iR31#FI2oBSy81} z%~gw|&e4_(SI+&Jn3@dSO2#wGaJ@td4A!;cLHjjBLXhmcVgG^F{sULHcI`PJ_U+kz zUF*i}*G=BJqS@TlM|P|6>lME*$>CsMzxRP;taA_hHfe;6Si+u@4mwCg^S&#VH*qL( z#QvSzuNTzd#702AOO4!SG06ioF%_<=hQ<8_){$HuB_J2j|Jwbm-{fY(Wlr;`bTY+mB*HB=z?ryY~mHUYri4kHOuEzS`YLsv_;CNgyH*d4HL ztHpU^t6VpCaQ@MX!GiyJqR~t*7(2;py5%q2UD18DR?FVp<1XKe?264NDK4jVl+b4G z7}ovw?!ERlzggXj@x{htgI(;ez6*;osp`gUcaAHX(VNi`i~zy``)TaD`jWAGY~A{$ z+bz?trSm${D|hD&`}T=F2X24!n8B&bupp_ULsZy3*6jRjWFI5i8g%di94CW11v10F z`i9-RAGFMUc!%fIs+advBU9aR>{6{;+_+QK-JBnQ_JQ^T4<|P~fYd`ROZpOL;7iud z>-O$MuPe4+cVO3Uap@&X*9kS?T<6XYT>3*Hqc7bBi(6YCs9P2QE%fep-QFGQ;@z`r zXKUx)8}=Ny%l*bwtYr43Rm)gc<*A5UkpzhIcJ0}JVEdk(;c8HjaZZBDt(s5=C z>_B-p=58-Y^p-lIoj4{px@l}t`$?2t>5mwd-fjK%cS1z^0Ds}Ykb8O6|a<*!xzlO#i9=2xwPKVGwVuLoB&sdjfkKbFc% zQd}LQ+c9=--+6y=bxU!&_dc*(-Blef3BtB=q}N?)68lqgNc_t-Bf(pfbP zYfz$J`McxhaCv7kkGu|*tgK@ zPfYIA^@+1{tji?c79MXlC2V1IsXp!7)7oYCA~BCf9xW%i%+L&Hn`Uj(t$Q~kSx?K* zr}b%ToAj36){Bo^cCW+z{(SE~S6zhz)8U1Xj2*LA&CDFl{%oTa>1TbJVHjzIa?2o9 zF*!nyUH^So{!xd%gk_9^d;MEW+EQ6O8iJK0})(ugtV|zI>V|C*3CdI;Blp zbXC1wJdb#2>(koAt7_)~;Vr$ZNiZc5zj|A*JS$w7IkGJi@UA?s^ko}fNLSug`W zcLyJwsJ~4n&6A1lt~@vMv|y5yR`|*@l}M?Q8(w)HPL7!4%LE6>A>mjL-1CEKLmdGE?CGJ(-H>aG2vHWBn@YZDU_wF@SHxAyxJzgz!( z{xp7{|C^XRbL&K{E`OYuXf$i{wXIvv(9W!BysS^uYQ{vpW=>4jtcga=o@mybiK&`9 zaYoIX*s|pcBp4ZoQ=2$5*}8S&vWe{z7fei?brzp4t8K4cP@6dGJoWjq`el>b>ujIm zw@LY%D$$T7)Tid))70S88G}z-I`O|4_f`A zTCH|Y5uLP6Mkh^7Or2SqoTxR`1+-kvfYvC+cGgPfSi-KzsGG zRanh7w0Okg$Tsgbb)kHIP8M}i`r(Qxr=iBGtG|Z&Yt|;}cPY9zRAhP(Dx&(ydtYSi zQ+FKcXu0``#+^of9jbAnd3kk9%ExG%QXovwKt5SRWv8=W&$y!c*CGgJn+k%N*s{`) zCbmvp#4Me8E?OcLMFd&EBTvrX-RpN_oR^LaWca;_GpBAt@w3hwJkB~R%ip7Z1?BHN zu6*|5#9770v+5V~`gzc?ZKdd=iLzt>j$8eXZ0e$^Ep&f!l5ii@o2iL9vr?1a zCvH=e_v&Antmh9eo|riQF7nd44+d*Aa#szV=lgQcOj{S$E>dr_J6EZLtDb5X)-Noc z%A=P*oV>Vrn4Pa){=9LidLGTRuNn+pZJ_-3=AD=Ro-KU-!qgoGFO*h2|M85hgR*wH z{Q1)@w;2>svz$NPnsrgW%wrnKpPxDOoRQ|w&#LHpxxTY2x;|9#In^&Lws7v{c}EF` z>s>Vn-P)!?DDz&Ncc=0@GLUQN=kuq2g*{rkK*lwptK}*zk^hPFN6B>Uf}xP(EoaEX z!TFrJ2OsJy6CG7xi!5-NEU+8}Qm<~6uijg}x+-#{%E+4pqEVY@j-K@`oa>3RCMPEv zx1E@}gwJPJoGDeu*|H7}-NZRnM@qdS?;!Qcxl3MQh14tO%2(vUI!_%eIa%lNsP5Hk zXD(i{@^ZceF64Gx&9~=fz5^HY9l4k9#I<}WxAL92l<&eCOHE>KzN3z$+qe%t#m1#ai4Y7psvg*YQBAKJ)a2v1QwuqIyWSu+v zb{pP>x8J}nyu)Vf!aJ^K8{KLByW`Ro7I@j+apx5ZFS|SLvigm2S(zeUUQI`9=gAxN zOk9*_FH_QN?JP;T^V_xNPh0?roekP^wc8H~=G$b6ZL_EtW|I0v!$0A+x-eh+>Ey(P zx0$%(@K34~WVIh45Z6}X$Ea8`Y{Tr>WCb_YsAk5R6%0*@7n`bJS6Dch{frekV;N3t zOBExwwUQ4TX14}Ai8z$rZcjG%kqdlbi1|KeIrNjjNPSr8$0~oT-Y1 za?Yq)C}+#iiWF1o471V2Ogd*)EY#xWa?Yw)h|=bAmS1OQg>`mTSZ8NhF4|d15^`!^ zs@EE-zkGg~-VX7xwbxHg-gDyO{&A%?l>}`dNjkOLmL4-FolG2a>X~k7a-cP8 z+^+OunN4(>R}{Y}L8@A^a;9#>TT9N)8J8}WMX1b>X{2wkqJhqqLGOlSPG{?oa_P)V z%9SjVE9b0XUDMfvA&dy6&N;&`o;&*D;MkpaS-GhJ&sI8l=U-m=M)~kvFrwVKu)5OH zKrR~9=iH{c6^aZ9$Hk*%ZacQjV0f3@WwG;B9G`Q$yB05u65g zg^C*214NJjYEV&wC#xcO<4I6dTor@5yXdOC->0g(r)Lrr{q26<_m4N|RCjgtQ%^nh z)bl*`)Kk@a?ysEpO+`a{?t3tYjTp6V3V+dSF3LKVbupW>dL=8_imf_&zm}ddp&tGCZ2uK^hq->t8xMB!>03`=@T!!XyS~SRp(6{ zKVjnZ8C9|A6K6~VJ`NLDkt)FPMopHP69p@}+FL&#cL-7!U()GpANf zoIZW(bd;Dd@ldZPX1#Fnq^VOR-89c|Hn}sKJd91==%F{?&g+tR+LM3&)Cr*N#0ja^ z%y`BVbY6Sb6CjKyjGs9kB#v58LBETp0E?04nEYbqGtbLEocYbw`GZ-OxhuciAMko2 zlP)l~78ILCK^GP@R~EEqA@jw8&gOLmIt$DD2<}I+s2M9fh-I5c7ap9I&_LOC;ui7o&|rd~92+C?+7hO-fVu5p%UjxEeL2NoS^mEo>eLl z)y$USilOcNd6b_{novG#MXdJhmSq*5@iWe69*>7D<{iwVN~+CSB_|{UN$bxu&YQeE znPkavewBl(EbAFA!*)5OxXp3h(j~I;GqSAg;x@9aN5N#WfG%gvn&lbiqhywczm!!V z>I*_0*Bw{-fHqEBpY=#qKtum?t_69>!$Uk2lz*J#8vo-7^5g^h!7csaB1DgQFzE4w zf}FD;XUu*|5AYWZiVFt?I>eaz!&6)UxL7kVFj({+6!0bKi3k4&K)BQ&JKM(pn z_?3U*h~Mv(*Qj2mEQ+FDAN^&^U-u7JD1{i;G}O#E{vomcaA&^SzAK?pjEgdb6B+}g zJRYkUp~a9V8)UypsmG%+v>sRf99b46@Rod?Xc6*wqDX)o^GSdn7WAoxMx)Un28Kd7Nl&dUdx zQ7iq)U-=UV1qeSN1;D^Fie_p&5X{m%Tw{Tt4_%A*{F=v;6$twAP{TO%Fl3*?yK&jUlh?$2Qt0(Kkn6h9?cUC0Ib*R2?PSnkN)%cviyF3!2D;& zj>!OshTcI%0pcQW&|44+1*6eiR;X!(S|M8HL+APZL2ouX-^+qtFhMp@(27{LH@mo` zw5(mRmve8FML{O?Nsy?ep=Ex5IpIr4D}vA+h}Yhm#ip@$`Mcbg)R-sNFL-5=C2DJ% zJmI7#A%7M=|BOsOkiU%bKa!X8wy9EEzqQhTsRH1VD*Z|3{w#m@ z$=_rxC(v``??W%BI+t5@&y~3i)?KajRAIE&%H8qNMv2ySvg~~M`=j;j26?x{dT)We zyXR3=NApWEjic4iN&s8r};)(G_?Rcb!te0+wQbs4+AY3!v>&w(wT>IY5eb08Xf3q*x59~Ac9s8R7hkeVw;UBR*{1g5a|CaCIk7+yk5Bxj6l{fQ8wSV!w zS__}2-KO2B-J;#BE!BSFb=uqf2JIegj`o$dOS>tl{Y_h>UFp3_+o+k^9omQdcI`+0 z27ik$(N<}~;C;e-jc=#-WACTluf6kp&0gWV+4nF1V_8dmPiEcZ+n9BuZ;@|_ zuhI9A?_uAoz9)Up`yS2u#`l8n9p8Nam%cB2%Y7gE_W5f4+kLbBbNtP|EB$r;k9=48 z=lXa0uJ+IK-|zd6?+4$t{;zzW`#$sSn&?~M`_%Wd?@>=*Y_Hdx9xVy(6vO9yol|oGqv7Z+REP9`}VF|<^Z&=jf{GAAKqcH8wrnyH>`7= zRdO%=1Y%pvW2<`c8|%zjRpo4n`D5j9{2ttGQt{t`4_?=q&SSgjP2lf|Eis?yHZ8j0 zO8G1itBGy6Qs^i8DgZ$&ny8ggx@w|UI#^bggNC7|xE5QYpe167fWl*ckHpUtHG>1H zagNrF@M_m%N}%Z)7bPC-(Jya(Yf60EV_93-D|`O6>njIDiB~|0_SMhZWqBIacskW& zsj$vK51V3tG(WC5C(v@WgXz(ETU%qfx^uFv?BQLmf3U&)zRO7d*#>iX z*RJ7RO9@^=i(Ol1PVZVp1i7p0K~(m`t^=rSVdc?O_Uy{^vNu%@GM}r?rSjXV-5vW! z^Qu0_@f+uu@AMhYZZgaIj>=m|O^$0(4>+&7294Th&gk2>Ty|L`E6j)Mfy6#`VIp!k ziuiPY?3y}rW8Y34t@lRKi@G+D9~NQ6yiH(H7F#^W)cQ^3Pt}<-``yUr(i_EQ$Nv7l zFG$|;R`P;48yWTKxGK?E!Z*|AjEK*M;!Q+qVnFre{=+SnV~XX>d;0gJroGnxM80T_ z*=;~Kw$D6%z!2V4XWoDWd4C^pD7eTB4U`235A18sny@ms{07-QyX(w7g9^;*K{?{o zqcNWtb8g7>X!h%!M1AXQV`nGv=P9f#RZ`5J!VY%b_6f00<^|`tN|=kzaTTCS#n;EM zj_DN&<5<=&Jxiju{gWJV?XfJ^45gt*m^Tc`F*l#^<3HD$i`-Ad#xbn3`Q;=xC8+R%X{=NB$8z|B`m&Qm(=^sG9i~}Z)0-+= zQ*$A^L)>{GyOm#GXKp>X3uQj}PU($PV!`FtOXAO~17-yxc0BWSzLVgh!xNrPv*|LP zm1vqzrSz=C-m9$QB6&0GL*T}}OLAsi!;}G>zzg89%=Ip8GB5Tar zB6Hu=b{u?4#HyY)hTUP#p7sscQ0%#cRTN3#M2uZZ%v++DO6h2>zOX&PedWR~(d$X# zfh{57P%)b>MP#4pC4hd^^goO6be3;kzC9ogy^NJ-6gEb-HxGRz2uNb#Wvpj#iPP*^ zBa6HMxWue~q@8zgP3%tbGZk2`3M{``TyrYxp1L*vF(Z*i<$TeCDYf|UIZT^RoOyO& zp)G9K`a<)~nN!(nvGK3$9JbnAdC};+T1Cikijd}*M+?mk7az(tn}5D|P+J9-U91y@ z|9LD=trE z_{1@F>~Q3k%&K+g_PPqWo6YZMUFym;uS-s0Tg|=6p4nR^yG3kvdvGq(QQ4Nc?BW!4 z#O$lx#QIIQ<{h)=OPK_;51ms&oqo!k=d6AVTAhC8sqMoth(1d)i7nT-J5ywz#~@dQ zaV_hT;-@UdPg#nec3jzkK(o3cyRx2johfI`>7{`ZusG>t(Q+-LaVm1JV>aKVP$)5* z>x$Ybe%1X@*vd{g97^4v2oNvjUUd_ww(P2b7S-^s9HizKS9L@`h38&RLudA@G!n%j z*Ru-OI2`|C(eNBXIa|-M4Fnn%d6XB^^PD_A7dAfZlj$lsJgVu%$dmYQyq=L>5-r!W z;sOiPG@#~~Gs}U4n7xfjJK@Y5SZPRVmoOT!&}=;OEP8+ADz7vj_TKL$B8iSGyq(&+ z%!ojokBo-Mpor*PRek0PZz&c1%L;Ehq~`YVM%coD$JSjbo>}3ImpCAZUh9do$o7am zVGbK#PA_j+sp|b=rL5PyeSD{1f?2!D8_!9Dxwcj`Jtz@y+VK5@UJ0bbYL$P`YFUTb z=j?n@v)UV@3R*wwU);AMxJyX|M^$xq<;;G)zd2@5$dzO4%*@#{=#bQNAyv}{ z%iE>0Om@GkfUTNx%yFOOro$%n)XA(K;(nLJ>h@0zbIM>h5|rC8#EmGlk~we1Zda|A zQmTKTmn)NAZ2qhR|DJSGzS;10uGxE~3$oZ@!o57@0auok>XqpjWso+&FDLY=E7CE>@M?ft0t!_D|5_)nc7M(X8yLcRr9ZEE4F&H z)KT9&e*ioSedQHPJF0iz#0V|g^|Vb{TTCC4}*4F zm;11n?O&m-DPjfYpC2i6DYe;;bO7GDCAiJX z#t~^p9?Z=oCwdb%<@2|8riqeI$wxbB-rb9tb} zrg3<~Ih~hE7KqSvD5^(n>d|z_pIFXn^oSmfEj4f6P|5ze?x_uDX*o(lfx7-G&vG`> zT=YzVdGNEv=4nkc(crC31C{P7Fv~ZRiW~lPp?T@X$#&-VR_1&=+39x1-<~}m(meKDRfY~UM?Ytv*7Kjc!YX;=^S@Z|rRQhcAkTV%D!%`P zq^k@1Z0hkpzPtVJPm|Ic|6;G67B50KGQGmmdSDLB7L?JK__nr5O>$_O0G0g8i_#AJ z>cy{HsbpzS=Du9j#j&aY$q16ta%)GAtsN)4oT;5{49xnMrFp#ZWvQfhyxiVa()pXE zwfBe3aT*rqZkF2lkLy@zpja=IjAP!vxh+-w-R2^CBl1cqx~j`7=b&q^f8`$bj@ft1 z2%AZRE^}<|mXq-CgDn@Ke~#aJGQGQU>+30XZQlQCXP34n(){DqvIEw!VMoU{F)?d2 zYX~lBYx;x1>||S0A9t2D?TLwoqgg0bl;%GDGV`nnP-a=of-;NR`lg!;BJ|9`saq)n zttn8ujBGXiiHX~eb)#g`9H{t@>||4v?GadL{1B*ANII%UskrFq1^#w8emRSQQ zq_RGkfGi+AF>U4)J|}JF^vk!0oC7vR&yPK>M09+e1@iNwSl%%5$=Oq?=c{i2CnlZg zvRbS;m&qvyrcI^dn{!!y%4V?VPCd?J-BXMt=T7bHS<&S47|CUE9i_7RGV$DbOq%=6 z=cySJo21hCB*(6?W=>Rli8y}}Yjf^Ij#!#Ii905-q}44F*HJZT(<5S#>uOkb3ddYE zF|uYpt7{~)i}VRit?8e)E?9q^WHXrWiTNewyYo6{6ndKlI1Fn-Aht0618#?-kR8b<|Eg2Plpi=yPhvIv*)Kj z@10)STr|IP-{oo2la zX5a7_$<-HckjBC{H@IiKiTTA@fGoW*2QRpsFRn8mU(k*(s#~{hK>;VP{O(0PC~0?N zA4-nBu`eZaZah1p-1s!Ph6{hsjWg3TE;NUNUCmYIF!QHdg622o5W=Bq@nFh5cku+e z+q_shaDQA}UF67iGGFq6Q42i%OUw~B4Iq>*yNOUTZ@%eC2!IoB?vvsDG;fv$$}=|` zt=FhF6M|y+YRlRR79IXdOMg#E625tWZ5jOzO>d0s?tcT#IwHE{e;Y1)2i+!M0 zBGRw*P;A>=bJ&uzGiq45MAG(+C0*lgLoA-3h+S+HKq>?-HuBJ_T1KsEe~YwQX&^jw z%aQ1(pKm$RWqyvi^-|Z}tG5oKR)ubp9vXV5;kLUns_%V!iFxtTY$EO4r9C>iQ7|~! zf{vR6D;Ua6^2XBm5NQuZ{AjNpK)XN@4;9ub`3dBUM%hX}#^Bm;EP1FU4oOkCk$c}R zDSqbdJz8PX&RlzYzU`p_0Hili{UBE+JnI2qEL&%;sjo1r@5)Jk?4`%%*>_zWXt34&j=RpyTfT^7 z6wqvt7g$ePck18zu{QF}JMaFc>uu7MX=_0VQ)_2I={9ryJ?*Q%lEV{aTP>rw%gRz} z8O3++Da85X?(IaJf7QJ=llQ*;ebReB`o1`M_NU%=v0wF?dBucP!Cj8kO14&-FF*At z&6l6bc6|9xmh$DNZXI8~lO=umsXMZS9ACbZo$}>7S#CpC`SQ~r*}nW#uH(y3WjS_i zReHOX3EQH4`A+eaFW7}5_xSef zf6Z2H!?(XsDbio??XUW8`u1E8S{3b?)b zTmOG={{QdIS4;2V|EV`W`P$mxUbko#%{!T4j(aoc%AWoDA+9@P=YaI*;;v6vW|rl_ z&Na_`Gvs=g-QPbW#}drs$1?vqUDvI8_jyK!$wlvvgmL=#`&EH@+sz6W zg6aF<3}m1E!62HI?s>n^T=M}f3;y)M3bKLj|4=&H{{CTiOUzpHwHvof`%1c?%_Fy; z+)4;bbMwvZ>8q*aW`pB7ORXY%%`2L_xD3h5n%!%uE1QR>=nrLRIh`XrCI%a9kAP^| zp%sgU?q08WtC$aQ0(UfNEic0Ky@dBQV;|+Q|CpUVIyhMOfSlv)!P?gjtt1N58aAJ0 zS(`U})FbpU#X3m)Mm$-g(b7^%al|!pL{-L{FInH3{<;t`M0{=37b;p5|THSH7 zBnk^wRuiMqicb0i;{GdJbN{r@OY)a+a-VG@Cz|>fTVqxnydrHpl$I{xwxf;8XL{Q9 zH$Ije$oMc z#BH~-{^o-ts@a-#+ei5A^`0W3FJ=B@~xGcXN`2ZW6W72t&zm;6in>~=6%}OOEW=BW&Sjl{IrI&x%ru|;2NoxmwnjY8%h zjyep>67!B4k{<;ofq`5{5*^6PDKR6Ll$;rr&V_h(O}u;{ub`HpHLA@8R*Ook`?81z z8lgxuz43Zfc0Mr59^o(u)`R4XZ{9_y_DAPgGh}a_Goo>V=o6uE}o}Cz1 zV-$m&Ap)1+Mn}7H8?23#IsEbBE+KGaR0r}wxU+zj5khkU@+yL>h`h=*_l$0zn>&y{ z#L#T?N&}@Kv*Xc!3N+ipIeByicCg%Y^ufnR^xW8MvU@_b&KMynlM1`^Cze7kh4hFj z6G2R&UkB_<73i4+S@kuQQ>_1^`P7EOB-Sf0tH};)+~;FH+N$6|j9jS7;{hE;p(0$e z#5eXOPke1(YKgDxOK+;IFY%q7>rZUAFSAk*0*O!T++bplCu!9YO8nE#4yQ^)65ran z(G=k96yTi1=XQZuVkcdqgTfT%=QKe}H9=mgCdf-P;Rw7VuT>l5rD}t`RBe!#S&7|t z*+624eHoNgKml{Xv4u%lsYupj`w+(O^hAWOXp-U~-;xb!xJT$P7Qitr>|Yw{hL?UiJgmp)k?^`pG?%YKxXs&9D03&5I1 zw~tYdDcq^QYFv_w|c zAr#34CRLu9Mn|{4fL22@%ghUA8R5@VGoz_yu9?*uG#Xm!D6e{`J-iRPEoJet#B6iy z6=mj@6C#09=w=e)=1V7aDGDamUMnfs6C!!N6DNt#zxMxMdRE;wo-jRN{TJ)mWbGip zt_C_LTdH7jsE-#8BlRQoAL+E9yb9}x)w$5f);q5nq0cos=$&NRo>T>_mP#Z+(!qu1 zJ%8$AEz7lo?vl#2P_E1s60~-9q{6;Gm4dfRPpL@e>$dF@54R&2SjI!_D_xh^=8;|i z%R#E}!;xJfi-7%bt?M4WgI>C-ZV>P9lrF=vuT#1jC9;Qlh9POMSFEaww;v?&^>pK& zL0YxB;grj|m+QgF3E#zpua4`(XCAY!X+JGF#ub_rpO>T!WI>>ns~OJ2T6~`yAlFRY*IcY;RVKs zQ|;JEMehK;5Ljv-{<1Dict%}FMQtYgh0xWZR`3sc%UKx7l17_aub}llm#It(h-922 ziJ4Vr#QPRWM%7CskLoam!IOHHVn=XgPu!K!us{gHKt32ru{ryUOS)!HPIxaia`|xB zVA+QgG4xzXt89zQvZXyP^y^u9wS{6h>Qj9V68iN5vcz)r0?QBs;hZHqn%yMPhU%Kw zS}+2|ucIPbzD8H(%C0Pei|yCC^8eJ6$yQy7HR+mouAU{k68Ol@EJ}5z>Tk?OARW>s zLm)A1m3->ZJZn8)mW@$PjvjNfA1q_q1QOBBac4#_sZNXOEw<(n{zMb2si0xx32)@g zFUBtO7Gq{A^gnPJ;k)k>Z*dpU$}4Hb;V~gv`H1c1G90e3J4x0nHr(PZjbcBEVsQrc z6?<>-#sfARi#0cUOU%*<6jE#Um>?sEpIGkgY=_cb{o0D)wzl1oscdpbrtWNaq?40! zN2apU+>uT;+%C!;naXjwUXc_dvgDR-okZ~0%8J2EXd<&Jc+l{?b8wKsj#KcL)^ zsRx!9MhyC#bv3VhBbaJ%${U$pn!J%asTa`~R!q6=jZ7~{{+O$v2V4bJge|<0V(A~) zue&4Rc*u>h{M-Zau(aWU-1^`3K#E6?Wd9=S#3o<00eVE-Joabg!ENJQ?*@%#} zR5l!gnOX1*W@hafl$mAw2Xixvf?G4QT4aohSdbcy2{hUl5uw7NO& zQMTLFFvo3JA(lSY3I;`nf9v=TGfGJ26~iBI^%imOj#k_&R<|y>aGUfqT0!CBybUau zJuLpR0S7Upt#g)c!?X!fR{7!%*loD&K$yC*+mopPy|aAtIE5!6SiDDIp}k^n1?Y7| zw0Mbl>?GDM1bn2g6GaiSS7rv+h0Qy@@rwnwvJlM@toXf%5u`=c%hHdw2vD$H-n^Xx*%q7R>E=Tgwz3v6`Wyw(xr z6y~?#1vVTJb3eU+NWj)bD9o?4@$605?;U8eS5yysk$nj5(^&BmwrsPnMfqk5+!GgU zW@jV87O67vT`y~0KUiAk@d!t`QWWY$SrQdvJy)@JdCt*I=;_XUJ+D?1qZ z)vu;S^sRn1Eu!x?-Tw78cC9s&pa4G^VK?-3_GY@9+05Jyb=VuMXL{s~9ffV}WhO(x z57so_VBOVrVB1WBjj8rNk%a(LC8}(0XLAv3x@E{DquInBs<<@JFyFLH^J2?1e|t_7 zO|HF`X)gZwrb52vO%;PmyR7V*XqQ!Kph$;*5KzTSZ)QdeqJ1Tz=q*MO+@jZ840|EO zsJGZu(7N$0_6bP>YY)})Z)XMxVlP(g4UxT#b?;%@k_bs}V;9wL*cMeQ=G?&62DfFb z6X{~#0*Ja)HtB?nEYb-XS)>!vvr;-CJ)bO?jG|IhAk;`#UV?N&wF?wgO6s7wwK@p522IvXU2AIw zt4pHqO)QT!ijg<5J5#DbWZ%rX9pDn{yaUn&`)+1~*Z3BxP2*G z&{}Gnk#A%y_W0Y~+aBHWT5QFmJjW}8!HgLt+?D2{Z;aGBtQdR;8btJu(L33x5R?1<$-0Vz>e-2Tdu1RHX1A%0!8m@SSUg&f zvoV&4Rd=z+X-0w&Yg#N8H~x*eHct@v_H4cQ@NX>T1GxEa)|>4V)pxU^u1dyWHtNS} zLTrg#9qE%^^j~}?J8{G?Y)AHLenn6xg^wesRw^}Kt8zcfs6-@C$D5N<}O>Ca?+{y z^#@sDmhyu7#6mB3t%QqLA57B@zvZ@Z&}tl=lCBCUxVn8fFXfvN1#4KZluj3;*MQbb z#H2Ow?5(FU7YW*1eeP8GpPOp;B!qAgfm# zzm{FcJ`vm3vi2$xUo92QW3*I+E)}tLjCOp=?YEumKyz&DksExbt;it5^c3+49tI#zXnVgF}5B*Xq?v^c0U?;&;w@hNQ$wI-|LkdIj%T5$futR1uz z5l&9l5pE6acYn;}>^ZcPJsMo^o-0s<4VG!0tl6&*FmrYWT{*_iLsEA%ops;ZQ`wt8 zftXD}fg;GrA(PS41MN};ooTEq&zcfolbM4Qy^LrYH=YzfaK)#)U4>ji|BTPmupt>v zLjMa}=;w()e~zQLoC?$|_KaQbCfI`iAFTwx*}5Rf{n>5F{qF2B^R~~MToV7uFAiAZ zuRkD(zxqo@;?pkCKYk5qUu#cl&)hv(h6+eIKe~lY5IG1 zv=mvp6)-=+VlM}m6l|Gb*LE@J=2&X;Z1nc4rk8V1>xf3wXggUr1C z-!4IKZvD1cguhpI+&Dig%eG81f1;AeAVz=BXkVY0`aNscZk?3#Var;}!WxmY=iOX$ zRPnki_hN_6R$J}%{NbF=uS!;GdmAZ*ozOPYSIx#By0P!p?f9XbqvwA9@nQTv^b_@; zL~k7DKC}2|=m7J~pGgIza(nC}&2T5dtw7)txK-YM&4x4f=Y;XMRkm z@e<2oMQok<$J5KipMyB@kFq9VF3`d9b&AunG6lb!3*3kIs%+kI$$e}dvRv0#GyPSA z9)o#QfISi1EDBQ3$l5cglL&|0FOnTU+y!URIi1AqX(b%< zU3{9BujF>`up62sw{yg$Vb;L~l>{l`)Wy^3r7c&^%i`?_jIC4~l^7RYqAajR05I2-yo8oHG zsT6bM)xs=gN9FBOrdr5lp?_VY>VCgcNMPd&I)hi}k+5I68(vwGt%hV$T0cHZT2 zC$B(s>BNrC-bARv?}0^ignxK$cacKu)i4yHS13+4)nn?p7dLNU#+xV`#vg7^kE$hOG&j} zpY5ir;|j#=KCG`LFha@$Ae#E1`Ot+eeWa@^*q0rjQDRbGHIZJ_H`CDB?<^uuwRo5Usk+3kgv)W}Xr4U7(PLw~re69d>F zl)h{Lj{e*tx(viAotwqhfowi|N1QQ;9l_of_YK0Kps$NF2E%}cO>*;K;IK`sAIz$u z^dG#iBrwQ2Dn$f`u#Tk`tvW;@7qIxw^sbi0*O(Z>MlimxPFyt<4i85~h{uMqGPLT= zp{&nA*4qVo41oh4J?vT>)#cXU9IX2~%n~JkI_yE!b%}#OPWUG#9mIN;TY!Rz6A!KBAqOW z1-ysiy2n*~HLZct>@mFrW{+n5Fd2YN3rW*H@6MjWtZ|W;_yy~oGD5^}m^v;z0Q;kB zV_Q?l7CSVx)zpza)!5L&y8JKZjpgFwZ`j~A?2?ot@&7b=bkp0JJcfk%9XmhJ^8eNh zvi&d2AR}2~_9j-?xZ!*D7z@-&<0vM?tpBim7^#iPAK8tZ|EE^u@3UQoX3bCJF_cS2 z*_Z>~RL*6%pEapTO&SYts^_Gl+n6{mcV%0X#&xbNpFLrGLF4WT<8N^aN{3=B!||Ih zjCt%}aj?e1Vy2H@WX~Wm<&tiz{mvj;T08h^X$IMLzMMh6YH^t>RxE9E-|wD9ZVPaE z+JrNU%n$PY%_3<OcLv^aq^Esbw%W7o1A3ZsEmVBNX*@wop9alsy6 zyKX$;8S42Hxqh&F;ydzjAS4+EG*tBvEv9=$@IhJtcizUu@3P}?SS$G&6U=ZnI{oWC zc06x^v*dluC3dZ}jfAi?Ni_j6=-R`#v!nRGuM{_KXML! z@?PvYF(k@w1|nOdyuGqHXriEz|M`a~-b3?BviT`iU*V)`>IyL{n@eNz!E8?JrY~gk z8LdmmZA#e$VQLQVk-bD2AM>aq4X@BoWd(Tki5v_-VDmu^&J15IE|0;QTp}7`_=Esn z4CnHx-uLm<1Ti+?EfR}!`3bNKKhNibtxllh0>sz?ew5QGPvr4HIH+q0uR35@JXgS7 z2ZG3*dm>cG!yOf0qqP)O(b0}1ZLnjZ)#kv_g*@tA;AJ5f23y<0pu>QGktG8-4IIvZ zpiM*-B8gy(A@RxxSOmGL*hD{t!lAEhP9mfShf+v`M3|&St?fz%5Tq#J5*@+76pd1n zwuen3dNNo-9;g#Am;{}QpR2+9Srj6gC4)YFWCaIwpF1#=`aF#aO|7W#SN_VC;vczu zfSdAv%;P7!DKCe^*1SxDm-#SX_&YeNh?k|4y|+#Ft?3n>pU

dU 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ð>p1O&8VZDIt%2a+2lJACej<@t<(?I6WfXPJ3tdb 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 XuM>Jt+xJ$Ogo!IY+5i8Qz0>~z@O2Of diff --git a/crates/cpex-wasm-host/wit/world.wit b/crates/cpex-wasm-host/wit/world.wit index 0990f8b1..6d7f6b8e 100644 --- a/crates/cpex-wasm-host/wit/world.wit +++ b/crates/cpex-wasm-host/wit/world.wit @@ -1,14 +1,23 @@ -// Location: ./crates/cpex-wasm-host/wit/world.wit +// Location: ./crates/cpex-wasm-plugin/wit/world.wit // Copyright 2025 // SPDX-License-Identifier: Apache-2.0 // Authors: Shriti Priya // // WIT world definition for the CPEX plugin component. // Defines the types and exported function that the WASM host uses to invoke plugins. +// +// Supports arbitrary payload types via the hook-payload variant: +// - cmf: structured CMF MessagePayload (field-by-field conversion, no full-payload serialization) +// - generic: any serializable payload as JSON bytes with a type discriminator package cpex:plugin; interface types { + + // --------------------------------------------------------------------------- + // CMF enums + // --------------------------------------------------------------------------- + enum role { system, developer, @@ -33,12 +42,9 @@ interface types { artifact, } - enum subject-type { - user, - agent, - service, - system, - } + // --------------------------------------------------------------------------- + // CMF content parts + // --------------------------------------------------------------------------- record image-source { source-type: string, @@ -70,6 +76,7 @@ interface types { record tool-call { tool-call-id: string, name: string, + // arguments is a JSON object string (HashMap) arguments: string, namespace: option, } @@ -77,6 +84,7 @@ interface types { record tool-result { tool-call-id: string, tool-name: string, + // content is a JSON string (serde_json::Value) content: string, is-error: bool, } @@ -91,6 +99,7 @@ interface types { blob: option>, mime-type: option, size-bytes: option, + // annotations is a JSON object string (HashMap) annotations: string, version: option, } @@ -108,6 +117,7 @@ interface types { record prompt-request { prompt-request-id: string, name: string, + // arguments is a JSON object string (HashMap) arguments: string, server-id: option, } @@ -115,6 +125,9 @@ interface types { record prompt-result { prompt-request-id: string, prompt-name: string, + // messages is a JSON array string (Vec) — cannot be list + // because message → content-part → prompt-result is a recursive cycle, + // which WIT does not support without indirection. messages: string, content: option, is-error: bool, @@ -147,6 +160,41 @@ interface types { message: message, } + // --------------------------------------------------------------------------- + // Generic payload for arbitrary types + // --------------------------------------------------------------------------- + + record generic-payload { + payload-type: string, + payload-data: list, + } + + variant hook-payload { + cmf(message-payload), + generic(generic-payload), + } + + // --------------------------------------------------------------------------- + // Plugin context + // --------------------------------------------------------------------------- + + // State entries are typed key-value pairs (serde_json::Value serialized per + // entry as a JSON string) rather than a single opaque JSON blob. + record context-entry { + key: string, + // value is a JSON string representing serde_json::Value + value: string, + } + + record plugin-context { + local-state: list, + global-state: list, + } + + // --------------------------------------------------------------------------- + // Extensions — base four + // --------------------------------------------------------------------------- + record request-extension { environment: option, request-id: option, @@ -155,6 +203,13 @@ interface types { span-id: option, } + enum subject-type { + user, + agent, + service, + system, + } + record subject-extension { id: option, subject-type: option, @@ -164,11 +219,76 @@ interface types { claims: list>, } + // --------------------------------------------------------------------------- + // Security extension — full coverage + // --------------------------------------------------------------------------- + + enum client-trust-level { + first-party, + third-party, + internal, + // custom trust level — name carried in client-trust-level-custom on + // client-extension when this variant is not sufficient + } + + record client-extension { + client-id: string, + client-name: option, + trust-level: client-trust-level, + // custom trust level name when trust-level cannot represent it + trust-level-custom: option, + authorized-scopes: list, + authorized-audiences: list, + roles: list, + permissions: list, + teams: list, + // claims values are JSON strings (serde_json::Value per entry) + claims: list>, + } + + record workload-identity { + spiffe-id: option, + trust-domain: option, + // attested-at as ISO 8601 string (DateTime) + attested-at: option, + attestor: option, + selectors: list, + client-id: option, + } + + record object-security-profile { + managed-by: option, + permissions: list, + trust-domain: option, + data-scope: list, + } + + record retention-policy { + max-age-seconds: option, + policy: string, + delete-after: option, + } + + record data-policy { + apply-labels: list, + // None = all actions allowed; Some([]) = no actions allowed + allowed-actions: option>, + denied-actions: list, + retention: option, + } + record security-extension { labels: list, classification: option, subject: option, + client: option, + caller-workload: option, + this-workload: option, auth-method: option, + // object security profiles keyed by object name + objects: list>, + // data policies keyed by data element name + data: list>, } record http-extension { @@ -184,35 +304,218 @@ interface types { properties: list>, } + // --------------------------------------------------------------------------- + // Overflow extension types — typed + // --------------------------------------------------------------------------- + + record conversation-context { + // history entries are JSON strings (serde_json::Value per entry) + history: list, + summary: option, + topics: list, + } + + record agent-extension { + input: option, + session-id: option, + conversation-id: option, + turn: option, + agent-id: option, + parent-agent-id: option, + conversation: option, + } + + record tool-metadata { + name: string, + title: option, + description: option, + // input-schema and output-schema are JSON strings (serde_json::Value) + input-schema: option, + output-schema: option, + server-id: option, + namespace: option, + // annotation values are JSON strings (serde_json::Value) + annotations: list>, + } + + record resource-metadata { + uri: string, + name: option, + description: option, + mime-type: option, + server-id: option, + // annotation values are JSON strings (serde_json::Value) + annotations: list>, + } + + record prompt-metadata { + name: string, + description: option, + // arguments is a JSON array string (Vec) + arguments: option, + server-id: option, + // annotation values are JSON strings (serde_json::Value) + annotations: list>, + } + + record mcp-extension { + tool: option, + // renamed from `resource` to avoid WIT keyword conflict + resource-info: option, + prompt: option, + } + + // stop-reason: `return-complete` maps to Rust `StopReason::Return` + // (renamed to avoid the WIT `return` keyword) + enum stop-reason { + end, + return-complete, + call, + max-tokens, + stop-sequence, + } + + record token-usage { + input-tokens: u32, + output-tokens: u32, + total-tokens: u32, + } + + record completion-extension { + stop-reason: option, + tokens: option, + model: option, + raw-format: option, + created-at: option, + latency-ms: option, + } + + record provenance-extension { + source: option, + message-id: option, + parent-id: option, + } + + record llm-extension { + model-id: option, + provider: option, + capabilities: list, + } + + record framework-extension { + framework: option, + framework-version: option, + node-id: option, + graph-id: option, + // metadata is a JSON object string (HashMap) + metadata: option, + } + + record authorization-detail { + detail-type: string, + // option preserves None (no constraint) vs Some([]) (empty = deny all) + locations: option>, + actions: option>, + datatypes: option>, + identifier: option, + privileges: option>, + // extra holds flattened RFC 9396 extension fields as a JSON object string + extra: option, + } + + enum delegation-strategy { + token-exchange, + client-credentials, + spiffe-svid, + passthrough, + ucan, + transaction-token, + } + + record delegation-hop { + subject-id: string, + subject-type: option, + audience: option, + scopes-granted: list, + authorization-details: list, + // timestamp as ISO 8601 string (DateTime) + timestamp: string, + ttl-seconds: option, + strategy: option, + // carries the name when Rust DelegationStrategy::Custom(s) is used + strategy-custom: option, + from-cache: bool, + } + + record delegation-extension { + chain: list, + depth: u32, + origin-subject-id: option, + actor-subject-id: option, + delegated: bool, + // age-seconds as string to avoid f64 portability issues + age-seconds: string, + } + + // --------------------------------------------------------------------------- + // Extensions container + // --------------------------------------------------------------------------- + record extensions { request: option, security: option, http: option, meta: option, + agent: option, + mcp: option, + completion: option, + provenance: option, + llm: option, + framework: option, + delegation: option, + // custom: escape hatch for plugin-defined arbitrary key-value data + // (maps to cpex-core Extensions.custom: Option>) + custom: option, } - record plugin-context { - local-state: string, - global-state: string, - } + // --------------------------------------------------------------------------- + // Violation and result + // --------------------------------------------------------------------------- record plugin-violation { code: string, reason: string, description: option, + // details is a JSON object string (HashMap) details: string, + plugin-name: option, proto-error-code: option, } - record plugin-result { + // metadata is a JSON string (serde_json::Value) + record hook-result { continue-processing: bool, - modified-payload: option, + modified-payload: option, modified-extensions: option, + modified-context: option, violation: option, metadata: option, } } +/// Known hook names (not exhaustive - hosts may define custom hooks): +/// +/// Legacy (typed payloads): +/// "tool_pre_invoke", "tool_post_invoke" +/// "prompt_pre_fetch", "prompt_post_fetch" +/// "resource_pre_fetch", "resource_post_fetch" +/// "identity_resolve", "token_delegate" +/// +/// CMF (MessagePayload): +/// "cmf.tool_pre_invoke", "cmf.tool_post_invoke" +/// "cmf.llm_input", "cmf.llm_output" +/// "cmf.prompt_pre_fetch", "cmf.prompt_post_fetch" +/// "cmf.resource_pre_fetch", "cmf.resource_post_fetch" world plugin { import wasi:io/poll@0.2.6; import wasi:io/error@0.2.6; @@ -221,11 +524,12 @@ world plugin { import wasi:http/types@0.2.6; import wasi:http/outgoing-handler@0.2.6; - use types.{message-payload, extensions, plugin-context, plugin-result}; + use types.{hook-payload, extensions, plugin-context, hook-result}; export handle-hook: func( - payload: message-payload, + hook-name: string, + payload: hook-payload, extensions: extensions, ctx: plugin-context - ) -> plugin-result; + ) -> hook-result; } diff --git a/crates/cpex-wasm-plugin/Cargo.lock b/crates/cpex-wasm-plugin/Cargo.lock index 27f96516..0d46add0 100644 --- a/crates/cpex-wasm-plugin/Cargo.lock +++ b/crates/cpex-wasm-plugin/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.102" @@ -19,6 +34,12 @@ dependencies = [ "syn", ] +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + [[package]] name = "bitflags" version = "2.13.0" @@ -32,10 +53,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] -name = "bytes" -version = "1.11.1" +name = "cc" +version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +dependencies = [ + "find-msvc-tools", + "shlex", +] [[package]] name = "cfg-if" @@ -44,25 +69,50 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] -name = "cpex-payload" +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpex-core" version = "0.1.0" dependencies = [ "async-trait", + "chrono", + "futures", + "hashbrown 0.15.5", "serde", "serde_json", "serde_yaml", "thiserror", - "tokio", "tracing", "uuid", "wildmatch", + "zeroize", ] [[package]] name = "cpex-wasm-plugin" version = "0.1.0" dependencies = [ - "cpex-payload", + "async-trait", + "chrono", + "cpex-core", "serde", "serde_json", "wit-bindgen 0.57.1", @@ -75,6 +125,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + [[package]] name = "foldhash" version = "0.1.5" @@ -87,12 +143,71 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + [[package]] name = "futures-core" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + [[package]] name = "futures-task" version = "0.3.32" @@ -105,8 +220,13 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "slab", ] @@ -130,6 +250,8 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.1.5", ] @@ -148,6 +270,30 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "id-arena" version = "2.3.0" @@ -218,6 +364,15 @@ version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -338,6 +493,12 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + [[package]] name = "slab" version = "0.4.12" @@ -375,28 +536,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio" -version = "1.52.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" -dependencies = [ - "bytes", - "pin-project-lite", - "tokio-macros", -] - -[[package]] -name = "tokio-macros" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tracing" version = "0.1.44" @@ -595,6 +734,65 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29333c3ea1ba8b17211763463ff24ee84e41c78224c16b001cd907e663a38c68" +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -780,6 +978,26 @@ dependencies = [ "wasmparser 0.247.0", ] +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zmij" version = "1.0.21" diff --git a/crates/cpex-wasm-plugin/Cargo.toml b/crates/cpex-wasm-plugin/Cargo.toml index 0ebf8f68..1695f1e4 100644 --- a/crates/cpex-wasm-plugin/Cargo.toml +++ b/crates/cpex-wasm-plugin/Cargo.toml @@ -11,7 +11,9 @@ wit-bindgen = "0.57" wit-bindgen-rt = "0.44" serde = { version = "1", features = ["derive", "rc"] } serde_json = "1" -cpex-payload = { path = "../cpex-payload" } +cpex-core = { path = "../cpex-core", default-features = false } +async-trait = "0.1" +chrono = { version = "0.4", features = ["serde"] } [package.metadata.component] package = "cpex:plugin" diff --git a/crates/cpex-wasm-plugin/README.md b/crates/cpex-wasm-plugin/README.md index aae0005e..2726cba8 100644 --- a/crates/cpex-wasm-plugin/README.md +++ b/crates/cpex-wasm-plugin/README.md @@ -8,48 +8,37 @@ A WebAssembly (WASM) component that compiles CPEX plugins into a portable `plugi ```wit export handle-hook: func( - payload: message-payload, + hook-name: string, + payload: hook-payload, extensions: extensions, ctx: plugin-context - ) -> plugin-result; + ) -> hook-result; ``` - The host calls `handle-hook` with a CMF message payload, extensions (security, HTTP, meta, request), and plugin context. The plugin returns a result indicating whether to allow/deny processing, optionally with modified payload or extensions. + `hook-payload` is a variant — either `cmf(message-payload)` for the structured CMF fast-path, or `generic(generic-payload)` for any serializable custom payload type. -2. **Plugin Logic (`src/lib.rs`)** implements the `Guest` trait generated by `wit-bindgen`. It converts WIT types to native Rust types, delegates to the plugin logic in `cpex-payload`, and converts the result back to WIT types. +2. **`register_wasm_plugin!` macro (`src/lib.rs`)** generates the complete `Guest` impl from a one-liner. Plugin authors implement `HookHandler` from `cpex-core` — identical code to a native plugin — and register it: -3. **Type Conversions (`src/conversions.rs`)** handles bidirectional mapping between WIT-generated types and native `cpex-payload` types (MessagePayload, Extensions, PluginContext, PluginResult). - -## Why `conversions.rs` Exists - -The WASM boundary cannot pass rich Rust types directly. WIT only supports simple primitives, lists, tuples, strings, and records. The native `cpex-payload` types use `HashMap`, `HashSet`, `Arc`, `serde_json::Value`, etc. — none of which can cross the WASM boundary as-is. - -`conversions.rs` bridges this gap so plugin authors can write normal Rust using native types: + ```rust + register_wasm_plugin!(MyPlugin, [CmfHook]); + ``` -| Native Rust type | WIT representation | -|---|---| -| `HashMap` | `string` (JSON-serialized) | -| `HashSet` | `list` | -| `HashMap` | `list>` | -| `MonotonicSet` | `list` | -| `Arc` | `security-extension` (flat record) | -| `serde_json::Value` | `string` (JSON-serialized) | -| `Vec` | `string` (JSON-serialized) | +3. **Plugin implementation (`src/plugin.rs`)** contains the actual plugin logic — `Plugin` trait impl and `HookHandler` impl. No WIT types appear here; the conversion layer is fully handled by the SDK. -Without this layer, you'd have to rewrite all plugin logic to work directly with WIT's flat types, losing access to the entire `cpex-payload` library. The conversion layer handles the impedance mismatch at the WASM boundary transparently. +4. **Type Conversions (`src/conversions.rs`)** handles bidirectional mapping between WIT-generated types and native `cpex-core` types across all 11 extension types. ## Project Structure ``` cpex-wasm-plugin/ -├── Cargo.toml # Crate config (cdylib target, wit-bindgen deps) -├── Makefile # Build commands +├── Cargo.toml # Crate config (cdylib target, cpex-core no-default-features) ├── src/ -│ ├── lib.rs # Plugin entry point (implements Guest trait) -│ └── conversions.rs # WIT <-> native type mappings +│ ├── lib.rs # SDK: bindgen, register_wasm_plugin! macro, __block_on, registration call +│ ├── plugin.rs # Plugin implementation: Plugin + HookHandler impls +│ └── conversions.rs # WIT <-> cpex-core type mappings (full extension coverage) └── wit/ ├── world.wit # Plugin interface definition - └── deps/ # WASI interface dependencies (io, http, clocks, etc.) + └── deps/ # WASI interface dependencies ``` ## Prerequisites @@ -58,7 +47,7 @@ cpex-wasm-plugin/ ```sh rustup target add wasm32-wasip2 ``` -- `wasm-tools` CLI (for validation and inspection): +- `wasm-tools` CLI (optional, for validation and inspection): ```sh cargo install wasm-tools ``` @@ -68,148 +57,167 @@ cpex-wasm-plugin/ From this directory: ```sh -make build +cargo build --target wasm32-wasip2 +cp target/wasm32-wasip2/debug/cpex_wasm_plugin.wasm ../cpex-wasm-host/wasm/plugin.wasm ``` -This will: -1. Compile the crate to `wasm32-wasip2` in release mode -2. Copy the output to `plugin.wasm` -3. Copy `plugin.wasm` to `../cpex-wasm-host/wasm/` for the host to load - -## Validating - -After building, verify that `plugin.wasm` is a well-formed WASM component: +For a release build: ```sh -make validate +cargo build --release --target wasm32-wasip2 +cp target/wasm32-wasip2/release/cpex_wasm_plugin.wasm ../cpex-wasm-host/wasm/plugin.wasm ``` -This runs `wasm-tools validate` on the binary. Silent output (no errors) means the component is valid. +## Writing a Plugin -## Inspecting +Any plugin you define in `plugin.rs` that implements `HookHandler` compiles to a WASM component automatically. The code you write is **identical to a native plugin** — `cpex-core` (no default features) is WASM-safe, and the `register_wasm_plugin!` macro generates all the WIT glue for you. -To see the full WIT interface embedded in the compiled component: +### Step 1: Implement your plugin in `plugin.rs` -```sh -make inspect -``` +```rust +use async_trait::async_trait; +use cpex_core::cmf::{CmfHook, MessagePayload}; +use cpex_core::context::PluginContext; +use cpex_core::error::{PluginError, PluginViolation}; +use cpex_core::extensions::container::Extensions; +use cpex_core::hooks::trait_def::{HookHandler, PluginResult}; +use cpex_core::plugin::{Plugin, PluginConfig}; + +pub struct MyPlugin; + +impl Default for MyPlugin { + fn default() -> Self { Self } +} -This prints all exported functions, imported interfaces, and type definitions. You should see: +static CONFIG: std::sync::OnceLock = std::sync::OnceLock::new(); + +#[async_trait] +impl Plugin for MyPlugin { + fn config(&self) -> &PluginConfig { + CONFIG.get_or_init(|| PluginConfig { + name: "my-plugin".to_string(), + kind: "wasm://plugin.wasm".to_string(), + hooks: vec!["cmf".to_string()], + ..Default::default() + }) + } + async fn initialize(&self) -> Result<(), Box> { Ok(()) } + async fn shutdown(&self) -> Result<(), Box> { Ok(()) } +} +impl HookHandler for MyPlugin { + async fn handle( + &self, + payload: &MessagePayload, + extensions: &Extensions, + _ctx: &mut PluginContext, + ) -> PluginResult { + // Your logic here — inspect payload/extensions, return allow or deny. + // This is the same code you would write for a native plugin. + PluginResult::allow() + } +} ``` -export handle-hook: func(payload: message-payload, extensions: extensions, ctx: plugin-context) -> plugin-result; -``` -This confirms the host will be able to call your plugin correctly. +### Step 2: Register it in `lib.rs` + +Change the two lines at the bottom of `lib.rs`: -## Cleaning +```rust +use plugin::MyPlugin; +register_wasm_plugin!(MyPlugin, [CmfHook]); +``` -To clean build artifacts: +### Step 3: Build and stage ```sh -make clean +cargo build --target wasm32-wasip2 +cp target/wasm32-wasip2/debug/cpex_wasm_plugin.wasm ../cpex-wasm-host/wasm/plugin.wasm ``` -## Writing a New Plugin +That's your WASM component, ready for the host to load. -You can write any plugin in `cpex-payload/src/plugins/` and invoke it from here. All plugin functions must follow the same signature: +### Denying a request + +Return `PluginResult::deny(...)` with a `PluginViolation` to block processing: ```rust -pub fn my_plugin( - payload: &MessagePayload, - extensions: &Extensions, - ctx: &PluginContext, -) -> PluginResult +if security.has_label("PII") && !subject.roles.contains("hr_admin") { + return PluginResult::deny(PluginViolation::new( + "insufficient_role", + "Tool requires 'hr_admin' role for PII data", + )); +} ``` -### Step 1: Create your plugin in `cpex-payload` +### Reading extensions -Add a new file at `crates/cpex-payload/src/plugins/my_plugin.rs`: +All 11 extension types are available — security, HTTP, request, meta, agent, MCP, completion, provenance, LLM, framework, delegation: ```rust -use crate::cmf::message::MessagePayload; -use crate::context::PluginContext; -use crate::error::PluginViolation; -use crate::extensions::Extensions; -use crate::hooks::PluginResult; - -pub fn my_plugin( - payload: &MessagePayload, - extensions: &Extensions, - _ctx: &PluginContext, -) -> PluginResult { - // Your logic here — inspect payload/extensions, return allow or deny - PluginResult::allow() +// Security labels and subject identity +if let Some(ref security) = extensions.security { + let labels: Vec<_> = security.labels.iter().collect(); + if let Some(ref subject) = security.subject { + // subject.id, subject.roles, subject.permissions, ... + } + if let Some(ref client) = security.client { + // client.client_id, client.trust_level, client.authorized_scopes, ... + } } -``` -Then export it in `crates/cpex-payload/src/plugins/mod.rs`: +// Request tracing +if let Some(ref request) = extensions.request { + // request.request_id, request.trace_id, request.environment, ... +} -```rust -pub mod my_plugin; +// MCP tool metadata +if let Some(ref mcp) = extensions.mcp { + if let Some(ref tool) = mcp.tool { + // tool.name, tool.description, tool.input_schema, ... + } +} ``` -### Step 2: Point `lib.rs` to your plugin +### Modifying payload or context -In `crates/cpex-wasm-plugin/src/lib.rs`, change the function call: +Return a `PluginResult` with `modified_payload` or use `ctx` for per-invocation state: ```rust -let result = cpex_payload::plugins::my_plugin::my_plugin( - &native_payload, - &native_extensions, - &native_ctx, -); +// Store a value in local context for the post-invoke call +ctx.local_state.insert("checked_at".to_string(), serde_json::json!("pre_invoke")); + +// Return a modified payload +let mut modified = payload.clone(); +modified.message.content.push(ContentPart::Text { + text: "[audited]".into(), +}); +PluginResult::allow_with_payload(modified) ``` -### Step 3: Build +--- -```sh -make build -``` +## Two Constraints to Know -This produces `plugin.wasm` ready for the host to load. +### One export per crate -### Example: Identity Checker +`register_wasm_plugin!` calls `export!(_WasmGuestImpl)` which is a WIT component export — there can only be one per `.wasm` binary. `plugin.rs` should contain one plugin struct (or compose multiple behaviours into one). If you want multiple fully independent plugins each gets its own crate. -The default plugin is the identity checker (`cpex-payload/src/plugins/identity_checker.rs`). It demonstrates: +### WASM-compatible dependencies only -- Reading security extensions (labels, subject identity, roles) -- Distinguishing pre-invoke vs post-invoke by checking if the message contains tool calls or tool results -- Denying requests when the caller lacks the required role for PII data -- Returning `PluginResult::allow()` or `PluginResult::deny(violation)` +Any crate you `use` inside `plugin.rs` must compile to `wasm32-wasip2`. The compiler will tell you immediately if something doesn't compile for WASM. -```rust -pub fn identity_check( - payload: &MessagePayload, - extensions: &Extensions, - _ctx: &PluginContext, -) -> PluginResult { - let is_result = payload.message.is_tool_result(); - - if !is_result { - // PRE-INVOKE: check caller identity and roles - if let Some(ref security) = extensions.security { - if let Some(ref subject) = security.subject { - if security.has_label("PII") && !subject.roles.contains("hr_admin") { - return PluginResult::deny(PluginViolation::new( - "insufficient_role", - "Tool requires 'hr_admin' role for PII data", - )); - } - } - } - } +| Works in WASM | Does not work in WASM | +|---|---| +| `cpex-core` (no default features) | `cpex-core` with `runtime` feature (pulls Tokio) | +| `serde`, `serde_json` | Tokio, `std::thread::spawn` | +| `chrono` | `std::fs`, `std::net` | +| `async-trait` | Any crate that does file I/O or spawns OS threads | - PluginResult::allow() -} -``` +--- -This is wired up in `lib.rs` as: +## Dependency Notes -```rust -let result = cpex_payload::plugins::identity_checker::identity_check( - &native_payload, - &native_extensions, - &native_ctx, -); -``` +This crate depends on `cpex-core` with `default-features = false`. This excludes the `runtime` feature (Tokio, task spawning, orchestration), which is not available in WASM. All types, traits, and extension types are available; only the executor/manager/registry modules are excluded. + +`cpex-payload` (the former fork of cpex-core types) has been removed. All types come directly from `cpex-core`. diff --git a/crates/cpex-wasm-plugin/plugin.wasm b/crates/cpex-wasm-plugin/plugin.wasm index 55472f0bc32b560e3fbc1b61ccc94b25d0a5c14c..e201bac6e3bc2df54c4eb47eee888bbeced3c691 100644 GIT binary patch literal 660721 zcmeFa3!LQFRp0r4)w{a8=G~eZN&hO1?HN#8V_Tk)wO*K-k0p!X{ot_Ptad|^Y0tEz zo@cweC5?6!sb$MBek7KOV+0s7#!f7-O%&Dy5nvgx69hO}0ZtI$U^y6D;0MAMAc(>H z{hfRNkE*VIjtu@J>7METt$*Fex#ymH?m6e4du}kjaAqtBgW4;?qe&9f=gyrzJ^lLA zGjSN+5rjv=WMSs?ZDANB!Op*XW@hn@b0_{@mgU8-KYi}_sW58p3WKe8PcO{gID76o zeR<^Ii(mTULzSR579I_gpfWQ*e{Mbwo0DO%Ez4)`e8#Z4Gm3-I=L-rTYEMPs(Va=q zo;^Ev=i=1D;{43?nV2fa&fU3KeiOw}809SmQ8*iiZ-{oBo?cj-IyW~nKfO46?(EcU z)3c{%PEfph{PeknnG*rMYP(9O9E?FlHN|B-1hw#vFgzLF9ab}RG=gRlbTf1efb!zw zcg!3=HFfv=?BYz=3g`Y|crsAI$ne;xA3t|?apvseLOU2sf<2?FaD~T<*2ubVbXFUI z*|WD#-FEuUg*(EDaJF6)>mN+}YXVQ})7FRH;4P z4UfJn49`@m3->H6&YWqSn7M1_v_>Ymb75w_F}<)bYb_*;=gyr@f@J<2BuGoA7Q*?Y zt(Q~Br%#`rnmw_QoSi;1v(T8Hzx~cLLghlkUd&A&pMeA#dEPjPmz8zjTK-x#6!qB! zH~RM{LCZ?d&n(<|n$A|jGqvP4XeI&AuaEDXpRJ#mUYvgY^ukO$JvUoDGjrzL{5|#Q z`Ni4WrjIW|mXthq=lt=Rsl|KdfWIaJIMoPG-5t)iw&h>W&%EKz87Py^s8ZLXsC8mS zgEA*ljRtF~XTU}VNwaZg_Ka&IYA($FUo%s$zh`k~q1Bo`3;K=L7Zz%F&CD;TDro%p zncJqa*5jZZ&ezrgW~lwv{Pfw|XPAKWwbgJRX3m~y)!_~^#~07d)5_MY1)92TW`5@E zaWF8>5Nw&7KX+zsk=J>*myaP-0KlMxn#KnU#6c&Vua2mt<@4Ei)**_8>D&2}BdZwM zF`M2~J_}8ZwV zg8Jeevu97iNk|t{h>2dF5n*+Zl)|x~T^{ssVtDAnEkl?Nw*?i$Vz?uy7&gLPLB)C% zUJ=wY7{fhc&3=YX%|Uf>us1vt*Qd`;pS}lP(L8?cjAsAz{5_T1W_bydI(`TAKXW<` zt_tU_3{PDRa>f_n$7iOe{9ITtz7l2xl4eD^l=id{2fc7U%syQM-uxFpanI><(|L|-=gz8oQL~sdQDc!=&lEf}7u6T%VRxD>wS~Fqvu@gK$?`I!GiNpTfjh3i;Xr)SSByd(*>Wc8L{c_{Qv zYfX`$>*aMxuDw3-VCu~C30RP!T#h(+Wq9hk@YMedVD0I-xzqPdAvMmNUbt?8Rr=iB>g;jl zU-*`BdNEskzOIqi^{sFhgEp)4NOq|EF;7-v&zjLJ-dVQV`BJs=keJ z$PB`}ZT7gWb<6=r#J?!PPPFsUU_#OY>q&;pj|F2;+d}DcOBBG0AdxnzIXo@=yTJJV zIboXH z2P5HRar$=1&xkW>GDu9W#n~Cnq;Wu;F4y$GFmg5e!m4}Eb#(pA8JJ*HT|758dwhX0 z-_BTy-#MArc<6c9=XZps?g>vl0Um1=6Q>a!SS&TBO!0|sTYeC)EhUH++ z0tm}~YK4QfFP!!g?S1K_gXlMR6Xh|s+_ecIP`j|l?Q zI(4UHtT=dmcsb^s-#`4$9UI29>BQ z6C9PL`}p+S^y_C&&uW4(U&cA5>B zm5ng)Y%5BXx^kL{2LUajgF2z1fy(4$^>3+V%(5XmmhQ0dEltP%Z<_^VIKu2*ux+H< zZ>dn{{It5AEn#k=yQ<^hYI5dfXil5o3g3N?|jyPbt;E}Z)#$l#A5gJv^Z@>!n;C5C#o z!bZqrT7IX%8|(dv>voLl!@`#lK{HWjp|!Yp8Y+g_ut3$broR$QWXWumZ{CKQftCpW zK$BzH$>F`@+*}+ihI4-+YOR2i*|UEdR8GvyE#7g@7UY84XU|R*z(*Z=er~?}rr|4_ zIdOjr^3T9oU=T{plm`3m@YFYlr+x+0e4PLPf&ZW2|EKu>IsX3v|3Aqg{osf3j@5g=7@J;q2&wPqS&Ed zi|3qfPIV_{-iAKX@j33AJqH7!|8ekk8aNQ1dNYr=^8X(JWObXGpFMl$j3gdJy9HF6 z`#W$0qy~f$FPH!0a9jSWK*u}6BBSrcLt!It@SP#5rdx5Cg)I}{SvJgK@V#MkXBccb zJ~wmzMhED1Hk&Ke+CJG*oLrq5QBV^WL2oYYV^Fy|40d~i%ItNeue>FtR=YAh8e>zE zX|AdvxN`yH9R`z`^RtU#$;5^rbe-P$g7xZ_Fb<+@$u&b-?O=G+>|JOq17FDSG8@X$ zLkAlIa$)hr?Ab7GUJ(Y{bMWk?ufJK{s`|4v#2J!mFZB(e`VM|G&_dIOV3&t(Yjryfw}zpv9q}!1L$b@O^*tJDdfRZ*WDAoOdOa|v+t+A($0i!r7_ZWJ zG1xn6hczkZQ6Svhd=yFFACT_2Ge|`}H?CmfX)za4hC8uj~`gP#*&Of8VjdeH|RDDsNWkzTjAe5BE`6PrY_e zN~!+)T%GqFtJ$~uKDy7Ez1O#-qILfz$>jcf4Tkvs`=}cT?#mja##WY(Cm$Q`<9*6m z2L}7$eTA~Ti{4$|xyGPfKRD3As6CH^+y8E^X`DdY`+E@Ox%ptkHp;>VoOW;tA6`z^uheDC0uLxpo*VJ!!1FusFVtyJ6r5v;Nv9vogk zGh7cWeg|!B9EsZ%wzO74)7o1;K{RqNg+W1N(B9bqW?yVOJwZIW%(}LGR%&pqjh~=8 zY8^jVEcQXoK{d1rA1p1_Ls>GunrGofP#dvk7K_ip5o^{+f+?*T2S+V7D;3INT2@HF zt-W`3Q0zXaug2Q7Z0F#=N*#m%UN?9^)7PZUKh``_ZLmVWsv(trCQ*|KEN zY&C;s6ot(w48lfGCC~C7GWee&YB-|`>Jkyt%Z(9~MN4=4(gIF22Lto@J;T>S(Q-RGynibJ9r zSR>;IZM;mv3b~rZ6;&ey3ib_J8{%# z){v9qu+gjtnUvc>joN^3G^r(yBhvJbc9S@Xg%qncj1dF*z8Xg{&x9|mv4mt2C2=KS zv#mkRd<+_mMo6WMsQn5Gx>BvxYSn7B9yBVopi&`6RdCX3HQ|>}Ad+o#Xs-zkGtfXw zD@ili*=mlDZw+_F@veAMov%P`)oL@DP|*+sM9~!hwmY0iCiW!Bc&Jg(xM(nto*1)g zb#HhjpHw5*%6^jMs)T*g@F)1K#CQ@^e=556{;sW7((Q`31}Ml>Pm*{eAXL4&-Njo}cy4C;jvHEZ1l3@6u;o`3L>;IbZhT z54k#)mL9i!k68a7`i+m;r%UhqMSFhG-hMc2)s7^7PdW;A9LL7>59ZD-EdB#*KqpRP zoMxvfczvt<={UA9a{97{NeDk(T|1yK6*5~7``WbcliGB z2g3gl{UpQs{o&7t|04Y7;m4zY6aG;6cOQ)35$Wv{GMd`ccZ7`r{f2bwg1tXOb@^k0k#k`R>a1 zRDLhHPxMZd3)s_Rem&Cs(dYZN9B)`&nJJ7{9*FJM9E>(V^@{5&^Rz6nwx0TIbSnU;UoyPgj4n z+WeCryy~Z5@U8!Xx5@q2Eb$ZcJEwZdK1-%?KR6WC(>RUyN89`1$wSfhH0dWN`J&CA z&ez^Vfi&44)w^+YBs!vG(yR2NSC0j0(0@0zoIeziOyfgQ(hgh7(C${1m)wieh~Fr`e~dmC|zO zDFLoF^Ka;HzjM+dv-JU%Wn}&&!*hD77rCzl!r*Tj?vH|A4EF(p3;i7$iCgyc?>{*{ z0XuX2T-tVpg-J;3Uw!hL2gn=rA9?ztOTo#`_v=G9=9}=wAOJiYfSUDIO{L*fko4kl zI6F$W%T`ym4%HV?^miD!O0V?B~?MMx@s z^|IZo_W$pbDBr4=CsBO%eV01qhI@4{H6zwQk5VU;7}^nBqyf^tJ!bW{FbkVPYYI! zY z?vP_hW5$m_Ox1O=I)E6D6!!?)G?wAUgIM6xC)g~0x`W{6! zLy>A5Mp0!Y6j8h8kOp9ohC!Rr3bV|k!N{cZ%ff9MiygspaRTvXFAijF8z@39~NeK(Geu0H{MjjSmx0XW)?&*peLdiB{_Z5;vs3wyiSdHN?1lTw?0R<&)BjVwG_ayOk#KD~*GEt+i$? zzzxna;09+IaD%f9xWQQl+*YG9;5nxi4U7IOj8^yFX5OnCSY);$x-v!Q0Qp;}vWK3w?9>M~V(cs{@$B9jE%JPF!1W6ES@ zlvpM!qr@^<86{Rq6T~Qy#Xig`9h@=cSyT11ChOr^^Jc9MT7Sy(#`;s9H`bqW2dzKl z4qAW89kl+GJ7_bj+`*M*)~{x>V%3>txmKK6zm(On>ddlS-;guwlhv@2t^7{~g&K$y zcq4)Y^1gJbL`vb_G&!o3d?`GPPAG|_Yb+{3?oIla&=wL@#LjP`lmwz8qoiaiI3gjx z|3q-ITkE&G^*H~c?*txKwt_||O|chtzK=Fc#e5aA2JeobkC;q!^H^e)#!lP1Srwly&1+PY27;D8|^Lc(08 zMG6K0dzGs#1&RG`T{KuzcW*cx*Inx1;aC;(E98G-<0Hi*epYT&qX1K>#ge=5kt z>NjIDMS{CVvnJ>tEKAlfxsJqYO|RHZuedb5Vu+9T%%`UZ=2JWsq+VXi3DW;Cytk7vyQO#)Ta05~EB`g_ z2UpU#7Pgx4oq-nx>WyZrJvKhk*|K%p_8kBt(+e~dN!+E+DQjMzPnTetQU7aEX34qu zMM(%=&kEDv=(wgPlM_o;dl%KbglfDEM%7>_xn-l(?4+8Pg_m8;4yt)M>e^*Ovz=-X z(J!}}ZB%p6a8vo2 zS8SYc#;NC<87f)1d_C1oR5M03((`RL9Eu*+oL>sRZLG=jTi?)Mx_=&fP}r~D)vd!Z z_^$IEFz6_)-57j73p>m3rEvbn;9(3i-LQ?!9QNCHbsN0*rCl+sY2Tie`vEKG%QyN@ z`SP(VZ&m-G&th2{zA9fL>~FoRKj+#6?lvoc^#Cx@)4H-)l4u`_n2TxsQ1mJggg^?S zH#EW1+D+hwl|45fsi6a&j$DPzc&mOxj6+hkhtQdZBKU?`m$0vOp3~aYM)1y?LUwi& zXyZ+pxQ;-u(Y~Ac*s$6YJ218;#t=xXVf2N_&gTUw!`;c30h9e9j14y@9bv8JE|_>$ zZq6wZ?OuZwvJ1V6jExrRy}GyKH6%^cwU&ixUDmH!wgS@&y@5!A2KI3DNhNEC zq9-*Q9*(pcJQ?!W{dO)|k1+ziCt_0Yd^qa0-KrxyAXOfSo`?=JPb~R3$s1hq63K%u z`54LlF8L_Q)FmGwIq8xYNp?sAN1F!_LL5gMT6!D>2mOab?X9HE{@lraa#yz*9Z4Jg zP@-_NTVsgA&IJ?HoJA87W)-hVRw84|P?EMv#;uks?d`9i3bDdiK@;)9V!+XUYZLkN=@c0m8-4l!~J^=Bp|F6pyHW2^Q> zJ&7q+?unU*r7s)=B9x>Xc=$~sL>5P4jYHA3qIkMIYz;Do#~n5p}2(87LE{+e&4& zjVQw)4>dBSMgnUO^Lzh~vSif%QkIPS&t}P_|NB|8Qe>zqg8_tbx|QyrkJ+w^YtMlf zq}z(tNM@~(%vvLvwMH^)jbzpu$-FfPCd%|$(U0akRGaQ(s6w70WY!bO`i(32PCD*6 z6eS@dG#@vUGpyu-!gHI;CAzT4o)%6rSo;-FJR*WQkepR||X4^Q);mIO?{boj!^G zou*-k=gQBAnXZ4~_e=bF_*NZw@FekL9^YH)Nzuv*`&@fD5g<{$4OaD z37(C#DLBT9ajohN#Bgs)%H+;PQq12uAgZ6_&od0{qZ-(>O3v=M9`)Wt#>GQq9^|A5 z0T6Pu20Fb?)&RZ(`=d^Gi)uHMjBf{%jN0j}+|E|l&bF+b7@cmrt8GU=!CoA6#XDR5 z$V5#nIM_ZF-Cg|eNEFD#z*hgFQ65DH#6Wb|0Yrm?q}`!&UVL1s9EO2` za$8jnZOwXoZI0P}uE(vR9zR}Sw&r@gE9-I7^|sTGyW2Gz8)=)x!F}7EOxxX7{{c#$ z?>6LeBOcf#n=)#QJ!eyiuzN*TO6;|&41UNP`H)Wz0rqPJV7sZ)27S^ss3mHk%e&QO zRLMyXdiM%ME*2C&UVF4!VH~Gn(TGMxLkAR{-rkH-ATPbaY9|r_E>9x7m`nG%ie;3~ zDxL}!yE{F3dY_5B5oo~>CNrmRKzJx&%i8jTLtB1utgRB+mvFGxVW8nE5xr-Ch`z7T zimVw44ca7RmPp-Cc|b=L)?*TI(F6AL@OnU9Uw{?leH0EPt9It#)fp^VD|vOU+N^qr#&m`@dd9faDKujV6|kv>(+IjOX{}63> zCBl91>V+@_m-q+_GDRPHR8MI4LwYio9${r0{$*ktC}OkF+KP^H)&3)Blx4pA@MlYW zmx-bc*7x|9E`}Oo2GGB1OY2|^0Ha_1wmmlnKNQNhtH@A25I-9Bt~^>z`Tx2-uT%z> z#%ljPv^th{I-dYaRBA1zV-mm$wivP&*PO!~U1EWMtZky^Z@{%-$wN-ij$MmZ94JRF3U#E*8@w|O!cy4z5i)ivSh<>V9BO|9WxJF45D!CVE8_KFe~zk=U|!x zMe3XtiL85;j7>vTZ|!4JG*Rh)E_+*bO{kWdE6QwBYp!0EMn;lmC&XJawj!2HYz!<} zvDP%G_OWV%YDqUxY~oNa`+o!sg~%} z>U1it)hRF_a)GTa()?rW6=S|}pj)Pj_05u1L%tNRy`yQK=!;{ zlF%^FPI~Eo^unbjxce5b;;z&Owr(~LRKbjLc@cCON-n`t{jExk{h0Hn`q9`9ANn1B*U)>2r^r z1^S%JhRZSN$N5vP&-FF>%%<4$(dS%x9T;a1;NJ*c)(7DPu z51DUPqHP5A=cCWLP&m-%l4v|k+oSuMiBD|@7Ja^kKJR0zH zm0n}dDahZUr~L$WmXlXP#sO!Qim+sn%!-IXehRnqa@bTa3w zyxwP7;JKtzs=6w@;=>Ofj;_yhZ_j5ZIb{26-Wa@+$Cc^64?ozuQby2f8rT-lL6>5m z_6C>gXrC4(=x}_idoigkT+5DU+G|}3Z?$7Cg$LUmq|l!5R(nXw7fCW+@JUK9`6M+< zBjS5nn`4*6S%%xu5v6bLH6?j=TQtA2p|~YOF{V*lUh9(j#?yM)O3c_`O-of5P3?~! z3bg}*R){%9x)%(?kNG8M&~$r}#>UA$SF&#uQX9l}(^xx0Z(-P@zJuQ6E%ybJ^Z2_0 zlifRHdBS{=q*tVqH_hV>x0?aZR7$L0uvT>b-5%6j8eEw?!!BmY?R_>_m(Jfp=eG+t zX4i3gs#}`#uP62OzM@TiT}Uf@*rE)3(f7NJ_z0gH{Y9ArdbS1J=%3dIR-BCd8M4Qy zoGIeWSw5SY5Xkm$cskqZ*Tmf`85q%hJzHP)_G~+X*3ELi0V<=8v}eu$q$YvByeGk{-0F@& z5KBq!XIi%Si6_WF$pallJb~?02rAv;cmk#*@dRv<+I@guzy;D>#sE?*eN1>+RW#R` z(AV;SvuGoORZF`Z7<}H~W;ireuz?1^x<%U9z*9?a!b$q9cf@^~m zjC|P8GU0i6z80`TY!h_oiADPpdNZyyu%LfTZz6gghUa0LWeyeWFdd23djI~vMGf`7 zIa8{#{KQ%n=c|)`&*b}SQFvc8bmXh753^Xr9RK=2|32)DDFLef&YtmB$wSh1`~v2J zygVUq4Avxqyz($}?zH2#*fbp6FHg-?tfz8X; z7MZ?Yh)C2JoNpu|ex%&}jZ=?mEzu79^*Xo7%M$Hm{hG{?ySM1HNAAA-JN$YEuy(M( zTBzDX)qZ`E>3OTYp!-tot=4-9;=#mJt4u&o9ZbO-NcUYk{|;aO{5zG}n^AA-s5cT% z3~fyh{hFSr+R(4*Sa34J4)ZmgO_HGJvpJra4D!TKYdY@T;)${6i8MaxPfk+%;L^@_ z`?BMt#+H1~_Lrb{Jv%rm+x4Yvt^as+E%ONM%OB-+tS1q`A9@$_mLtf#VtYye4e3P{d`Gjw>mLZq$P1Z8x5x&VUxTwEqnI=J6Cv9u>vRVH zbt`vcVCB6tIOStk9*46VgWvF3ENkHmZY6SOaLTOe3=S%^JN!x}BHk+CXCt$EOWVsXgMjK;`ajnMQCY%g3h8PZfm+bec`z^e^+Y*+%H>?cLZ5b*<8K#a#U5lgS+`Dnu z76ck|Lpy_9~$=O-s1eZx9$&ef9|dQQU2V`!k>Gq z^XG1s$}~rmDg3#!MyAw=?P71uHpx-7wW00&xk)WE}xQM5)f^XDd+wMH^)jbzpu$*eV!d23Ps!k|AlmP_c8A=G}hR2shLZy`=T zR*n}1D;aR|H2$*8Yr9kS+U{T?F{;0LtdUNnEx&_Js_Yld^fs1O_S&|};X!h6EWzjv zmP1>7Ly&n7?Uf-J&mkdIhQ$2G9dorKHnh)gr_5cqMMvO_p1*#l%n3sv7FfatXTI5F zUy`>XeE6$jcZZ(Og?Ov+d^YUv(({=R7e1a}z&TIPr^D`UJwFq6_vral*kvKu4@2&< z_0Vx%+6`@JG?~rJ|IuFBn#;x=WG5=Tv@@J)&_VQf5>9Oxu`79F)BC;BrS@RF%@jG_ zU7>fA=@t1qY=*GlrSm1@>JHfv3zH4Kxt2pUJkZpnquI$Cyq+OyT`r1w>J& zhh^I-U*?Xd^5@1W-1$hboMXV&js%B5%%CYx^@DuEB&ziglc=|TYDvTvn)1NLsZ|jR zDBC9KmCmBd5O?^r^1?Xa%H8SKqswyTZleL0x(N3Usj!vpGoc|HH8P>_ywy~8>*c^! zL@1j?Zd8VTbHqR*qg=W36JjjgpVODBga5d|x$F=O0>+Be&M*wH&wKk#7M#H{`f| zG;$&|2rvG_Y;1((NcyI~eAjSegm)7$Me|9J3!@ z(J?ziBz>&Q+qt>i-O3^BO%3SwMp$`I!p`)ydULAu55YRF+O$XbFk)a@*o zCtvoQMnZ??otFVM^%TQ-}7qjf?OELG!KUTj;!~CPl(Ufs#QFMtBPS(?D!ie({NP#PjiZ- z+Arq(Bc&i|IIl|<$$Wzo8cM!x559+T9X=4x_4XW1QvSbg&j)x}%?F238}HwHa=ac8 zf0{_jk^H>VG0CdWe)$(ZSVigwk>(6)FF*5(zxFm>Mqt`JImZqJ!O<`N$~%Ac-Cy{= zfAi4fa^TtWmY1vZ)fFxsgcbxmjIy{^&Rz9AIPq=?h(lfe2?d!%@J|Q$ zlGv!~)1RXWRGdGnJg6<{+;A&4LK=NZ>pI}+9jWC{EH}o&b6Kv{<_R2{d#ZOAmWQ)< z7na}ia#vyb{n}gSb8E?(L1f9g`Dt6S;p(+PXAuXYps(u93pg}yY)eLk*Kln9v-UPI z=q*_>7i>!o_z_F>vA6ZY8`-%_+uNqO=@JXVk*BRqH$X@_(aeJhDuxUL;V%vkf5mLX zy<#djuT#{+vknT}AKeRw%=xUAQxMv~DRO{BZE`x9uQavFb6<_zGftdxuBNeiSMtA2 z*iFsOoRac7L%x;(v=c4O&!cd3SO&aXJ^H`-#!E|Lmxb$Ew$9{zS=~Kx4R~@?=Bxg_ zU9k`_cfRMrlj&d>w*^k&mi4ytRa>(4nZ?Qc@;tkiz&>2sckXxA?wqT5lgM7Ra~{04 zJ7)x=b6NW%I(T*3>Q2Iz!O;%4KCRNhPq_{@R~fz4oetMKdbwzFZ#T_G&x5ym2jvQ8 z9n43tYs*WzTQhJ@=d-uVq^4bJ zb{n;2rie@o6`=*`)v}401wcB#_msjqAe|p&U&e4$0qOjE%@~d$B*o`(j~UWkRd^d_ zJY-LgwQWCK6)S?xy**L@%|{Wpr`Ng^-i&g9gc5O&I^t6JGK$WfFXKTR?VMZLScIu1 z^JN^{urDKaqapJ_g9>wB#@EV~@jRx3F)hSQ6OY|XqdXaXU_@h+TD1M4OB~ti#VVrJ zj26s$sf=^pS(Jy#lD{2umpNLp7}e7~A1KL}^~^?%t_%%Y4@oVVBLFL#r z@}+Uz^X`};=wS;iXWXPb<95Yz#?5)<%!AVa6XQPLqPY^|&U#J@2jcVM+S=bD%>vG$ zVw<%kD;_%(Og%aKX-13?=9Kaww9W_ zGbBAz{g#{3o%7UI%Fen;oDRNQ-pnXqzRvE159RKJe{$`BLr#8YhAeoov+kA2cfDz6 z+e->sPeki$hiZ&<2w3-^V6;@M4J>Yv=bVqbIMI1D{A>#lGE=zRpgoDbN0A!sDgakY zMe$TOR*T|2Dp2F))gs3p#c8~}TIATnbid46=MhQjV9@FK?^1!=szTIATH z7>$=#iyV70yllQla_murMy}6LWJTenic8K$2}Yhh3ed>)Bw01Jjn|?O8SDtiQrYukzxMb4E1N7!HM!J+6FDWdM)sz>TW`W}i-L`Twy^&sg+S~mEOeWD&r>$?;JIp{UB zja}U!!yBZCS1M&v#Py_b060iL3Bz*-4^Alam*xJ00+YJqsbJ1U^(Hv)BH#>$v=(zj zlpxm|fH=n0OXZ(Lb^DO@lR`^j20|$6P;BWyv5PN+y^5|jA>Oe>gTujLdmkKBq-pVy z;9K2`8x&@micYYf#N)eY2*1&MYmD;pOGgwZ2DVZhD^jiURhOg?rjdyTjOPC64x}v} z$As7Y(XDn!hz#!3QC$wP>?6CFRN8RUD9D#J^uDa-``Q4< z&yIcNH8rIZuAOCpcu#S@gm^w+neitE7yhK2f^!_dS>O zLH7!Y)+&3m`)HNjmr*nCaOH@a2l98g4nO4ULTG>~!aUu51h(;C=9}s;BqY{K zTz$kPe`}o<3wL83XLxEDDiC|>JeY_H59S_s3LH%@uwj%|K7a$4=||RA8`bai?IS3x zt9{{N#rCV~X#byNNML=)H8@*W;VgGBp_seJcmVl#xrY{-CKN}IGL{);?GSGV`>@cZ z5GG;1LqZiV9wY>*`|3o`nCy4;>_j|kma*YvIg3riViU30(kO^a_gL8!p-QaUmb?MT z^F<^+52)6358KZoA8^y1)0+YWYkRBS-GPe_k7Mu{9=Dpo9pG-($uvl0RrqX7yE{a9 zQt7m#U0w-xWW&vFZoRZ7pxZ8mY)x>I&7pBW;~M$S~4-hY$zJ2^WCN_4qF|16#Zs<*RcSN1Q`}>^F;G8q*DnJJzh0EOae3EWEowo_S(!wn^Uy54EVTvt zjBIZjgS9ka!`|S*%Rcz%{64E0VIv3AX*0ida{xvW9R1T|X7+$EuC=_N#P)t?5kgR8 zqWU8JA~8RI2+ostU=p0FPrWPX)ub6<%VOPNe~QzSl(G}PgFO-%P)}(-V_Loy3k+j! zVhsaUw!QWSdf{fgw*^L8eJtXV8<%WQ^3h^)%?ROB=hYz{?zjU^@A!;Sksf(}P}!}r9n~U=FEiORkx$H`nlE&Vd-$#u-6opY$3Ym1U=rxCff540QKya&5!PL1!!R_wd zF?ZPy-`;Lon;j_}P{gGDI7RG;OQDF};8JExMy5CJV#-`9*qAYD%_U!ii6g&F2)9A{5$S z>HLbb^5f@HPwN>Yqb8#aJE=+PwcW^As=M^kTsgDk5KDC>Ii%}XwbwEgOC3|S`<&#$ ziB3T*q(L4Ta@WhcT{|3__3c2M!|mj_fI4zqND>}E1OVB*7G#Bnt_6qK)1LT7SPdL` z8|a~z42MAL#7c_CfX6!7@WI_5N$Os*eU|Zh?&Z6vs z5!q9?o}}Wwv+GF^h;Iy}e^=arv^HKj6xZU+Qguzf$_zE`$telLk~N7=O`?Y_)8ppbVe&3w1CY58)n?UWXyn3EpjDl#L$F{1uj z)@*X-*TlXl45JKLrF&v?>}II9cU{YqzrL;VN{ID22Ni5?EI&PG`Fm%48&lU>HyKFy+ zdE+?gefi()e%ZEMw<^_|U8BWzBy=G2Dl3LvRj#R6x~lxqFMV{$?2wOHqPVI&u!iOD z6lgO?^u{9~-ZLp`%wJ$Q58PGdK8J;g#uA_BkO{nRw@&&T2tt9VJI^6wcHeHD^f_2t z1DS>M90(UEW2F~pY(bw<&2wN$4*Eo0%3q*W1$`Vd^B0*<<8s_ZtpBL|oDXdITRR7^ z2zSV_$hih>jBW^thO;sf4QFK}8qVN9Ijn}WG7=4EWh5HT%1AVvm62#TDh@?Q@|$+w^#JNdN_72Y_kmiz#4>j*mv`x2UdNlVIuO_3 zf0;sKvXdHhbyzN-$OVRi#eO1=8pV}?Fxkkly==r)L7R&mRaq~Q6dBo4l}5RdR;LBM zk(L!irVQ=@Tw0=6`ir_>#SFo#Ox?$6gE~wf*P_QfVO2nR(594Ul!!`$C8*YmViQ=L`QV(JpGVjQAO~n`YQp| zgsq(@kQHg-X65aXnT3wb^rtYVNYaoSg)9q_7O*aWA%`FKt^5UtR!q?Aco#l96iDsh ztAKTOI0jCtHuoL2)l*>T)sO+HN#v?p*%WCjWt$=rR*FjpC9ApdnvjH7$t1jgfwZ8j zyt#pJcea2oeIwpL_~-^gO&ozl;3c;}ftO&w<%joW~^g3@KTos(5m=gkLg&PQ0t%ik3 z+_M|fD;VCA?P9Gj=)?m$I_iT?EP<2t2Az1LmAf&p@;>MU<>OM&i8uKymbC~vVI}gQ z6O;k&%ejGYHO#CTb;1!q^ePayp&8G*ZYBsgrwkd|<2SD?>OpIMqt?s~+(5Wm7i3SQ z($QA}u38X5!eGo|Pr%kh{MaInh}o;doj+h(1X-Z8Ct`MQ1Qal{F_dT<-!0;rVWTfR ze1{1W<>s8|a@pZ0_R+SxfKY)b%xx~}FGb?vSSdw_{C0laddq@$!EPVadFMR*gzVN) z|LxJq{>KBm8qmD&Bv$%Uvo#s89Zvq5;z=y&dKwF%p`NT6PeSAQUiEM=pv0xWnSc_U zh8HiofrmFd%Km3U9+T!;(meL-p_c;zC04jTaCATk$uBo_bt2Hml=wKiMG7bZ&J^QI z_XYZ^1DUDz)(-uNFtiKr!%XPvKyB}=9%jP2;C6cs$Eo_`!%VQ_F>q}ldmK-R=Hy}| zc@nYk`MEZ5S(u5ZqM>U8x4AGA+xG`~mq z8p*6Rl38mcv(`vvt&z-ITXt#v5R=ymNxW$mY z)Dlwu1d4I4CFn+E>J6)*WdCxQi4u(0z)qKf;%+*(&4IpJhRk*{Wocgdn!*ecZrdwE z^0bEpc*++6mbs|5f-P=@_-Gzxq6A_A3y#Dt%tShY0(OYl5+HlxK*Un5;!b=ha&adf#W7s@J`!~;?!;q}i#zeNQTIx{zZ7xih3Ch(`A5&k zqpoHv7Z8E*r_|cGIvPKT6~W&ncsWrjtiPg}jYp~2DMfMDV8{uJQn9-fr2-`TyQFf5 zX+^OvLR%318%ii0N(j~DArAq_`V9YqU3(U zdi!L`HmpzLg0&Q}%M`keoybScs57RGjRXj>3lQaPTEZxcQAmZVi`FzJdVtUT2(^X7 zl+5Edls8|y#xKaeRNBHU@e?$KQNc3`*#UvfcH~NP*s&Sh9M*`QvC$$J+ki3zWSXRJ z(z`L99dYeRo8bkVaQQumtI54o)?do+LHzQ|37=89ubtT_)_D)2WRkT4XcY0ypBuVF zqI@Jm^1BH*&tCuyg$Zy0G)|CWTkjZlA9pL*Q^;aqd}V(%Jy@`*@#FQ)T}TIy5MYi7>&n92C9$|HbW;p ztxKy~BaDUznJ^jxnH}drkn_{3fNFs)0CiL-4Yq&%8;Bv2!84VkuxkZ1>N3- zX1pwHhQDs_ycX#Sdlh5bf2EDb{F3SILae!=qd)? zENN@Ju*b=%_k&QKHJYpGF1{Zv?&7ly92vV95`3dZ@Qs@8;v)m>+0FH_bfWW5 zACUGw`Yt~41Kq`EdE6m4J3nSfXgeI#eO-21?`x7J$vBJ*Eo%LLNUVu4BGN>)QcG&F zg`5a5Fj$NRU5^G%C6en7HA}jCwgC)0EoCr&9wMY|8J=~MBe(Cx@3zgw6 z8}tD*6y*YgI{7e_SpOAP!lGs~N zbSSf=LzyKV$}H(nX32(i*pkg6*hZ@ew&CuW<6I3;K~(ubS|kkD5lcGV%963&P-jV} zTUpZGerL(53!tIOYDF{-cQ>JBa06+{rpti!uejAuSP30ggh)%e_ATk!x1?*|lCFJA zy2}VH>F!3fWZi}qX_c1Pwz8h;UP5M0MzWTS`d`T2x*@Z-iPvw*>uzDRw=S%RCEfLe zY?V?y?pU?_vOYKOHLX3T@S9^ysOl74L4vd`sY_xI4H&b<1KJOx7(UIY zKN^nL{a$mRX65Ir*7KDlzpAgca;Oloh0OPdOPhh$F9ve7Sq8CJLZ4c?v;^zlHaglw z+uaMdNno6814w1jMsPVn!$YSLT?SpTHp7AIZFXuX!-CvPTisplaaH2io7Fn^71z0V zh0d{6vUyCe(K!#@Iyxtwlr7ptN8SBwcHg2G4_52wpSq5&5b?#|`gXaaSeC$wQS{KQ zx1%h590JjS20x-NU_@W&E?L}eE`YVwI{M}Zj9g2%vSl)yf&uuDGMCv=sL_5q|6WUL zHtn5n=yF1Zf<{byMMlxI?rwK?Ddv$;5H-tu!_UGmy~VnWmY&mDFfE3g4}1^?T`E@*LqXNJ(S z0(Ko9z;4%P*zfL#yuu0kt4+%{zSZcrbV206v_uNNz-38tsUj8aqZy{TK`9xXz}ZW1AoV&`TuPEj>T{C;eRX) zP`V$F_^Sx3qfkIEB5ju~=0ho2E*G1;j+u)Zx+Baj)f`>RmSyfe#IxD2T?DH0OebHd zM#hnWe0An&(DwU6-etC5ZGngBKAO&4x56w!O)k}ptXb48NAdJ1_$apER2-XtC>WRdI2^o)1HSQot--`$(SDt! z=G2m5wQz&sz zgq>HC;R>CtA;X=c zT&&rN3HC~fUYyPBvwaU_2V$1gJ+#F>m%iG=#-0Hw6IkB|D8eG>ZL*F2MQ4Ne_n1o2 zZz$R_1n=NWGx+Fd)<+#N0GmX3&q4K8<5fZR5uMmyD?sIdIpA?rk!CLi)~+f(SZdX= zukICQ!_$|q%v-(Q*{YYdT9#A@#$;yoj19uoGYCwh-C+)+mXwq6FmoVslHw-qO!F{H z7B|T(BIu3ATDw<>p~Ih#@WB494K~74iJ{}rpba*bH*K(S=u-?GzRm`ltkz=am>0#+ z_c?|Rdw&g|V0Q3uMWGXWccH7q<%3a-J!9)RABR25p*7$rtoh`$pVzxK%VDkvsCO*A zIvp2Jeu_Q8<#%nC5o%y{enV=Vu>K7Zc&fO^^6gjVzPaPszSl#4W4FK^(dOQbL7#0o zCI)}skzWJ%#1}rg#D)eQLNuCto8d~?@X&04;> z>#Sxi-`sUpvzBk}I;&aBH+P-YtmT`#&T7{3&0S|TYx?G{v!1nlbJtnTkZD`+@0G3a$yM&$C0AkoH{p@HK74H4BX@oE zDoh4_FcE2lNA6dRRkJQyG%m3e4&^wOf-12=&a~Vkcbzb<$0PT1f|S81qLeK6$X#c1 z>+#6_9v1$=<`55sM(wsPqK}ehi^jPy)a8*9`5*y`@6Zj?y5!XS`Xu?P@CG?`pHg<9 zq~wwNoO!cOW`=y+RJdsfzchLN^n74d;?^T6mnONfnEce@K_UG7i zzRMrXv0HkY%iM2(wWi1oH6H@QbPNJjm5$s2Y0BIM`upG7cQQ}n{~o{^c&6z z7r{(UxIE;9YZrN}?1amhYr86wrSk!o&R|MGsEt#v=Z#COj{1zX908gnou@6gJjo@1 zyS;HTz|%I%ZkzlaXiQ*~FLj&bO*qQi7Mb)}28S=}=3R?8YA%GCw=GiY7xX=p^R!W{ z05AVS2~4x4^V;CAS*TbpQ(O+GvSc8?UueB$+An`p8b_i zzpusxaFi?G;6{d+drSgu#qb%B89_n~tY*ch^sVhaa_^ekMn}q)XX|s>Tk~wS%KdF@ z7_o+LV>4JfMP($j%1CCFk<2SIKeF7_6r8b5mickXQUQ&~(~Q3b(|PQ(Cbl^z2PSJ- zY$IHmVK#SE)OC15y^EyGv|h;(waFQNT)fRCmXzOputAiosOTw&lj?jsQjM1b?M*80 ze$yr7%l!U=WuT7L>~zKEVfv(DMv%FkNxJuxi~mIGlVQ&TscEttNNLn$h0mH`(SRO2%6O-yvS&1B^S)e0S`?009{>8brbi0 zD8In$m*c2Xoc|>vMdZ$tvIN9so@`74X@eWq3+8OGjk%vazx+g;;Ofj|`gqyzlG*Lb z=^Sj}&gPqe+6jdajtPHM3!x(hi}tuRh5*c~Tz zD=y#)=DakKO+pqEVne8UPS_^yll-Uv6w`&ZV~3mDy`=LdLKg;+%?Ngp#1{BoDy|($ zl!HEEX&vIK9fshof>L)s{;cucKO|^kfHbqLyMj#7K#QLArEUd4ZH4O{vt7c8Z!uqON z)3C6%Y*9-X{S5WKh9vfjBRl|7GAyUw__lwkeEjI^YsR>k zpC%T5HfzXu>>JLIfzXSRDsfzf3~#B@*|Z+E1YO-19VvbbGX`fK2h12PQ>73}y&0oqNW8b_a}6l)j+PGI>Nre~Z+5O&-qwq-^pSK484VoyOfMi^lK$?oYq3 zw!rblQ3s5-7gms+?jHNK4kZ_ck*tv^HDV@^ow8-P1I8q?BfligmVqcuGCT51GCT51 zGC%UW-7F_N@v#}QoEUg^L^*4XbIDn2B(v5?X04ITS|gdYMlx^BPR-^f3&sWxAkzx_ zt720OCGjjpy-v529aI{wjv2c1wtW^0hV8lXv126p%-mv}BsYUByWxH%J4*vA+g5L9 zAyu}sTvXH0a}G2dD{laHmG!_wIna~5AH&CyhamP=kCma@l0!$T4Bc}cI$&g?0ge3* z@ODLrhULQDa~ntjcBUQLInEsU1wI)UJD}XOjeBi$lR6OXKMZ5BRC<^VZc9xbmQ`5a zb`E2puvGmpjNMWJDi#Kvjt8EUL$?epb(y#E9OS~`)vP??3dWEItvsHBP}IfpGD330 zk@pyF6VEMg0{e(_=&t!llxU3^`Pe3Fc3SkosAo5wfjrK(=DuPwVT}6&zwd%Nvw7n! zPttK=ik%Oe9|vWz;vX|{(WbDpn1`CwaD%h|xR2~Ha`nwDNX;P&lER`d-%VJ{f~1(7%PmOM zy(TMLh7qit4?l>TUP5gLIO$+)O!R&`Kj^Z%NV?8)SCEHh>-ZxVi**(*5+{ z`Cg@LgRD6DiC>B0;OEC&lI}QEkKa#_o#2(tXaFK6ziAyTG<;u^!pdO2SN2hssu zwT)kvZyUcXwas=3r%so*-O1YC>gM#awka5x*RyK(_l&Ti<^uA{`^o@G!I1Ag&R&Ax zT7Mjq?&EJ&oWyh>@b_Vu%EvUe4uUyt+NlqVUCeD6AJ=%We98j{by9Z&8uk#zdI)zM zlhw`V1hjxf>q}Cw=(bD2q7zaqp#kcvyK(*+xY$xK6|k>@q1%1x2c0*{IJUdxKI1Of zB7{Ok2Ik~6I<$QHo+s@=Lf4w&o8nthkQXU^)R%vlJ4lVmPk9>j3bb!i>S$tWJQsB0g?NyBT!JC;@kXeGrUI; zdu)xzM)9yny0i%%8fzfg^IjOtjs4J84o}7$H zd8>kvv8JKun7<&!iOfPQa(7F}BG!sYn9!FiD`sikJ#tj_w z(UYYi@49DjuB&UG{S>G9Z=lGIl#6;oeC9B2ZTxIy&(2mVK7+ImLy41P2Um}rOPlXV znL#lxwYVS?UF6u`C7*tn^kby$Jkd3JJ`#5KApvIW}6*?)SXM| zRi&e7E`PUXF*-J)L+;CTNDqov8Jnh931-X#(k>Y^#d6p(Wy(hT~$xbMTc^=QC?mmTArD;Yovxx;k9${=g` z`e>aMX`Q$%4Qidxg$WPcZJq*ifoA2&AcsQ6Os10aw}6dZLRObIgKF`^P69MTj%l$a zX(D$Gg;oYl%?+m2;EB!hL}Nhdz+Ox+JhorXiOtDeY45;zOJ-Wt>~D9{QNF6*2tl%* za72OgauHLr?K~oRoRatXFo~g>7j%`5sRHhf9dI|V_wWB(ysogv_8oE!W4Egt zrjcZSx9@mk@W9HgGzvsQ=wg>c9C+u;&y(cKvQww03(&S&q9Mfteo zjrBV|i)Agmv8+VyjYXMNy|IK0UCt=~1I1?*k~TE&*L2A8l;B#}5F=Rq)YqS}N-cBA z+I(Y~Q`UwNAY(22{kZz}+}#LeXXi@-mqA|cm$mU0haIymlUx?;4sJzx(TBF33qxIbS7M*tIWY{754q3ztqgD1XIvpJBt`#< z6ywYox^rUTD(A%VTNy%gZ#fV@l<1t8%=OsTKp%pwU16|>0~D%dek%hPd^@M9HEv}H zozqmgk*VPMa$^!e<}|hJRt66qGHuZs$;@erWaczQGWLhtNM`0|l9|&KN#`_0Yf=9K zmz%jeW%W@z?sA&K|LrLVi=oA7O8zfSW#N#Ng#}=}wUYcr7G>3ZtQ?3Jj*)r2*zq#E zPQfYc3{B>yGAYW<%*Z;Y?KiEPx}m{&@C@A502CQt&H<*n;kz0#$YEK6(t41?KkO_O z7LZcC;!itol$9Xk#s&{r`R)daJ`7!ZhRZyoS^FJexH|w=;rET@4&2?4Be{TvUevYK zKb}r_X1v192`W3t71;GCdow9Rw>-2OO~4z(lij3{2N@djVj6ZSLqBu1(q}Q=pESlx z8-9&I4Ry;0HiDz$-^tC;bRB;mDO{P`kKF{Ad-de!+2kh%T@0KY9u=*BRTreFf2r)} z%TU-g0}pZQpORcNaKR_{W7qIW3?H^=lL5mAl$Jr-@WA)JF4qh^6l;1(es$LjfKP87 z7_e*@3gPq53|un+ANLzU>pJ4*=eEW!pAl;~C~cyM?XZdsn-IpSJ&^9?SkFT=D~)ix zjH?CS@sY5OT=EOdS4~3oODZyjwmkwb&9$7 z7La1zuvO>u$?+(Rq=#c``uRM5%7xFCHj|r7*2o#PYKGI!sW;2n&Ca6d=GGhGJmC^9^^Y0KyHRFFxGLq zE!sTK8^_Q)a9hDj0HZqRWx$r*R^UVa-|QSYK>ZH61C*&oU^mMt4;qPuhX& z3_4=ZP2zd&A@H`vEX!-wOyjD8tc9aN_iCTsMLPBAWi5GX$)XeNAa9rdJ?G2D>RfAP^tJ(--D54lHehQLqNx7y5R5h)AL#xSsF!&#dPe zeBMl+V``iFCX`^;9>;aOgB`lp^cU2QGX^H{a)7((WMPp#>0C(Ghg^KNiSrS;CgS?> zsGiKZ59tY6Ju>1r0W&0*iNQwExQ^HuVdZ_4o+?7d(!wrMGL!fjn%C_G(YsvqG4CenLh9m!%e*#jmKgE7(P0 zVHN7#6=RMe4Kr>>>g5@;R$5KIB^qy)thaJL(m{AZn9t7KGeWZ47B4uGDsL$Z^Fo9d zX(yR?MI2M{dE3}ou}@xB$fH8KE6IktNNlgoL+9bk*Al!^fRXxwo}W!_M|X8V3pQx1 z^D2X=-rb+Jx(A3oMva~YIOm7eEAzen%F?AJ973`%@x#5==H{lyWR+ffKsVlN_)>P- zsou^o@J(Rgn~5I}20Sy`00#2y)Y0RPx&5*+kgtfParc035(A#|wiUovqSCcsV2Z%< zU?2~+xNge-3r7Q41omMXV4vXYf&mZcWntio_Fj*H7lVQ8xU&Ha>@COAU)LvHkD_}I=w*%iWP7(C^@EK1OSt0!3}n0VCiAb3QP2Cn*SR`nH%AO(*m+W29tNJU zW!VN$Fp>y*13o;f16qu^IKJ^dH}iM3iF-6=oO>d`z@F?fgw@gN$N!(X-Z#B7dYvBSKk87v(o*9t zOV|v`9mfHib_PcPO8azE4*m105nMr<;DBtKGguR^fMs9$0`E#_DRU*X6u9OfPxnRc zHl8dR{)je)tB&NFad&FbS-aGt}C^RSbIMi_Pp ztKMxZ(yeiy8`^?36R^FK;5xa66QOVZe-y#$kcG;s7T1*$(%GElI*%y+l2oJru90j+ zho-$<0n26E#$j{Z?cF)Q%|Rmz;S4b@n4v{R5D{6IefJy8VY8cFZ_g{!m)i5{^q@V{ z^o4q+hqxG{S4OxqAH8TmBlc<=Yi)_yXX;mP9CMLb8}LTX? zcfs6Qcy8Lw<1Zh)d3@Nh^JRmMos&iBNy#ZMVUlD-M5CND<0kGRkKOEX9dAl`%nfpP zk!QEcOfiE?dE|RL=muJ%O&HL!dp%0Hm6Q@_n@_@-L&Wtxly?pt_i#@SQYg)$4g?rZ zn|{AfrqDbo?#9Rxbs+GOf+>8k$2PcCLoA44kJ0?zb2x!^Io$3wTesE0eLw2CUcAs? zaaYag!$G$dvY%)&e44Z)-}Dl0MvLzLnUw)8JWT1V4FJr#44|h(>T=wN&e`e*gQ~eJ zXk;vH4#uu*3XS=hb^4umOJ5i&a`{dQwCkzgcctShaS2P9+jqriOEJ}$$_b*Mc$sFU zJqpmD?Ww48v%CXM=VXtlH;JoBOH|WYHdSTVopg-5+w|G<-Kg<=H;T#zc879p3h7pn zL%=@Ltv_)peJTgTLW5xb^^ErB?xarQp3Jw6_FDgn#j8kh;fBA5aGZ6s#|4qe43HPYjXd+oo__cHXnvEQ-{y36I=DV{-8g*4DPePt7KgJSpwa^-YJ07-c=d?bp-M%|J_!d zQpiSlJli%KAKW%u0kozLLK`+%20S+WzSvgog~>318NG~&;)pxr_#!`wTfImyRtJgJ znVW@C+vIyWqczxXU6`$gMYKI+^t_`Pu?^hglHtacv3p!Ru-xOKO+2_+4lHOJ05&&b z0PKOB=t}0hoak8D>XI<3KEwhWb1xI*hi$|%h@Svrme71!9LXq4)51ZzGoKQ#$t@OHr=${HsdYXSNZe^wYVo2z_a&TT(fa9ng zjb;FCc2$tj#dVL7bnm|{c8sGHmk`SQ^;#aB3^XuVWd2ly#6pEosu^A&xA%Md`Np1Eyuoi1~-cLd+G4$ zkT4g8q!izznwbp()b7tnu(3TgQe3G@!-fORyXFcm#&wAb# zY}eYrcIhYV)d5unBeS2>^P1kNNW~pf>Wl6(WtKtYvw}!~0)tq^JehyTi-{&%feJ8` zX2@|9a1MrEC8lyqm793Vv;yU8zS0Xu=>u^M;`Hf7Tmwg?M(c3D>QahM8a~7BI*n5B zOdK1CDvs^ha#N2|mWdcQ)|nW$=UkH9K*IS#{2Ku*c9K9_uOoqqmpDy0#;bVAZx**nmhD9wbe15zW^#ag0fxcmsaF zS7ZpfzDiH!r{WxznE6HRWA0S!?#vo0=6c$e&SBB-9#f6LrAH8l_CBy1yR zx^HUMYC2q#7Ftb*W70ya>2OP0Xf+*9Neiu}!zF2<)pR%{Ewq{rccg_@)8UMyYUGQw z1LHurN&YV`&X^T(I$V)d51vT#uBO8g=|94KQ?r3}PTwOp?OiW{nyubBf7QMcyy2{3FPg^$x? z-!*AxxAj;%g*Ob62t%_BqlbEws+eCf-}xYN3>uUZKRWe=6*#e7s|vm z@ga?$yl)D&U?KuG^2F(O-_&djlKgP?0i-ghaJrKrHeYG{x z$36^3vl0bknR~BdMGOp;&O=}{XODmZ03-yyjO9oC3!pNeRXFDf)J?h7KA**13VqSg zSfu9Us1l_~^Q?$z=jn5S&4k0}JV!k(XY=Elq!$hi$m{%#OJ$kDJr3All?&WpjTJmrkSt)A|HU zlvOUrr4!;!s9(GZBK-!e@Q^#6f;dmm=~VuG8kEL2XA}%3`E7=al^__WzA-^CICtuB zsxzwhh$8fa&Y^W(6D0WoNvfoJIE4$`pp$6vx41=URCWknttqa1v@H&g!>M=S%@#|O zNN57*H>g+_phSpu(X?D=tP6Lb$rhV$pKcRSkII(>S-0Vm^57T53JUQqCWUL^k|1zo z!u`(UT|DYh^jusFr^unVV!GzO7del2q0^RJ*e5zK3G(qSM#iK3lAw`xZI^zY{j}~3 zy1A`8hR{hBmjvM-DV5_K@h%3CHehcskmNx%h-4h%U62hCD9DBg6l7~Y0tML+fr4y^ zKtVRYw#UW0kRhbk`S|uQtFaIsM5_p`kqxbp4Xu$4t&t6_kqxbp&0DM9Ys}msAOS;} zLb(Wnlx^9nm-)v1vz>+)yZJ-=r-*ga?gVOz@)X#g>*Hq%aS>=4IP0yNq@pGSgkg!y zsX9CC)y^IMFrA0?#dJ-=NvSPo>QVR57W4?qB~MtjtmuLLEy(M`T((6gsr*=6zBZ$? ztA!u|t*iyNP&|V;Tdq$}$Jq*{SZ=OV%Bgvk(i1V^>q!YDd!EwcSa{A52}4s@B@e^4 zkc~u$EV$SbfFYlB&A12di%0Vqr8zL2m0$-jrH~!ttCEJ&jOqx}n?J*h&t|Not6xzy z?PDk#kOPveYgt?j69)FZYCcyd^YOS-jFL+kUOBQNkD9^!L+5VPjLuHZ&fO^Er2|K^ zzm5b&y>>sl!oxKC+5s_xk}Uulb?t&onRY=Lz;@pS7x@dJhcu>UCRrzsu4oN|wDZ5t zXEg10oey<+CA+99E0?yuvmIu&|8f7%I$>Zm{AX)xO9+wCACBe| zb6w=4%{Xd#YjB0cXiN`@@$XRxQgU#H%X`EaGeLEkDJil+%i>{lyS%5^4PDMfzUX?H z4@Mgb;=ne0nQ~@mNYDexjR=*13qJpCDn-2*!OY046mkNZ$GjL4Rp`98$3kIx`ExN^E5#3_Az0c_jhO7Dlj)UHYOu<1+9%cYk zd<)mvQg90lntVhH-%Bj_)JqI(%Dv2uE=OFriAHNUnCfxZ)KXopC>aXAQN%j#-aU@a zmF@wLy69@?zMA}o~2c&;7kxQ%6#v7F!s*eN!2F?`NWciqaUOoiQC?EZ$%I{B6{|5s*O3nOm>?okW#eTZzYgkk5wx5m}h1tH5pDdmm!gxDV;BDKMRy~)& zbPF>v&}Z&EAQ=#)WpPb{bbtGlg~cyXhEhZ@%)-Qfj9P#R_)==Gy#$94v$R}a+ud5Z zy_WLtn$_>*;Q&03`%6eR?mHQX68+&m59VWuv9w?L{de~>8$fVGSYQuN{=uLAA`fL8 zJEBc`$@(bT{>4wf>5qT)^jkjif1*1<$HuN_^X$y0<2xs>21OBhj4;pOKXGLM&DQ8g zgl7YU9m@@*^4{=yXJ1CA^@Y>BJ*@OaBk%0XXr(V$=BX19ON~3U)TckOd&bjIF~au{ z_+v>lQ2i6D1@Xpz7vW%}BZB?m?LtzJ1uKXU8mEI^%7#^H)3l{ z%zS6(-LBvM%pFq`81o0$?wQ8o94!~vryQJlSdX3A2)$#J*R?8_oK%;(>pZ(!1-=Z_ z!PoWadU*Gd>?t53HF}aZ%Xt5)xAAI?E_e=R9k1`A$b09M`04s<2Xj<0&T0pe<10C641y-{)bEN2hRsj^LV zxc^~OIQg=mbMo7H{X;mS(1B&*SIZFQncdI3THd<;1_Bs)7WTxR{7~PQu6$?V*2(Jk z^AfIi$m#gZ6K&T!EG^vgFwe()zA(=(5zxF@S;7~+qNmfZap)KdWMo~7txLB#42lrj z6$(t*Jm!dx)BGBTRjh!rut9VxfSgkWyp7-S;A@N;>LRm9AxBdZ_ID6aNGSmWrw9oc z*zF}?0DXnGFz&)z#0|`n11t9Q;pDQq#kXuR{vrs8CUrN2$r=Wx4XSdEps1O^S5FdD z9hN7h@dMMwJ&-PS2TBH8vE1*nVyU*<31|8_$diWBWR8^p{RJe3PFznF4e_7oGOA2djsq@;*TCRcbv9TUB@V~^) zTW#zyGW}j7Kwv zDTPyCsv6p{FjYiyXf0A0gAM!9w9lx(r9lylsu9uESxjKI7TD9FjHsZwm!E*`OdN8x-VZgMyrFP>_?&735AGuUo-8)bZ9mba_Qpo&LkQ^y;9=~)Xuh-+-P&Vig4+Z7f?9a{MEMHAE1e7#*PF_ZRv z5b;9j3Au7l@GTG8cUBM(vcDDTx(v+C>1%$mM5&7q$VJXpjdN2&l6W&AK_LVke$;E= z&v^~}S+9XdGjAGrH1no`M>B64cr^2?_1y`=cPd3QPaxN3+E8q(LCH~u;B1hOG+21k zHS(3aDY|Rq?JkNuX3rejcr%b#qhs@GB2k62IuNmFIIBw0aJo3qFRwb#Gn#LtQhrQw z6+0zsGej}9i3Hue_Cw@3<0Qy)v?ZdO*FhTHymm#TbW+%#@6OXqag$#dC(f`j22=Wy zrk+O&)zpvt2h!BTHP4}{hh&s=^-wL=z`j`yz~cT6<+}RAPp^&LRx5P%`{vTsn~<;v zef=BrOGS=!=<7q^AB1a07<$*$e{1RKy=ZQUgkU2s@>8R=)ID`qQY_P}CYJ})(1?o` zPQR6cOMsmzWf#4D03?ztvu*0Cv#%pt!hm5zZdov=&VHro0I)28nU|wc0xc(Mg=Cjd zhRPKbGxbht`|u6IwuU5CeQY?n36pFzg6RU|HZZK>d?>?!Ws)(MmF!K1;FauDCe<%V zcO|>K>{Uv3kuBk>`&&}8FZXJ8L*%ZPO^Aa$4|V%$FGyUj{*7neq5~~FaN$Oa0g9}V zXkPN(aAm|fY0+-Z&Nqpqa!Z`{T^B^4-_CTA#d)OR(h46Jk&7%`r39%mE_4L7J$AU# z!5b%u8gLL9Cn^GT244Ga)(0JGdC#ym01{{+-8@S8-6ZhW(#0cizaapnng&_3IMEp{ zeCT(YctoTnRScB|O?uBPK#S|dW-%Y5WIw2hE z(!>v&0xxt7%Lnw|ji{zS+bf*G*Sn8p&0z3iI!_w-`p#Uc;4MLD%kPV#krBD9j zr+)F1@A>S9z7*YwI=;4ReH3l~yHjufm(PFkGavkFga)4&C}63Duo~VPy;|YefG}eV zoU8PY?k^Qg>%7^@o-Hg1hGmUl554x@bsoTssOhpL!AM1WA39GMLUrCsmpd;*-07W1 zT|nmnSha^v>7V3Ue@ta((faeYr1iI7PU}xLX#L3+T7S)HYA?@nVyG)yX#Iyp33w8; z{$yjPop+x&t-pQp1=Hk{4O)M)LF-R8X#L3stv}hI^(X7K{x<303{N66D&WwmM*o@W zEt|Bz60)wJ_Oxy`3zqFO0|d+Vx4#_94z#}%vJJCEumXc-&0yJ~_E%Lv6XuK#SfEmB zu|Q=+#R8S)9sT$cOZlB5^_lM!QP*>$1Jj9lGU=4?)y{i8TCS{e5%s%`#YNR$V1IH@ z<^~71W+EYsa1T-Cjo|7CYj@+6DOwIwsAYp#@%m!I_GdPyhN|%7dX~ZED%Y6@=PG_Y z2Isps_uvd+oCO5~T$>JOtIYa{_?x!-&oaR2CI!ZcJUB%y&GD((C#zO-1&K^dvdvL4%oEEQC?ehUUysU zx8+dx$BhgCgN|_bx3)%12QI=vjyNA$d9F0;&&9eQDM@?9pD5P-wbfAfqBFS_lD6S% zTR_D{yRSk;Q)t#r)27!!;d&QzaVPg@LCQPVQcs2&)ds(nKj98l$t2)&cfqj@NhVR) zc6d4p(n@&I#kM0e+m6-g1r}GkMf_>q)*_X(Q77@fIaNhw6eyTCqHX6SCs+Pgrpzon z(v@byxKe!UvXXy*XFuh6lM*S}o%xXf=**AYTW5Zx13L429Xdw=o%z?eXw0kA6f=+2 zCP;TxrOR^~vbx3IHqK6k6d~2xBiOR2E?74&tBIhBi^djMmG@h8%$DUyI!7^Ex)kmYcGtr^Xxj=a-RzRY1j%aFU&obs~0IxLW}A}N1MogN?oGBAed4O*W57< z;I^)1)ZUwWVEJMeI`fzu>gdhfgClKW?ooGvhaFv|qa)fA2$<+>rkcB8Wh}f*>i8Lq zbwT`0cm5G!)NUPQ+9?5wjwxqKCJa|n?H&cS1_-HxlIV46l6ui=P9@@*Jllp5icw%Q zSrgUWqqD4`)kteFC*0&b;Rd}NBxvQ|gZ;a3n> z(me9WUm*kebRZjKo!8O&ZrMr&?Gnb`~WIWl`$KBL)7nS@bg zH8>CJTOjI4r$ zf!L6N%K19l0V$3}?KEjmfzwI*%eghq znOTI9x)>#XW>nAWe8Tst1~KxE`Bu|mj2sDk z+LxZ72pX(^Z>mGkDkgUhQyqHNe4AlxC5XnkZ%hykwD>bcctnD+fmtLOS2E33XLE&N z&f_~`XHW^DF|2cOENzHmsk24m)(uPAKMmxb+~NQgQ)>ilV6vuAI_-}p7dd%YKXDH!S|T^ z?TmWhrH|@2=yl}lGU^$#i&qsSLn3n7Q*;{jEO$mdE5?ykf9`J-R0H#YT9+6bV?~UO z70#${U#%bSh(&vyUL-x?fMm2(nnxno1;*xAzHW>BrG#K@e4Msg=gHaFJ+^A;^ zclTkOGf;f_8>d`*^xtA^j1{es4Xu$4t&t6_kqxbp4Xu&QTT9wUszyCSlC39FSc(cU zmE6oC?Dao61wfywF7Vg`~se)NJht@50OTP|j8PG_fGQ5) z`Iu@c>ck*)nbR=H)$VCo^JAjvb`a*w40J|yjK&8h?l?E+M`*~RI>-?^_DATN#5I6< zJgr2h(4$#mTtZ58rG&OZGrP(+$aCCVH}0OEAuCw`1-QGUCP4sMU2-QW09oB}H`DeR z40=YaBNlqY40<^EBYc@}Y|P>6;Go`dg3u}VkI@Qdvb3w}Y756y%o%zkg#84f-9m2^ zn=W)J;H}MHzP#p$y%F#S)avyUj=1pKaUOgF7R|V7PnI-L8=d2~=^{Txnd0_rr)c5Z zncSr3>@#Ys@X+ZCLV**#nDit;Yu$rs&)?>zJ*Q5V$R*O0uoQoixm%AV)N0`YVhrtU21O?5YCvAeCe@`z> zMyrXyShemZ0#WBqE-0k8GE|~R8>8#l>>O&CiLbO7^b-G3Q8jYfI$X}S6O)W?-~m%3 z!iZ#4$cxlev=NNa(F4h-$Rkqvo3=*i+frRWC_-vEYV^;%mnv_)=PJ3>d9#umcOI_f zHldEoC4@p-2@TDaaM40t35TjO)twkynM_r4Iv8Zowa4=3xBwoL{`Ae^31C)&mx(}v>HC3pb%=`z&z4>9g(1;R-J1Vj2##{;x zUtBf?Bl3xa5g31IrX;W@0qb;NNlCoT0yB-NLWsA5DcG8Jzs$_q{ad5Ges3|!ys*6; z-|T{)q)UZ+rNApojW)3AhWOmf;?mp-tT5c7W?`@a%~^ceB0>y-^|E_PmP=x_z|{{{t>)F875Y+={&X6tYD7^ElcP;8qYH__Dv7wvs*V(`M z*yq0XPsb0Pj%3c&*!7$!+J5@uZ~vWsi^Wm{(j#U)ww~u!_K*MS&=-B#XY%atW%9+v zxlD`22{AJ>e+-j(*f<9!AL`JI$t(tpwfu$8|LeQn@h6Xb=qnK_b?Cdre5vO#Up_@p zD6I$>!u)ttgrH$Gqt($2=RNeJ8J=M@1PCa~`q8NDw9&98ht+*Q8qCPLj7GDemW$DR zkHW?<4pjXCJ&L{#3`F`u2Rm7Mfl%RKXW7I~yE;|c&szI*$kxqX%AWSw3DmOv7Tm?M z14UWxjg4laItBn()}3rE>rS?obthZPx|S^Ktl8=i0>RqV=?vhkPFa%G z^fd8dZ^$}}J9}C;6E@5C8N6iCW!EAq!8F8>HR9I)xsH%Yhv1bd-w!AzjUh z+o9&#V~@a4ixH@?uS-J)~vo%VTyEuxT7s)nYwU@_O&X{U3{+JN1$W}TlyHy4)JJZaCpB`H8aTwH? z1B0Bs;>^&RdFcZhZ(q~P9Oj@cF)U?1cqYQ8{^=kh7lne-1mid@B+nDMVb2l8s7hqN zl|LyP$H}9WgLh#ZSFqiRwcEuwj;nlc%5*r%w;jh-R!Lq?%Eje-YQS8J6W~E*0WVu0 zE#4BnR~#={Q23zFa2wj)~~>E&|62NOGFB||vBNe)QD z68UvD9|;S-7!&;2lP&i{&6$Lmy96KP8EJ=`iw4ZoCBmpojE`vZU9qgKguwiZ?4+s3 z`{EX7-dmYjws9t1C*WK7MIspq51Dm6yk2~%zPLNibS_s3!xxGOMM!pfsK3FE>#|sL z%}O+9O+ht-7ROGyIyL7f_q;F@qkd7iIKXS$^vf`}#$q)|mf~YvQDBf+sG1j+=f#0x zjf*BV_Vq-QT9rqWa(-e#%3uSI0uEwc=$S*#k&NhiWbzdplswI@wN}8{rTIX*+NO1w zgLOu~V&5&+)&k7IPUPlb2a`An65ma)XvHoTGq8(v-FG520y*5L^ZvSb>+*M~bW86X z-;X(Mz=);U4EDNTHYwRK-eINR69z`S8?lD-7pW z1&Dp)qF8N16oa?cg4#PQ=^*tNT{UxLWF2t^qIvyh_i4~9v z((x*&YNP$ao7xk%-JCTbb1}P1?Mf&hHXB=`&)RoeBm3SP&hdTOaPF^s5i434&e<2a z;T+#A$Z)PV$jskxuD5#2A+5R?&Q0EqjHmNnDMwsNHhMiaHQjKo#}AR&K^2R*R;Wp* z8P0tkUMpg1XEU7ZvAJFh=iVT|RGNc3po`e`b4@M>8Fd#MA&Z{hPAX*4^P@L06S)x*`q4Ix@l9tb zhYVFlB_yy_$oUN|LR2dhFBMC<1QG?N7|Qf|-IR}QB?CDi=k!j&6mH(vP0eTuhqG5D zz7=>P!zCu_36F2J%-M~Zx4hR{JbB%e`J5wC*cg>e;ikv8k|CMwo(w6c#kV^6`9cc^ zp~{Wk?DFOO9+U3ZP0eImHp_KWW)=+2mM!1%Rzw#$AeG zWdW}o$t?%GD$`tl-4s;UcU7X*&aM`q!$dS(H#M1LcHPuO;;x$__?2BZMer+@-8kU~ zU9hV&KG@Z1AMA?YR~GDw;8*9X+=)25;4!&|%3v!@$Vam9YpBlG2Dc_XzjScqPAU_F zl#cuJzNc!lyQgZy*K<$Rru?2N&Yfjh)f~2SNb@$cBQ7)+)&;_-UD$Le@TTr9V2B;PdSxw`CT zjBPO9a;|%<79Ja`+dUO_iPd$1)``P3b2+X7ri^+G&5h5F;T|hG?&;3oG@H<9csGR*3p(a zJ9C7ryxVykik;3D=(%UA|R~jD;eJgXV0Jv34*5 zzP;QfNR1_jyPCu+#y9B&N+);y6y{J@IeZ0UF|uvnZc^(ppRPizX2?84Y^;d*RsIW2 z+SR#5ln+gi4TOre<6~{|YH_DnZuu}-J|Y&86J12CP(0M?F=N1|!Qg5B5wR|1*tx-z zU;r9OL=IEFn*?4~l5(WEHw18Wrr?$BWkrk*?m}7|M^O^FKehTmp9$3=^Z?u|_B0V8 zCRTAf)v1cHlif}=Jth`w9!J?3k7hgH#EW8o`NuvnV*uo49e zQ1loD3vq#~)Abj-vAn-nZ&_D!gQdJb{rrhTxGonB%H4G31Mh|;RzGCN<8HcATl%G# zSKyE=aL$CRv%rzrkp(xim$HQFQ#lt)N>)N4AeM`HWdX5V%qz>fo34DAESdaR*2TQC ztc!VNS#Kqz>Rrq$d+K6dS=Pn8k{MAEXiGh+xk2P1wcL$d_S6|cS=N~*;kQ!`S=9i! zSwW(jtRQqg?w+jX|27E)if?~OR&_;~CS_;Y%ph!Y0K_iID&tdcF{|7~S#_WxiQU}qcRw0I{ zkjm#2Wkz*6d8*dJyXtm}}X z5)Bir%j|ai)|T6Rlq&OsogU1}wNh|nhNjN)}EJigW1jP+RtYh>_fjW1<%y(n{H-PL1UTbEp%OZG%gKR9u79xRj%`M50-;i^@g`B z4=^=t0_V1&`8N4v&0`W^YOMvDI*$p?37y!-f&wyG#R}{s2-D0Na-1eGfst0-tJCE!y3}}B4g@IjcyxtDmy%J= zc@~4}yz*6JW4NEX)v&t7o`pe{xT~w>_-45s@_Vz~Ht)Q$zIJKu(1M!=;jg{DjNCMG zdznR9XIz)6y-X3}g|B)EIz`|kch%S;SqY53L25e+>p*4cXucLdBELd4umUE_~dWOkb(>=qC zP0NyJcgv7NyIy{5H+zG8JeYOYbw{ zc;!G+S$BY-p3D+S>YF2uRB^G}pN-XPy4da3;b06ig~Mp#jAb~=)H)Bvz5jOdu)3oR zIz!hLH*L|1h^T4xzW#y6;Lz~M=%U5VB}|s<((6B1G@o{=&WV zU<4bN;{CJYY40&!Dc*0!NUGy|t*^X(+U$Ifxrqgy)&3V>l+-`kA`%fDXeVvf%Ez|L zLeBm~uiGwK%%5}r9(~_xz@iwn#kbF(?%qF@U5QlJF%nS3M%N-JorqsDiqJ8bP}Irx zzeeUhA}qU8vP(i&DmdEYvvcSEs^=f0FS--h5INszu>DM(pT=|f%b{`=B?4S zW_!WMR_hPD_bN8vYjLG>!;5Q?J;Wm>2sM-T z@8zD>2fivfXY(J_7p5vwaa#>g8+lnr?MRJ=s7T>Lu$=}RS^i!tK&V>p+*>xBU>o+| zBkQnE&-VRsb)AVW3@?z0XrKgfBWZ98i?0KoMwS~cdX$oF`UOzywvE9(_$i<=53yupbS*C8xPS0g*ONn#-eFle zMa1_i>q7Ek18Z6Kw*6$;aI+;EyIIwm^~PkZ^H0*Ar1rnloHCSg*-H;#wMe(pShfO(`-YC1@%*(ZCr2Dulra>-Nidc#aUV{7GGPkeG z%>?{~w`j#oSZB^FuHfUN^!??2^^28%|U) z!c5vz;NR;F^%d|&Eo`C+FaV`wkXEA=6&}@Fyxrgm$%euq7pxVdlY-8)Q=e5`G%PdK zc0Vw6o!Bf0>Si4-rx)y^JnTl~8TAMNdfTEt1g)_Qstr42 zR_?sUWdk;zT&`IF$m38FNU{$lBtQ$73M2GT6iVuf7RJAl8h}ySXJSyB=%3K_=7~GO z9JQL(47pQAdb#KzTa=A{J%p%pPoeI+;>xDN_il29wQj;wmjemLQwZZL6gYK=(UPBO znmXsk*^R+B*Ka9o)CWd=+y_cqudgK>N=@^k&0@OgRC$Y_5W3vohh>llf!pBtBy4Wb zTFtwRkXUEF!mz~w7##vwjmDYS$=tR$n}lG1z8G~EsOQ0YChl&H?kE1+(5n3ZFRaS* zaIE`kIV0@`KA-tjc?=W-hDp9;=zls=UW)=CUg9v6{K8%6qJ4F01k$s}bkVk;NEUxL=`P zH5_Y?^}y7}9A7eRtG=E}IMyDk>98t4z+z}mR1Y-+JmOH(5ZTKQHCTtgP3Dc>s{Ft_ z!m<9gedoXTR^@zOW5+>|ZieT- zti3^I{#NC^)tetYx}SEwW*ZO347h9;>zU(=kc2osu$bD)`M<}ebVg}6%Asg`{7@W) z<17in>N&H{aIF8Ro`Mm@;l_9k5QFZ{ub;hGmH)!+oz1~{Gvi;kRryIaf^Okh4`2z! z{$2{l`Ux8`oXr6rj`etFIM#8(VdfVnAH~Sr*zfhpLu4iN!<_s0QCwZTR(TNboH<_Wh;_Kvc&%qeT1)X-&$!=tyw=nD4SJnQy0KuKf>{I; zdnBTq?Gol`@mkL$+oSI#dMjajtD#iKqdc_Elkr-;k$N>=>#0)cQ|1N=a488&1(}C4 zm0jbtTF_L!1c{*e%+-<48#8P746;||rt3yY@^Grtu4kUfMqKe)2?Ay!A#!Dr&;c+= z)WKVfn5}Hy0OTr+02?#&kSLFYRp`&n9aiieJ)SdO>yo^8enkzPB3ybFJF^FS~NL~QHn~r?MNYCaG&DB9{GHIE@6yWVO z@mk@yzQ{|u@R3;*gcDF%Nn6CEu+28_H)$!~o{OQpMrKSV<-a5Xv5w*UOa@4MLG25n%4{BBO*y%f+mxrvoW2fQ z^?)r*RlYh~N&9`TpPE3d1O~NFm}PPE;LpRMnVe_=pCEKPkgzqNT7ae*^FBS?E2A?( zYr$QD60@Dsg( zMzG=Y|9*(dLz-vmqw?J*`6Y7;Bqvuys|S1*Dad=B;Mg8|$V*!=cc|yF15g}QNVHy( zgG3W0L^(*bj62yvW$Wv%OTG{)Hi6WxX+d+{uXfbDLuBi`VfPy=iecp3trgqjTb+8J zpB%HK%3oo+eZL7#7@yoy;c~eKt&O2aTyEy)jII~62(Q0D^54xYLUZZNA~ctbh`AI1 zJuc;T5%7;((mB#^@68#6&=c6ejjE5=1SB^lX9fyKdS^rO+aiT9oC&+4#rrW+oU4lR zPPr+4b+&p75Q7htfL zvX_KgBPy~#PRf1~%TbQ$5FySbY87`nrcEO++atoZ-mdQAvJq+Najo5S9|>ujZAHsB z0w)+@C7Us>fbOXe9t#Jd0ty~$J`_XY$Yghk=j|AHvH=H3E-0s6brEII{Jkb*-<^9+ z0BPa&!y-eR7mq20J|FdD=?I|d{&!xU6HEe47GYwK*a|Ino#PeHiAO9<>`bl`$6^Rj zJ>{RfPrUF%V9LyQVlb8~0{#9D`F2iAwS<&vp>F#zh7|ZJ->Fzqpgu4O6vgN3(<~X51vGyiJW#y?06l+gIR95qX$jcUiVy%!16l+-*DAuwr zP;8&gaOje*@iylPxqSF+(pI!rd+MA#WSK0N3Zn!zlHbBLXi;Q$sr&z8B!Ik%EzYWa zo3#I-s74o#+tYecd7p)nwWs}dY_{xx#nZKH!!8joJ9M~Sn!kqYrDa1fSlX}KC-$_@ zd{r##fM(eNi@9rA7s?iL3yu@yVPiCM*VV#>U_h zD>lYLSajJ>0BFbEzS-up+HFQ7?IdbUQv9d!;dnxh%zmEo`9~ zHv770RL4jH&GKwb9`0?L!-NY34ucrI z#kyB%a89Lbwh{ZKuER9m0%B4*b`g!WHs$je$<+>%Utc>EXBR3x7-#F?=sgY3>D4r^ zg?xo}@o~AJxYZ)W0F_Q4**2eJ^SW?suAY*I?3AoNBNEVIlA-8aO-$+pxrRro;r4!~ zeClw7?q5wiN@?qT5S&%0CZ+9-vB-$t3#9hW(^34#~8 zi>B5&%jy+rcK@U)4js8I-x=?YX63`LO(y1XSXWw>L-bys7e>wI~kQ3C^5<9v5~SlEU6?(`NG7U`tt4>`zv18)946D;k9 z|6!)B?M zx2fz!&DwTDkAy77yaV}PpCe?BGGV7GV~ebx>B909bJHo?>a&Cbz~=PhSP4`B=rT>Y5Z{ROo26iRf(9_1Ey7^Yw zhtj`TN;8?ZlQb|or?*6x#G00m&4iZVl2rfbFggOWua(n4?N#l88%YOu&@*{EQcJs# zXB_!lNoy&G0e~;+iHW6^MrF|4UvA%QW^`zvb@0%c4dY&d&9J_`o`-z9lZ3g&eL!=I z?XYe)(g50roEXVANfQg9CNz^9DBmBl=%ZvC!(oJbTHae{*P^5rA)^8&SfWxP|cUq_6*k>Zc=*=r_-iOj)}~rtJAq31)1v9*i^dW}wh1UkLu#3LjBn zgiJ+g!r{F)q}aTdUXo7A152Jj)g(?5|p@kGIS>h1ybjRWj*T1Npsn z8aFk$W3B-hlV$eNtAPtSwo#dvIWuOurX0H{iurOU3r*m-vqDcZrb;&H*lf_4v#Cq# zK|L7PSo(SIXB~(wsOh&s)`1ht(^C{?S8|9wK|3f+uD2^2tt7OVml!bJ_yN1ohc2M@)xbUyEzODegqnL1aZw$DlAIYuvom{PC$K+FLJLk5wi$w~Um& z^P28&R?aT6le(H5(;%u#j%jF+V?{%z+iSFnh$FIuTd;Cdjt0mef85+CsD(ls+`7-JW_n`qvsPXkA^^BF zh}ygx4P@&ofkfG!S}=*P?3V3&%80D08*M{4n4nE&)~ zpZ^IdHi4Jszt#5yU;uP8$19udezh(8UqCu0``^-}4~9v%`!;%;^udlvAF50`$S3s} z?3B#&dYSY-P4YlWJR|Ru<7anmnB7x7%x)T1_rE@&tNY)cT;20r-5>3~y4T!@XxD6) zw5?PY_qiUXKVRIht0jHduv{q0619uv4I@}fwpITcxesh=?o^I5XMQi7`jMmI@$!vL zU5E_F-qdXx-oMHAWOM7+7D7XupwLXvI;uK|_7E zrE33)9Oc(`)zw4zGXlAt9q5thfc&|{7Yu0UOmy@q4Rc@FKUhbK5|O4cS(Q6(Ktwex zJH)9aSGa#^B>lxCoPpozFE-gQ2M+pP&X(KD67%wwUG0SP8enR)mx~|jrx_akV@;|3 z@S>_l`;biu<$TOW=hO^zg7-hvh&|17FR0~gFXvDupetAd!X(pDYY#%as?sD>$Z@qb zq#d4^E0PBd(g(R2*JZXCVSnd2LVIU`zh0$m%C945$0mCKH>RkHyn-9km~hy|g4+2H z=GvuA`o^Sk<1WY6oif-dSW=C@`Ri`<BRi670O+2~AUAWl4K7khs z{jlPBj{z*8^#+83JLl&etJ|*>);mo$_3T9JO1m7q!dyowjuI55-{BPnz8nQ2eYm_j zI4)5hXo2M{XI6z=%0Zp}_VCt7<4XN^j9|Z;#5hdiKWfK>-N+}=xX}LQFB%$iQq*h! z7kQLwy3AB)#6{w9c0vLnIp8g@l4A}oO-J78CJioEbs*Qf)@+HC<50z}CE!~1@Wc`} z+N>$`C;%IRH3An&?9|d=Dra_&ZRkt|A2mG8muB)gh(M}*ma_KgAh1yTbkI62(aDUH zt`0u&Q_ehCKmY;WPU3UVx}Q3cH^bede&sHB?ls*G5nUfb-r5ACD}YNFYA$k%bYdKC|J{>WVpSKC+*=3dIG=Cui>YO>Wfnj|{r+;#ll#kmy#1v*=}+!26L5hA(}SAEc#i7+@+9$j?1|6*CyCF0 zJmOQG^e2f=FU044NnGzPy9^v{cV3o|O^e`*xeV5w_hU}~_P6;bT6%C@2<+`>3ddy< zLdOc)xy|GnyoYnssmx9ToaLhSME~>RLij+KC1XCNdmzUF78!$NfKI|tZm*5-LmgJD z1nHEp-CE_;C7rB(Y>b}b&YRY{m`%jsg)hAdx%Q>xc@u-MLvR<-#PB{*Fy9z2*N zfN@}=OnVG4g+}O-M715w;6@;}J8#B;$xf=i8BVIEI_`v$@7NOqC{r%E5j=i?@{N3> z0ClXEB!zsd8%`D2<#IFN7>3@BA(o{=STc1nh>(jLPkrGN6P$XTLvqcoaN#tG5rBeU zaEK&_n8i#Y>gK1SK$G?Dw|&2YT%v-qYHeQi>S6I&UOslV8i5{oXBO*;Y+X> z-En!lY|P5y%i-6@aeN!-#Mv`-w>^!$NXMLq4^rPG{p`QWl?#V+qw zY)7fs@O$MERs9P@UcGD4a1DQlxW5m_2YEfj>+!}obMj;V^z(1{>-T;ox|2E@yY%eu z|Gz)SE#$A?`-SMvNgg(L$${h~Kdwfgj07){cX;LNFWJ{M`}&I~{`6n|{{G`1^u8n# z;UyAn+Xr9#^#AkgZ#n&!@B3=>y*REUys68=A8qJ(l*cK+%x0H&{yp9I-e)Q?LK{O{XY4re|g83KBJcDcAxd;Z$JAhzy6{39Dl#{ z2Ekps-^2yB2{E6bc|82(r`DFS9Q(q?c%blFEx_njV5hjbeIw>6M&uFXwtp)(wmV$* zPKCTWTI@8Hl55(CB-A&@un~oZyhWbcCRpaC?#yJgYTboSv}vFH9@ru}#&Fj7?P|lM`;3lTT_y zg7F491ZqaYHA|=p7PXU`H6&TF=mH5jy=f?_VL~Z>o;I3dxEdBzs)35y4J+mPDz9It z@df?Ce!}+$S6=l8r_sDW|F7$hhc8xL>RA`0rT>pI^G?6MM0$;8x};ODUV#aHreoB% zyM-RaYQBYj21PC8dvR$76-O6gcCs5jgxu)X=qV(NwnkD2QwiKrLpd|^8nLpH9NM#S zfaIe1BMrB34^U6v7bcerx3IxV6#B3sF+7Rswuhuq@fJabsSSLyk-ekN#)gW^gt%Cd zfb)fof+;UV=3%FH(is;m{92d8;`eHogL&OSBSY4RFq8ZhtzlXo%_RCC@@<;;IjC5c z_tPp)I+a#gW4V%C!?mF3qv>K+INw&xBD%~Q%>%t1ngZY}Q`>wBB${|QpHtplqf($5 zF*GUFX)^)E0x^t8dF_{`#@oY_fX*^+Nl6UK_|R9li!XP@xUvh12<_1$eNZ8UTJch^(KEVTc(|_cAA8IEY^ft&M=V&UR-SWs z9hM_2hX}g@OuI>?;f^|1n9ZVGS+UJVMdL1?#oVmH-U-4H^|#R;u^boIkzcjTtY|c8 z!Y}*5BxqINt^k8xYr);mM{JUoLWxUqf7D*sT z$fD+77!?6|>y`Vr@-idos4L@^5CG6xVfPR z9CNBh^LP9z?u!`W$-nV)A)B;Mg{*Txv;xk*(XxH!Qfb-#_TPrG1MRPctaCuL0)yr^ zY1tujVYKXUTbEW@b_7MH&yJe2WWp{oq)pyB_!VB_KdTO^oMn@?{6j74Iw*6|A;HEz z(LmVsKy~|Uzc~Fku|JV3lUPH(0^Ou#S$9#EW&4T(?shIQT%m=;hEKe+jZw|YS+c zyGgVkn@i0&J6yMohZX}Clv7pzn%vfcwGYhcRG>VdvEKyi+Nk`2nw&dO(=dxoSSNgQ zhNwf@B63e2tyAxBIa|M-H9`BgC#E=WwHJ8@!!C4KC$EL~QtQn1HFvlKT0?Z0I&baj zZ#i4917WK&F+ju)h*UYLTYrbL^XWBhEHljLGjoc00OgPaO7Obu1{C(qHvq2INx*K{T%LG7e&?gWP-}(GhQ}3nXm)qyBshpwY}(OvZFUKoQ?_2up$3 z-B)6TiNnQ8hq$2;=Trz@oR?leAc&>h;AW1e%MCZoWPCmg=A#Zb24-0>oHHK_Xc&Vl zC_s)L8=O$bgcHkk6wU|j37I!9>MvqA^>@Z_`ZoH+Py;Be(s?g-3_a0d#E`k87Ic~O zt&w6-PfjbtyNq2EKPHgLJE8!HbFS<{x+-)TNw3%kya2^M;QqJ6c3f(7G}0YU!P-FQ z2wdl8A zcv`==3Qqpu#7guBr%PRqgVQFL;F)QpjOAdoewN*A4RjxfRKVxmL0vdqk>ueAp&I;T-`4d|a>? zV7w5dLSQ91a4;RuVY&gB2H7FsZw}~1#hdd&mr|&7L>tCN%!H@|&IdtTR4&}?Zy(6L zN&YZ4X=UK0(nJ^Ic5v45dW3#o{p8!;{TKHifA80#JCUgMJBixqU-{(^z2~zZLgpud zU;>bwfKDR2ul&vLhL|)an?tn6$YQRFP;q)Mg_ac)6!qmEA$L{Vn>qRF&N`UH8)kL6!*jtZ=9)R+Xd3J{e+NFouzlJ)Tm*{kJjiC( zOe?moTe0y{vEfHzX2IiDo#3odMg zKq`G!q-CRw^kt4_;@bmlNcGsPH6eD&K^x3{#_13NpHl%MjIhrnp`a=yY&<2$j$`pm ziary0^fJosBV3b@P>w(JpTQ$J;6HrOEChrbV%=%y#Gu(oMHuKPM`5QK)Cv#;I4!eT zNizu0{Ge|4g(_-^F$jPVpVEBQ{7prW9R6Cx#9iKGHD~&W2{!WZCJCan5g9Q`R8!kd z%@Y!$j`3*9Nl)abw25dcH# zAXiWO{ArAwKcQLk+klAN(^~z_Fb1HSkC0{4L$8oKH>!cLR-({C`i`>2mZyV0kIf)> z+~uwoUQ+OK?B#i}BhyC0?=`J{t4VWQLu=c7SaNg&38FOWWa*|Lu$k9u`pr>{uBjG6 z+uE{OFPFAeG=wyVrL@L7e5!hf87wEK%&nFD)=u>$x?az_HdL2!K3z^1Cw!lMIfGYS zma@>>EoJ-y%(X34T%@Ssg-;gZS}qN%0r8^7NaWO*x;E_M7?KUM_R>*mk-Q3Mr}6!Q zsJ90w9Jid3)0=!tjj83~zt&Qi&(|=qq9eGC|9kuch_U+K2|-kcHbknDt#yzZSt8aS z^pkV;2jQ0=%>*t9GG_>1-~WjoJ_oJ*aP+wxFT&@&G;X_tAJ9Z{4G5QikKgZ{eP=Bh z00fl)#vZ;?>5{*vHZQlCxU0N)M}f}yb4qG$J0d-1^)E>nMjRvBjEWjnU~7bLqIzWB z1on-Xf)XKz>_P7!ouYhk^V3H5j(oB9ol6N;pf!+?)=lN7JXcQIzl3*vuWb;%HnD&4 zYX?Yc{6n|v|Ea8dKYUw1D#JSO>(r(9b^b|>So3A|EPn0EQvEUC{6!q}jEB~~q7^&R z+x%;h0d=?u)gh7NsK$WfC<$Ab7LYC%<3dGk527Rk?SWge1`rfMGK__83t_TdK*;o?uHa2fzu^aN?5C{b*Y!_*WU zEUhZ=<0#Z~2zHpznL+A3=q2HcizUCXTspWhiWUtId;12i=+xn{y>1te5#J9&J&9Lo zno;xvNZNTmy4rRt;@pm`;lyV@cE2uNbbo7X53j#O8!GdGa&OEa`?~;t9=Q#>3^r?aNijpe3_i@pYJL$-SrBy45{l%Na21Z`Y&vA! zz((_#Lk~EJ_Imm>YeHP%MR{QaW^)x@oEJudH5*P4@ppu~l~kYRXNk>^Fpe3XL>juJ z-FQ_-9V_gGn`bwPT@VaaRk{Z8DWTc~4u>QyP4yaA-vxRBKLsqCHdePUW@~+V9!qVB zWEMIwhCZe0LdMSx*2PxFy|jbvqtvn_U8!0WT)$a-b(s~jTh=LescZQ{eS>cn2)0Du zEHTo6cp|>ZR`xpepq@Qd9NM7Hu7bwX)bdmL{gD06o}NA}Tc`KPKJp=0Mb>F55{~Z4 z#$7FF8CA=;hbfGl0d>7%dkzB+?EzXwchT4GO9gFVL`7OEzi|$euoe{jQtN6Kjg$A*$4ggCX zqCC$b%F1+wpt8#F4Lr}kUze6|RZ-FS)#+u1Akg+_rq8qN z9Mocvjz1sb`dm}EP_NsTUO>*_$P2B77ldk`;!&^*;TfW;7ozQk84lH;F!$B8tr}Y+ z5DX1sX~){CpxSnaYL4)w+d}K-Gtb)cG|NxfK1_q(Y4BmIZQnoHdS>>W>2@|J*x%5D z%kv(*XnsAoqSAxQyZ2y(9=tg8;6>Jh7pGUy1DMFrgB^JfUQ+a6rJH^?)1JPR_bi)i zM`{CNj=Kf$T{#}@2z{DmPDbhAOF;JTVtT1lBw=y3a+t-+?G?;|j)QUY^c{8<)EUfp zs&u*R5eM6U#i{qH?dGe*<_@<1Qoo*O**_wCQU2l&^~=4uco&;M_+TSH++*nwFLt#+ z;?nQflBhxmc)C$%WTU|8de`Vlo{qbxC&)g{Wgj8?beClVN!Pi`AQI^&_Zb920rylH z^elab%d%&rx*nXT5Tg|7zOt9@N}uVzLUwSz%Wc?|E^!61Uo$Hup>FyV_m&Ve$olmy zcHx-dDA(MTE^}`=TZ}0eV=EBvYGGS|sJ6`o^B8Chq@$*N7&1;b+;YMMz#OL#%Nj9% znHhd%Vto~QuW`%GtVm{~fxU+&g;~>NvAnsU%y0{N`3Z%`f-{9BAEO+}s@8dwOej|4 z^q*wjG1LT@mq_RW#S|eSBJmM)FLhkJhrMDLq_+@{-;_z*v&;Y@MxoLKz|M z8>J_u(?C7L0Fh`4?zZt!odzz-PXl-1z$~MUy-Kn8nH&Oy7r6sLGkvO!YLr7kI1h*$ zb(S%URH>0o*f>vk9QIER8Tz*;hjumpDPID#z|GJjZoWt031s<(ErRA7;}PW$!~18< zyx|3=S{`c(9H7fW63#eH@v6=AB997B1?E_G`M41_gJ;W=lTV2q#m;C-2C-TV0E4yw zs4)NjC^aka_Iu#n?SZ%NF#z5^54?R9;L*Zk13bFi8}NuSbV+ir21a0s14{XQj|RBT zGhDYDJ7{6$K*38v6~|~eROHcyoM&|MUAu7Fh->tmWE1=ZQ?xO<+^rhAW$dag+VNyt zYh5UL8GDkIygF~aoo3TcwkqFvb)P5IKBJxz?M!$LY zx{Md)%lOEcTgDG5Wf@P6sdBfBxo(WJeZ$W7T2ET5O=9$u=x|{c%Tr@+$xb1`e6t9k znJ!b?=&sfhXY9HJrIK5eOWJ$bfwW|4ON5yQ>42j7Luy7@#+c1P9*jOOWsF2wEHBFY zYS^=0=D~p`^tT_{yM)Mzg7m?fBWU;3vMYrg4=34GO7BXt7b$%p&WyrO#Muk<`eXvX8Q>{;nrL^d#r{Hnw8lSJ7I z5v(xg=cjA4v>j(zZJYIJk$8aZNc10C37{ZxQ!Al0n?Z@3whRKCksDjgil@$-xo|`c zQX`_8Bl>XjCFKg3MmX*fDDsR`xzbPN%F0x(?0qU%&M=kFs!pZ#GsU(z0P9L*>HHoC zO6kUEO?J5hgGSi0jj7eSF%71zeCTFr2u7zPS<8#64vFlvE=lh5^!4CS39E(CbAaNY zr=r3F?~Td!D62fAZ*Cn(%<_lEdclWtxnLZFjA5Wy_n=tugTWMneiBtmm}>GitS zXn37+z!*-SpT_HxgG1ux$c`t6FyoTgj_eV(Ma!-Sown4D3wvFhm=ivWPW_ZtKZPG2 zW4|Rm>R0zsR`(b34H?WIVHvV~(`T#F!Ssa^3)(OEFjadTtpk(}u8ydJgW?l5kr0V* zoCFH6j)Y<-D27Z-*;8cEw^Fk-87#6aU3vww_qA!(-b7nKL#qh<&?@4WRuPF-QN}k} zreEdthu|vR9Cqb0-L2%oq`b^pXv(G=Dm7X|4@+=uAS4qhO z7XVvG$)O8+Atk6|0x3DH42zlJ3yu$H`>|(5RG@7 zVcVKNjK!XjLa4%_3%1mZ4DP5f8l&=X(PWnwkM>l4kCowenGxd4C_7r+h%PG=VlJHo zIYzR}N;-qfoX!9m5Oo4=$E;Z755olk#-O}Mu0qGXZcN3F3i^n|F&_Gg0zG@CJIEE} zU@*NbkOsq_%t3B@l6?mta7ec?+GK)AJxNo-j%MqTQy?w`ewVYelWfag!nm22D&#M> zFHl{#oGb$xf0bywn>tbM{e;vKX@sEH_PM;#?g`5~lD-!=2o2*=PqEK>ihUM}{iU2@ zLtaKi#YD0>o{JV=u0Z*N>GKs@b1;30Bgyu`5ym%qM21@%xrXyw=yMpeSfF8+AItz^X9tU*p4j)5CN4KZgXYA7#xKI5j zT*Nn>7YCineE8ilqMc}Cv@d2Hq?2*xdNF^-+~_VKGr9}KIV9`dKyi9t%pX4z+=Vee zP=-wblUSg>2bdT*dxL=}NMN;%>H>qVRJR8hJbqNS3k(i+hk+?^@0}kEY9$yL{RReI zS=k<7@c6N^U0`r1ZmrElTIpIZ$TF$PeR1~ed7uG|UycT1TY*4H@w_(3nV!8MIg{U5 z?#|Dv_=P+s5+-=zihyaY7cg<&D+`zm4y_7ddcLPJcg3ye2gHsk{~SZv1O73FWe3Twhsw!F0&WUITwmri zd&ql|>t=XcWn`%z*@^P{5Cp>Cf?hwi{66VaoVE4-!C16aLHM&5 zstQDLy~!>Mx^iemY8Z8&QfTWzy!qxsTMw>q?Crr7W%icZdSy@OKzIJ6%bGe>hFbxv zSTHy_Ke|$;t*1N;PI?%em?sQQco>|hz<}C%g#jJt83t#%!@y|k>G{E+Ok2OD_SkdX zVPM4d?EGL*FYPbiQhV$~QsTjiwP2=EkIoMUeI*!tOYO0HlhzB0J@y5DkL?pVm`pMV zV$US8KYhV0d+ZCs9=q+EdyjpA-($C>FDUP^en)zN!J~QzX!{-MNZe|Nz4jsY+84XM z)=T1jA#SjAx^~n84^!wn!vPJS)q@hVO5(;{;s<>G1o?fUscfdFD6`-4?)-gVt$VI> z`9aIO&3m7JE*af^pVx+c%5T`($WIhI_HqB1ef*gDo%OIm_p+fIs9sb#^gL8vaM~m* z$DOIf9d{l`+*W-sK_3}hyfg$Z%8 z+?RO7*`Kst60qy;WHP(JVV7r)e4%@j){8=+0~ES2TzKY0;LombQak)dhmw{vH2@{= z;9GXG?bs`&e0(sJ?z3LmEMb6hvP(#-dH*w=h4X$^<&9HsCjUNnMWR+5ru zt5^xy{MR%}vsYF6`chxxU9}R$o`yhsYYu@P^$2t($zH0loJ_LsR(d?iUS^elyCTq4 zHoBKpK=(2ax)Vw3WnnTNVKOdtGxyz<1$sJZT@?zQqR?{#0)4mD{L)IzSNodJC9SK& zM`!tHyCKj%EFbKnDgsT`THhTCPt+!}E4m@j_xPIk*IM5b3g1oP@A3$=#UYSkMG1kd zxhpFJy0_MH)IH^^Zalq&+|k6`6=2x=%1gU-2?vlarN0hKc=xb8>Vz5h$NAy<#~gnw`cS- zF;(lZF3F7;?3DFjS!WRp8A=L9CMlQec36IN<(6{G5034U5!X1Ec}wtKEI-OpisD#~ z%;T{+m(%6y!8}-gtetN8aik2m9B8rltB2+Xv?MLeWv1_EUnj(#Fd4>7pfb(}79=n) zh!VuRtVdFneV>;5!J+ITiPTH$y_`3keOiS+UE^{0bam61wr!m9+r}f_nCTeCO;+9f zw~4OEJP%f*jJSoO;<3;acW@lSzlnM`@tUFLqu%b}OfCEVd4K{<<19yjT+DXmf)@1? zhN3~tc4bw}=Gl`gyI|T6cuYH9YyCjjuO4Q%`A=?htCG@_wbqrP&k55;1$TeY z*Lbeh`oU26EQNo_c0ks|5BoK7jrJJxa12KP$>Y|}wNftjttHvT*3!o4Zf>6OLwtq} z5r%26E(CIX=ykqB`|7RhLWl0EPi8-2oBaXo(;S{ucHevIt!IS7`zidNt?+%cWQD6% zPCx2fI9PA}X!z(}KH6y?JM%FkP!_gL-vQ@AW*`3(ogqin2=6C6a-68Qej;GgBVf~i4NLkb{gS>u zU(zN`V-I9WPp$NvtDWkd6o7}44$QUI$NgHrot3;NyTQ7_X}Ujqx#r+#U$)Dyag@t! zjyyY4tb11+?Qmadv!GPa#u)qeP+#^6eRsGoyHV+Vec4YbJe9aH^wSFpmbcm1syQ}%eO3hP!tyhLZ4^n84$9b4R6Rx|Z zaOeWnS>gk&*I?&_ZfUHA59y9^EcvR5lT)0h3M(vyAJ&nZ3Lm)$C~e7H|m@8ij_ zzU(!RJ$hm}zJR066V3(UcF{!;dZWSP*2+}h>ZkfdU+dN|i;pmiuXR)XbCn4?-PgJ? z6gowr+X52(oYnlZm70In*L<$8^|RrlvwSq+?PgwGsd}=%^>d-rME_)VJJ=GKqzXmt zf<{obmsgtJ<(qz}j8HlCvmJfPQ_=_evpaaOF*?|vz0NZC^kE&H9>E#BGNd;z*m#E`5ClxV9OYh$P)?^$e^8k~1r=RlI`44=#L;bCn zhhq0p?B{*4JF4~G5$b)gzja4m?_oapA0;F)KFiUXgE&GP?nuaPN=^hiGJZQz+JX;y z0ll_nQwDo0D<{NvcC!apcEL76N!qBf0o&_(hN*eK=Z-<7WBB9~IaZ-Dg;#%yH;!da z_E*_$=*a8B$WHWUzhF4`NPqTv%N*~|ev#ls6tR!b(0~WCv!tnV$ajT`PWQL&%0YAr z5dD(-{?U%c{S2Jz$3tDtdq~>OQu+<9^j4nL^mLCijVhtfe>^y71Ov_S5jfgH75F)(YO_4Lr+1GM{Q z*WU*Qs-X3wJ2c>3RV5^+^ewdw%rAil$547HzmwT9U_b|;Y?RxebwydMfO+g1Y z!3?aZAlGlYhBroM3+&ox6ToboDzM9!8&A#*w0<*h_%sdwmcy?J47lCIx%xeRJv!i> z#KEs~ls@1}-$e)w4-gD0=v5Wt-WI^Ix6yiA-r8hiGW%`!{r>K)-Q8%pSOc`SkJ7)> z1)J{;bsuQ7?#=7IhthA?o3LHK#m!Trg4WLEaXh)NK`86#=I)KMx%bO(lq;k zrDNF#Ni~ljvNXyL`#^lzhh3-nSg~pxoAD9NoVv|e`KsA^M_g#6c`%ZF)P2QsyoVli zBYjXKwNFcv&cn)3na+=3ot!-w#wvlugNi~#`VrEIHs7JpfNI}t{y#c920VtOmnnB% zaUYH8Ly7uI0->>a8u>A!0fY2AsEHh67;=2jD6JoWz_+FZK7;-$X2Xg)@O|8Y@8bgB zhdb5`4@=-nv>HAv@O``tzK?tGeYh+5KKR6g4`k~Cz7GfR{T|5W5RHN#a2r!V#}CBL zKj7mzXoUv?<9H_+*Hy7_%7Jl8V5F7`>EU4~7+Dx2*;E;fQyz>T=nBSf8Z7)k0ORl5 z?({%FH^FFg>w|7fT4Wx)e~<(E0f%t!_gsJnzkEyrc(;|ni$IaPkf0?cEd7K7?G5bzQ02674cLt;8!g+BQKK(T}LA%4r91CS8eB>e+^%8T!V zyq4?8@AF$uBSJ?x^8Fsa*N|$J%Wvdk{FdX0h$d`W`XN#*qtnBrb^kkps5(y&wiNaY zoPmEgW-Gzvk115xg{gd<@#7QaABTz`Y&L9aY&OiZbN*n*+Zf^SZRzDqw|;NKZ&~Rk z0s(19+>nh}Ixa_MhI%kyU)|koq_TtDx??Zj8D2Wd@D3xfs$~qsv;)bzxkjxynSD}_ zr#2tDC^9E#8d+ltA`+T&*nY|}(?ke{6yg4TNQ^I1= zf1P6YdNVT#m?wN*W@Bf(*_h19&MkMRGzF%SrzxKvzMy4sN6OwJh1-xE_QVYeNB*au z+;@@6WmCU3$w#;XO_Tdax(bbylk~(2hY!@h7b^*&2v5&o#2~EU={am0gh<5q_#qN5 z_W41Q=GN$1l21|5)uivC0U_ll!KTYe-@$JoB_Zg9l#E%>IHDFkDf6RpIl8|p$|mOu!Cn06by|C84#O1*zi%{VDJyavL9h`DS5QvV{-Xpgph^OM;hMq zm(mYWTByL|hiS{ipAGJ~;`bqLveMZJ95q!iz;`;^>Zg(`Qj$sYEsS0Y6}QuoHWp2mSK{v^C|QALRKF<%wJKh*CT=k1NHu?U>T+ zlH{n;&0C|p!S6IB5`|Lyl<)Ds_bXk$CAmu}es6n~(*8c$KdEn-#Qn5$+VUVZ?Vs__ z_wxLle|`p-JnEmHTSgods|VXp^7r{VPf+Gw|NJ=55BulG2v>8+KR?3rhy3#?%0K9z zALjWn|NIc;kND>YdH#rhet_~1`{(<4e#$@JNBI-VfA5y$9wOJI+py74`dw(DlztDF z4Jo~1OERhSySZ9S>GmzPbBjm`M1NN4bGIa?m0m^NN-w2er7xooO1IFy(wEX#rI*l8 zrNfL#>2ny5Qk)JCE8W1jmF@sWN-qRHN?*i0DMehjUny>hcPm}PJSlxP^Q82$t*bEsxEZY`UxrmM^mPQvN4dA9{a1>qF^}gYQa- zp>$B`e_?$n{W0)T=}y*%(jR4gDE-e{YI~IuJZVztk1$?^G`NoOD*b-uN9o0vBxjVW zf7qf;sjTo9>U%`_8yJVy+s(|6Qhpy*`d?WudjAvr_TR{6`Gnq4mu#7jD?d*Emyo`O z6QELl-=}mB^QQEb%$w5x&b%r8DdtV-tC%;XyO}qo|BcN_=}%vhJgRzc*^-=9`U?85 z^k)b`p!B9ol9NhBNa@d%zM(&|vp%dmwEj`0uLchO_gSU4vsau}=I20Er9`W)olxdy z!BnNUfvHMw1QJSL3#KZ)6--r1i?aCJyHt5*RokiZ|AUGSDsu1>hn4=4s#oSNRjC4sLbog99HH{RAa~Zy<`sR z$=k{7m8s#ql$liKcc}SM^#i%KV0^QRY{9azYvWvCaq=weO!*hAsY4r9>~75TWJwq*6_j zQaF(0E@fa>?^Y@fV!tv3nmC~JEz~_Je1bEr?NtWO<6gaCy6=KB3P$>qa7@m4e|@r# zFdFxGJ;8p;+)MsKg7Z#9?f2>bUE;+EbQrhq8QOLKB;1d`1ma8YpW-c1+&()=G2Y`Q zrvHg0^0nLD6P%`avJcm(f7TQ61@Jcg9L?zyG0`5DGXXbF9aBnlv!hD6&5X>-dXaI9aQ7Hh3WHi7>A{q+6kA?L~9Xa zX`4=}I_r)I_ea|U=?LR}SnT|WpX6f?*d*)uF&_qiSwe0nK76E|l!7M(%zeLMuAt%S zkc^XF9b4`|GT46m`#j>%-GDgb$pb4i`GPXtdw@|N*Qm)KthHEDH4v@qgKS8)&VDXFHT%wsxucfBFLHwZJPsFv9JXVK2pzG^ zp7apzS_Rh7bXJ+H8f;)J(4AN6DO1xgA)V}_eo7nBL;^EyuHF`?Hcnj;QGEJ-eUkSzYKo+Mi`Y(CS`uhwnqX zp1P#HN1Mz&%Uj(CT-I0`!fpT5iTY`D1q4$)kq$t^F`ImBs8Y#VJk`9gJJYVxt+dsO zQ!>50QoojZz0@6pZ1hR(SIGSdRFZF(dQVc+8@%f_?2Ow_~ws^`tAbP`{nbT zz7+r2qpoIu=01~<70YJ5{3%`55|qB~B%SuA$t2b0eXA)(DP~6}Kef)OtV6@6qtgn( zbw;m0;OR%NwH(388~oz0HX;9-A1a<>`B;T|&N;@033oRaHCmVBtQSt{`Xra*?XGpY z6DTEvUG8g58A`{}AJElYTcIQE@Mh(^!ndef`4(Dt5d3uz|Jefm`h(!F2mkX0e5ahJ zre_2AcNOqA9wh&b;NMxm-*gcCP2m4a0e|yB@Hd11xdQ%{gWzug|C0s$tp~y13jXE- z{>Ao5@Z~*xyXPX#mH+e#?Np&E(q#a7OQa2JCJo-!WiUGx@Fo_cfF68Nm6y zf1~_x(e1K??akzV190&@V8GU9@}L1*n#pbh@Ety6z@}#MumKyJ$@dM|&`kCifRFEX z1J*T@9~i*;{0|LS(@cJ3z+^MoYryJe@?!%CGx&)CE1St92CQf%`wUp#Onz#>vS#u# z1C}7X}bz@k;~tHIf4c>}@2!GGI?5xx)a?EdI3tyBf)F z3?NMAF#~oslE)3$(MW!40Fg6K7_hC8OmOQDg40N@GJsf`*Bij$$5$J$sgW!-U}Gb> z#sH#%t~FqNBYA@X>l(>20|+;|&VV(I;bYj%?X+v}{i>!)q?dB-5llA1i2CS|p|7^gj zdUA^aE9=P`16I@%Ge(xz2`49)W%cCcdM>Re?=ysndh&h)9;>myub&Rol3Oihe=S*S z071RP2Jq9~TJix)*;7kCXaI4(gc0!5u3FO7^FGC^^Ca;1Hp3wD_qj?S^7mDG5+wf$ zJ-619b%sF1`|Sp7t|=6OpElK!J1m7D_zxMdp_Z&SV0|t5umQxyf5d>bd}Y8Is5F33 z`HvZ}x|V$0fK|2R69%lLk_N1(C3hOIyq0{@00QdoGGJ*f*<`>(EqSleLyY~WEQKKZ z&shpl_Mf(tebwYM2JEdSn+@1gP5#9Ic4mCmfL(lLz$B&QBD5U0JgGx z!GLYmWUB#NtI59^u%()O(SXg>7G3+|7SZ(0hw-o9nP-iX*F{lpf*Z(GXlD7nvoT~TtQ{lu=p z?^w#tDEY1dJECNz{lrGX?^z1RrSCFeYn0q?0DC_lFko|}I1%c;DM}u+6wXxNZNP>o zdB}kEQSz_>>mtSfLC@MK`M#yBiIP1AOh(D|_S5Pp`GKXZijp51urf-P+fOT^bt|k{?^jM5M?kQrJZQ6H7S|CXX1fKU7o=Ke4a=`FidR-O2TP!epOe z><&4jkRNu1GRf&zVe(V^ft~lPt1(`MMBek<9wskUrMHF2&kTX%1|BtFOPK68U~`!K z+<;AC@(TkthRH7t;DCq&2CNU0Um372Ox|Rqtqqf3TgsX+`HcaSVe;21l1cjC>A5OQ z{#MVGAxAg!ToJN2h3E1xdCWdo7IH!q2}{G|ay=))dBT8w zLGpepes7RWL_*#ZBv%=*TQOux*%c(Ox0L$=0;rU-laO3X*%2g54cHze*BG!ZNUk+t zYmmIbfGq^R8n8J?t}|d$ki5}=jY0Az12zQ7as$=}$(s#W7Z5w5vepL4TP$TwkX&!T zWRR>dV0DnZ)qqt&@>(^@${=~0C9DXNw;QlLNLCuKEJ)sAz|tUjrvVc|a)SY{NmkkO z%H&3SUXk2n&&!f`+4It5wLM>vyxX2HPj0s7CCPhua>pT;d+=DCoE8UX1ixspLkwDe z&bqmt`&$ZTrXu@V$)D=EwJ%k(QDyhn9*JFmM!KzorkaT z98H1C|KWMC2_RRD`iZCeK-a)=JgA#7<~KX%KQ`k^e9{X?!}ug7^zFEkIxv})wwdIX zGpmr9j_x+R_@wq*WsY~+-1%5WWbqsyGe?)p8LcJtG46`M`F=SkQns(SoS$1S8l?{L zC_Xt|^W(jy6l!N_SW-Od^Z!%%JWGVI?#_~zmfP`d&*EIR(yzLiwJG*cej5iXK$~*7 z#iCG-ThUcg-1g;ExXGcM6NIt|&!O!xo>EnZTG2T2S#!VipW;yMjJCfkCa9&@*nI{+ z$}Kw-2O^FM@CJc!!Il>AyA}cjsaHf`AeddQ;IN-sr$v>{Qx=WA7^k9LYcj7=Nw_ZT zjLOTn=Yq#PlFwK|s`$ul8ZB;YPoAS0_my0L!Q+Y~*5k4y)Z@~mrpGIijvg=P&O9EM zBy~M5P7-~7Q9_=&8}ylaEatil9v3F3>$mf`G=sN=9Ane^GbTa=Cum;O(Btfc0EW|| z`P@>(8#grRF`qj<_-?Llg3#QkEtHtMU3i;A2jCGW&sO5BTi3m5qQpF%t0i?4 zoaRdeL}RP`%=aWZXBA!G~o+m~{q1B!|PdIS-cjgJ((aSs;<*udR zRp;Q^qoS=YcPvRusdrW`PlH2i^`%9g727Pb0-&s;7x8vuJ zTA8e-r*aDi#^OINN`)?iK#>%3dbV2BlHxHh6`Uo)!O^3NbC6WfuruSQlM5)DGXFO4V96QkmY@Z`)Rz>_~aC{HBaIZtN5lXf3ZxWAM2 z=_k(Po1>MAnJ4GA+b2)Tl>XF!;#ia6ssuJCPfu$@t|j4e3T zlCq9FtOF=Oe1nv*gx|j_z8-e4Z(;k7FJ?uJD{0y(NPd=Dj} zGn+3K)Th&}LD1>5DH)v}OKuE(7Gn&JoT+%5?wnJZL$jY$RPhc=Y95n2k=e*s*jNZ5 zLpb02u~`}0Yi46%Po}IJr*x!ZZdUpnapF1WROVaur>EV{6LBU*oE|^Z*{;u|;Ah3B zr{z$t?PRs2_?ey)&pLn9nENb+Gmk1T@Z7AdGhI#3VP~Tsy5>PrWWA?g(O3kU(EgFT^ywcw}K8o*znx7nwu0#J!o3?9f$#Av3?{Lht<4 zj`dPc_d{iz;#%$$TCTzEf_cj|B#@RApW<7NyXtM+LYJIL*2G_)J@Oa64)aH7)V?{A zSsqwY(m=FRJR%;bp=XWIWo-b;Go`4@4re2@z%DTE@BxJnWgtC{w3i$z#R zP=mZ%8+6kij!g+pF|*MEAt$A<^L|>mprUE7w)mWucNsfhsrI1}4L=Kcek1#2d)>q! zy%C~pK-9#(#}6tgj%^6k@ZSGleA=Su#RELgCzqV3Q-;`Kl4ehg;$4ad!Gu5Cu%iB^?0&rmemk* z9*y@dp0Y>!i1MZhdj&l+`xgLYrm!l0qbKS{5yf)3wijl8n463!p9-oc>L!S)l?#Qa zn;>Q}MBT_!_Ddg87dm^1t-@0hCF6xv{2M$`H;5>f_xU6zYLyY?Q$h7at%9g}xlo8& z1u+*x)D1i@8AO!Mr)0C{)Fi#!evjYbNqUD!dS|Fla+2O@B>7ZOJxT8zNYXnYsxnDYUwTNc26^m+oCOhQ)dc_i6 zMlM~VMJZdkI5FlJul}BGd2@+sB|ed5C_{-D8x`O;BP|j0GQGIk(6fV$JMEGKj71ns zFgd`onXfv6y=G2W9AGsmMrKvAX}GN#8$%G+)?q+!b}~&pCgm7uJFM=BrGw*>du4az z@4*0~UqMRe^V=W#Gwg0zU?bW*RgRDN>2uqQJ(&w`CSFu#&W&b)I z58;KM2mxNp;{W6Ho`CCgmW7GCOFHWE-aKCN{z0oL%s@`27x~p{CGDb`%o)18z)+$w zcr7#ZM=3*>XAE^oN1dTI%R3$lWG`Y~iDAax@E{CbBZg{!+S;6<*B2N{~zMvjG*C z4;oqHr#yDbawHp5+ipyqr7`u?d`z8XV=7Dduk6m!m1JnAa% z@~FGV+L-p)6Vg6Y4!rDcBMcJrjyB#Hj5K>^Mhu;~am2urzZBiM`@%?`Ezh9$2p>lka526p)=I^k|;CR@x-!g{6jBg!`p=?)a*;xe>#1Z=$*Ubfn5~|K?nW6Wl481vH zs7pHP43$B2Ts#arjiE4O=fN1tz8yT0Na(Vhp=%2EEZYKjE!(qSPZ_$#7%F?#C8Z2K zPWG%Ki4=o%r0iKtl;2}}_8Zp5w9hvVX3w&xs%3|ZtO$uP!w-l7n47IwU-=8xP5zP@ zsC;s`@L?Ic@#bj^+?8RxEi>Wz9Y}a>*(UQvvp~Nn3zXe6;OFMs-2*Mqm10GT%je#x zyBntVxcsq*@5YwHkKO_g*M!W$lC>#+*s9REqW>);wcpxYql0$;EsVe5t<5jU+GHOP z-{jYdm59It^j#MHpL@ws^v}h%ojp@l8(H z=Z!F*3aTgU^AOf=27exUHi)n-02>Dqw%!x=xoHU7B*NHM^)H^Ve-UBqx8j?eu+JJ{ zJ{43?*k>WEe{%C#=-Dj7{smymK*Ba@&%Ga8ZYqo|UkW)lr)O;WQmP7X%ElI#l&Zp` zW}hE7b8@rQ6lTzAr}yR|#H`^kwro=~Zl0!{w>yOR)4rWQt#F4;9Fh+mTZ+97fF8+57)Nl8he)G6|=$UmF%<9)~cfljJ z&~aoaz@CAfVwdl&cV?va>$kmX#XDJe@U8d>wc>o(jUQgJ^ry=Zs2>W;#Z}5a|5Mdu<#jQx#$4tC@DyW{Yk3m?!e)|~oJSM_6 z08B)KMuY>Nu#Zkd*wP5XKFSiQC+x!_j2*9hlQ-~3j4+=HsweCt5Z15XJ_0@LduxHm zDQE);TWV*}Xm3%K^ThJ$@~1+*mTl)xrTXo?nSOIg|9f8B4v~!R4Lce{8=4F2%=d=P$MuYU* zs>ta#I)ARX=y3T9LY}`MSoup)zd?!rp>wZObJC$9lTCW3GH&D9Q6r}qvy7MnB?VB5CU}u7B`{qXCzv!OJ*}C)m z=DDAerR|>MC@R}M$CLB<{(@enNI(Q$M<$=Uq(jgZzuUn~?X>b752meZr|WShTikeP zj~b7;Y*pbg&TcjybC@gf(B?EAv)Nh4V^%UpkC{05d9+zM;4#8xI3~MnJ?juq1I46O zvt1v&G&^>~K(_B=BwO~*f-~i%cl$m@JX=}zhCgh3gEL$Z;3lVII@7Oe&QiaF6E}Q3 zrZ|%(f=zs+i!#v;sRmP`Tx5NqA4%D|0)c+)+#FNT@CXmq0xafyOd~EnMR&wfl|5yd zo9Mm#~Eem!JV>lZW9sAJaM zS+-^UKVKpcXY4iG{Ylg@Ce?^`u@RW0fB^P<3L+pt$7fkUfUXoM1q87AU;zOmF;Hpo zf?J!lG-34RJj((CbZk+FCbNJ5UG&Q8$0S0Vkdj&y#>OESwHi zmh02(s&MNmd3rIwUw4VZp7r# z701Ji+~(Yup!j%OOh@K%q4wd9hZkrQvCckXr@?r*P-~gel(q|azS~4RSKD#5V|S)$ zS`;5*isA!tT>FHjJ?$+{=IM?5PxUxkyN0!0_|)?7q(oLm^=P^+a-+8Wg=)Dia%{vZ zM+n(#7E{dHB3&aw1Hen^N(&HTUDnRKw3FhdQ)8Vow4w>qiptFT3A($|DJU^18pH62wY#^BX--DMt=wlj zjH$WzjNA7cu*KHEaW_=XcoyV^pybw>S=N>m zKg$~cSk+wWHKr4DFXX|FlEp}qqwfMNn;y9;4oS~ShbPYXt)fEGAn0stdg7{y37I@D zx|%u4^EvxkV-p9~ZX9T$KFUzV`cB`TxHm74+H9j{FvbmfWElE7vhuEANv!^(f zSv})()rq8i?T6Gj8Q_c)!7k25QJ*f(MzJ>|rjk#x%z6S%3B;knD8$9Z5C&5oM(j0< zi@}CPx<_QidAm~Mh~>-PO4+Gn>9FMN)HdxhAZ{l;rG#M#6)&^Y4M^<}LgW@n80|@IQF<|CedYTEcN|Sq6zC+OinuzsD{67}}!}|3m!PvJI!|dwy`BUv)>Mbt8|>8*7ce=`P_ftx0 zzh8B?{3rya-Rk`)x5|&gvJc>Wth?t&lX zxw&=rs`$CNb;e0VWAUqe$9%4YV+;jk32DbH*tth`vTj}Pcudw`p?baw$t%nUd-=;w zzC?Z*iuX2C zy#FJwJy(h; zZvj{}up_LfIxF<9X?o~tyrVG(11^VHY&V|tP)-cwo1EM?OOK|hpm@UG3}O8%TW^M* zNfEXjV9h|nR(rzUl<}~i#cM?vhZ$by3A;{&dz2l-YOW83pLdw+2awA%2tZ-)~a?CWex4a&nhj|Msb%dcu}NSpS&19C}uYur~p$ z8c5g*PuLr$A#Ak>dt(?c^MoxEVb_Hu6$rb|2=l3+dcv+7*n-zV&!h-j2C!xzVXHl1 zZ>3evZAemquxpJlp9-oc>{MZ8i4f!30vz4TRIJ4 z8$=i(&ad}`y}n&-r-JGUyBflpeX9Cu=-DX3UJtNoAYmIkVOLE<*k%!S z6=O!=49X`02qUJQZ*m!%Fv5H)sGhJ12?B0}0!VBQhOB))!*D z-%22gi}B7Ks&7pLf7aU=Dp#sYO2?4nH1Q*({)l?qG-Cb!`cmIUQ&(Vo{I-M5{I;p- z*VFXdYi?LWcjsdciZg#fS>-P%i2Q|4nZM8x@)v|Je>u!4V*K08fbCbdJJb=la^x}J z5gt=V;4Tur$veX1+DDhBg6ccMK zg#A>6adQpdRa2@J&wGBSx4{1=SPw2!!>IN{>L#G7dEd4+CQ$VM|$@NpaA8ie*`e;3fMCZ4QtfN*nj)5r{G~xz#Un9lT+}0qrj(v>M8g> z6!fd5?^EzvQSdOpx`CZ+P2E}X4^2bZdJ*RQB|w)n(k5E-wm)^z|8== zlvB_2NFh1*)#G<%-rasnaIb!jZ^XCDp#fTq;4&WYa~*cmz{QB|VtImFPxB{u5Wx^S#)%qA}1gnZnD9zz7hN7Z4JN1-(?8kSO!fzxTf-N3S9c$z4gL&VXX0%$bHsw~$Z~M{e+ZwGn;Dm4THoebA zE1wFgC+t25>le2Bpl5>!`!>MFfrPEMHvQH#WNi{z-wNWLo~)fB>ze^d1+u^%Ux1`@WDUXf0^7Urv9 zm&OZwRz@!M6Rw3aY_tMq38`Ik)E2;wTdc~BDbGAKn{)ookqT2kOJU4V}8{Q2E%4(Y*up;k|V0 z{i^3T01gZkqkUe!zdnuPc}(*Cb!^@QyhsCafj&qNc# zz6P+gIcV^G%oBFcG=weFR;_!2c)KTTy9oPgKvIFQuNq-K6;w~yS0Su_@ckjQDog6=$o9ZyNxWL3aTgT?tx_84K-^-))xWRHi^I~ zStGKdCY#RuMppAag?!{Mv`PK~AMzJU_b)bR|83fr`gQQSrq_ne_^%5af&Pv+ZTm?U z`mpKe$A6ccW10OmdZJrN5)$x8Ngsl_B;hK+)9j)^ta~;)&#U}5t}Es7GPm>QCEWYT z?wZBi3(Did&ZlvUqUDRiLfo8Mq(Qe_h ztNG4NIzl;{TZMU?$z`ZKX6n{2-6Yt}Z7`*`J47_IjdIEzGuee~awE^s?I_tUAGZCG z_WU?;yL_5%{~xW$E+04oH{#LkK-jn$kDgP(np8YBIR5@ea7MD$h?_DtHQ^a4-U_GK z?_91bBE*uiIB~Z#v+1sRX)3mRrjVKv8x}p%;cYA*-gKu{apC{j?8bv$Yf$c(Sx=cNgeTHbfv0+As${3ww75Zr5tZPt*42c8u4!-M_6S+pYy)>E3=2 zos;VjV>jC%{V6mjCeO;5e0n^qkIARQM=E??fk2Dj9SLnP+IIVXV9AL+BT6WZz%our=hs#w8GWJ z0b)ULJ6p`-IUJPWM%?(!xT8&b z&t+q$4YM7;o%6X|bm1$m`q*pS@nXz&PSVV(9Ou#D&;S{i{9lG*wvV)#?jyzEYs&pS zpI`sN%+ETNAnh;CsdNkGr!)5cBySWmKGi6H5})h3*|~i8moay{WmmBLrRcMy_*_pP zo9fW#P3_sbfCt<1tWnJPq)`$zO2QA6&4s*+<8xr_Iq^a@kGnglQ;?l=Q#f82pYuv4 z5^4rO*9_;hud`-2C!Jvwo8fQO41X8@t>!7+rlYt#i^8Y-C^EAVUTS~odOiOdha$7N z^uPF5)A*-0Y1;QIqCLFK{!&7FAk;TGhYncal}`oLbLqf9$JqhM*(@@C39x10Y;=Uk z(rDow#?JURVf>~*(*A3r!JN3iDe&_C>yV#Q(j-wnfiH5Q?{zaW86-Fd(FO~zUeoPl zhr#(YmrCU?{xS;6X_r{~_wm(!hJQ7r$7_T5?^CMjNNa6^?-D`u zl&@ue-xC{)+^vP=-Qwrf0DGE)0)uvm%V;HX@^nim;|r#yabIp!pP%o-x-dS!XHoXT z)b#Lb=zR4QMdjHKnykonE(~7jZo3Lr>wj!;hFSe*@z63)W`pwuV*Q0Q>h#ef`z0#p z<7>=Y_`JM@|4V#cPYeGSZ;qUowyY%_=ax0k^%2}>_xruU`G1M^|0Vuk2Wi>I)UxN{ zwQV_XUeN~09OON=NtRyLQq2DLIYpL^UimZrqrt1{%beNzKjRllHLt@N923Xe8GJr$nGf&6| z20FDrfbL$?qWzWvtQF__8f3ENri%X4&r}Ii4ud|d;H>@1HG$Q z#({O>z>Be29D^@QxzO*x-2g^>S^RoGaJ^mw*R4T(Sz0Jm+-d`tO9asueJgeD}6`Sk#s2G)6l>Ky;k6szcewIJHc_cHi5w+8vONwo{+UYHJhNdJopFpZKA>Mt(9 zP+k})Az^rGu51@qhU3M~u3ZdIUI2P794}B?SW>*$^Wz1BRytq6`lh(C7*k{*Gqxj5 zL3jNrOXg_$S~)=@`r?#M+A!AdITDdZXE*5~9b9luljb2L47L zLV5fgfTUSVAZ2uPCzR0IE3*uj{uB0Ws#9~DdeGzx4n7T z9?khqtZLn}q3%qmv(&8>mt4isPWrYAKj`QWE63zeVm~)6UoS>XUI&MKrprdZpIbye zyG*P@-s}q%HUwyf;Jd>Xd+W`7TLGV?j{^Sof%tn%ID2|=D0QFDG3p*rwfC#qcxT6f z=KvZ@=eR0Sdr~OKMZg!R zDBo!*M7m8sm?o58J6&kFUG^kphWuW;E2=CB2~GJHAkb2_2n|w9oaI}svFBCY&f(MXbg6X7lgnMbzWsnincCCM>H>J4&z+LMHzxL!`aOXtt7w&3X(DvtO+yK#w zCRevFWUy=dc{_^c_LRct+HeK2RMx|_)3s-5dz=Wt}l79bgah3eLs9qLKW%kjli#Nq7?YvAbWV56a93wdw`1&r+FfJ@&^t3B&6)U`Nt@M+1dekI zX-NfaIW!f(w@1jF5)IqSG;BN8G&rX0NSQLPvNP2(yK;5Jd6s!=xG#36XnS+CJ-ujX z^m6s~9a{C&XubC6)bo-9DJc`aOTtzTLd+=^pO;ljZN0R5Nb3H`wYB5gayjc9xWf7E z#yO$Ri7G^LYF$pXLtCAqt;x|Q`_a~>XzOz{PIT+dZBvT2B}d!bkG3^M+n%FwXjz_{ z%z+(35|QH^pjBe-&O>7^`5i_p?@DRflhefcSw)(#-^+yTJsO1UPwPFeav%lAISMIj zMUF?xsdBBj)MnpsYI%;vsYShLD^j#oIT~jO^`fm#(bnW>ll^FGQ?&It8t3cu=C&b4 z+mxem+D$Lo<`ivfj<%(UX4A^h*U3`IkB7%(p#rpX`S}uXuJE-_NHk2 zb2JXo=*{gwiZ)TtWuYH!sk!4M3pv^{87bLtCrwZ*!y7&B5Ge3b}wY@EzV<)DC^ zR43hQ(i*MH(bo2(txwT5=4kA#@6Byfinb+3W2bwLCPCX)=Fiq+%^z3s?J0-mRraP- z@6V}b-)~N}`{F=~Hqppsq=+WQFSYd+Nl1>wj@aJ3CWYo(ApK%p{skLBd%swpqHWC4 z*p`{2sq{@L(v}>FO_Dj1D{^aI`nL3o9r+h*EA0JZXNtBfM`M#;j;4m)(`AJL>r}Q$D^_-4X94JnrY?d1_bFvs*C$l4+RucT~)1u-8(Umk)+=80f=>&dz^(SHRm2JYR>Z|U6@gGL4lfdD&MrJd{ffl@MQTHybcJ`+)SXy3mH%y zgx(4P#}@)rDuit`x;|;h3K>#2a(QW4uspBal=jyxd4DBDH19x^N4MUZqHWL72-wWg z)P3*EepxgzMx;j^OPn+Rhw}IF=l3_#_Bve@`JRrI@DSi-RzYQi-!Dl{oA+wWW*468@aB zB>m>c7K!rxs}+e7#g#>43nKE>4h$r#{gAQ;KVIU3U52-ZK}7UP@jtPn((!w5U)_Bw zG*&J+dF-k6!oQ#%*6UHdTEq7SdNt7%y#UP)AsG~qL`0xO?T=Zkctku%2F6Q7mR2b% z3f_-dF{%+@6Lf1=q}@=<{mN8c(yHZ(f6otIC*(ho4FwMP$=8x%(;Ty}ruwOwR$#B;$>u6Fw zc{C}XJerhG98JpCA5F?v9L>x(A7QyB)yRTU?)~JLL|AO>Vq&Osc zsJr@gOU7nYYUO_cYLH)p?){bL*-jnz_A4%cj0A>+=XfycJ|qwuJ5s!o{kWf$P+&v` zfL-m-BTR~vO83X41Ofy5(!j90J57zcKS;r-CCeU5cURxW=&Zz{9HVT08${Nj_ z&TWRT2^i3PP-kJdv<87~))4IKmEKlNXV|R|8b(V!jWH-4M%nhJT`76lCFgWuT>bO_ zF(?W_$iE;Y&P)F}_BbJ^mj4B=Ia48U?H3r!t#O?Y_;!{MgeC+I&oS59!wCU=Dzt}$ zz_*9K^z9)b@a>Vck`n?;bwXgE69NOB5E$r$z(6Mi6%ztB*3g>AxEWUzb=s3@#v>z5 zrLpq(kx?4>jvpDNvEcZTQ5vbA3>mvx71w>se^Du~MVkjT_>^_>NM%_nJu-g533fA(^>fHy^)P|`^%b{ty*}ztgJCq{~cYm6GHj?K% znj?(mG@M{c*)nVA`OYw{ZK)^PP|-lu1OyeUG8M}c_owQmDjFEtCpywG;OIM3b%uL0 z=JFjE{D9d_fT53 zQm+R(MOB6TB>f_^&AHjD*7j+8dtE? zEK5qFS`BVsWbJ8S)i`Hh&9XPJZlkJ!4Wr7yW{%LZ@-219%CDko0}Gl?1&(1V6*vQ3 z`3Aa58|VbiKqqhpI)O9L37mmW;0$aTRR#{($YkKKH%};d#H5UQ5>)xCnnGas<~%QMYELbu8CbI}V4y3>Kqoc^x{?fRxgxrR2ocHM9ZnQ!of=u} z6$*wwQk~Y89Sda2nmx<2;%ttJ)yfPKrwF{6qNem_inwK9HOF-YRAA~Lrmn6^U_5Bo#tOAx=%}b~`dC+H02o1qCqk&DUVNfuy zogr^#my>Y=qwX&#$v|&YD^J(CE!8pGK&R0RY#LLJqy1f)(mk2M`N$Y<+l!OGCqu?J z>VtYvD4XAAg#Wl?Y&Z&QwV+mI@>g*)KG%iVzoot}p)t@6bOuHyDF(VOWT5Lp2D(-? z&~+gLT^BOYbs+;?7c$UwAp>0(GSGD)16>y~&~+gLT^Fj_#G_aPo29xM_zA1i=hVy9 zz*1ce40JUx(AB`ehF9%;*0iD6Qd`CY1D%xg z&Ll9%Y&?zMYTUJf6#a@T3-3%NyrW)wXf(iq*jt;_XJM`({zeI!j zRW`wHRx~n!^Z!RTOE!TIY-}-Bmxct5X_o4YTmzlGX`quy16|iM(Aj$ix~^xSGjgBQ zKyhG7)fi^#(M_b7Ce&+|7dMe&qgiU#8tD49fli1Gbat(QZs;@6*|i2b zAvVy}(?BXO{5HT6REnX&a_^d^syw8 zDt`mrM2c-bbQLBOR)0-fO#ixBlY!1kG0^Fss_CgzE31c_K6BQBfhAR#Dd==6KuPIT z05F{j0H#v`z;r4Am`()%)2RSpIu!s+rvmcqIqm5h(v+jCh*zYlo`I2R3^`N-WcKd+@WZJ1E~?X_j2r-4Iep&K}C#%R&G z?MSlrbR-$*2C>3CL;2b-jwA!!;Ao&D$-ssg69)QuhV`^`MsuWw;i5A<^Q{IZz1FHvN&AC6!idpUf=j zy~Cx0IHLR@ju2Cz_+K1q5pB5AXf|=MM%8*Pbw5fd%{S>(tAQcg)_tlol^b$!JSV8w zNeIf1ZT9j&z>RhI-RC8^* zQVq;YwO3cFfv!{oo!Mfbo4pz6W^V>I%{(@+WkZsI&OktlyAyTMf$2okJ0rzBjj8Em z0-=WUUP(6=lA4-MfT`&On3_(2sp$lmnofYuNU_ZPgjNL)cOSMQAZJMAIL~P6WCI~} zvH>~|f%k1XCmX4$lMOI+vH_+}Ho(-$2ADe808=L$VCrN8Or30isgn&bb+SR^h=@#` zrCOg3oKQL&3tUH}r8*~ofsRN69gzk)A`Nsz8t8~L(77-TbVM3BY^`bFi1)lg*_bHH zR$QUPIqaU6`!8+I=>7z*3W2u^jBe>(&s-UV2Bron`M2|$JCCgqkS?l|gB#Kd5V@VK43dLfHTL%XP z-R-sv+8t>hc)h~H0y4nAOFl6tO0k8~f{DAq1)3Ftp01^nd3sdQ5n5I+g*!c&k>bb< zSf!Yb<3~m*;^xVaF;&2pZZ?R+p2)zGFO%50G^Amw$iTzOytJ5lq%$wATOaAnORLdG zI`dKsAL-0X!3{?|^P=bbNM~MB14laZk~MmyGcRe5Bc1u=(d7BQ!_0hXwNf;^i!iFi zIp&6jszdcwV<;NZzX}%*2d7mki+=XSFa72lU%v0d4^^&VG-)nT+V^(9<4yP8@y$E; zSFTw~;;<$D^oQ?w*B5?r?}ptZEfz2^tF{o?MOn_T(LC4VYn9}ZM7GsocsKUKB@9BKvlvsGqh#kMcQNPnzwgnH-+t34zF+yT*imMU1lI7X`~L5K z>cL<9Xy^L91&#tQT?Dcwa3ahEAVa?UzK_6^jH7!${cpE@{El~jT(YK*G?n3!QWvVC zk8%P1uXs=^-Ku`nG^hNdliok$1Elw(b(fj&Yr>aQ2!G%1PyOiHw|wVAKQjvIebO!p z&=iU@bTJwIA2|`--$S+-)6hp+({fz>!MCott1dyUF6l(-7Z1@du-Yox3{XV;#XWDm z4Mf9vB*noj5RS|0{vW>eZl9GhG9)$AkdS2kip<^Lee-Si-hSgJ_E%o46Y#MY?Eu5awUR|+l@#P5Ic@teMI$0}2LD4h_clk?Vp@Dchw zA2gRpad<^2r?CqJQO=Kk@x!-$eED}i@iPPoG2$2cDv=^7gVikJ_lOzv?GJ0~$H14{ zr~IY#&6-0)D}8hGp?z~(5$z+zmyZ@-N?Dg)J~$=X2d4aGa>`%UPx;IHr~IWvPU+>= z;y3RrzLe1Zx%kU_i!UE4zPzXSQp(_#;x8MDFD10KQ~vVq;xFrpFL&6>WDr!n($^N7 z%2-IiHPWUzCEIn-5=ah6AcGO5yDNdUyW~HMe7MNgGXH4*~ zVt=IBZ2Fo0W;56P)L3qrN?$77nc2&>S`@u5_@_o1=$)V5tMZq!N~Y&*uJ@{N10L6e z@pfgxI4~Yw$}zD#E=ihtyo3X{d0dbTnTmzTVJMCH)9j3>I@)ndO{wc6nmgMs+;pvH zH6B^g{fz~shni21IOHYTX~lT!BE>^`w%3>+c1Hm}1^qaGNqe=j>kNw{Im#A?qT%A) z0t&PZ6;310@xS0t7Y3gLLfb2Sp6&v%fnVJ1zL4(Vh|t0pMKZqOkVZjT5iDunl9CD? z&4IZ-=swWsysMNeUu4&im1_FKoN|sKZLca)ULRP-Puv%>C=T?NQPXkK1wQ`L;SRB( zZcQ;~CuKSRuKM`q3C?jAH`t=~wjydYkcj2(166b>oXHu^uw%=z+Ij8TJAop^Na9rK zu6G0l?T@PrFf-3eJUqNNq`&>4^@qYa)RN0MiQu5^HmE8i{xmyWtD`ej8N>>5sb3O& zl|1>WM=lL{O1Zq_6UKxQ$ef!BUA4^5f}EW_1NOD%{ejHWUsRnv)QH2egs!6@5oS53||r(I3gjwjWWg*D~LANDGXg|(RY z??P+omjvUPCkF-hu+WgFWZ`#QiIF!RSZPo7TZ=W4BWryV|UE zwHeN8)0(cpuiW8f+eLwW(#b!P=RsHaGd&q?V_@)@ZuH z#$Ekz%v8YlduxL&H&tzJ_O(gPJ@2oZt~Oc}KZgBgZ*Ogg>YA!H@A0*$qfNU^{Hy}USAtfOsAzY#i*8yl1OS2B5sSmLt7Ww@N2_$wkd zl17BvBxW(HiSdqizSHXdze_qp-IHE`4>^d3uIRpfVxsygJjyYl9-K;ZRydP+R-_ZI zq9xssX%{E!gx%U%;Uuyp-w*~kt?{`0dn8N-92%tavG{Xg`(dS8-01XGDF4cZV((*v zP0||9{zV=N>>0YZAtvCh?*Fx9Y@}h!g!aeQiH!8lkcc0OVabqc!yDSDNu{(b;R0eJ z{jUGN;N;HG^G5NwySj24>CCt(JQqUUinCus;|Ys}md##H&`>aMmoJPX?Im%&{o67( zI1_><%D6Gp6~mD~(^BYSa}Q3TtI3oUKGaKLJb=Pj6wb*ge9ECw=we0>PT>F^b{vO2 z7hOndp$d^_vRk9=tHS&NRhX|ToS9YOL>eUyPYW+v5N$_N1uo{cIteXHU)EG6tKF?Z z>PFrAgTe_{L)`u~Wey6rJd`Y0vYLtrf<+G{hq1IJB#>d3i;d`SmG)P!vhH?TG!b-$ z7ey~dat9XVI#xV<>sZ7DT@I_$qh&#&2{P-JA|M|5^JuNOau9B)DyQjNp7KfCx5En# z>02t1f6F54;e4w;+2xWv0$#0GJ3j1!52&>E6Rx9;hr8vOM*wyOXtVHnqoaD+6X@zx zJluiYMFSZ`Dv~G$NI|R-BUpnKfd^yAR4jk;IpX{e6rwjvr-89rIt$9#3vs>zgY&w} z|G!!E(Fj{|Iyd7q-Xp097wSLR0h2h#tm5NTEE!@o#ihSDO?OosC%aD6{*e=@^fS1X zedbJ~%;nO4RMti^G~9g$uH`Gmcn5h#qSYpCNgNv9OTqlC_AnE_-#*~st-$XE)Cl$8GS`?PWRisjd_-DfZL64n$#s93?|36L2xXqqVf4ouZc|6Y?tagPK#EULSa2vpRE!jMI68MX;ROL zl5XLHC!t$Z!f6rTiVcUUTi{Xt?P2N`VtsN`hSLuF25uZ4SWVfOQo^4#03T^#aWKPY z2SJ|y_yGKI;m^(RwNI}*%Nr7#5y`&BG{HMB`v6C8x$BYLZiH?U%RX?zp9}FleL{R# z;;Ws0CdvKc%d*8F@x?A0B);0Rmeum?0hK&k1)ZB!QsxzRGe{Kqv_P}}T`3>71els)yvGt?Wt#8_OQ7~?!Y!7d^>Rkkd?Zm#r9Lgo&beIS#Xn9LC?vq?xyA{_)3r)9PGDQ7i` z-2oIX6xdFcp+=mHw_2;#D!w(8je zcrP^(@1+*vz0^Rwm)eK-QuFX$Y8~E7jl+AXZFny=4ezCv;XP{@dbMwu_e4Q0ZJ2!B zC1wF31}}^i zys@lBx@ox9_9s-go^ zQ9d@=!hv~_se9~8y#H1ht*A%pr_?@ffh7}ONOIbglB*7+j4ZD-o*_xla#6(s86fvZJD(WaoP9A=%BFdr3~ckC0rPSf?c0nLtj{B~dp>nh0*I zM0&}W=SlDJ>8eI|SS=-Ysd)@owstq;7_wrcV9GrHYN< z(#_fe6R$9n^fg+j#>gjbnoP-wuC^;pBt7~pp=T3GSzo;ph>JGMp)|GhIh3cCgL0Tr z$ib#zsn^=)aJFd3reT9}(58#Dlc`;yNZ*zmloxe|#f5@T{U5!Pmip{&A9Q{kRtvX(1-ecv&)WG znv&4fe|i;SU2@>DZ(8Q$A%p@{TZmF_&NOJRQCA3a5S3I+_H?TtNS8M-o*R z2lQrs09fDtn&$Ya|Hx=%+S+LEC??wum&`L(B-3fIBERJmyN{|?`pI-YF-K-UiFWsb zq+tL_a-(?K&qopVl4nsc1KeBcE9Rt0*P6VXTElxMO+LXN&}B^VI{^)x2c7oCd1N9g zkC%<0m$)4#X@A2J;O8WVBDx4v%kj6--gOOpv)MQ<9T6`lWA4%M)Grnc=~Lga@is+gt*Ip>R@TlR3oViXE~m{k1n zOyH?bJ&ffHZPW~9wrDS&c}ftS5m=5E#Jth4ss^wQ6Spp>NU5iN*>w5+)v%r$E%RU5 ziQGKe#&1mxtl!&GD7sqyh&oH5h%M2~OKjW-7up(Vr%4nu<8r6gozG}%pEY7brZd?v zoO@Qyw-UtRqDuQg^&#;j>W-cj#yk+ls`s`=8i5tUt3Rp3kXY_Tk-H>?x;AMhNT_vR zQ)hX6 zY)*xfga;fZTuiEaKOM3b%1mHbbF5;Yshnxrhh^n>feD!2NgU}gxxi>kCli+M=vD?A zqWd)aG83}&8@Qi&^lcj^?GYm7luO?V9gCD;q);0?c~mo8h;*bCYiQlSu62J@d|c1g z{R5v@u@;Pw3?h(4jiBnOu0Gb%P8Fww%@Sd0OB)?&9hmO)RfCJ;a`{Gvf~*na@Zaa{ zK{h%gtOITjn&PJ;cze*~@BQAZ&0$baJ)*xKU6(;gT^Hds_h(t!%kt~G?&IaITO13T}MHCUj z?^fR#An2`5%b;z@uZq^BQ)f%?WFj+Tj*Rg@dFfI;40;ZKQJFD*vTl&nQBRq@9cgs- z_BPi!v9M-%10!TA6}pETH8W8@O;ro)K+EGQ${IXHAB^Dwd@m zrl?u>Omm5hTYEY?RmP$Dx0>uK;d@zi^bod0X>V~#E*m$Yui2eT3(@ecV!8AsxeuIX$}2icW%-eq4~E5mnQd&;7g8>CZwd2NC@wV$oGfNCcj%^)45dFg z1VgEOCqqPc6hQ;bCggD@Ez@f@%^YMl4OXIbU~(wf9qwB+L&1EK22JlQ#P0a?)9{Uk zsc&?B9<`aWZZR&3ss|jkEq(rR9X)MiW<$GNL-XS^M#}N^)(|{ANz`Y8$g&t36v1}quGw+@aC!yRH8dUNy&C5FG1s&^hP_Jx`E0c7X98zdz7;ye! zh-)P5auC3xC(?hFs~5?D;NO8KD*wmH#@dFweZz~MyX;m}I^{KF(omS@$Qd25K{S-y zrB^s#>t$@v!$KQTov!rmQpRUDj0YhePczx%_#Dj)wW4FRrBE6YQY!}<^fPuI{(+nDUiEUIqbxO%z}YF$x@|c)2#@o9CpA;J*o92ap;&7EHDR{^>I60 zz``MAC0?e$LhbFBn^}zM2A3zTXrXkuet(rWgpFsgWkt}=EX>O=VP#|D)#H$rh|Y>Z ztb9(x`%0(9|BOdiF3q?#uR;LY9nclGI3wmc!8(Br&WTt+aGL|03!+KD@$d>;&IY&y zhm>;B){OcHnx=7GYvc~I)t4{>2(NyTOl6l7rEcsIfSu*mZ`2#@QMY| zga6#zF? zuwg{f0`{F&=M%!r9^vi~NbGQQr4tLS10j5v{{&=EwHSrkU|mIJD@jufr!1=ZyCeN?t0I#3smExyS~VnN%5_Y)bi}kzvY$sLjLs=dq*YpVx{2kJ@trJg2eFHRAzO zZGmHb7H+t!FrO$)VyW;1%8H;usqC4tQ`yYC8HQvZlY1=aw6L-uoJG#WATDWtThsh- zp&YXnH2~Z)<@dzhp=pQ) zLfS`$Y%H0FD5FizSf*RqY$BZ(n=0@VLdHQ)XTbv@gQR^|sy@d3#j^NDC~x+q*}+3p z6JkfQpYbly#Z3`OT=E&)b+wX%J1oxF`lcFWf_uOgL`IT2T)@5ONdHQWs1zn0 zwJeU^710)Z($$bbMVL_%cF#z6^GrVSxe1mZzb_do$-B%c`bH)8vlSXyc9VJFD4XKM z`N#SPxu9*D#o0`*y=LPcv-~8Oj3JDBI$H;c6ELS^>Y%1Jh$1l{>e{Mz2WJGx5uN|Nmrn)3i9EGuV*X1M)3%prZp005aBq(yOWERNpO1F5A;E# zQ$py21m(qf@P+ld0h~d+PYdG(*7q?pthZ-tEuTA*o&7t7&JLaQZMs9&%d7Nq6Cv?; z>YhHJpNHMz`i9(Jef?ZM9MqB>dwtmb9rYkl;(@wRUpK=-gFmDmLVtd|5jOIL&CR_Y zhojr2(V;zE8qTv0Bif6dn#wWa=mHSmkAHK}eypqu{W#CJ*pt(I&4D8Y@R@8k-iiVJ zH%0I1vWzUTQ`Ax(wB%g(&~Ss|q?g#vIqW!vE81w_DoGb=f5XY0Yp?k@AgvcH84Dv$ zJ`^9$Oq!|W&FMtu?Ti?L<;bQ`5XL)rnl1!J)iHF7fb)1GQQ__+GJjO!nPQkE*7TEC|=Y&Vh5#~b~}YyDG`A(WY~sdjb16cz7{aO^e=9H$dSM?E*c z9acpj7*=ixjxbI^yyiaPI944Pa};P0xUnWT58XV4))CiRZW(|YXmD2>gpNxzbd36i z{W!q=WWjvirjzTb7PSMXz)baMK|=AiF33zLQ6}4p5+J_IZap_{58ql?ve}-8VuJG| z3?`ekQm@Lq3g*8zmbAhVA%Yl75z>N?2|9*mlR85hC=qsvV^!N&6Bg|zZ^BBC`X)e7 z$HzF2F$&zcGj206H*a$?GuNFONp|yX(021SNiK@GG;K3XD^r-c)ErDtGokSfbx&ci z+tt@lUgMyD7D<0#*<$2McOzNjg3Pjwk$|u}b4hzwVX)JvY)vw2L4^8HJNG3#F!b^^<3LB1+0>sjx+U;~ zCROrdAFAtiSudWaGc#M3xPY`ft7&#+Jl)VNj(yvY?bvsbt7XZY9S~B6MnNgML7v|0 zR=((C4NmCPWGa}(mQ@jjG0?>@!o#*0Su|#g`Flh5E7?dmF<>MdKoyiknH9^BD1}DL zpwRx1R+&I2w7UU1!M)*~e7y#RXAHY{d`ucwI;};)YZS8-5?Z72%a3M025!5BZ`z0^ zbfj}JrL;_NHPF~Jo?WHoq1L#nk*ze=DsHoPti70}%&^NzkS0h{nzpVcoqq8GH=m~s zmeK>{$1+s@a9}2`m(MxNIb2BNpgL9z8sk>7(>WW#^D%Cfjd5GZRtkEvvZqBh={+6$ zNYBSU4bE!9)%h5=)qiYb9mpE%Y)h|==6;M*@qUbB45YONj&Tl)CeQ|4uySpsF%Ft= zkS$yg&2rEM3!>OToNqt}##1dOj)!M!#0iR{pWn!=Mv`@6wuD6uY6QR1Wr7mA#6j_b z=oJnc$HoX@D~yGFd;xA;q*+}%p+ES^<~rU3#+a?Kx9n&r3OPEO3Z52`wN3X=izrTB z2ht!x9tS9t#{pj2tYdpNWbP_x;@{cF@IXNmJ8}Hq)yI&zD9b*ETzU>R)?q69vF^Rq z|L`#!ijQIaRGQiJT4~InC+6DO`xvg00xow^RJQXn$N@9%I~~C6bYdduq!Y?6W%Aoq3^d*ZndHsmi!Y2LpXRpPf!HoN>?Yp$ke04i*$wp^Cnyz^3!RfhG`_i zQWXc&j9W_*e5^Y2XoM{^^;e?iYTehpnv-CYQAQFM*a?RDzs3ljCH_(Iv8}F-P=H4@ zvZ*S#_e>tFK{mV#O0ls7!4Us6OMkMkiaDe+z{YYK;~7Z6{v5y6=QL0!;3fuBwV(bKo@b^o`?jL!?3 zxj(O_^72M=Auk5ND!H|9io=utoN?R2>|zp)+43j>6`3uf884GaJh!Q&8zzS=0=ioC z5Z4vMl6FcWDQm7zVzn?&b^VZiE^yBRWaOP>MM#6SBBBWvm-E3KC2>m=wWbBd|MikF zO~fpQ-?|}-N-hhe+InI$*0+?PCC5_8J(hsw;R5SXWp-#uO?_<+%|sitd>ho{xnO^9 zTYpjuxq$^vM-x*p$1Lcop|S!*1s`TH>k$RJqeQigm(rlRb?+wby>cPsHMx{Z7RgqWYWPX#m0`*kh!{S$ONt< zAWpP)Xib8bS%>HGaEIrSi^k~=4eiDkei`Yc%O+8Gz74?XCXOcO=Eb<&oh%Y(T9CzV zOFxPgK0BJ;)Hc-Gw8p-Z@2K}HM9*~%3x5lz+cKatQIwx2!V81X5)sk>3SVqM)$@;Z zv%9B>zLafUC3K^YbQ?<4e2|h^!4*kWj{9IutSxRN4<~ z0Cd%+3-G&?a8^qcKyNKe(baN1=h$?<(5s6ysJgWCK*MJy0&z4vQHU$gV;tdvwSB1O z!Pp|8S5i?88KRvyC_a@p^e{4E+>s8f1o8_rQIY!7cdl#G0#bfndTb9!QF2l)ArlpA zFZh@WjXUJdZ#wcqV^mo#@EJD)?D8D&L6PY$n*eu`6t`*b)Hh=s$f6pf;k84#JwJh$ zB`WpsO3G`X(nG3;_|#9?E^ zqO5+tYwh3TJT6{*NQp!c`0cZQ`?nu!ueJ8tYp=C-8MCov>zUz|ElDkjbRA$R49wlB zVUsoo?r_}5W)$R2Mbfa4HIV?p$PlrSI`RTF!^ix4MQr2*K6L}ZF^?r3z;nf>VY8LZ3P@us`y?$Oujp2un& z>7F}9=kUv&q6_f4n3_X>y#LtKP4s`S`oFK}KSnL7ZU=jYhbAC~ynoNOMp4+H)MZ?g z>hiDamBIZsRdC1am4S-TIuX9{dWAs8RJVCwuZ)>f3uVPQQb}L%kKE!9)!34}o(Xhd zZ#1I+cWgxRfs~d>s*bXrx2XpP9lR<4)6+0o9#0Li*`qq8k4sqtzmZ`}s&IwZ>nH1C zD`!4PU9c`-En}TA|HXj$RR>18@o%J4>(ynllA3W3f>>d9#3&E%Pi1vPr;b!hO&hQF z0}`LqGW>o5DY~j9m-yhFCL0$gW`Q5p z+XxJW69$rV;xC--iOqhP9vJ2*D8m(oQpBXtLZibxF(v5%d~_VeTh#FWu#Xqy{k=>5h4Krx7h`)O2;ZS!R*sc;aC-- ztl4}DqFq8{sj9bCrTgC{UEvs%^0rZDMM>D>1Ug;J{0 zQtqM_%4#0#poUu*6I2d~aq6sxYC-@6)Es`UiAbjf8+l0lOoe!$!8Mx-n#zrZZJM2^ z+J9$yxbd3=GXmu_Fq4~SGinBNi=dYLCm@U8MxO3ys&X$jywHGXomng(Nh1X$@i&Vz zuCjxt!^dNXqXESDyu(F%Spk+03%Ep8=P;lNyA(wPP`zC7YDR>eU`r$MzYT#tD5Edv z&+048u_UWYA})y=C>vp$^egl*uGDm2s+eJXEVs$Jx{N*Nuk^p;j^R`!5EP#)z+XP!%>k2`Xd)Z3g8=5ayt{* zcaPy@j*-%gN@rMLq+tLpzZD1c*+eseoGK6#VwhibC^ZL?GnCh~PSkqy+_&4!-{keA zOC@v*oYi2!NaK5*tW$~vK9UYHL4R}B2~vT>Hwbw-6$>IzNm#h}@p^2RXs0tX(Tx3< zuxn`#qT*h1E)Ylar;Knfxx)5%fr~G3(>Kxjc=v~wfA)8D>vt=gHyiRAfx6+IcLbY& zGiDPoYd6~$;IPGXgzs`tLcivw z4dNHZA*;*VHW);e1Kbq`5h_*VclEH2)2wI!K7>A&K37CDI1(ExKn1%l5plyd#_4=? zNFrS^;ody5jn$FQO#co+JG;}&2picJGo@%`X#Zae>=jNH*ek;;=AO+=c6R}qe)E3( z9$ML58402YWhkTX?{$W0HeVp-{k?m9aJ_}Rb9%leJ~5jIaw^cseT779wfKMhzqr*Xvt~@4X1Ap@xbHmtR}0U?o~i$8pb6;E9rxx^$IyE= zj}E1hCVpN1vd`kTh3;^S(zNn~*pt?ad6^jrXxLIA{UxAbTjAGKIne=8E^MJ_HJ~BI z@&c|Kp7GmfP}Ong3h}u9w@e ztI}(UXp635?KIidr`6TtMOSeRz3HyHX6~lC>N>iSuI6B`1}-cM_>}m?HyQ}bKsk_W z(*|-v19`p}2%=nxJDa=TlqAFz(_KH(2J<7Kf2-d-mfbt#Oua3+moH%W+8g-|*DAp| z4VcuH1jtVUVjj&FB>M{+b%w(?+Co-WKq%@SIa-aVvFJaDW;i1ks?Foe{z3KsP|<%> zj79%Rc7OMaHUPH03ffmn)|h)vPAW%$rT<#nIA|e?uTT5nNqz8C(YT>aF1t(5Hi6;K z=!55q4{$Y&d-nh*!BU`Fz<~^@%0aB7cnt=>4~Lb*ksSrnQ^{UbTKf5DKH2zOf_xlsyK~s(2iGY z(k9+xBSRc5-^#dS!IphF3Dwk9|0z35c4n2L8P0&>PBQ|6IjG(1%KzSiAP3%Qz^r(Q zvtF+V@yho#9W&jG5ozh4iNJX&4vEI!*PE zfGLh9F47h>izd(HfItx=GXun+1#~zDj%*4vGC?EPx&~4pR*IEJZ#e-yTZMzp5;5Q9 z7sfV%o8lFlVIjS?P_QyM1r$kB{vZEuN>(UR8$%SsxY2Q;9-ipuPK_&950`?{5Dk`% zjZ>J3ZwC4%KZWscp!5osD(_y$MR}99*}#q98!C;7O1r8w*Mhf2DPbA39jI+Ie0u>~ z&}@axup~PUg5v_Qt{66fnpYjI{&kSeC8QP1i?f-D4(wH(`*M757t$AkFkcqCxol2+ z=`LPZ_i{l@ZZeCTh6-ZPH1>6e%5EVmNL6$^Cma+h;cl7LQpb)2`{k98 z#Jv7w41#8}VlA_7DVL8&{{4;NIhLrFs@8C{jSa&Q#LmUqUblR!n}0@G-QKPD^tv+w zs%#c|u-OP(SGS)S+r6^ohE0dr0C^@AI>IPNfjI0Qa&uE+ zcqh=$P60jBH34!)e=hEuh(Qv-w?_fbs^{C2qN|$C+!nH}b;qXc6a$lOazmFQ?ZG0+ zk|#+rZKh6>$`WxIRc)hklJm^b_-F!_~#^|7w~=6!crA{H>`}CN2Hw%veN}fOD*vZfU&{9&=1E1&=4d zUaeDyln+M_81o;S^s!<$G3gVt#)y&N1CJM?RqL$IxQ;-IV;IMl3G%CaJ2K<{G>vES zBMW9c9yzgaJ1DpKkyjbIi}V;Li#mzJ;LGfy>Uy2Cr3882l>zV&gwUyR1~rp^)%2;V z3C5D(7KE}vYmh{cj0cl35?K?O6XYAQWl|hj@S`>SL$(28Evhv)3L=t_q(^0d0PnC8<)WgR;p(#xfzC4nbUBVw11{n#!DSP6uhm4 zMpM~T07ltq+YRx+0$_C^KMu5%2h!OGxc2qY? zo)U{=$`r>AaSzKCWl=!CuVXdF}3O)hlzcp%B{1G0<<6t~NA|4=RltjRpa8^}F zV}A4tOOtvWu z4{;M`s+xld$>|nY^OU*OvISZd%=+RjAO$nK&`)K36WdIucE~ppLs~YLxgC=QY%pD19e*;b)oz-T$XCvFN;x|%!33S zV^roxtY6E37H*Vb+tS@w@vs};i3Pv(+zcgXoIM&M~4J*E|CKZ^`74H;mEZoKfbn2u5W! zv4{53wZ55`NxbVdfK{9tEZtbDVSX>Sg#rq$`BMD1zr?FZjpGBNzA)pqK5h;1JVl_^Vg3=QT78N58PlYLALzr6 zer(p!lUP1`0+aCZw>xUc6M=RVUM{WS(%~Qe!7u+#6}?URW=$7%cHefyWCrQ9|4@5awWn9kfr^YVvH2D?yo}!sO1zh-|=sYC;9t zQM9jQ3ES^021mnQS!DuRkOVE4!YZ`#unIYR|CN*t{TU0yg3^~*3c4sgBLk%*M492& zH{ijq43sgZW=p>jIeEx@jOw)8Bw3sSqBY1Ud@m1xpzseoc2aVuz{Z7x?`h$*QD4vR z-uZ&f<`!^{A?nUY9-UrVjM88m6V_zovDL%aDC1)zW9rxpV&WEXFNJ>?9T?1eVnmo$ zV)AnwL}JVv3~#Rwl&}IjJw;f7fu~@58Q-nt{_gWR)-S)SS?T`Pi-z@Kszx*a*?&AI zpJ#>ax+`Ew%2Rpq7ixLn@a5S8W1tRFBfkX zi#K>i^X8P^SRL@P%likO30*dFH1U3roT*7X86@j6AY)0poWkT7%3G-;*F~tAbB(C-KFP+q<6QT=MxSFd9EechVXnN;c$lMxukcWp06jp`}I7V^tS4`n)Dvf z^K{bNrst`o_c1+RNqU){D+=Yz^W~)1)APlo*Vpr8(t8lnBlQ{!;yJCfjN;W~PkJ__ z-y^*WzS~!z_Zds?NKbNE)Jn)-A^%iJpC*0M%OtN*W;x`aA^%!z^{1=kUyVhNXVczx zYd!5f^Vk2Hq{*6$Sp?{TtX=~JZ9`dfOH^qSgB z-cEae&PGz}eO%9zwcbF_mutNpUiNj$o(kW+L3%Z$&yzkA(w9gthx9t>lOcVDG)FYF z^IEO<3Dx^%t@lYiFV=dCdcIZbJ*;*H$u-#B32*W$pFNW--BjS4N3uu6#U9N$w0^>; z&ak1%9;LTD11C6Zi(+5J>6PAl(NnEtDSHqpC3}#?wI`MG`?8$`j_mK$I!;q|OG=*w zxl_gur5YIb89IKA*k?+wlD-!dk}%efhPNLCO7H* zfIzzrQmf$`#1N{(3`RidEdP=Q2`De}90jGX5saLb%b4I0_z3R))eVA(ko2o3kG=Y__P!TXNW#*dx4=GjbJg?t1^Q zxmzvWmEAoZHdiXJS+Bt6QUx{_mBu8}rsfVzA~i*KWSd>CdBc8jB8#D85G2+;S8Cx4tpx5G)Tgyy?%Fv>e}{#%Ta z9GIF>C8I1kH@w8ofs^4NXUE##Iy(-c^H!{q9=RVGBFQj_J+1L#n0iW2PQYIY&lR4- zm6=dWkfaS_Vy8BDKu>Je=99G8@0z|QEw+3iq-EK@%mJ+SD6(unX=%zM6DlOYfbkq0EiIS6kgACSkA}Gi3%SX!q0 zpn52Cmi*O_e~$b!q5K=hrFDvf6b!viGhpW)F{aoeo;pcV(Yc z*B7%-j&*&NW=;mY=JJGe+0)6Zq_2jqyhfR8-bG{-W+3FBB>#%%CohwKG338O{v{4N z3^sTIULheYc!p1tQ9@uJGTaBsKXL<<-#UTvlrJ=>N&FkFuqA3n?; zUa*j|Mj2O@k554bjd77fg;U4(Ksmk#!ubAfF+O%bM#CQquG$; zmGPadjBlkfzEdH+PQ=5tkiJ6tLP%dFeKDl3k-lVU_89c)TEK#{aMG)!ZGMc02JY$L zy})N@0;^afy&BTzNE=gP3eJ+&U^E@eP0FC~5FMVx1m}#Ucfd+06Y{U2`8*Z!*U4WF z`B%t48Pbd`@|kXt)hk7ogC)z7ZgPkn@vTJwqopqh|y?m zUg6`XJb8&EKJ=>RBK17&xfjTN-gDRZjKBcu>N#@RNm=2u6lRB&X1kamJ+WMRIlMaq zLb1!Ua;v1zg!C!Wt0BEYnq6EfH3LRfX@<NKI1Z3KgeOSx#T6%nD!Cg*iX4CpoR>gW#v0z;cahU~87XKpM7Y>5HV#h4dxTYay)- zH)L;R&X9)eEq#_WWN+znq}N0G6lv(r@>fVhYnIlgA6m2YGHK|GG@DtLcj(vhud=*D zr+SJ=v4IVs{wcfrjZ3(2K3w*|?wi>KGI-Z^SGwyjY7}k#Qam9sR~+(G(o7 z-~k$ zWA%Qm())8E?OUY4k1pWg8Tip7KeTBkd!*z*?1GW~Xug$MQcG~xlUjinJgEgZ^W<5Q z15chKxzm$tB=>sq0?8$*62OD^YW-~^2(!$6HF|Nf?uhboUCIDN!MbbmUaW&Srg1q9 zT3pg#P!+twAZ<@WTHNxowJ6lX7`7gu!s9Fa$F0@Wc!(bM5BrY z7buv(67LwTNe&Naf2ebG>~_d*{sb3Wisisc1m0OnYaERoYl4!^+-y ztMm$6gI-~qWRzC6ZTkN3rqLSSY0Z~JX-76#L2LP+Aj;YAKD54CqBUmV?d*;ct?!T} zgLCLBk)74T#kerIx6KjrV1IrnXIA0fAzc4_H`(-;FUS3$y$U6Wnw&xMy=_$R0Mqo1 zuYBVZKTL=yAuh(*%9Q~=Uu$jx(X}YLt)M#$yXUg3_W*LTj&Y6HS)9)ciypwrS*(iu zSA@Z+_!RgEO_QCyAgJQABd*=ueef02Eo*5OG0;@Bjz@lE{7!#kj8R}X`9gCeR zg8DHJW5GP-#|gk^6QVoccX)t;;Bk4JpvG~N>RVlyY|%ffYJpU&D<{I6t;L(|_6C2s z@MciF*=cXy)f=m0pWfIp#V%3^8i#o>1Ba{}VGlyNgGD(Y7?>lM|B*ybPrsk^`5WJv)+k zv*`@-RI~Sx%Dp0oAD%1C-p5t$WV7e0@upTC4aVD zzmkG-KkA<#jiG$U9VQ%6T)-645m^8}6Yv=(DPB&sWt^3GVKEPfnp02#S8J+>?I- zX<4|2(7nnco^^dsj$N#8Y-P$hqGsbjdw24VpvunNjv^q=x5Fy7&U3iFk#;TL>PqZ6 zWQxF;_n7iFSKC%R>-GbI1mtzsW-WcslTYxnumPq&Gbk@(L>CoOH+k$0xnl3uzZ|h zY69un>)|PBT~ZYP@^MwMD)8E>#lWgEe|0b5hctoOw7AosQvYn)!mL*TcEKDT4Yc@Z zNsDDCep`fXbul+*XE0zk(ZlF*2R-j?4>b4sOwI&)0z#j*CTI?|J@Kcr2S)+_iRm== zkO3ZYFxB>>fQlRdZv=zl{+gi11YG6(&QBbJO^Ar+L11`wX2dE4z`raE{UlhZClv8w zcrNoCE~f%KA6Ef?ZKj{kF`&OX(+?*G!pJK#eKhQW0db7j{{h1j!`9ViA05WJF)HN# zref$`(J#u_ri?Fb`l)X%4CI2Ie05Pz%3snGsGoywY|z4L=w=-cJbQ5o3;cJS7xK}b z%6Qtnm8aYp5Ij>NSy`v&X!B_Nz_Rlb1BIH-~M7ouPg?`4; zkgN1ImWEuhjqo(&D&36buakZyq_2=Z?P(@u4>lMo0|!UXgBVapmn89|E@AWy{9HW> zQU(nm4QX4L{D&N2qt7s+MLzMHZY1-IQpwoLj*kb9^m=URVe}b4939|ngBn`BxiI+` z%iRiM`W;!8ZI!nbhQspv$khH;9)Mwi2fga@Kz3?nS&1eYa&UIE|A5i*K*deC-AO7Q zF6xDh&UO_q_SuWQ`4>yYi-V-Vjzd9iJ=y9%Fe10Uj3s60_+lZwvx&JAM$e?;imaXz zK&+JBkxz%OwDLYE{Ihp%E2&s3cIaJJycr1G5Ae4oa7&lXwyfZX$Zc^Ya+St@(m^I5+v z09Cq{t^*+TnJ0%>1@mtN=HDopzbrL;1^O4%UNC=2hPJ#38&87-f%75$Ip5RSo`i9o z_x0w+MKK^|d%I3`S7*(zA=rxP;!TwBa*(a+Cp->l-mzzN(Vf^eehe9 zu)rf8o&$q?R(GjT*wyLH`Qi;g32zSCHy8EBNO(wP3E(FL*U{yl}Fz! zaqd7A{>ze2+FJT>v{vU9h!v5TNzxA?qvw)Dn6b5T#^!%$)7{y8IaTvvs$fHwbp4#=GDX!8`qG<+niN0LgM5?*5L3 zTZ$jAFEnMAp7J*_gX0ISw&B|N&51sWoBP*gsPxOY!Vc-j{`fz-f3BmEr?q;c*_xR} z$HBEex)jhj0ecGJvg9TNpUypc%fZ$3w|BD)xx*TK^@53$fk>AHNcWz>ei^Y}v~ngE zp!`}{j_vY2^>9^DY~F}{Koot$MAX*N?#trPeFSwNjLmLrZVXDkYOV(5{d$ lMvR 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%0D*9O9SWVV5ykYu@Kj;(~Vx%f* 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

i{7aL|GV79@=mDSS$0ptE!i2mlB2$``GHze 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

dU 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ð>p1O&8VZDIt%2a+2lJACej<@t<(?I6WfXPJ3tdb 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 XuM>Jt+xJ$Ogo!IY+5i8Qz0>~z@O2Of diff --git a/crates/cpex-wasm-plugin/specs/current-implementation-issues.md b/crates/cpex-wasm-plugin/specs/current-implementation-issues.md new file mode 100644 index 00000000..e1e13f3f --- /dev/null +++ b/crates/cpex-wasm-plugin/specs/current-implementation-issues.md @@ -0,0 +1,254 @@ +# Current WASM Plugin Implementation — Known Issues + +This document catalogs bugs, gaps, and design issues in the current CMF-based WASM plugin implementation (`cpex-wasm-host`, `cpex-wasm-plugin`). + +--- + +## Critical Issues + +### C1. `modified_extensions` always discarded (data loss) + +**File:** `cpex-wasm-host/src/conversions.rs` — `wit_result_to_native()` + +```rust +modified_extensions: None, // WIT modified_extensions would require OwnedExtensions conversion +``` + +The WIT `plugin-result` includes `modified-extensions: option`, and WASM plugins can return modified extensions. However, the host unconditionally sets `modified_extensions: None`, silently dropping any extension modifications (added security labels, injected HTTP headers, etc.). + +**Impact:** WASM plugins that attempt to modify extensions have no effect. The feature is dead code on the guest side. + +--- + +### C2. `metadata` field lost during result erasure + +**File:** `cpex-wasm-host/src/factory.rs` → calls `cpex_core::executor::erase_result()` + +The native `PluginResult

` has a `metadata: Option` field. The host correctly parses it from WIT. However, `erase_result()` creates an `ErasedResultFields` struct that has **no `metadata` field** — the value is permanently discarded before the executor processes it. + +**Impact:** WASM plugins cannot communicate metadata back to the pipeline. Any metadata they set is silently lost. + +--- + +### C3. 8 of 12 extension slots missing from WIT interface + +**File:** `cpex-wasm-host/wit/world.wit` — `extensions` record + +WIT defines only 4 extension slots: +- `request` +- `security` +- `http` +- `meta` + +Missing from WIT (present in native `Extensions`): +1. `agent` — Agent execution context (session, conversation, lineage) +2. `delegation` — Delegation chain and strategies +3. `mcp` — MCP entity metadata +4. `completion` — LLM completion information +5. `provenance` — Origin and message threading +6. `llm` — Model identity and capabilities +7. `framework` — Agentic framework context +8. `custom` — Custom extensions (HashMap) + +**Impact:** WASM plugins are completely blind to 8 extension categories. They cannot make decisions based on agent context, delegation chains, or LLM metadata. + +--- + +### C4. SecurityExtension fields silently dropped in conversion + +**Files:** `cpex-wasm-host/src/conversions.rs`, `cpex-wasm-plugin/src/conversions.rs` + +The native `SecurityExtension` has fields beyond what WIT models: +- `agent` / `client` / `caller_workload` / `this_workload` (workload identity) +- `objects` — HashMap of object security profiles +- `data` — HashMap of data policies + +The WIT `security-extension` only has: `labels`, `classification`, `subject`, `auth-method`. + +On the guest side, `wit_security_to_native` uses `..Default::default()` to zero out extra fields. If extensions ever round-trip through WASM (once C1 is fixed), this data will be permanently lost. + +**Impact:** Security-critical context (workload identity, object profiles, data policies) is invisible to WASM plugins and would be destroyed on round-trip. + +--- + +## Moderate Issues + +### M1. No context writeback from WASM plugins + +**Files:** `cpex-wasm-host/wit/world.wit` (`plugin-result` record), `cpex-wasm-host/src/factory.rs` + +The WIT `plugin-result` has no `modified-context` field. Native plugins receive `ctx: &mut PluginContext` and can modify state in-place. WASM plugins receive a read-only snapshot and have no mechanism to return state changes. + +**Impact:** WASM plugins cannot maintain stateful behavior across invocations (counters, caches, rate-limit tracking). They are forced to be purely stateless. + +--- + +### M2. MonotonicSet add-only invariant bypassed through WIT + +**Files:** `cpex-wasm-host/src/conversions.rs`, `cpex-wasm-plugin/src/conversions.rs` + +Native security labels use `MonotonicSet` (append-only — labels can be added but never removed). The WIT interface converts this to `list`. The WASM guest sees a plain list that it can freely modify — including removing labels. On the return path, `MonotonicSet::from_set(...)` accepts whatever the guest returns. + +**Impact:** A malicious or buggy WASM plugin can remove security labels, violating a core security invariant of the framework. The host does not enforce monotonicity on the return path. + +--- + +### M3. Epoch ticker thread leak (1 per plugin, never stopped) + +**File:** `cpex-wasm-host/src/sandbox_manager.rs` + +```rust +std::thread::spawn(move || loop { + std::thread::sleep(std::time::Duration::from_millis(1)); + engine_clone.increment_epoch(); +}); +``` + +Every `SandboxManager::new()` spawns a detached OS thread with an infinite loop (1ms sleep + epoch increment). No `JoinHandle` is stored, no shutdown flag exists, no way to stop it. The `Engine` clone keeps the engine alive even after `SandboxManager` is dropped. + +**Impact:** For N WASM plugins, N threads spin perpetually. High CPU wake-up frequency (1000 wakes/sec per plugin). Memory and handle leak on plugin unload. + +--- + +### M4. `Box::leak` for hook name strings (memory leak) + +**File:** `cpex-wasm-host/src/factory.rs` + +```rust +let leaked: &'static str = Box::leak(hook_name.clone().into_boxed_str()); +``` + +Every hook name string is leaked to obtain `&'static str`. These are never freed. + +**Impact:** If plugins are dynamically loaded/unloaded (hot reload), leaked strings accumulate indefinitely. Minor for static configurations, problematic for long-running services with dynamic plugin management. + +--- + +### M5. Fuel budget never resets between invocations + +**File:** `cpex-wasm-host/src/sandbox_manager.rs` + +Fuel is consumed cumulatively across all invocations of a plugin. Only the epoch deadline (timeout) is reset per call. Once fuel is exhausted, the plugin permanently traps on all subsequent invocations. + +**Impact:** Long-running plugins will eventually die with no recovery mechanism. No API to query remaining fuel or reset it. The only workaround is recreating the entire `SandboxManager`. + +--- + +### M6. `plugin_name` always `None` on violations from WASM + +**File:** `cpex-wasm-host/src/conversions.rs` — `wit_violation_to_native()` + +```rust +plugin_name: None, +``` + +The WIT `plugin-violation` record does not include `plugin_name`. The host sets it to `None`. Whether the executor fills this in later is unclear. + +**Impact:** Violation audit logs from WASM plugins may lack plugin attribution, making it harder to identify which plugin denied a request. + +--- + +### M7. HashSet/MonotonicSet uniqueness not enforced on guest side + +**Files:** Both conversion files + +Native types use `HashSet` (roles, permissions, teams, tags) and `MonotonicSet` (labels). WIT represents these as `list`. The guest operates on plain vectors with no uniqueness constraint. Duplicates introduced by the guest are silently deduplicated on return, but the guest's logic may behave incorrectly if it assumes unique values. + +**Impact:** Subtle logic bugs if guest code does list-length checks or iteration expecting uniqueness. + +--- + +## Minor Issues + +### m1. JSON parse errors silently swallowed + +**Files:** Both conversion files (10+ occurrences) + +```rust +serde_json::from_str(&s).unwrap_or_default() +``` + +Malformed JSON in fields like `arguments`, `content`, `annotations`, `messages`, or `metadata` is silently replaced with empty defaults. + +**Impact:** Plugin bugs that produce invalid JSON are invisible — no error, no log, just empty data. Makes debugging extremely difficult. + +--- + +### m2. `PluginError::Config` used for runtime errors + +**File:** `cpex-wasm-host/src/factory.rs` + +The `WasmBridgeHandler::invoke` method uses `PluginError::Config` for both configuration issues AND runtime invocation failures (WASM traps, serialization errors). + +**Impact:** Operators cannot distinguish between misconfiguration and runtime crashes in error logs/metrics. + +--- + +### m3. `eprintln!` debug output in production code + +**File:** `cpex-wasm-host/src/sandbox_manager.rs` (10+ occurrences) + +```rust +eprintln!("[SANDBOX] Loading WASM component..."); +eprintln!("[SANDBOX] Plugin instantiated successfully"); +``` + +Unconditional stderr writes with no log level or compile-time gating. + +**Impact:** Noisy output in production that cannot be suppressed without redirecting stderr. Should use `tracing` crate (already a dependency). + +--- + +### m4. No graceful shutdown for epoch ticker threads + +**File:** `cpex-wasm-host/src/sandbox_manager.rs` + +No `JoinHandle`, no `AtomicBool` stop flag, no `Drop` impl. Thread outlives its `SandboxManager`. + +**Impact:** Resource leak. Blocked clean shutdown of the host process (threads keep running until process exit). + +--- + +### m5. Duplicate conversion code (~1000 lines) + +**Files:** `cpex-wasm-host/src/conversions.rs` (484 lines), `cpex-wasm-plugin/src/conversions.rs` (598 lines) + +Near-identical functions for role/channel/resource-type mappings, content part conversions, extension mappings. No shared crate or macro. + +**Impact:** High maintenance burden. Any CMF schema change requires updating both files in lockstep. Drift bugs are likely over time. + +--- + +### m6. Plugin-side `http` extension coupled to `Guarded` internals + +**File:** `cpex-wasm-plugin/src/conversions.rs` + +```rust +http: ext.http.as_ref().map(|h| native_http_to_wit(h.read())), +``` + +Direct call to `.read()` on a `Guarded` type. If the guard pattern changes, this silently breaks at compile time (not the worst outcome, but tight coupling). + +**Impact:** Minor maintenance concern. + +--- + +## Resolution Mapping + +Issues addressed by the [Generalized Payload Interface spec](./generalized-payload-interface.md): + +| Issue | Addressed? | How | +|-------|-----------|-----| +| C1 | Yes | `hook-result` includes `modified-extensions` with full conversion | +| C2 | Partially | Metadata preserved in `hook-result`; `erase_result` in cpex-core also needs fixing | +| C3 | Yes | `extra: option` overflow field carries all missing extensions as JSON | +| C4 | Partially | Extra security fields carried in the `extra` JSON overflow | +| M1 | Yes | `modified-context: option` added to `hook-result` | +| M2 | No | Requires separate fix — host must validate monotonicity on return | +| M3 | No | Requires separate fix — stop flag + Drop impl | +| M4 | No | Requires separate fix — use `String` or `Arc` instead | +| M5 | No | Requires separate fix — fuel reset API or per-invocation budgeting | +| M6 | No | Requires separate fix — host sets plugin_name after WASM returns | +| M7 | Partially | Monotonicity enforcement needed regardless of payload generalization | + +Issues NOT addressed by the generalized payload spec should be tracked as separate work items. diff --git a/crates/cpex-wasm-plugin/specs/generalized-payload-interface.md b/crates/cpex-wasm-plugin/specs/generalized-payload-interface.md new file mode 100644 index 00000000..52963779 --- /dev/null +++ b/crates/cpex-wasm-plugin/specs/generalized-payload-interface.md @@ -0,0 +1,683 @@ +# Spec: Generalized WIT Interface for Arbitrary Payload Types + +## Problem Statement + +The current WASM plugin interface (`world.wit`) is hardcoded to `message-payload` (CMF MessagePayload). This means: + +1. **Only CMF payloads work** — the host bridge (`WasmBridgeHandler`) hardcodes `downcast_ref::()` +2. **No hook-name dispatch** — the guest receives no hook name, so the same function handles everything blindly +3. **Limited extensions** — only 4 of 12+ native extension types cross the WASM boundary +4. **No context writeback** — guest modifications to `PluginContext` are lost +5. **No lifecycle hooks** — WASM plugins cannot run initialize/shutdown logic +6. **Duplicate crate** — `cpex-payload` is a manual fork of cpex-core types that drifts over time + +Custom payload types like `ToolInvokePayload` (used in `cpex-core/examples/plugin_demo.rs`) cannot be processed by WASM plugins at all. + +## Solution Overview + +Two major changes: + +1. **Feature-gate cpex-core** so it compiles to `wasm32-wasip2` without runtime modules — eliminating the need for `cpex-payload` as a separate crate. Plugin authors use cpex-core directly, writing identical code for native and WASM targets. + +2. **Generalize the WIT interface** to handle any serializable payload type using a variant with a structured CMF fast-path and a generic JSON-bytes fallback, plus hook-name, extensions overflow, and context writeback. + +## Design Decisions + +| Decision | Choice | Rationale | +|----------|--------|-----------| +| Backwards compat | Break v1, single world | Early-stage project; no deployed plugins to preserve | +| Hook name delivery | WIT parameter (`hook-name: string`) | Explicit, simple; stored in WasmBridgeHandler as side-channel on host | +| Serialization format | JSON via `list` | Already used throughout (context, arguments, metadata); serde_json is a dep | +| CMF optimization | Structured WIT variant (no full-payload serialization) | CMF is the majority case; keep existing field-by-field conversion path | +| Extensions overflow | `extra: option` (JSON map) | Future-proof; avoids bloating WIT with 12+ record types | +| Guest SDK approach | Feature-gated cpex-core + registration macro | Plugin code is identical to native; no separate crate or different API | + +--- + +## Phase 0: Feature-Gate cpex-core for WASM Compilation + +**Goal:** Make `cpex-core` compilable to `wasm32-wasip2` by gating runtime-heavy modules behind a `runtime` feature, so WASM plugin authors can depend on cpex-core directly instead of the `cpex-payload` fork. + +### 0.1 Cargo.toml Changes (`crates/cpex-core/Cargo.toml`) + +```toml +[features] +default = ["runtime"] +runtime = ["dep:tokio", "dep:tokio-util", "dep:cpex-orchestration"] + +[dependencies] +tokio = { workspace = true, optional = true } +tokio-util = { workspace = true, optional = true } +cpex-orchestration = { path = "../cpex-orchestration", optional = true } + +# These remain always-on (WASM-compatible): +serde = { workspace = true } +serde_json = { workspace = true } +serde_yaml = { workspace = true } +async-trait = { workspace = true } +thiserror = { workspace = true } +tracing = { workspace = true } +uuid = { workspace = true } +futures = { workspace = true } +hashbrown = { workspace = true } +arc-swap = { workspace = true } +wildmatch = { workspace = true } +chrono = { workspace = true } +zeroize = "1.8" +``` + +### 0.2 Module Gating (`crates/cpex-core/src/lib.rs`) + +```rust +pub mod cmf; +pub mod config; +pub mod context; +pub mod error; +pub mod extensions; +pub mod hooks; +pub mod plugin; +pub mod plugins; + +// Runtime-only modules (need tokio, task spawning, orchestration) +#[cfg(feature = "runtime")] +pub mod executor; +#[cfg(feature = "runtime")] +pub mod manager; +#[cfg(feature = "runtime")] +pub mod registry; + +// Factory is available always (trait definition is sync) +// but its full implementation references registry types +pub mod factory; +``` + +### 0.3 Modules That Need Conditional Compilation + +| Module | What to gate | What remains | +|--------|-------------|--------------| +| `executor.rs` | Entire module | — | +| `manager.rs` | Entire module | — | +| `registry.rs` | `AnyHookHandler` trait + impls | Keep `HookEntry` struct definition if needed by factory | +| `hooks/adapter.rs` | `TypedHandlerAdapter` + its `AnyHookHandler` impl | — | +| `config.rs` | `load_config()` fn (uses `std::fs`) | `parse_config()` and all types remain | +| `plugin.rs` | Keep trait as-is | `#[async_trait]` compiles to WASM (just boxes the future); lifecycle methods won't be called across WASM boundary | +| `extensions/filter.rs` | `chrono::Utc::now()` call (line 809, test-only) | Gate behind `#[cfg(test)]` which already only runs on host | + +### 0.4 The `Plugin` Trait Decision + +Keep `#[async_trait]` on the `Plugin` trait unchanged. Rationale: +- `async-trait` generates `Pin>` which compiles to WASM +- WASM guest plugins implement `initialize()`/`shutdown()` as trivial no-ops (return `Ok(())`) +- These lifecycle methods are never called across the WASM boundary — the host manages lifecycle +- Avoids bifurcating the trait with `#[cfg]` which would complicate every plugin impl + +### 0.5 Impact on cpex-wasm-plugin + +Update `cpex-wasm-plugin/Cargo.toml`: + +```toml +[dependencies] +cpex-core = { path = "../cpex-core", default-features = false } # no runtime feature +wit-bindgen = "0.57" +wit-bindgen-rt = "0.44" +serde = { workspace = true } +serde_json = { workspace = true } +``` + +**Remove the `cpex-payload` dependency entirely.** All types (`MessagePayload`, `Extensions`, `PluginResult`, `PluginContext`, `HookHandler`, `HookTypeDef`, etc.) now come from `cpex-core`. + +### 0.6 What This Enables + +A WASM plugin author writes **identical code** to a native plugin: + +```rust +// crates/my-wasm-plugin/Cargo.toml +[dependencies] +cpex-core = { path = "../cpex-core", default-features = false } +cpex-wasm-plugin = { path = "../cpex-wasm-plugin" } # for the registration macro + +[lib] +crate-type = ["cdylib"] + +[package.metadata.component] +package = "cpex:plugin" +``` + +```rust +// crates/my-wasm-plugin/src/lib.rs +use cpex_core::hooks::trait_def::{HookHandler, PluginResult}; +use cpex_core::hooks::payload::Extensions; +use cpex_core::context::PluginContext; +use cpex_core::cmf::{MessagePayload, CmfHook}; +use cpex_core::plugin::{Plugin, PluginConfig}; +use cpex_wasm_plugin::register_wasm_plugin; + +struct MyPlugin; + +impl Plugin for MyPlugin { + fn config(&self) -> &PluginConfig { todo!() } +} + +impl HookHandler for MyPlugin { + async fn handle( + &self, + payload: &MessagePayload, + extensions: &Extensions, + ctx: &mut PluginContext, + ) -> PluginResult { + // Exact same code as a native plugin + PluginResult::allow() + } +} + +register_wasm_plugin!(MyPlugin, [CmfHook]); +``` + +### 0.7 Deprecation Path for `cpex-payload` + +1. Phase 0 makes cpex-core compilable without runtime +2. Update `cpex-wasm-plugin` to depend on cpex-core instead of cpex-payload +3. Update conversion code to use cpex-core types directly +4. Mark `cpex-payload` as deprecated +5. Remove `cpex-payload` once all references are migrated + +--- + +## Phase 1: WIT Interface Redesign + +**Files:** `crates/cpex-wasm-plugin/wit/world.wit`, `crates/cpex-wasm-host/wit/world.wit` + +### New Types + +```wit +record generic-payload { + payload-type: string, + payload-data: list, +} + +variant hook-payload { + cmf(message-payload), + generic(generic-payload), +} +``` + +### Modified: `extensions` + +```wit +record extensions { + request: option, + security: option, + http: option, + meta: option, + extra: option, // JSON-serialized map of additional extension types +} +``` + +### Replaced: `plugin-result` -> `hook-result` + +```wit +record hook-result { + continue-processing: bool, + modified-payload: option, + modified-extensions: option, + modified-context: option, + violation: option, + metadata: option, +} +``` + +### New Export Signature + +```wit +/// Known hook names (not exhaustive — hosts may define custom hooks): +/// +/// Legacy (typed payloads): +/// "tool_pre_invoke", "tool_post_invoke" +/// "prompt_pre_fetch", "prompt_post_fetch" +/// "resource_pre_fetch", "resource_post_fetch" +/// "identity_resolve", "token_delegate" +/// +/// CMF (MessagePayload): +/// "cmf.tool_pre_invoke", "cmf.tool_post_invoke" +/// "cmf.llm_input", "cmf.llm_output" +/// "cmf.prompt_pre_fetch", "cmf.prompt_post_fetch" +/// "cmf.resource_pre_fetch", "cmf.resource_post_fetch" +/// +world plugin { + import wasi:io/poll@0.2.6; + import wasi:io/error@0.2.6; + import wasi:io/streams@0.2.6; + import wasi:clocks/monotonic-clock@0.2.6; + import wasi:http/types@0.2.6; + import wasi:http/outgoing-handler@0.2.6; + + use types.{hook-payload, extensions, plugin-context, hook-result}; + + export handle-hook: func( + hook-name: string, + payload: hook-payload, + extensions: extensions, + ctx: plugin-context + ) -> hook-result; +} +``` + +--- + +## Phase 2: `WasmSerializablePayload` Trait + +**File:** `crates/cpex-core/src/hooks/payload.rs` + +### Trait Definition + +```rust +/// Opt-in trait for payloads that can cross the WASM serialization boundary. +/// Requires Serialize + Deserialize in addition to PluginPayload bounds. +pub trait WasmSerializablePayload: PluginPayload { + /// Type discriminator string used in the WIT generic-payload record. + fn payload_type_name() -> &'static str where Self: Sized; + + /// Serialize to JSON bytes for WASM transport. + fn to_wasm_bytes(&self) -> Result, serde_json::Error>; + + /// Deserialize from JSON bytes received from WASM. + fn from_wasm_bytes(bytes: &[u8]) -> Result where Self: Sized; +} +``` + +### Convenience Macro + +```rust +#[macro_export] +macro_rules! impl_wasm_payload { + ($ty:ty, $name:literal) => { + impl $crate::hooks::payload::WasmSerializablePayload for $ty { + fn payload_type_name() -> &'static str { $name } + fn to_wasm_bytes(&self) -> Result, serde_json::Error> { + serde_json::to_vec(self) + } + fn from_wasm_bytes(bytes: &[u8]) -> Result { + serde_json::from_slice(bytes) + } + } + }; +} +``` + +### Built-in Implementations + +```rust +// In crates/cpex-core/src/cmf/message.rs +impl_wasm_payload!(MessagePayload, "cmf.message"); +``` + +--- + +## Phase 3: Host-Side Changes (`cpex-wasm-host`) + +### 3.1 PayloadSerializerRegistry (new file: `src/payload_registry.rs`) + +Maps `TypeId` to serialization/deserialization functions so the host can handle any registered payload type: + +```rust +pub struct PayloadSerializerRegistry { + by_type_id: HashMap, + by_type_name: HashMap, +} + +struct PayloadCodec { + type_name: String, + type_id: TypeId, + serialize: Arc Result> + Send + Sync>, + deserialize: Arc Result> + Send + Sync>, +} + +impl PayloadSerializerRegistry { + pub fn register(&mut self) { ... } + pub fn serialize(&self, payload: &dyn PluginPayload) -> Result<(String, Vec)> { ... } + pub fn deserialize(&self, type_name: &str, bytes: &[u8]) -> Result> { ... } +} +``` + +### 3.2 SandboxManager (`src/sandbox_manager.rs`) + +Update `wasmtime::component::bindgen!` for new WIT world. Change invoke signature: + +```rust +pub async fn invoke( + &mut self, + hook_name: &str, + payload: types::HookPayload, + extensions: types::Extensions, + ctx: types::PluginContext, +) -> Result +``` + +Calls `instance.call_handle_hook(&mut store, hook_name, &payload, &extensions, &ctx)`. + +### 3.3 WasmBridgeHandler (`src/factory.rs`) + +Store `hook_name: String` per handler instance (set at registration from config). Invoke logic: + +```rust +async fn invoke(&self, payload: &dyn PluginPayload, extensions: &Extensions, ctx: &mut PluginContext) + -> Result, Box> +{ + // Build WIT payload: try CMF fast-path first, fall back to generic + let wit_payload = if let Some(cmf) = payload.as_any().downcast_ref::() { + types::HookPayload::Cmf(native_payload_to_wit(cmf)) + } else { + let (type_name, bytes) = self.registry.serialize(payload)?; + types::HookPayload::Generic(types::GenericPayload { + payload_type: type_name, + payload_data: bytes, + }) + }; + + let wit_ext = native_extensions_to_wit_v2(extensions); + let wit_ctx = native_context_to_wit(ctx); + + let result = self.sandbox.lock().await + .invoke(&self.hook_name, wit_payload, wit_ext, wit_ctx).await?; + + // Context writeback + if let Some(modified_ctx) = result.modified_context { + merge_wit_context_into_native(modified_ctx, ctx); + } + + // Convert result + let native_result = hook_result_to_native(result, &self.registry)?; + Ok(erase_result(native_result)) +} +``` + +### 3.4 Conversions (`src/conversions.rs`) + +New/modified functions: +- `native_extensions_to_wit_v2()` — populates `extra` with JSON of agent/mcp/completion/llm/framework/provenance extensions +- `wit_extensions_v2_to_native()` — deserializes `extra` back into the appropriate extension slots +- `hook_result_to_native()` — dispatches on `modified-payload` variant (CMF vs Generic) +- `merge_wit_context_into_native()` — context writeback + +--- + +## Phase 4: Guest-Side Changes (`cpex-wasm-plugin`) + +### 4.1 Registration Macro — Framework-Driven Dispatch + +The guest SDK provides a `register_wasm_plugin!` macro that mirrors cpex-core's `TypedHandlerAdapter` pattern. Plugin authors implement `HookHandler` (from cpex-core) and the macro generates the WIT glue with automatic dispatch: + +```rust +/// Register a WASM plugin with its supported hook types. +/// The macro generates the Guest impl that: +/// 1. Receives WIT types from the host +/// 2. Converts to native cpex-core types +/// 3. Routes to the correct HookHandler impl based on payload type +/// 4. Converts the PluginResult back to WIT +#[macro_export] +macro_rules! register_wasm_plugin { + ($plugin_ty:ty, [$($hook:ty),+ $(,)?]) => { + struct GuestImpl; + + impl Guest for GuestImpl { + fn handle_hook( + hook_name: String, + payload: WitHookPayload, + extensions: WitExtensions, + ctx: WitPluginContext, + ) -> WitHookResult { + let plugin = <$plugin_ty>::default(); + let native_ext = wit_extensions_to_native(extensions); + let mut native_ctx = wit_context_to_native(ctx); + + match payload { + WitHookPayload::Cmf(mp) => { + let native_payload = wit_payload_to_native(mp); + // Dispatch to the HookHandler impl whose Payload = MessagePayload + let result = cpex_wasm_plugin::dispatch_cmf( + &plugin, &hook_name, &native_payload, &native_ext, &mut native_ctx + ); + native_result_to_hook_result(result, &native_ctx) + } + WitHookPayload::Generic(gp) => { + // Dispatch based on payload_type string + let result = cpex_wasm_plugin::dispatch_generic::<$plugin_ty>( + &plugin, &hook_name, &gp.payload_type, &gp.payload_data, + &native_ext, &mut native_ctx + ); + generic_result_to_hook_result(result, &native_ctx) + } + } + } + } + export!(GuestImpl); + }; +} +``` + +### 4.2 Plugin Authoring Experience (Identical to Native) + +```rust +use cpex_core::prelude::*; // Plugin, HookHandler, PluginResult, Extensions, etc. +use cpex_wasm_plugin::register_wasm_plugin; + +struct MyPlugin; + +impl Plugin for MyPlugin { + fn config(&self) -> &PluginConfig { &DEFAULT_CONFIG } +} + +// CMF hook — same impl as a native plugin +impl HookHandler for MyPlugin { + async fn handle( + &self, + payload: &MessagePayload, + extensions: &Extensions, + ctx: &mut PluginContext, + ) -> PluginResult { + match ctx.get_local("hook_name").and_then(|v| v.as_str()) { + Some("cmf.tool_pre_invoke") => { /* pre-invoke logic */ } + Some("cmf.tool_post_invoke") => { /* post-invoke logic */ } + _ => {} + } + PluginResult::allow() + } +} + +// Custom payload hook — also same pattern as native +impl HookHandler for MyPlugin { + async fn handle( + &self, + payload: &ToolInvokePayload, + _extensions: &Extensions, + _ctx: &mut PluginContext, + ) -> PluginResult { + if payload.user.is_empty() { + return PluginResult::deny(PluginViolation::new("no_identity", "User required")); + } + PluginResult::allow() + } +} + +register_wasm_plugin!(MyPlugin, [CmfHook, ToolPreInvoke]); +``` + +### 4.3 How Dispatch Works Inside the Macro + +The `register_wasm_plugin!` macro expands to code that: + +1. For **CMF variant**: converts WIT `message-payload` → cpex-core `MessagePayload`, calls `>::handle()` +2. For **Generic variant**: looks at `payload_type` string, deserializes bytes using `WasmSerializablePayload::from_wasm_bytes()`, calls the matching `HookHandler::handle()` + +The dispatch table for generic payloads is built at compile time from the hook type list in the macro invocation. Each `HookTypeDef` in the list has an associated `Payload` type with a `payload_type_name()` — the macro generates a match arm per entry. + +### 4.4 Conversions (`src/conversions.rs`) + +Update to use cpex-core types directly (no more cpex-payload): +- `wit_payload_to_native()` → `cpex_core::cmf::MessagePayload` +- `wit_extensions_to_native()` → `cpex_core::hooks::payload::Extensions` +- `wit_context_to_native()` → `cpex_core::context::PluginContext` +- Handle `HookPayload` variant in both directions +- Include `modified-context` in result +- Handle `extra` extensions field + +--- + +## Phase 5: Examples + +### 5.1 Update existing demo (`crates/cpex-wasm-host/examples/wasm_plugin_demo.rs`) +- Adapt to new `HookPayload`/`HookResult` types from updated bindgen + +### 5.2 New generic payload demo (`crates/cpex-wasm-host/examples/wasm_generic_payload_demo.rs`) + +```rust +// Host side — same as plugin_demo.rs but routes through WASM +#[derive(Debug, Clone, Serialize, Deserialize)] +struct ToolInvokePayload { + tool_name: String, + user: String, + arguments: String, +} +impl_plugin_payload!(ToolInvokePayload); +impl_wasm_payload!(ToolInvokePayload, "cpex.tool_invoke"); + +// Register in PayloadSerializerRegistry, invoke through WASM pipeline +let mut registry = PayloadSerializerRegistry::new(); +registry.register::(); +registry.register::(); + +// The PluginManager invokes as normal — WasmBridgeHandler handles the rest +mgr.invoke::(payload, ext, None).await; +``` + +### 5.3 WASM guest plugin handling both CMF and custom payloads + +```rust +// Guest side — uses cpex-core directly +use cpex_core::hooks::trait_def::{HookHandler, PluginResult}; +use cpex_core::cmf::{MessagePayload, CmfHook}; + +struct DemoPlugin; + +impl Plugin for DemoPlugin { + fn config(&self) -> &PluginConfig { &DEFAULT_CONFIG } +} + +impl HookHandler for DemoPlugin { + async fn handle(&self, payload: &MessagePayload, ext: &Extensions, ctx: &mut PluginContext) + -> PluginResult + { + // Same identity_check logic that currently lives in cpex-payload + cpex_core::plugins::identity_checker::identity_check(payload, ext, ctx) + } +} + +impl HookHandler for DemoPlugin { + async fn handle(&self, payload: &ToolInvokePayload, _ext: &Extensions, _ctx: &mut PluginContext) + -> PluginResult + { + if payload.user.is_empty() { + return PluginResult::deny(PluginViolation::new("no_identity", "User required")); + } + PluginResult::allow() + } +} + +register_wasm_plugin!(DemoPlugin, [CmfHook, ToolPreInvoke]); +``` + +--- + +## Phase 6: Verification + +1. `cargo build -p cpex-core` — default features, no breakage for native users +2. `cargo build -p cpex-core --no-default-features --target wasm32-wasip2` — WASM compilation works +3. `cargo build -p cpex-wasm-host` — updated bindgen, factory, conversions compile +4. `cargo build -p cpex-wasm-plugin --target wasm32-wasip2` — guest compiles (now uses cpex-core) +5. Run `wasm_plugin_demo` example — CMF payload flows end-to-end +6. Run `wasm_generic_payload_demo` example — custom `ToolInvokePayload` crosses WASM boundary +7. `cargo test -p cpex-core` — all existing tests pass (they use default features) +8. `cargo test -p cpex-wasm-host` — round-trip serialization tests for generic payloads + +--- + +## Files Summary + +| File | Action | +|------|--------| +| `crates/cpex-core/Cargo.toml` | Add `runtime` feature, make tokio/tokio-util/cpex-orchestration optional | +| `crates/cpex-core/src/lib.rs` | `#[cfg(feature = "runtime")]` on executor, manager, registry modules | +| `crates/cpex-core/src/hooks/adapter.rs` | Gate `TypedHandlerAdapter` behind `#[cfg(feature = "runtime")]` | +| `crates/cpex-core/src/config.rs` | Gate `load_config()` behind `#[cfg(feature = "runtime")]` | +| `crates/cpex-core/src/hooks/payload.rs` | Add `WasmSerializablePayload` trait + `impl_wasm_payload!` macro | +| `crates/cpex-core/src/cmf/message.rs` | Add `impl_wasm_payload!(MessagePayload, "cmf.message")` | +| `crates/cpex-wasm-plugin/wit/world.wit` | Rewrite — new types + export signature | +| `crates/cpex-wasm-host/wit/world.wit` | Mirror of above | +| `crates/cpex-wasm-host/src/payload_registry.rs` | **New** — PayloadSerializerRegistry | +| `crates/cpex-wasm-host/src/sandbox_manager.rs` | Updated bindgen + invoke signature | +| `crates/cpex-wasm-host/src/factory.rs` | Payload dispatch, hook_name, registry usage | +| `crates/cpex-wasm-host/src/conversions.rs` | Generic payload + extensions overflow + context writeback | +| `crates/cpex-wasm-host/src/lib.rs` | Export payload_registry module | +| `crates/cpex-wasm-plugin/Cargo.toml` | Replace `cpex-payload` dep with `cpex-core` (no default features) | +| `crates/cpex-wasm-plugin/src/lib.rs` | `register_wasm_plugin!` macro + dispatch logic | +| `crates/cpex-wasm-plugin/src/conversions.rs` | Update to use cpex-core types; add HookPayload + context writeback | +| `crates/cpex-wasm-host/examples/wasm_plugin_demo.rs` | Update to new API | +| `crates/cpex-wasm-host/examples/wasm_generic_payload_demo.rs` | **New** — generic payload E2E demo | + +--- + +## Dependency Order + +``` +Phase 0 (Feature-gate cpex-core) + │ + ├─> Phase 1 (WIT redesign) ─┐ + │ │ + └─> Phase 2 (WasmSerializablePayload) ─┼─> Phase 3 (Host-side) ─> Phase 4 (Guest-side) ─> Phase 5 (Examples) ─> Phase 6 (Verify) + │ + └─> Can proceed in parallel +``` + +--- + +## Key Architecture Diagram + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ HOST PROCESS │ +│ │ +│ PluginManager │ +│ │ │ +│ ├─ invoke::(payload, ext, ctx) │ +│ │ │ +│ ▼ │ +│ WasmBridgeHandler (hook_name = "tool_pre_invoke") │ +│ │ │ +│ ├─ payload.downcast::()? │ +│ │ → YES: HookPayload::Cmf(native_payload_to_wit(p)) │ +│ │ → NO: registry.serialize(payload) │ +│ │ → HookPayload::Generic { type, bytes } │ +│ │ │ +│ ▼ │ +│ SandboxManager.invoke(hook_name, wit_payload, wit_ext, wit_ctx)│ +│ │ │ +│════╪═══════════════ WASM BOUNDARY ═══════════════════════════════│ +│ ▼ │ +│ Guest: handle-hook(hook_name, payload, extensions, ctx) │ +│ │ │ +│ ├─ Convert WIT → cpex-core native types │ +│ ├─ Match on hook-payload variant │ +│ │ → Cmf: call HookHandler::handle() │ +│ │ → Generic: deserialize, call HookHandler::handle() │ +│ ├─ Convert PluginResult → WIT hook-result │ +│ │ │ +│════╪═══════════════ WASM BOUNDARY ═══════════════════════════════│ +│ ▼ │ +│ WasmBridgeHandler │ +│ ├─ Context writeback (if modified) │ +│ ├─ hook_result_to_native() → PluginResult │ +│ └─ erase_result() → Box │ +│ │ +│ PluginManager continues pipeline... │ +└─────────────────────────────────────────────────────────────────┘ +``` diff --git a/crates/cpex-wasm-plugin/specs/implementation_phase.md b/crates/cpex-wasm-plugin/specs/implementation_phase.md new file mode 100644 index 00000000..50970cf0 --- /dev/null +++ b/crates/cpex-wasm-plugin/specs/implementation_phase.md @@ -0,0 +1,274 @@ +# Implementation Progress + +Tracks the phase-by-phase implementation of the [Generalized Payload Interface](./generalized-payload-interface.md) spec. + +--- + +## Phase 0: Feature-Gate cpex-core for WASM Compilation + +**Status:** Complete + +**Goal:** Make `cpex-core` compilable to `wasm32-wasip2` by gating runtime-heavy modules behind a `runtime` feature, so WASM plugin authors can depend on cpex-core directly. + +### Changes Made + +| File | Change | +|------|--------| +| `crates/cpex-core/Cargo.toml` | Added `[features]` section with `runtime` (default). Made `tokio`, `tokio-util`, `arc-swap`, `cpex-orchestration` optional deps gated behind `runtime`. | +| `crates/cpex-core/src/lib.rs` | Gated `executor`, `manager`, `registry`, `factory`, `visitor` modules behind `#[cfg(feature = "runtime")]`. | +| `crates/cpex-core/src/hooks/mod.rs` | Gated `adapter` module and `TypedHandlerAdapter` re-export behind `#[cfg(feature = "runtime")]`. | +| `crates/cpex-core/src/config.rs` | Gated `load_config()` function and `use std::path::Path` behind `#[cfg(feature = "runtime")]`. `parse_config()` remains always available. | +| `crates/cpex-core/src/delegation/payload.rs` | Gated `use crate::executor::PipelineResult` import and `from_pipeline_result()` method behind `#[cfg(feature = "runtime")]`. | +| `crates/cpex-core/src/identity/payload.rs` | Same gating as delegation — `PipelineResult` import and `from_pipeline_result()` method. | + +### Module Availability + +| Module | Without `runtime` | With `runtime` (default) | +|--------|-------------------|--------------------------| +| `cmf/` | Available | Available | +| `config` | Available (except `load_config()`) | Available | +| `context` | Available | Available | +| `delegation/` | Available (except `from_pipeline_result()`) | Available | +| `error` | Available | Available | +| `extensions/` | Available | Available | +| `hooks/` | Available (except `adapter` sub-module) | Available | +| `identity/` | Available (except `from_pipeline_result()`) | Available | +| `plugin` | Available | Available | +| `executor` | Excluded | Available | +| `factory` | Excluded | Available | +| `manager` | Excluded | Available | +| `registry` | Excluded | Available | +| `visitor` | Excluded | Available | + +### Verification Results + +| Check | Result | +|-------|--------| +| `cargo build -p cpex-core` | Pass — default features, all modules compile | +| `cargo build -p cpex-core --no-default-features` | Pass — no tokio pulled in, types-only build | +| `cargo build -p cpex-core --no-default-features --target wasm32-wasip2` | Pass — compiles to WASM component target | +| `cargo test -p cpex-core` | Pass — 12 unit tests + 6 doc-tests, 0 failures | + +### Usage + +```toml +# Native host (full runtime) — default +[dependencies] +cpex-core = { path = "../cpex-core" } + +# WASM guest plugin (types + traits only, no tokio) +[dependencies] +cpex-core = { path = "../cpex-core", default-features = false } +``` + +--- + +## Phase 1: WIT Interface Redesign + +**Status:** Complete + +**Goal:** Add `generic-payload`, `hook-payload` variant, `hook-name` parameter, extensions `extra` overflow field, and `hook-result` with context writeback to the WIT world. + +### Changes Made + +| File | Change | +|------|--------| +| `crates/cpex-wasm-plugin/wit/world.wit` | Added `generic-payload`, `hook-payload` variant, `extensions.extra`, `hook-result` with `modified-context`; export changed to `handle-hook` with `hook-name: string` parameter | +| `crates/cpex-wasm-host/wit/world.wit` | Mirror of plugin WIT | +| `crates/cpex-wasm-host/src/sandbox_manager.rs` | Updated `invoke()` to `(hook_name: &str, payload: HookPayload, extensions: Extensions, ctx: PluginContext) -> HookResult` | +| `crates/cpex-wasm-host/src/conversions.rs` | Added `extra: None` to `native_extensions_to_wit()`; replaced `wit_result_to_native()` with `wit_hook_result_to_native()` returning `(NativePluginResult, Option)`; added `wit_context_to_native()` and `wit_cmf_payload_to_native()` | +| `crates/cpex-wasm-host/src/factory.rs` | Added `hook_name` field to `WasmBridgeHandler`; per-hook handler instantiation; payload wrapped as `HookPayload::Cmf`; context writeback from `modified_context` | +| `crates/cpex-wasm-plugin/src/conversions.rs` | Added `native_result_to_hook_result()` (wraps payload as `HookPayload::Cmf`, includes `modified_context`); added `native_context_to_wit()`; added `extra: None` to extensions output | + +### Verification Results + +| Check | Result | +|-------|--------| +| `cargo build -p cpex-wasm-host` | Pass | +| `cargo build --target wasm32-wasip2` (from plugin dir) | Pass | + +### Post-Phase-3 WIT Redesign (v1 → v2) + +After Phase 3, the `extra: option` overflow field was replaced with explicit typed WIT records for all 8 overflow extension types. See changes below. + +| File | Change | +|------|--------| +| Both `wit/world.wit` files (v1) | Added 8 typed extension records; replaced `extra: option` with explicit fields | +| Both `wit/world.wit` files (v2 — full audit) | Added `client-extension`, `workload-identity`, `object-security-profile`, `data-policy`, `retention-policy`, `client-trust-level` enum; `security-extension` now has `client`, `caller-workload`, `this-workload`, `objects`, `data`; `plugin-violation` gains `plugin-name`; `plugin-context` changed from JSON strings to `list`; `authorization-detail` 4 list fields now `option>` to preserve None; `prompt-result.messages` kept as `string` (recursive WIT cycle constraint documented) | +| `crates/cpex-wasm-host/src/conversions.rs` | Full rewrite — all new security types, typed context entry conversion, plugin_name wired, client/workload/objects/data round-trip | +| `crates/cpex-wasm-plugin/src/conversions.rs` | Context conversion updated to `ContextEntry` list; `plugin_name` forwarded; new security fields stubbed as None/empty pending Phase 4 | +| `crates/cpex-wasm-host/Cargo.toml` | Added `chrono` dependency | + +--- + +## Phase 2: WasmSerializablePayload Trait + +**Status:** Complete + +**Goal:** Add opt-in `WasmSerializablePayload` trait + `impl_wasm_payload!` macro to `cpex-core/src/hooks/payload.rs`. Implement for `MessagePayload`. + +### Changes Made + +| File | Change | +|------|--------| +| `crates/cpex-core/src/hooks/payload.rs` | Added `WasmSerializablePayload` trait with `payload_type_name()`, `to_wasm_bytes()`, `from_wasm_bytes()`; added `impl_wasm_payload!($ty, $name)` macro | +| `crates/cpex-core/src/cmf/message.rs` | Added `impl_wasm_payload!(MessagePayload, "cmf.message")` | +| `crates/cpex-core/src/hooks/mod.rs` | Re-exported `WasmSerializablePayload` at the `hooks` level | + +### Verification Results + +| Check | Result | +|-------|--------| +| `cargo build -p cpex-core` | Pass | +| `cargo build -p cpex-core --no-default-features --target wasm32-wasip2` | Pass | +| `cargo test -p cpex-core` | Pass — 8 doc-tests (including 2 new for `WasmSerializablePayload` and `impl_wasm_payload!`) | + +--- + +## Phase 3: Host-Side Changes + +**Status:** Complete + +**Goal:** Update `cpex-wasm-host` — PayloadSerializerRegistry, SandboxManager invoke signature, WasmBridgeHandler dispatch logic, conversions for generic payloads + extensions overflow + context writeback. + +### Changes Made + +| File | Change | +|------|--------| +| `crates/cpex-wasm-host/src/payload_registry.rs` | **New** — `PayloadSerializerRegistry` with `register()`, `serialize(&dyn PluginPayload) -> (&'static str, Vec)`, `deserialize(type_name, bytes) -> Box`, `contains_type_id()` | +| `crates/cpex-wasm-host/src/lib.rs` | Exported `pub mod payload_registry` | +| `crates/cpex-wasm-host/src/conversions.rs` | `native_extensions_to_wit()` now calls `build_extra()` which serializes agent/mcp/completion/provenance/llm/framework/delegation/custom into `extra`; added `apply_extra_to_owned()` for the reverse path; `wit_hook_result_to_native()` now accepts `&PayloadSerializerRegistry`, handles `HookPayload::Generic` writeback, and wires `modified_extensions` via `wit_extensions_to_owned()` | +| `crates/cpex-wasm-host/src/factory.rs` | `WasmPluginFactory` gains `registry: Arc` field; `new()` takes it as a parameter; added `with_cmf_only()` convenience constructor; `WasmBridgeHandler` gains `registry` field; `invoke()` uses registry for generic payload path; passes registry to `wit_hook_result_to_native()` | +| `crates/cpex-wasm-host/examples/wasm_plugin_demo.rs` | Updated to `WasmPluginFactory::with_cmf_only()` | + +### Verification Results + +| Check | Result | +|-------|--------| +| `cargo build -p cpex-wasm-host` | Pass | +| `cargo build -p cpex-wasm-host --examples` | Pass | + +--- + +## Phase 4: Guest-Side Changes + +**Status:** Complete + +**Goal:** Update `cpex-wasm-plugin` to use cpex-core (no default features) instead of cpex-payload. Add `register_wasm_plugin!` macro with automatic dispatch to `HookHandler` impls. + +### Changes Made + +| File | Change | +|------|--------| +| `crates/cpex-wasm-plugin/Cargo.toml` | Replaced `cpex-payload` with `cpex-core` (no default features); added `async-trait`, `chrono` | +| `crates/cpex-wasm-plugin/src/conversions.rs` | Full rewrite using cpex-core types — all extension fields wired (agent, mcp, completion, provenance, llm, framework, delegation, security with client/workload/objects/data) | +| `crates/cpex-wasm-plugin/src/lib.rs` | Added `register_wasm_plugin!(PluginType, [HookType, ...])` macro generating the `Guest` impl; added `__block_on` synchronous async executor for WASM; replaced hand-written `Guest` impl with `register_wasm_plugin!(IdentityCheckerPlugin, [CmfHook])`; `IdentityCheckerPlugin` now uses `HookHandler` (identical to native plugin code) | + +### register_wasm_plugin! behaviour + +- For `HookPayload::Cmf`: converts WIT → cpex-core `MessagePayload`, calls `HookHandler::handle()`, converts `PluginResult` → WIT `HookResult` with context writeback +- For `HookPayload::Generic`: returns `allow()` (full generic dispatch pending Phase 5+) +- Async execution: driven to completion via `__block_on` spin-poll (safe in WASM — no I/O awaited, future completes on first poll) + +### Verification Results + +| Check | Result | +|-------|--------| +| `cargo build --target wasm32-wasip2` (from plugin dir) | Pass — 0 errors, 0 warnings | +| `cargo build -p cpex-wasm-host --examples` | Pass | + +--- + +## Phase 5: Examples + +**Status:** Complete + +**Goal:** Update existing `wasm_plugin_demo` and add new `wasm_generic_payload_demo` showing custom payloads crossing the WASM boundary. + +### Changes Made + +| File | Change | +|------|--------| +| `crates/cpex-wasm-host/examples/wasm_plugin_demo.rs` | Rewritten — clean structure, extracted `build_extensions()` helper, uses `cpex_core` imports directly (no `cpex_wasm_host` re-exports), correct `OwnedExtensions` handling in post-invoke step | +| `crates/cpex-wasm-host/examples/wasm_generic_payload_demo.rs` | **New** — defines `ToolInvokePayload` with `impl_plugin_payload!` + `impl_wasm_payload!`; defines `ToolPreInvoke` hook via `define_hook!`; registers both payloads in `PayloadSerializerRegistry`; invokes through WASM pipeline via the generic path (`HookPayload::Generic`) | + +### What each demo shows + +**`wasm_plugin_demo`** — CMF fast-path: `MessagePayload` → `HookPayload::Cmf` → WASM guest `IdentityCheckerPlugin` (Phase 4 macro-registered) → `PluginResult` → writeback. Full pre-invoke + post-invoke with context table threading. + +**`wasm_generic_payload_demo`** — Generic path: custom `ToolInvokePayload` → `PayloadSerializerRegistry.serialize()` → `HookPayload::Generic { payload_type: "cpex.tool_invoke", bytes }` → WASM guest logs receipt and returns allow(). Demonstrates the host-side infrastructure and wire format. Guest-side typed dispatch for generic payloads is the next milestone. + +### Verification Results + +| Check | Result | +|-------|--------| +| `cargo build -p cpex-wasm-host --examples` | Pass — 0 errors, 0 warnings | +| `cargo test -p cpex-core` | Pass — 8 tests | + +--- + +## Phase 6: End-to-End Verification + +**Status:** Complete + +**Goal:** Full compile + test + run verification across all crates and both targets. + +### Changes Made + +| File | Change | +|------|--------| +| `crates/cpex-wasm-host/config/config.yaml` | Added `tool_pre_invoke` to the plugin's hook list so the generic payload demo reaches the WASM boundary | + +### Verification Results + +| Check | Result | +|-------|--------| +| `cargo build -p cpex-core` | Pass | +| `cargo build -p cpex-core --no-default-features --target wasm32-wasip2` | Pass | +| `cargo build -p cpex-wasm-host` | Pass | +| `cargo build -p cpex-wasm-host --examples` | Pass — 0 errors, 0 warnings | +| `cargo build --target wasm32-wasip2` (from plugin dir) | Pass — 0 errors, 0 warnings | +| `cargo test -p cpex-core` | Pass — 8 passed, 0 failed | +| `cargo run --example wasm_plugin_demo` | Pass — CMF pre/post invoke flow runs end-to-end | +| `cargo run --example wasm_generic_payload_demo` | Pass — Generic payload crosses WASM boundary | + +### CMF Demo Output + +``` +=== WASM Plugin Demo — CMF MessagePayload === +=== cmf.tool_pre_invoke === +[WASM] handle_hook: cmf.tool_pre_invoke +[WASM] PRE-INVOKE: checking identity for 'get_compensation' +[WASM] Security labels: ["HR_DATA", "PII"] +[WASM] Subject: Some("alice"), Roles: ["hr_admin"] +[WASM] PRE-INVOKE ALLOWED +Pre-invoke: ALLOWED + + [tool executes: {"salary": 150000, "currency": "USD"}] + +=== cmf.tool_post_invoke === +[WASM] handle_hook: cmf.tool_post_invoke +[WASM] POST-INVOKE: verifying result from 'get_compensation' +[WASM] Result authorized for subject: Some("alice") +[WASM] POST-INVOKE ALLOWED +Post-invoke: ALLOWED + +=== Demo complete === +``` + +### Generic Payload Demo Output + +``` +=== WASM Plugin Demo — Generic Payload (ToolInvokePayload) === +PayloadSerializerRegistry: registered 'cmf.message' and 'cpex.tool_invoke' +Payload: ToolInvokePayload { tool_name: "get_compensation", user: "alice", arguments: "{\"employee_id\": 42}" } +Wire type: 'cpex.tool_invoke' (83 bytes when serialized) + +=== tool_pre_invoke via WASM (Generic path) === +[WASM] handle_hook: tool_pre_invoke +[WASM] generic payload 'cpex.tool_invoke' — returning allow() +Result: ALLOWED + (guest received Generic payload, logged receipt, returned allow()) + +=== Demo complete === +``` diff --git a/crates/cpex-wasm-plugin/specs/limitations.md b/crates/cpex-wasm-plugin/specs/limitations.md new file mode 100644 index 00000000..cdd1c63d --- /dev/null +++ b/crates/cpex-wasm-plugin/specs/limitations.md @@ -0,0 +1,160 @@ +# Implementation Limitations + +Tracks known gaps, stubs, and design constraints in the current WASM plugin implementation. +Entries are grouped by area and linked to the phase that will resolve them where applicable. + +--- + +## Critical + +### 1. Non-CMF Payloads Are Hard-Rejected + +**File:** `crates/cpex-wasm-host/src/factory.rs:161–169` + +`WasmBridgeHandler::invoke` downcasts to `MessagePayload` and returns a hard error for any other type. The `HookPayload::Generic` WIT variant exists but the host never populates it. All non-CMF hooks (`tool_pre_invoke`, `identity_resolve`, etc.) cannot be handled by WASM plugins. + +**Resolves in:** Phase 3 (`PayloadSerializerRegistry`) + Phase 2 (`WasmSerializablePayload`) + +--- + +## High + +### 2. `PayloadSerializerRegistry` Not Implemented ✓ Resolved in Phase 3 + +**File:** `crates/cpex-wasm-host/src/payload_registry.rs` + +Implemented. `register()`, `serialize()`, `deserialize()`, and `contains_type_id()` are all available. + +### 3. `WasmSerializablePayload` Trait Not Implemented ✓ Resolved in Phase 2 + +**File:** `crates/cpex-core/src/hooks/payload.rs` + +Implemented. `WasmSerializablePayload` trait + `impl_wasm_payload!` macro. `MessagePayload` registers as `"cmf.message"`. + +### 4. `register_wasm_plugin!` Macro Not Implemented ✓ Resolved in Phase 4 + +**File:** `crates/cpex-wasm-plugin/src/lib.rs` + +Implemented. `register_wasm_plugin!(PluginType, [HookType, ...])` generates the full `Guest` impl. CMF dispatch calls `HookHandler::handle()`. Generic dispatch returns allow() (full generic dispatch pending Phase 5+). + +### 5. Plugin Still Depends on `cpex-payload`, Not `cpex-core` ✓ Resolved in Phase 4 + +**Files:** `crates/cpex-wasm-plugin/Cargo.toml`, `src/conversions.rs` + +Migrated. `cpex-payload` dep removed entirely. `conversions.rs` now uses `cpex_core::` throughout with full extension coverage. + +### 6. Six Extension Types Silently Dropped at WASM Boundary ✓ Resolved (WIT redesign + Phase 3) + +**Files:** Both `wit/world.wit`, `crates/cpex-wasm-host/src/conversions.rs` + +All 8 overflow extension types now have explicit typed WIT records (`agent-extension`, `mcp-extension`, `completion-extension`, `provenance-extension`, `llm-extension`, `framework-extension`, `delegation-extension`). The host converts each field-by-field. The WIT contract is the authoritative schema — no JSON escaping. + +**Resolved in Phase 4:** Guest `conversions.rs` now uses cpex-core types throughout and wires all 8 extension fields in `wit_extensions_to_native()`. + +### 7. `modified_extensions` Writeback Not Implemented ✓ Resolved in Phase 3 + +**File:** `crates/cpex-wasm-host/src/conversions.rs` + +`wit_hook_result_to_native()` now converts a non-None `modified_extensions` into a native `OwnedExtensions` via `wit_extensions_to_owned()`, including the `extra` overflow field. + +### 8. Generic Payload Writeback Is a No-Op ✓ Partially Resolved in Phase 3 + +**File:** `crates/cpex-wasm-host/src/conversions.rs` + +Generic payload writeback now deserialized via `PayloadSerializerRegistry`. The current implementation downcasts the result to `MessagePayload` as a proof-of-concept; full type-erased writeback for arbitrary types requires Phase 4's `register_wasm_plugin!` macro to know the concrete return type at compile time. + +### 9. `hook_type_name()` Hardcoded to `"cmf"` + +**File:** `crates/cpex-wasm-host/src/factory.rs:201–203` + +`WasmBridgeHandler::hook_type_name()` always returns `"cmf"`. This value is used by the hook executor to match handlers to payload types. Handlers for non-CMF hooks will be misrouted or silently skipped. + +**Resolves in:** Phase 3/4 (tie `hook_type_name` to the registered hook's type definition) + +--- + +## Medium + +### 10. `global_state` Overwritten Without Merge on Context Writeback + +**File:** `crates/cpex-wasm-host/src/factory.rs:193–196` + +When a guest returns a modified context, the host overwrites both `local_state` and `global_state` from the returned values. `global_state` is shared across all plugins in a pipeline — overwriting it with a stale snapshot from one plugin can discard writes from a preceding plugin in the same hook chain. There is no key-level merge or delta strategy. + +### 11. Context Always Written Back (No Dirty Flag) + +**File:** `crates/cpex-wasm-plugin/src/conversions.rs:332` + +`native_result_to_hook_result()` always emits `modified_context: Some(...)`, even when the plugin made no changes. The host therefore always deserializes and overwrites context on every invocation. A "modified" flag or delta diff would avoid unnecessary serialization and prevent the overwrite race in item 10. + +### 12. Fuel Is a Session Budget, Not Per-Invocation + +**File:** `crates/cpex-wasm-host/src/sandbox_manager.rs:191–194` + +Fuel (Wasmtime's instruction-count limiter) is set once at plugin load time and consumed across all invocations for the lifetime of the sandbox. A plugin that handles many hooks will eventually exhaust the budget and trap. Only the epoch deadline is reset per call. Operators who expect per-call CPU isolation need to be aware of this. + +### 13. Epoch Ticker Thread Leaks Per Plugin + +**File:** `crates/cpex-wasm-host/src/sandbox_manager.rs:128–133` + +`SandboxManager::new()` spawns a `loop` thread with no join handle or shutdown channel. With N loaded plugins there are N permanently-running threads. They are never reclaimed if a plugin is unloaded. + +### 14. Single `Mutex` Serializes All Concurrent Calls + +**File:** `crates/cpex-wasm-host/src/factory.rs:87, 177` + +Each plugin has one `SandboxManager` behind a `Mutex`. Concurrent hook invocations for the same plugin queue behind the lock for the full WASM execution duration. There is no instance pool or connection-pool style concurrency. + +### 15. `unwrap_or_default()` Silences JSON Deserialization Errors + +**Files:** Both `conversions.rs` files, multiple sites + +All JSON deserialization in the conversion layer uses `unwrap_or_default()`, silently replacing a malformed or truncated payload with an empty default. A bug in serialization, a truncated buffer, or a type mismatch produces no error — it produces empty data that continues through the pipeline undetected. + +Affected fields include: tool-call arguments, resource annotations, prompt arguments, prompt messages, violation details, and plugin context maps. + +--- + +## Low + +### 16. `plugin_name` Always `None` in Violations ✓ Resolved (WIT audit) + +`plugin-violation` now carries a `plugin-name: option` field. `wit_violation_to_native()` reads it directly. The `WasmBridgeHandler` sets it via the guest's returned `PluginViolation`. Host-side attribution remains the responsibility of `WasmBridgeHandler.plugin_name` (still not injected at the host side — low priority for Phase 4). + +### 17. `Box::leak` for Hook Names Accumulates Per `create()` Call + +**File:** `crates/cpex-wasm-host/src/factory.rs:98` + +Each `WasmPluginFactory::create()` call leaks one `String` per declared hook name to satisfy the `&'static str` bound on handler registration. In test suites or hot-reload scenarios where plugins are created and destroyed repeatedly, these strings accumulate permanently. + +### 18. Debug `eprintln!` Calls in Production Paths + +**Files:** `crates/cpex-wasm-host/src/sandbox_manager.rs` (10 sites), `crates/cpex-wasm-plugin/src/lib.rs` (4 sites) + +Unconditional `eprintln!("[SANDBOX] ...")` and `eprintln!("[WASM] ...")` calls write to stderr on every plugin load and hook invocation. They bypass `tracing` and cannot be silenced at runtime without recompiling. + +--- + +## Remaining WIT Type Representation Notes + +These are deliberate design choices documented here to prevent confusion: + +| Item | Decision | Reason | +|---|---|---| +| `prompt-result.messages` is `string` not `list` | Kept as JSON string | `message → content-part → prompt-result` is a recursive cycle; WIT has no indirect reference type | +| `tool-call.arguments`, `tool-result.content`, `annotations` fields | `string` (JSON) | `serde_json::Value` has no direct WIT equivalent; the string encoding is explicit and documented in the WIT comments | +| `plugin-context` state entries | `list` with value as `string` | Each value is a `serde_json::Value` — typed per-entry rather than a single opaque blob | +| `delegation-extension.age-seconds` | `string` | Avoids f64 portability issues across WASM boundary | +| `delegation-hop.timestamp` | `string` (ISO 8601) | `DateTime` has no WIT primitive; ISO 8601 is the standard wire encoding | +| `delegation-strategy` custom variant | `strategy-custom: option` alongside enum | WIT `enum` cannot carry payload data; split-field is the canonical workaround | +| `client-trust-level` custom variant | `trust-level-custom: option` alongside enum | Same pattern as delegation-strategy | + +## Out-of-Scope Stubs (Pending Phases) + +| Phase | Deliverable | Status | +|-------|-------------|--------| +| Phase 2 | `WasmSerializablePayload` trait + `impl_wasm_payload!` macro in `cpex-core` | Complete | +| Phase 3 | `PayloadSerializerRegistry`, overflow extensions, generic payload + extensions writeback in host | Complete | +| Phase 4 | `register_wasm_plugin!` macro, migrate plugin from `cpex-payload` → `cpex-core` | Complete | +| Phase 5 | `wasm_plugin_demo` update, new `wasm_generic_payload_demo` example | Complete | +| Phase 6 | Full compile + test + run verification | Not started | diff --git a/crates/cpex-wasm-plugin/src/conversions.rs b/crates/cpex-wasm-plugin/src/conversions.rs index 88b2f8c8..baf76aa8 100644 --- a/crates/cpex-wasm-plugin/src/conversions.rs +++ b/crates/cpex-wasm-plugin/src/conversions.rs @@ -3,28 +3,48 @@ // SPDX-License-Identifier: Apache-2.0 // Authors: Shriti Priya // -// Bidirectional type conversions between WIT-generated types and native cpex-payload types. -// WIT types are flat/serialized (e.g., JSON strings for maps), while native types use +// Bidirectional type conversions between WIT-generated types and cpex-core native types. +// WIT types are flat/serialized (e.g., JSON strings for maps); native types use // proper Rust collections (HashMap, HashSet, Vec). use std::collections::{HashMap, HashSet}; use std::sync::Arc; -use cpex_payload::cmf::content as native_content; -use cpex_payload::cmf::enums as native_enums; -use cpex_payload::cmf::message as native_msg; -use cpex_payload::error::PluginViolation as NativePluginViolation; -use cpex_payload::extensions::container::Extensions as NativeExtensions; -use cpex_payload::extensions::http::HttpExtension as NativeHttpExtension; -use cpex_payload::extensions::meta::MetaExtension as NativeMetaExtension; -use cpex_payload::extensions::monotonic::MonotonicSet; -use cpex_payload::extensions::request::RequestExtension as NativeRequestExtension; -use cpex_payload::extensions::security::{ - SecurityExtension as NativeSecurityExtension, SubjectExtension as NativeSubjectExtension, - SubjectType as NativeSubjectType, +use chrono::DateTime; + +use cpex_core::cmf::content as native_content; +use cpex_core::cmf::enums as native_enums; +use cpex_core::cmf::message as native_msg; +use cpex_core::context::PluginContext as NativePluginContext; +use cpex_core::extensions::agent::AgentExtension as NativeAgentExtension; +use cpex_core::extensions::authorization::AuthorizationDetail as NativeAuthDetail; +use cpex_core::extensions::completion::{ + CompletionExtension as NativeCompletionExtension, StopReason as NativeStopReason, + TokenUsage as NativeTokenUsage, }; -use cpex_payload::context::PluginContext as NativePluginContext; -use cpex_payload::hooks::PluginResult as NativePluginResult; +use cpex_core::extensions::container::Extensions as NativeExtensions; +use cpex_core::extensions::delegation::{ + DelegationExtension as NativeDelegationExtension, DelegationHop as NativeDelegationHop, + DelegationStrategy as NativeDelegationStrategy, +}; +use cpex_core::extensions::framework::FrameworkExtension as NativeFrameworkExtension; +use cpex_core::extensions::http::HttpExtension as NativeHttpExtension; +use cpex_core::extensions::llm::LLMExtension as NativeLLMExtension; +use cpex_core::extensions::mcp::{ + MCPExtension as NativeMCPExtension, PromptMetadata as NativePromptMetadata, + ResourceMetadata as NativeResourceMetadata, ToolMetadata as NativeToolMetadata, +}; +use cpex_core::extensions::meta::MetaExtension as NativeMetaExtension; +use cpex_core::extensions::provenance::ProvenanceExtension as NativeProvenanceExtension; +use cpex_core::extensions::request::RequestExtension as NativeRequestExtension; +use cpex_core::extensions::security::{ + ClientExtension as NativeClientExtension, ClientTrustLevel as NativeClientTrustLevel, + DataPolicy as NativeDataPolicy, ObjectSecurityProfile as NativeObjectSecurityProfile, + RetentionPolicy as NativeRetentionPolicy, SecurityExtension as NativeSecurityExtension, + SubjectExtension as NativeSubjectExtension, SubjectType as NativeSubjectType, + WorkloadIdentity as NativeWorkloadIdentity, +}; +use cpex_core::hooks::trait_def::PluginResult as NativePluginResult; use crate::cpex::plugin::types::*; @@ -32,14 +52,10 @@ use crate::cpex::plugin::types::*; // WIT → Native: MessagePayload // --------------------------------------------------------------------------- -/// Converts a WIT MessagePayload into the native cpex-payload MessagePayload. pub fn wit_payload_to_native(payload: MessagePayload) -> native_msg::MessagePayload { - native_msg::MessagePayload { - message: wit_message_to_native(payload.message), - } + native_msg::MessagePayload { message: wit_message_to_native(payload.message) } } -/// Converts a WIT Message (role, content parts, channel) into native Message. fn wit_message_to_native(msg: Message) -> native_msg::Message { native_msg::Message { schema_version: msg.schema_version, @@ -49,7 +65,6 @@ fn wit_message_to_native(msg: Message) -> native_msg::Message { } } -/// Maps WIT Role enum to native Role enum. fn wit_role_to_native(role: Role) -> native_enums::Role { match role { Role::System => native_enums::Role::System, @@ -60,7 +75,6 @@ fn wit_role_to_native(role: Role) -> native_enums::Role { } } -/// Maps WIT Channel enum to native Channel enum. fn wit_channel_to_native(channel: Channel) -> native_enums::Channel { match channel { Channel::Analysis => native_enums::Channel::Analysis, @@ -69,29 +83,70 @@ fn wit_channel_to_native(channel: Channel) -> native_enums::Channel { } } -/// Converts each WIT ContentPart variant to its native equivalent. -/// Handles all 12 content types (text, thinking, tool-call, tool-result, etc.). fn wit_content_part_to_native(part: ContentPart) -> native_content::ContentPart { match part { ContentPart::Text(text) => native_content::ContentPart::Text { text }, ContentPart::Thinking(text) => native_content::ContentPart::Thinking { text }, ContentPart::ToolCall(tc) => native_content::ContentPart::ToolCall { - content: wit_tool_call_to_native(tc), + content: native_content::ToolCall { + tool_call_id: tc.tool_call_id, + name: tc.name, + arguments: serde_json::from_str(&tc.arguments).unwrap_or_default(), + namespace: tc.namespace, + }, }, ContentPart::ToolResult(tr) => native_content::ContentPart::ToolResult { - content: wit_tool_result_to_native(tr), + content: native_content::ToolResult { + tool_call_id: tr.tool_call_id, + tool_name: tr.tool_name, + content: serde_json::from_str(&tr.content) + .unwrap_or(serde_json::Value::String(tr.content)), + is_error: tr.is_error, + }, }, ContentPart::CmfResource(r) => native_content::ContentPart::Resource { - content: wit_resource_to_native(r), + content: native_content::Resource { + resource_request_id: r.resource_request_id, + uri: r.uri, + name: r.name, + description: r.description, + resource_type: wit_resource_type_to_native(r.resource_type), + content: r.content, + blob: r.blob, + mime_type: r.mime_type, + size_bytes: r.size_bytes, + annotations: serde_json::from_str(&r.annotations).unwrap_or_default(), + version: r.version, + }, }, ContentPart::ResourceRef(rr) => native_content::ContentPart::ResourceRef { - content: wit_resource_ref_to_native(rr), + content: native_content::ResourceReference { + resource_request_id: rr.resource_request_id, + uri: rr.uri, + name: rr.name, + resource_type: wit_resource_type_to_native(rr.resource_type), + range_start: rr.range_start, + range_end: rr.range_end, + selector: rr.selector, + }, }, ContentPart::PromptRequest(pr) => native_content::ContentPart::PromptRequest { - content: wit_prompt_request_to_native(pr), + content: native_content::PromptRequest { + prompt_request_id: pr.prompt_request_id, + name: pr.name, + arguments: serde_json::from_str(&pr.arguments).unwrap_or_default(), + server_id: pr.server_id, + }, }, ContentPart::PromptResult(pr) => native_content::ContentPart::PromptResult { - content: wit_prompt_result_to_native(pr), + content: native_content::PromptResult { + prompt_request_id: pr.prompt_request_id, + prompt_name: pr.prompt_name, + messages: serde_json::from_str(&pr.messages).unwrap_or_default(), + content: pr.content, + is_error: pr.is_error, + error_message: pr.error_message, + }, }, ContentPart::Image(img) => native_content::ContentPart::Image { content: native_content::ImageSource { @@ -127,50 +182,6 @@ fn wit_content_part_to_native(part: ContentPart) -> native_content::ContentPart } } -/// Deserializes the JSON arguments string into a HashMap for the native ToolCall. -fn wit_tool_call_to_native(tc: ToolCall) -> native_content::ToolCall { - let arguments: HashMap = - serde_json::from_str(&tc.arguments).unwrap_or_default(); - native_content::ToolCall { - tool_call_id: tc.tool_call_id, - name: tc.name, - arguments, - namespace: tc.namespace, - } -} - -/// Deserializes the JSON content string into a serde_json::Value for the native ToolResult. -fn wit_tool_result_to_native(tr: ToolResult) -> native_content::ToolResult { - let content: serde_json::Value = - serde_json::from_str(&tr.content).unwrap_or(serde_json::Value::String(tr.content.clone())); - native_content::ToolResult { - tool_call_id: tr.tool_call_id, - tool_name: tr.tool_name, - content, - is_error: tr.is_error, - } -} - -/// Converts a WIT CmfResource to native Resource, deserializing the annotations JSON string. -fn wit_resource_to_native(r: CmfResource) -> native_content::Resource { - let annotations: HashMap = - serde_json::from_str(&r.annotations).unwrap_or_default(); - native_content::Resource { - resource_request_id: r.resource_request_id, - uri: r.uri, - name: r.name, - description: r.description, - resource_type: wit_resource_type_to_native(r.resource_type), - content: r.content, - blob: r.blob, - mime_type: r.mime_type, - size_bytes: r.size_bytes, - annotations, - version: r.version, - } -} - -/// Maps WIT ResourceType enum to native ResourceType enum. fn wit_resource_type_to_native(rt: ResourceType) -> native_enums::ResourceType { match rt { ResourceType::File => native_enums::ResourceType::File, @@ -183,95 +194,114 @@ fn wit_resource_type_to_native(rt: ResourceType) -> native_enums::ResourceType { } } -/// Converts a WIT ResourceReference to native ResourceReference. -fn wit_resource_ref_to_native(rr: ResourceReference) -> native_content::ResourceReference { - native_content::ResourceReference { - resource_request_id: rr.resource_request_id, - uri: rr.uri, - name: rr.name, - resource_type: wit_resource_type_to_native(rr.resource_type), - range_start: rr.range_start, - range_end: rr.range_end, - selector: rr.selector, - } -} - -/// Converts a WIT PromptRequest to native, deserializing the arguments JSON string. -fn wit_prompt_request_to_native(pr: PromptRequest) -> native_content::PromptRequest { - let arguments: HashMap = - serde_json::from_str(&pr.arguments).unwrap_or_default(); - native_content::PromptRequest { - prompt_request_id: pr.prompt_request_id, - name: pr.name, - arguments, - server_id: pr.server_id, - } -} - -/// Converts a WIT PromptResult to native, deserializing the messages JSON string into Vec. -fn wit_prompt_result_to_native(pr: PromptResult) -> native_content::PromptResult { - let messages: Vec = - serde_json::from_str(&pr.messages).unwrap_or_default(); - native_content::PromptResult { - prompt_request_id: pr.prompt_request_id, - prompt_name: pr.prompt_name, - messages, - content: pr.content, - is_error: pr.is_error, - error_message: pr.error_message, - } -} - // --------------------------------------------------------------------------- -// WIT → Native: Extensions +// WIT → Native: Extensions (full coverage) // --------------------------------------------------------------------------- -/// Converts WIT Extensions to native Extensions, wrapping each sub-extension in Arc. pub fn wit_extensions_to_native(ext: Extensions) -> NativeExtensions { NativeExtensions { - request: ext.request.map(|r| Arc::new(wit_request_to_native(r))), + request: ext.request.map(|r| Arc::new(NativeRequestExtension { + environment: r.environment, + request_id: r.request_id, + timestamp: r.timestamp, + trace_id: r.trace_id, + span_id: r.span_id, + })), security: ext.security.map(|s| Arc::new(wit_security_to_native(s))), - http: ext.http.map(|h| Arc::new(wit_http_to_native(h))), - meta: ext.meta.map(|m| Arc::new(wit_meta_to_native(m))), + http: ext.http.map(|h| Arc::new(NativeHttpExtension { + request_headers: h.request_headers.into_iter().collect(), + response_headers: h.response_headers.into_iter().collect(), + })), + meta: ext.meta.map(|m| Arc::new(NativeMetaExtension { + entity_type: m.entity_type, + entity_name: m.entity_name, + tags: m.tags.into_iter().collect::>(), + scope: m.scope, + properties: m.properties.into_iter().collect::>(), + })), + agent: ext.agent.map(|a| Arc::new(wit_agent_to_native(a))), + mcp: ext.mcp.map(|m| Arc::new(wit_mcp_to_native(m))), + completion: ext.completion.map(|c| Arc::new(wit_completion_to_native(c))), + provenance: ext.provenance.map(|p| Arc::new(NativeProvenanceExtension { + source: p.source, + message_id: p.message_id, + parent_id: p.parent_id, + })), + llm: ext.llm.map(|l| Arc::new(NativeLLMExtension { + model_id: l.model_id, + provider: l.provider, + capabilities: l.capabilities, + })), + framework: ext.framework.map(|f| Arc::new(wit_framework_to_native(f))), + delegation: ext.delegation.map(|d| Arc::new(wit_delegation_to_native(d))), + custom: ext.custom.and_then(|s| serde_json::from_str(&s).ok()).map(Arc::new), ..Default::default() } } -/// Converts WIT RequestExtension (environment, request-id, timestamps, tracing) to native. -fn wit_request_to_native(r: RequestExtension) -> NativeRequestExtension { - NativeRequestExtension { - environment: r.environment, - request_id: r.request_id, - timestamp: r.timestamp, - trace_id: r.trace_id, - span_id: r.span_id, - } -} - -/// Converts WIT SecurityExtension to native, rebuilding MonotonicSet from the labels list. fn wit_security_to_native(s: SecurityExtension) -> NativeSecurityExtension { NativeSecurityExtension { - labels: MonotonicSet::from_set(s.labels.into_iter().collect()), + labels: cpex_core::extensions::monotonic::MonotonicSet::from_set( + s.labels.into_iter().collect(), + ), classification: s.classification, - subject: s.subject.map(wit_subject_to_native), + subject: s.subject.map(|sub| NativeSubjectExtension { + id: sub.id, + subject_type: sub.subject_type.map(wit_subject_type_to_native), + roles: sub.roles.into_iter().collect(), + permissions: sub.permissions.into_iter().collect(), + teams: sub.teams.into_iter().collect(), + claims: sub.claims.into_iter().collect(), + }), + client: s.client.map(|c| { + let trust_level = match c.trust_level_custom { + Some(s) => NativeClientTrustLevel::Custom(s), + None => match c.trust_level { + ClientTrustLevel::FirstParty => NativeClientTrustLevel::FirstParty, + ClientTrustLevel::ThirdParty => NativeClientTrustLevel::ThirdParty, + ClientTrustLevel::Internal => NativeClientTrustLevel::Internal, + }, + }; + NativeClientExtension { + client_id: c.client_id, + client_name: c.client_name, + trust_level, + authorized_scopes: c.authorized_scopes, + authorized_audiences: c.authorized_audiences, + roles: c.roles, + permissions: c.permissions, + teams: c.teams, + claims: c.claims.into_iter() + .map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v)))) + .collect(), + } + }), + caller_workload: s.caller_workload.map(wit_workload_to_native), + this_workload: s.this_workload.map(wit_workload_to_native), auth_method: s.auth_method, - ..Default::default() - } -} - -/// Converts WIT SubjectExtension to native, collecting lists into HashSets/HashMaps. -fn wit_subject_to_native(s: SubjectExtension) -> NativeSubjectExtension { - NativeSubjectExtension { - id: s.id, - subject_type: s.subject_type.map(wit_subject_type_to_native), - roles: s.roles.into_iter().collect::>(), - permissions: s.permissions.into_iter().collect::>(), - teams: s.teams.into_iter().collect::>(), - claims: s.claims.into_iter().collect::>(), + objects: s.objects.into_iter() + .map(|(k, v)| (k, NativeObjectSecurityProfile { + managed_by: v.managed_by, + permissions: v.permissions, + trust_domain: v.trust_domain, + data_scope: v.data_scope, + })) + .collect(), + data: s.data.into_iter() + .map(|(k, v)| (k, NativeDataPolicy { + apply_labels: v.apply_labels, + allowed_actions: v.allowed_actions, + denied_actions: v.denied_actions, + retention: v.retention.map(|r| NativeRetentionPolicy { + max_age_seconds: r.max_age_seconds, + policy: r.policy, + delete_after: r.delete_after, + }), + })) + .collect(), } } -/// Maps WIT SubjectType enum to native SubjectType enum. fn wit_subject_type_to_native(st: SubjectType) -> NativeSubjectType { match st { SubjectType::User => NativeSubjectType::User, @@ -281,22 +311,149 @@ fn wit_subject_type_to_native(st: SubjectType) -> NativeSubjectType { } } -/// Converts WIT HttpExtension (list of tuples) to native (HashMap-based headers). -fn wit_http_to_native(h: HttpExtension) -> NativeHttpExtension { - NativeHttpExtension { - request_headers: h.request_headers.into_iter().collect::>(), - response_headers: h.response_headers.into_iter().collect::>(), +fn wit_workload_to_native(w: WorkloadIdentity) -> NativeWorkloadIdentity { + NativeWorkloadIdentity { + spiffe_id: w.spiffe_id, + trust_domain: w.trust_domain, + attested_at: w.attested_at.as_deref() + .and_then(|s| DateTime::parse_from_rfc3339(s).ok()) + .map(|dt| dt.with_timezone(&chrono::Utc)), + attestor: w.attestor, + selectors: w.selectors, + client_id: w.client_id, + } +} + +fn wit_agent_to_native(a: AgentExtension) -> NativeAgentExtension { + use cpex_core::extensions::agent::ConversationContext; + NativeAgentExtension { + input: a.input, + session_id: a.session_id, + conversation_id: a.conversation_id, + turn: a.turn, + agent_id: a.agent_id, + parent_agent_id: a.parent_agent_id, + conversation: a.conversation.map(|c| ConversationContext { + history: c.history.iter() + .map(|s| serde_json::from_str(s).unwrap_or(serde_json::Value::String(s.clone()))) + .collect(), + summary: c.summary, + topics: c.topics, + }), } } -/// Converts WIT MetaExtension to native, collecting lists into HashSets/HashMaps. -fn wit_meta_to_native(m: MetaExtension) -> NativeMetaExtension { - NativeMetaExtension { - entity_type: m.entity_type, - entity_name: m.entity_name, - tags: m.tags.into_iter().collect::>(), - scope: m.scope, - properties: m.properties.into_iter().collect::>(), +fn wit_mcp_to_native(m: McpExtension) -> NativeMCPExtension { + NativeMCPExtension { + tool: m.tool.map(|t| NativeToolMetadata { + name: t.name, + title: t.title, + description: t.description, + input_schema: t.input_schema.and_then(|s| serde_json::from_str(&s).ok()), + output_schema: t.output_schema.and_then(|s| serde_json::from_str(&s).ok()), + server_id: t.server_id, + namespace: t.namespace, + annotations: t.annotations.into_iter() + .map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v)))) + .collect(), + }), + resource: m.resource_info.map(|r| NativeResourceMetadata { + uri: r.uri, + name: r.name, + description: r.description, + mime_type: r.mime_type, + server_id: r.server_id, + annotations: r.annotations.into_iter() + .map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v)))) + .collect(), + }), + prompt: m.prompt.map(|p| NativePromptMetadata { + name: p.name, + description: p.description, + arguments: p.arguments.and_then(|s| serde_json::from_str(&s).ok()), + server_id: p.server_id, + annotations: p.annotations.into_iter() + .map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v)))) + .collect(), + }), + } +} + +fn wit_completion_to_native(c: CompletionExtension) -> NativeCompletionExtension { + NativeCompletionExtension { + stop_reason: c.stop_reason.map(|r| match r { + StopReason::End => NativeStopReason::End, + StopReason::ReturnComplete => NativeStopReason::Return, + StopReason::Call => NativeStopReason::Call, + StopReason::MaxTokens => NativeStopReason::MaxTokens, + StopReason::StopSequence => NativeStopReason::StopSequence, + }), + tokens: c.tokens.map(|t| NativeTokenUsage { + input_tokens: t.input_tokens, + output_tokens: t.output_tokens, + total_tokens: t.total_tokens, + }), + model: c.model, + raw_format: c.raw_format, + created_at: c.created_at, + latency_ms: c.latency_ms, + } +} + +fn wit_framework_to_native(f: FrameworkExtension) -> NativeFrameworkExtension { + NativeFrameworkExtension { + framework: f.framework, + framework_version: f.framework_version, + node_id: f.node_id, + graph_id: f.graph_id, + metadata: f.metadata.and_then(|s| serde_json::from_str(&s).ok()).unwrap_or_default(), + } +} + +fn wit_delegation_to_native(d: DelegationExtension) -> NativeDelegationExtension { + NativeDelegationExtension { + chain: d.chain.into_iter().map(|hop| { + let strategy = match (hop.strategy, hop.strategy_custom) { + (Some(DelegationStrategy::TokenExchange), _) => Some(NativeDelegationStrategy::TokenExchange), + (Some(DelegationStrategy::ClientCredentials), _) => Some(NativeDelegationStrategy::ClientCredentials), + (Some(DelegationStrategy::SpiffeSvid), _) => Some(NativeDelegationStrategy::SpiffeSvid), + (Some(DelegationStrategy::Passthrough), _) => Some(NativeDelegationStrategy::Passthrough), + (Some(DelegationStrategy::Ucan), _) => Some(NativeDelegationStrategy::Ucan), + (Some(DelegationStrategy::TransactionToken), _) => Some(NativeDelegationStrategy::TransactionToken), + (None, Some(s)) => Some(NativeDelegationStrategy::Custom(s)), + (None, None) => None, + }; + NativeDelegationHop { + subject_id: hop.subject_id, + subject_type: hop.subject_type.map(wit_subject_type_to_native), + audience: hop.audience, + scopes_granted: hop.scopes_granted, + authorization_details: hop.authorization_details.into_iter() + .map(|a| NativeAuthDetail { + detail_type: a.detail_type, + locations: a.locations, + actions: a.actions, + datatypes: a.datatypes, + identifier: a.identifier, + privileges: a.privileges, + extra: a.extra + .and_then(|s| serde_json::from_str(&s).ok()) + .unwrap_or_default(), + }) + .collect(), + timestamp: DateTime::parse_from_rfc3339(&hop.timestamp) + .map(|dt| dt.with_timezone(&chrono::Utc)) + .unwrap_or_else(|_| chrono::Utc::now()), + ttl_seconds: hop.ttl_seconds, + strategy, + from_cache: hop.from_cache, + } + }).collect(), + depth: d.depth, + origin_subject_id: d.origin_subject_id, + actor_subject_id: d.actor_subject_id, + delegated: d.delegated, + age_seconds: d.age_seconds.parse().unwrap_or(0.0), } } @@ -304,40 +461,51 @@ fn wit_meta_to_native(m: MetaExtension) -> NativeMetaExtension { // WIT → Native: PluginContext // --------------------------------------------------------------------------- -/// Deserializes the JSON state strings into HashMaps for the native PluginContext. pub fn wit_context_to_native(ctx: PluginContext) -> NativePluginContext { - let local_state = serde_json::from_str(&ctx.local_state).unwrap_or_default(); - let global_state = serde_json::from_str(&ctx.global_state).unwrap_or_default(); NativePluginContext { - local_state, - global_state, + local_state: ctx.local_state.into_iter() + .map(|e| (e.key, serde_json::from_str(&e.value).unwrap_or(serde_json::Value::String(e.value)))) + .collect(), + global_state: ctx.global_state.into_iter() + .map(|e| (e.key, serde_json::from_str(&e.value).unwrap_or(serde_json::Value::String(e.value)))) + .collect(), } } // --------------------------------------------------------------------------- -// Native → WIT: PluginResult → PluginResult (record) +// Native → WIT: PluginResult → HookResult // --------------------------------------------------------------------------- -/// Converts the native PluginResult back to WIT, including any modified payload, -/// modified extensions, violation, or metadata. -pub fn native_result_to_wit(result: NativePluginResult) -> PluginResult { - PluginResult { +pub fn native_result_to_hook_result( + result: NativePluginResult, + ctx: &NativePluginContext, +) -> HookResult { + let modified_payload = result.modified_payload.map(|p| HookPayload::Cmf(native_payload_to_wit(p))); + HookResult { continue_processing: result.continue_processing, - modified_payload: result.modified_payload.map(native_payload_to_wit), - modified_extensions: result.modified_extensions.map(|ext| native_owned_extensions_to_wit(&ext)), - violation: result.violation.map(native_violation_to_wit), + modified_payload, + modified_extensions: result.modified_extensions.as_ref().map(native_owned_extensions_to_wit), + modified_context: Some(native_context_to_wit(ctx)), + violation: result.violation.map(|v| PluginViolation { + code: v.code, + reason: v.reason, + description: v.description, + details: serde_json::to_string(&v.details).unwrap_or_else(|_| "{}".to_string()), + plugin_name: v.plugin_name, + proto_error_code: v.proto_error_code, + }), metadata: result.metadata.map(|v| serde_json::to_string(&v).unwrap_or_default()), } } -/// Converts a native PluginViolation to WIT, serializing details HashMap to JSON string. -fn native_violation_to_wit(v: NativePluginViolation) -> PluginViolation { - PluginViolation { - code: v.code, - reason: v.reason, - description: v.description, - details: serde_json::to_string(&v.details).unwrap_or_else(|_| "{}".to_string()), - proto_error_code: v.proto_error_code, +fn native_context_to_wit(ctx: &NativePluginContext) -> PluginContext { + PluginContext { + local_state: ctx.local_state.iter() + .map(|(k, v)| ContextEntry { key: k.clone(), value: serde_json::to_string(v).unwrap_or_default() }) + .collect(), + global_state: ctx.global_state.iter() + .map(|(k, v)| ContextEntry { key: k.clone(), value: serde_json::to_string(v).unwrap_or_default() }) + .collect(), } } @@ -345,14 +513,10 @@ fn native_violation_to_wit(v: NativePluginViolation) -> PluginViolation { // Native → WIT: MessagePayload // --------------------------------------------------------------------------- -/// Converts a native MessagePayload back to WIT format for the return value. -fn native_payload_to_wit(payload: native_msg::MessagePayload) -> MessagePayload { - MessagePayload { - message: native_message_to_wit(payload.message), - } +pub fn native_payload_to_wit(payload: native_msg::MessagePayload) -> MessagePayload { + MessagePayload { message: native_message_to_wit(payload.message) } } -/// Converts a native Message back to WIT Message. fn native_message_to_wit(msg: native_msg::Message) -> Message { Message { schema_version: msg.schema_version, @@ -362,7 +526,6 @@ fn native_message_to_wit(msg: native_msg::Message) -> Message { } } -/// Maps native Role enum to WIT Role enum. fn native_role_to_wit(role: native_enums::Role) -> Role { match role { native_enums::Role::System => Role::System, @@ -373,7 +536,6 @@ fn native_role_to_wit(role: native_enums::Role) -> Role { } } -/// Maps native Channel enum to WIT Channel enum. fn native_channel_to_wit(channel: native_enums::Channel) -> Channel { match channel { native_enums::Channel::Analysis => Channel::Analysis, @@ -382,29 +544,58 @@ fn native_channel_to_wit(channel: native_enums::Channel) -> Channel { } } -/// Converts each native ContentPart variant back to its WIT equivalent. fn native_content_part_to_wit(part: native_content::ContentPart) -> ContentPart { match part { native_content::ContentPart::Text { text } => ContentPart::Text(text), native_content::ContentPart::Thinking { text } => ContentPart::Thinking(text), - native_content::ContentPart::ToolCall { content } => { - ContentPart::ToolCall(native_tool_call_to_wit(content)) - } - native_content::ContentPart::ToolResult { content } => { - ContentPart::ToolResult(native_tool_result_to_wit(content)) - } - native_content::ContentPart::Resource { content } => { - ContentPart::CmfResource(native_resource_to_wit(content)) - } - native_content::ContentPart::ResourceRef { content } => { - ContentPart::ResourceRef(native_resource_ref_to_wit(content)) - } - native_content::ContentPart::PromptRequest { content } => { - ContentPart::PromptRequest(native_prompt_request_to_wit(content)) - } - native_content::ContentPart::PromptResult { content } => { - ContentPart::PromptResult(native_prompt_result_to_wit(content)) - } + native_content::ContentPart::ToolCall { content } => ContentPart::ToolCall(ToolCall { + tool_call_id: content.tool_call_id, + name: content.name, + arguments: serde_json::to_string(&content.arguments).unwrap_or_else(|_| "{}".to_string()), + namespace: content.namespace, + }), + native_content::ContentPart::ToolResult { content } => ContentPart::ToolResult(ToolResult { + tool_call_id: content.tool_call_id, + tool_name: content.tool_name, + content: serde_json::to_string(&content.content).unwrap_or_default(), + is_error: content.is_error, + }), + native_content::ContentPart::Resource { content } => ContentPart::CmfResource(CmfResource { + resource_request_id: content.resource_request_id, + uri: content.uri, + name: content.name, + description: content.description, + resource_type: native_resource_type_to_wit(content.resource_type), + content: content.content, + blob: content.blob, + mime_type: content.mime_type, + size_bytes: content.size_bytes, + annotations: serde_json::to_string(&content.annotations).unwrap_or_else(|_| "{}".to_string()), + version: content.version, + }), + native_content::ContentPart::ResourceRef { content } => ContentPart::ResourceRef(ResourceReference { + resource_request_id: content.resource_request_id, + uri: content.uri, + name: content.name, + resource_type: native_resource_type_to_wit(content.resource_type), + range_start: content.range_start, + range_end: content.range_end, + selector: content.selector, + }), + native_content::ContentPart::PromptRequest { content } => ContentPart::PromptRequest(PromptRequest { + prompt_request_id: content.prompt_request_id, + name: content.name, + arguments: serde_json::to_string(&content.arguments).unwrap_or_else(|_| "{}".to_string()), + server_id: content.server_id, + }), + native_content::ContentPart::PromptResult { content } => ContentPart::PromptResult(PromptResult { + prompt_request_id: content.prompt_request_id, + prompt_name: content.prompt_name, + messages: serde_json::to_string(&content.messages).unwrap_or_else(|_| "[]".to_string()), + content: content.content, + is_error: content.is_error, + error_message: content.error_message, + }), native_content::ContentPart::Image { content } => ContentPart::Image(ImageSource { source_type: content.source_type, data: content.data, @@ -422,55 +613,15 @@ fn native_content_part_to_wit(part: native_content::ContentPart) -> ContentPart media_type: content.media_type, duration_ms: content.duration_ms, }), - native_content::ContentPart::Document { content } => { - ContentPart::Document(DocumentSource { - source_type: content.source_type, - data: content.data, - media_type: content.media_type, - title: content.title, - }) - } - } -} - -/// Serializes the native ToolCall arguments HashMap back to a JSON string for WIT. -fn native_tool_call_to_wit(tc: native_content::ToolCall) -> ToolCall { - ToolCall { - tool_call_id: tc.tool_call_id, - name: tc.name, - arguments: serde_json::to_string(&tc.arguments).unwrap_or_else(|_| "{}".to_string()), - namespace: tc.namespace, - } -} - -/// Serializes the native ToolResult content Value back to a JSON string for WIT. -fn native_tool_result_to_wit(tr: native_content::ToolResult) -> ToolResult { - ToolResult { - tool_call_id: tr.tool_call_id, - tool_name: tr.tool_name, - content: serde_json::to_string(&tr.content).unwrap_or_default(), - is_error: tr.is_error, - } -} - -/// Converts native Resource to WIT CmfResource, serializing annotations to JSON string. -fn native_resource_to_wit(r: native_content::Resource) -> CmfResource { - CmfResource { - resource_request_id: r.resource_request_id, - uri: r.uri, - name: r.name, - description: r.description, - resource_type: native_resource_type_to_wit(r.resource_type), - content: r.content, - blob: r.blob, - mime_type: r.mime_type, - size_bytes: r.size_bytes, - annotations: serde_json::to_string(&r.annotations).unwrap_or_else(|_| "{}".to_string()), - version: r.version, + native_content::ContentPart::Document { content } => ContentPart::Document(DocumentSource { + source_type: content.source_type, + data: content.data, + media_type: content.media_type, + title: content.title, + }), } } -/// Maps native ResourceType enum to WIT ResourceType enum. fn native_resource_type_to_wit(rt: native_enums::ResourceType) -> ResourceType { match rt { native_enums::ResourceType::File => ResourceType::File, @@ -483,115 +634,62 @@ fn native_resource_type_to_wit(rt: native_enums::ResourceType) -> ResourceType { } } -/// Converts native ResourceReference to WIT ResourceReference. -fn native_resource_ref_to_wit(rr: native_content::ResourceReference) -> ResourceReference { - ResourceReference { - resource_request_id: rr.resource_request_id, - uri: rr.uri, - name: rr.name, - resource_type: native_resource_type_to_wit(rr.resource_type), - range_start: rr.range_start, - range_end: rr.range_end, - selector: rr.selector, - } -} - -/// Converts native PromptRequest to WIT, serializing arguments HashMap to JSON string. -fn native_prompt_request_to_wit(pr: native_content::PromptRequest) -> PromptRequest { - PromptRequest { - prompt_request_id: pr.prompt_request_id, - name: pr.name, - arguments: serde_json::to_string(&pr.arguments).unwrap_or_else(|_| "{}".to_string()), - server_id: pr.server_id, - } -} - -/// Converts native PromptResult to WIT, serializing messages Vec to JSON string. -fn native_prompt_result_to_wit(pr: native_content::PromptResult) -> PromptResult { - PromptResult { - prompt_request_id: pr.prompt_request_id, - prompt_name: pr.prompt_name, - messages: serde_json::to_string(&pr.messages).unwrap_or_else(|_| "[]".to_string()), - content: pr.content, - is_error: pr.is_error, - error_message: pr.error_message, - } -} - // --------------------------------------------------------------------------- -// Native → WIT: OwnedExtensions → Extensions +// Native → WIT: OwnedExtensions (from PluginResult::modified_extensions) // --------------------------------------------------------------------------- -/// Converts native OwnedExtensions (used in modified results) back to WIT Extensions. fn native_owned_extensions_to_wit( - ext: &cpex_payload::extensions::container::OwnedExtensions, + ext: &cpex_core::extensions::container::OwnedExtensions, ) -> Extensions { Extensions { - request: ext.request.as_ref().map(|r| native_request_to_wit(r)), - security: ext.security.as_ref().map(|s| native_security_to_wit(s)), - http: ext.http.as_ref().map(|h| native_http_to_wit(h.read())), - meta: ext.meta.as_ref().map(|m| native_meta_to_wit(m)), - } -} - -/// Converts native RequestExtension to WIT RequestExtension. -fn native_request_to_wit(r: &NativeRequestExtension) -> RequestExtension { - RequestExtension { - environment: r.environment.clone(), - request_id: r.request_id.clone(), - timestamp: r.timestamp.clone(), - trace_id: r.trace_id.clone(), - span_id: r.span_id.clone(), - } -} - -/// Converts native SecurityExtension to WIT, collecting MonotonicSet labels into a list. -fn native_security_to_wit(s: &NativeSecurityExtension) -> SecurityExtension { - SecurityExtension { - labels: s.labels.iter().cloned().collect(), - classification: s.classification.clone(), - subject: s.subject.as_ref().map(native_subject_to_wit), - auth_method: s.auth_method.clone(), - } -} - -/// Converts native SubjectExtension to WIT, collecting HashSets into lists. -fn native_subject_to_wit(s: &NativeSubjectExtension) -> SubjectExtension { - SubjectExtension { - id: s.id.clone(), - subject_type: s.subject_type.as_ref().map(native_subject_type_to_wit), - roles: s.roles.iter().cloned().collect(), - permissions: s.permissions.iter().cloned().collect(), - teams: s.teams.iter().cloned().collect(), - claims: s.claims.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), - } -} - -/// Maps native SubjectType enum to WIT SubjectType enum. -fn native_subject_type_to_wit(st: &NativeSubjectType) -> SubjectType { - match st { - NativeSubjectType::User => SubjectType::User, - NativeSubjectType::Agent => SubjectType::Agent, - NativeSubjectType::Service => SubjectType::Service, - NativeSubjectType::System => SubjectType::System, - } -} - -/// Converts native HttpExtension (HashMaps) to WIT (list of tuples). -fn native_http_to_wit(h: &NativeHttpExtension) -> HttpExtension { - HttpExtension { - request_headers: h.request_headers.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), - response_headers: h.response_headers.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), - } -} - -/// Converts native MetaExtension (HashSets/HashMaps) to WIT (lists of strings/tuples). -fn native_meta_to_wit(m: &NativeMetaExtension) -> MetaExtension { - MetaExtension { - entity_type: m.entity_type.clone(), - entity_name: m.entity_name.clone(), - tags: m.tags.iter().cloned().collect(), - scope: m.scope.clone(), - properties: m.properties.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), + request: ext.request.as_ref().map(|r| RequestExtension { + environment: r.environment.clone(), + request_id: r.request_id.clone(), + timestamp: r.timestamp.clone(), + trace_id: r.trace_id.clone(), + span_id: r.span_id.clone(), + }), + security: ext.security.as_ref().map(|s| SecurityExtension { + labels: s.labels.iter().cloned().collect(), + classification: s.classification.clone(), + subject: s.subject.as_ref().map(|sub| SubjectExtension { + id: sub.id.clone(), + subject_type: sub.subject_type.as_ref().map(|st| match st { + NativeSubjectType::User => SubjectType::User, + NativeSubjectType::Agent => SubjectType::Agent, + NativeSubjectType::Service => SubjectType::Service, + NativeSubjectType::System => SubjectType::System, + }), + roles: sub.roles.iter().cloned().collect(), + permissions: sub.permissions.iter().cloned().collect(), + teams: sub.teams.iter().cloned().collect(), + claims: sub.claims.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), + }), + client: None, + caller_workload: None, + this_workload: None, + auth_method: s.auth_method.clone(), + objects: vec![], + data: vec![], + }), + http: ext.http.as_ref().map(|h| HttpExtension { + request_headers: h.read().request_headers.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), + response_headers: h.read().response_headers.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), + }), + meta: ext.meta.as_ref().map(|m| MetaExtension { + entity_type: m.entity_type.clone(), + entity_name: m.entity_name.clone(), + tags: m.tags.iter().cloned().collect(), + scope: m.scope.clone(), + properties: m.properties.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), + }), + agent: None, + mcp: None, + completion: None, + provenance: None, + llm: None, + framework: None, + delegation: None, + custom: None, } } diff --git a/crates/cpex-wasm-plugin/src/lib.rs b/crates/cpex-wasm-plugin/src/lib.rs index 5bab38d7..3e6e62d7 100644 --- a/crates/cpex-wasm-plugin/src/lib.rs +++ b/crates/cpex-wasm-plugin/src/lib.rs @@ -3,76 +3,138 @@ // SPDX-License-Identifier: Apache-2.0 // Authors: Shriti Priya // -// CPEX WASM Plugin — Entry point for the WebAssembly plugin component. +// CPEX WASM Plugin SDK. // -// Implements the Guest trait generated by wit-bindgen, which the WASM host -// calls via the exported `handle-hook` function. Converts between WIT types -// and native cpex-payload types, delegating to the actual plugin logic. +// Provides: +// - `register_wasm_plugin!(PluginType, [HookType, ...])` macro — generates +// the WIT `Guest` impl with automatic dispatch to `HookHandler` impls. +// - Conversion functions exposed for advanced use cases. +// +// Plugin authors implement `HookHandler` on their type (identical to native +// plugins) and call the macro once — the WIT glue is fully generated. -mod conversions; +pub mod conversions; +mod plugin; // Generate Rust bindings from the WIT world definition. -// This produces the `Guest` trait and all WIT types (MessagePayload, Extensions, etc.) -// that the plugin must implement and use. wit_bindgen::generate!({ path: "wit", world: "plugin", generate_all, }); -use conversions::{native_result_to_wit, wit_context_to_native, wit_extensions_to_native, wit_payload_to_native}; +pub use conversions::{ + native_payload_to_wit, native_result_to_hook_result, wit_context_to_native, + wit_extensions_to_native, wit_payload_to_native, +}; // --------------------------------------------------------------------------- -// Plugin Template +// register_wasm_plugin! — the core macro +// +// Generates a complete `Guest` impl that: +// 1. Receives WIT types from the host (hook-name, payload, extensions, ctx) +// 2. Converts WIT → cpex-core native types +// 3. Routes to the matching HookHandler based on the payload variant +// 4. Converts PluginResult → WIT HookResult (with context writeback) // -// To use a different plugin, change the function call below to point to your -// plugin function in cpex-payload. All plugin functions share the same signature: +// CMF dispatch: for HookPayload::Cmf, calls HookHandler::handle() +// Generic dispatch: for HookPayload::Generic, returns allow() (full generic +// dispatch requires compile-time type list matching — future work) // -// fn(&MessagePayload, &Extensions, &PluginContext) -> PluginResult +// Usage: +// register_wasm_plugin!(MyPlugin, [CmfHook]); +// --------------------------------------------------------------------------- + +#[macro_export] +macro_rules! register_wasm_plugin { + ($plugin_ty:ty, [$($hook_ty:ty),+ $(,)?]) => { + struct _WasmGuestImpl; + + impl Guest for _WasmGuestImpl { + fn handle_hook( + hook_name: String, + payload: HookPayload, + extensions: Extensions, + ctx: PluginContext, + ) -> HookResult { + use cpex_core::cmf::CmfHook; + use cpex_core::hooks::trait_def::HookHandler; + + eprintln!("[WASM] handle_hook: {}", hook_name); + + let native_ext = $crate::wit_extensions_to_native(extensions); + let mut native_ctx = $crate::wit_context_to_native(ctx); + + match payload { + HookPayload::Cmf(mp) => { + let native_payload = $crate::wit_payload_to_native(mp); + let plugin = <$plugin_ty>::default(); + + // WASM is single-threaded with no ambient async runtime. + // Drive the future to completion synchronously via __block_on. + let result = $crate::__block_on( + <$plugin_ty as HookHandler>::handle( + &plugin, + &native_payload, + &native_ext, + &mut native_ctx, + ) + ); + + $crate::native_result_to_hook_result(result, &native_ctx) + } + HookPayload::Generic(gp) => { + eprintln!("[WASM] generic payload '{}' — returning allow()", gp.payload_type); + HookResult { + continue_processing: true, + modified_payload: None, + modified_extensions: None, + modified_context: None, + violation: None, + metadata: None, + } + } + } + } + } + + export!(_WasmGuestImpl); + }; +} + +// --------------------------------------------------------------------------- +// __block_on — synchronous async executor for WASM // -// Example: to use the audit logger instead, replace the identity_check call with: -// cpex_payload::plugins::audit_logger::audit_log(&native_payload, &native_extensions, &native_ctx) +// Futures returned by HookHandler::handle() must be driven to completion +// synchronously. Current handlers await nothing in WASM context, so the +// future completes on the first poll in practice. // --------------------------------------------------------------------------- -struct Plugin; - -impl Guest for Plugin { - /// Entry point called by the WASM host for each hook invocation. - /// Converts WIT types to native Rust types, delegates to the plugin - /// logic in cpex-payload, and converts the result back to WIT. - fn handle_hook( - payload: MessagePayload, - extensions: Extensions, - ctx: PluginContext, - ) -> PluginResult { - eprintln!("[WASM] handle_hook called"); - eprintln!("[WASM] payload content parts {:?} : with length {}",payload.message.content, payload.message.content.len()); - - // Convert the WIT MessagePayload to native cpex-payload MessagePayload - let native_payload = wit_payload_to_native(payload); - eprintln!("[WASM] payload converted to native={:?}",native_payload); - - // Convert the WIT Extensions to native Extensions (wrapped in Arc) - let native_extensions = wit_extensions_to_native(extensions); - eprintln!("[WASM] extensions converted to native={:?}",native_extensions); - - // Convert the WIT PluginContext JSON strings to native HashMap-based context - let native_ctx = wit_context_to_native(ctx); - eprintln!("[WASM] context converted to native={:?}",native_ctx); - - // Call the plugin logic — swap this line to use a different plugin - eprintln!("[WASM] calling identity_check..."); - let result = cpex_payload::plugins::identity_checker::identity_check( - &native_payload, // the CMF message being processed - &native_extensions, // security, HTTP, meta, request extensions - &native_ctx, // plugin-local and global shared state - ); - eprintln!("[WASM] identity_check returned: continue_processing={}", result.continue_processing); - - // Convert the native PluginResult back to WIT for the host to consume - native_result_to_wit(result) +pub fn __block_on(f: F) -> F::Output { + use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; + + fn noop(_: *const ()) {} + fn noop_clone(_: *const ()) -> RawWaker { + RawWaker::new(std::ptr::null(), &VTABLE) + } + static VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop); + + let waker = unsafe { Waker::from_raw(RawWaker::new(std::ptr::null(), &VTABLE)) }; + let mut cx = Context::from_waker(&waker); + let mut pinned = std::pin::pin!(f); + + loop { + match pinned.as_mut().poll(&mut cx) { + Poll::Ready(val) => return val, + Poll::Pending => continue, + } } } -// Registers the Plugin struct as the WASM component's exported implementation of the Guest trait -export!(Plugin); +// --------------------------------------------------------------------------- +// Plugin registration +// --------------------------------------------------------------------------- + +use plugin::IdentityCheckerPlugin; + +register_wasm_plugin!(IdentityCheckerPlugin, [cpex_core::cmf::CmfHook]); diff --git a/crates/cpex-wasm-plugin/src/plugin.rs b/crates/cpex-wasm-plugin/src/plugin.rs new file mode 100644 index 00000000..590e670b --- /dev/null +++ b/crates/cpex-wasm-plugin/src/plugin.rs @@ -0,0 +1,110 @@ +// Location: ./crates/cpex-wasm-plugin/src/plugin.rs +// Copyright 2025 +// SPDX-License-Identifier: Apache-2.0 +// Authors: Shriti Priya +// +// IdentityCheckerPlugin — the bundled WASM plugin implementation. +// +// Implements HookHandler using the same trait that a native plugin +// would implement. No WIT types here — conversions are handled by the SDK. + +use async_trait::async_trait; + +use cpex_core::cmf::{CmfHook, MessagePayload}; +use cpex_core::context::PluginContext; +use cpex_core::error::{PluginError, PluginViolation}; +use cpex_core::extensions::container::Extensions; +use cpex_core::hooks::trait_def::{HookHandler, PluginResult}; +use cpex_core::plugin::{Plugin, PluginConfig}; + +pub struct IdentityCheckerPlugin; + +impl Default for IdentityCheckerPlugin { + fn default() -> Self { + Self + } +} + +static PLUGIN_CONFIG: std::sync::OnceLock = std::sync::OnceLock::new(); + +#[async_trait] +impl Plugin for IdentityCheckerPlugin { + fn config(&self) -> &PluginConfig { + PLUGIN_CONFIG.get_or_init(|| PluginConfig { + name: "identity-checker".to_string(), + kind: "wasm://plugin.wasm".to_string(), + hooks: vec!["cmf".to_string()], + ..Default::default() + }) + } + + async fn initialize(&self) -> Result<(), Box> { + Ok(()) + } + + async fn shutdown(&self) -> Result<(), Box> { + Ok(()) + } +} + +impl HookHandler for IdentityCheckerPlugin { + async fn handle( + &self, + payload: &MessagePayload, + extensions: &Extensions, + _ctx: &mut PluginContext, + ) -> PluginResult { + let is_result = payload.message.is_tool_result(); + + if is_result { + let tool_name = payload + .message + .get_tool_results() + .first() + .map(|tr| tr.tool_name.as_str()) + .unwrap_or("unknown"); + eprintln!("[WASM] POST-INVOKE: verifying result from '{}'", tool_name); + + if let Some(ref security) = extensions.security { + if let Some(ref subject) = security.subject { + eprintln!("[WASM] Result authorized for subject: {:?}", subject.id); + } + } + eprintln!("[WASM] POST-INVOKE ALLOWED"); + } else { + let tool_name = payload + .message + .get_tool_calls() + .first() + .map(|tc| tc.name.as_str()) + .unwrap_or("unknown"); + eprintln!("[WASM] PRE-INVOKE: checking identity for '{}'", tool_name); + + if let Some(ref security) = extensions.security { + let labels: Vec<&String> = security.labels.iter().collect(); + eprintln!("[WASM] Security labels: {:?}", labels); + + if let Some(ref subject) = security.subject { + eprintln!( + "[WASM] Subject: {:?}, Roles: {:?}", + subject.id, + subject.roles.iter().collect::>() + ); + + if security.has_label("PII") && !subject.roles.contains("hr_admin") { + return PluginResult::deny(PluginViolation::new( + "insufficient_role", + &format!( + "Tool '{}' requires 'hr_admin' role for PII data", + tool_name + ), + )); + } + } + } + eprintln!("[WASM] PRE-INVOKE ALLOWED"); + } + + PluginResult::allow() + } +} diff --git a/crates/cpex-wasm-plugin/wit/world.wit b/crates/cpex-wasm-plugin/wit/world.wit index 19f6b3d8..6d7f6b8e 100644 --- a/crates/cpex-wasm-plugin/wit/world.wit +++ b/crates/cpex-wasm-plugin/wit/world.wit @@ -5,10 +5,19 @@ // // WIT world definition for the CPEX plugin component. // Defines the types and exported function that the WASM host uses to invoke plugins. +// +// Supports arbitrary payload types via the hook-payload variant: +// - cmf: structured CMF MessagePayload (field-by-field conversion, no full-payload serialization) +// - generic: any serializable payload as JSON bytes with a type discriminator package cpex:plugin; interface types { + + // --------------------------------------------------------------------------- + // CMF enums + // --------------------------------------------------------------------------- + enum role { system, developer, @@ -33,12 +42,9 @@ interface types { artifact, } - enum subject-type { - user, - agent, - service, - system, - } + // --------------------------------------------------------------------------- + // CMF content parts + // --------------------------------------------------------------------------- record image-source { source-type: string, @@ -70,6 +76,7 @@ interface types { record tool-call { tool-call-id: string, name: string, + // arguments is a JSON object string (HashMap) arguments: string, namespace: option, } @@ -77,6 +84,7 @@ interface types { record tool-result { tool-call-id: string, tool-name: string, + // content is a JSON string (serde_json::Value) content: string, is-error: bool, } @@ -91,6 +99,7 @@ interface types { blob: option>, mime-type: option, size-bytes: option, + // annotations is a JSON object string (HashMap) annotations: string, version: option, } @@ -108,6 +117,7 @@ interface types { record prompt-request { prompt-request-id: string, name: string, + // arguments is a JSON object string (HashMap) arguments: string, server-id: option, } @@ -115,6 +125,9 @@ interface types { record prompt-result { prompt-request-id: string, prompt-name: string, + // messages is a JSON array string (Vec) — cannot be list + // because message → content-part → prompt-result is a recursive cycle, + // which WIT does not support without indirection. messages: string, content: option, is-error: bool, @@ -147,6 +160,41 @@ interface types { message: message, } + // --------------------------------------------------------------------------- + // Generic payload for arbitrary types + // --------------------------------------------------------------------------- + + record generic-payload { + payload-type: string, + payload-data: list, + } + + variant hook-payload { + cmf(message-payload), + generic(generic-payload), + } + + // --------------------------------------------------------------------------- + // Plugin context + // --------------------------------------------------------------------------- + + // State entries are typed key-value pairs (serde_json::Value serialized per + // entry as a JSON string) rather than a single opaque JSON blob. + record context-entry { + key: string, + // value is a JSON string representing serde_json::Value + value: string, + } + + record plugin-context { + local-state: list, + global-state: list, + } + + // --------------------------------------------------------------------------- + // Extensions — base four + // --------------------------------------------------------------------------- + record request-extension { environment: option, request-id: option, @@ -155,6 +203,13 @@ interface types { span-id: option, } + enum subject-type { + user, + agent, + service, + system, + } + record subject-extension { id: option, subject-type: option, @@ -164,11 +219,76 @@ interface types { claims: list>, } + // --------------------------------------------------------------------------- + // Security extension — full coverage + // --------------------------------------------------------------------------- + + enum client-trust-level { + first-party, + third-party, + internal, + // custom trust level — name carried in client-trust-level-custom on + // client-extension when this variant is not sufficient + } + + record client-extension { + client-id: string, + client-name: option, + trust-level: client-trust-level, + // custom trust level name when trust-level cannot represent it + trust-level-custom: option, + authorized-scopes: list, + authorized-audiences: list, + roles: list, + permissions: list, + teams: list, + // claims values are JSON strings (serde_json::Value per entry) + claims: list>, + } + + record workload-identity { + spiffe-id: option, + trust-domain: option, + // attested-at as ISO 8601 string (DateTime) + attested-at: option, + attestor: option, + selectors: list, + client-id: option, + } + + record object-security-profile { + managed-by: option, + permissions: list, + trust-domain: option, + data-scope: list, + } + + record retention-policy { + max-age-seconds: option, + policy: string, + delete-after: option, + } + + record data-policy { + apply-labels: list, + // None = all actions allowed; Some([]) = no actions allowed + allowed-actions: option>, + denied-actions: list, + retention: option, + } + record security-extension { labels: list, classification: option, subject: option, + client: option, + caller-workload: option, + this-workload: option, auth-method: option, + // object security profiles keyed by object name + objects: list>, + // data policies keyed by data element name + data: list>, } record http-extension { @@ -184,35 +304,218 @@ interface types { properties: list>, } + // --------------------------------------------------------------------------- + // Overflow extension types — typed + // --------------------------------------------------------------------------- + + record conversation-context { + // history entries are JSON strings (serde_json::Value per entry) + history: list, + summary: option, + topics: list, + } + + record agent-extension { + input: option, + session-id: option, + conversation-id: option, + turn: option, + agent-id: option, + parent-agent-id: option, + conversation: option, + } + + record tool-metadata { + name: string, + title: option, + description: option, + // input-schema and output-schema are JSON strings (serde_json::Value) + input-schema: option, + output-schema: option, + server-id: option, + namespace: option, + // annotation values are JSON strings (serde_json::Value) + annotations: list>, + } + + record resource-metadata { + uri: string, + name: option, + description: option, + mime-type: option, + server-id: option, + // annotation values are JSON strings (serde_json::Value) + annotations: list>, + } + + record prompt-metadata { + name: string, + description: option, + // arguments is a JSON array string (Vec) + arguments: option, + server-id: option, + // annotation values are JSON strings (serde_json::Value) + annotations: list>, + } + + record mcp-extension { + tool: option, + // renamed from `resource` to avoid WIT keyword conflict + resource-info: option, + prompt: option, + } + + // stop-reason: `return-complete` maps to Rust `StopReason::Return` + // (renamed to avoid the WIT `return` keyword) + enum stop-reason { + end, + return-complete, + call, + max-tokens, + stop-sequence, + } + + record token-usage { + input-tokens: u32, + output-tokens: u32, + total-tokens: u32, + } + + record completion-extension { + stop-reason: option, + tokens: option, + model: option, + raw-format: option, + created-at: option, + latency-ms: option, + } + + record provenance-extension { + source: option, + message-id: option, + parent-id: option, + } + + record llm-extension { + model-id: option, + provider: option, + capabilities: list, + } + + record framework-extension { + framework: option, + framework-version: option, + node-id: option, + graph-id: option, + // metadata is a JSON object string (HashMap) + metadata: option, + } + + record authorization-detail { + detail-type: string, + // option preserves None (no constraint) vs Some([]) (empty = deny all) + locations: option>, + actions: option>, + datatypes: option>, + identifier: option, + privileges: option>, + // extra holds flattened RFC 9396 extension fields as a JSON object string + extra: option, + } + + enum delegation-strategy { + token-exchange, + client-credentials, + spiffe-svid, + passthrough, + ucan, + transaction-token, + } + + record delegation-hop { + subject-id: string, + subject-type: option, + audience: option, + scopes-granted: list, + authorization-details: list, + // timestamp as ISO 8601 string (DateTime) + timestamp: string, + ttl-seconds: option, + strategy: option, + // carries the name when Rust DelegationStrategy::Custom(s) is used + strategy-custom: option, + from-cache: bool, + } + + record delegation-extension { + chain: list, + depth: u32, + origin-subject-id: option, + actor-subject-id: option, + delegated: bool, + // age-seconds as string to avoid f64 portability issues + age-seconds: string, + } + + // --------------------------------------------------------------------------- + // Extensions container + // --------------------------------------------------------------------------- + record extensions { request: option, security: option, http: option, meta: option, + agent: option, + mcp: option, + completion: option, + provenance: option, + llm: option, + framework: option, + delegation: option, + // custom: escape hatch for plugin-defined arbitrary key-value data + // (maps to cpex-core Extensions.custom: Option>) + custom: option, } - record plugin-context { - local-state: string, - global-state: string, - } + // --------------------------------------------------------------------------- + // Violation and result + // --------------------------------------------------------------------------- record plugin-violation { code: string, reason: string, description: option, + // details is a JSON object string (HashMap) details: string, + plugin-name: option, proto-error-code: option, } - record plugin-result { + // metadata is a JSON string (serde_json::Value) + record hook-result { continue-processing: bool, - modified-payload: option, + modified-payload: option, modified-extensions: option, + modified-context: option, violation: option, metadata: option, } } +/// Known hook names (not exhaustive - hosts may define custom hooks): +/// +/// Legacy (typed payloads): +/// "tool_pre_invoke", "tool_post_invoke" +/// "prompt_pre_fetch", "prompt_post_fetch" +/// "resource_pre_fetch", "resource_post_fetch" +/// "identity_resolve", "token_delegate" +/// +/// CMF (MessagePayload): +/// "cmf.tool_pre_invoke", "cmf.tool_post_invoke" +/// "cmf.llm_input", "cmf.llm_output" +/// "cmf.prompt_pre_fetch", "cmf.prompt_post_fetch" +/// "cmf.resource_pre_fetch", "cmf.resource_post_fetch" world plugin { import wasi:io/poll@0.2.6; import wasi:io/error@0.2.6; @@ -221,11 +524,12 @@ world plugin { import wasi:http/types@0.2.6; import wasi:http/outgoing-handler@0.2.6; - use types.{message-payload, extensions, plugin-context, plugin-result}; + use types.{hook-payload, extensions, plugin-context, hook-result}; export handle-hook: func( - payload: message-payload, + hook-name: string, + payload: hook-payload, extensions: extensions, ctx: plugin-context - ) -> plugin-result; + ) -> hook-result; } From 18988db87de8eb7b1f52c7bdbdb3a3789704221a Mon Sep 17 00:00:00 2001 From: Shriti Priya Date: Thu, 2 Jul 2026 19:51:54 -0400 Subject: [PATCH 7/7] fix: rebase to dev, add wasm files to gitignore Signed-off-by: Shriti Priya --- crates/cpex-wasm-host/wasm/plugin.wasm | Bin 660721 -> 0 bytes crates/cpex-wasm-plugin/Cargo.lock | 1005 ------------------------ crates/cpex-wasm-plugin/plugin.wasm | Bin 660721 -> 0 bytes 3 files changed, 1005 deletions(-) delete mode 100644 crates/cpex-wasm-host/wasm/plugin.wasm delete mode 100644 crates/cpex-wasm-plugin/Cargo.lock delete mode 100644 crates/cpex-wasm-plugin/plugin.wasm diff --git a/crates/cpex-wasm-host/wasm/plugin.wasm b/crates/cpex-wasm-host/wasm/plugin.wasm deleted file mode 100644 index e201bac6e3bc2df54c4eb47eee888bbeced3c691..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 660721 zcmeFa3!LQFRp0r4)w{a8=G~eZN&hO1?HN#8V_Tk)wO*K-k0p!X{ot_Ptad|^Y0tEz zo@cweC5?6!sb$MBek7KOV+0s7#!f7-O%&Dy5nvgx69hO}0ZtI$U^y6D;0MAMAc(>H z{hfRNkE*VIjtu@J>7METt$*Fex#ymH?m6e4du}kjaAqtBgW4;?qe&9f=gyrzJ^lLA zGjSN+5rjv=WMSs?ZDANB!Op*XW@hn@b0_{@mgU8-KYi}_sW58p3WKe8PcO{gID76o zeR<^Ii(mTULzSR579I_gpfWQ*e{Mbwo0DO%Ez4)`e8#Z4Gm3-I=L-rTYEMPs(Va=q zo;^Ev=i=1D;{43?nV2fa&fU3KeiOw}809SmQ8*iiZ-{oBo?cj-IyW~nKfO46?(EcU z)3c{%PEfph{PeknnG*rMYP(9O9E?FlHN|B-1hw#vFgzLF9ab}RG=gRlbTf1efb!zw zcg!3=HFfv=?BYz=3g`Y|crsAI$ne;xA3t|?apvseLOU2sf<2?FaD~T<*2ubVbXFUI z*|WD#-FEuUg*(EDaJF6)>mN+}YXVQ})7FRH;4P z4UfJn49`@m3->H6&YWqSn7M1_v_>Ymb75w_F}<)bYb_*;=gyr@f@J<2BuGoA7Q*?Y zt(Q~Br%#`rnmw_QoSi;1v(T8Hzx~cLLghlkUd&A&pMeA#dEPjPmz8zjTK-x#6!qB! zH~RM{LCZ?d&n(<|n$A|jGqvP4XeI&AuaEDXpRJ#mUYvgY^ukO$JvUoDGjrzL{5|#Q z`Ni4WrjIW|mXthq=lt=Rsl|KdfWIaJIMoPG-5t)iw&h>W&%EKz87Py^s8ZLXsC8mS zgEA*ljRtF~XTU}VNwaZg_Ka&IYA($FUo%s$zh`k~q1Bo`3;K=L7Zz%F&CD;TDro%p zncJqa*5jZZ&ezrgW~lwv{Pfw|XPAKWwbgJRX3m~y)!_~^#~07d)5_MY1)92TW`5@E zaWF8>5Nw&7KX+zsk=J>*myaP-0KlMxn#KnU#6c&Vua2mt<@4Ei)**_8>D&2}BdZwM zF`M2~J_}8ZwV zg8Jeevu97iNk|t{h>2dF5n*+Zl)|x~T^{ssVtDAnEkl?Nw*?i$Vz?uy7&gLPLB)C% zUJ=wY7{fhc&3=YX%|Uf>us1vt*Qd`;pS}lP(L8?cjAsAz{5_T1W_bydI(`TAKXW<` zt_tU_3{PDRa>f_n$7iOe{9ITtz7l2xl4eD^l=id{2fc7U%syQM-uxFpanI><(|L|-=gz8oQL~sdQDc!=&lEf}7u6T%VRxD>wS~Fqvu@gK$?`I!GiNpTfjh3i;Xr)SSByd(*>Wc8L{c_{Qv zYfX`$>*aMxuDw3-VCu~C30RP!T#h(+Wq9hk@YMedVD0I-xzqPdAvMmNUbt?8Rr=iB>g;jl zU-*`BdNEskzOIqi^{sFhgEp)4NOq|EF;7-v&zjLJ-dVQV`BJs=keJ z$PB`}ZT7gWb<6=r#J?!PPPFsUU_#OY>q&;pj|F2;+d}DcOBBG0AdxnzIXo@=yTJJV zIboXH z2P5HRar$=1&xkW>GDu9W#n~Cnq;Wu;F4y$GFmg5e!m4}Eb#(pA8JJ*HT|758dwhX0 z-_BTy-#MArc<6c9=XZps?g>vl0Um1=6Q>a!SS&TBO!0|sTYeC)EhUH++ z0tm}~YK4QfFP!!g?S1K_gXlMR6Xh|s+_ecIP`j|l?Q zI(4UHtT=dmcsb^s-#`4$9UI29>BQ z6C9PL`}p+S^y_C&&uW4(U&cA5>B zm5ng)Y%5BXx^kL{2LUajgF2z1fy(4$^>3+V%(5XmmhQ0dEltP%Z<_^VIKu2*ux+H< zZ>dn{{It5AEn#k=yQ<^hYI5dfXil5o3g3N?|jyPbt;E}Z)#$l#A5gJv^Z@>!n;C5C#o z!bZqrT7IX%8|(dv>voLl!@`#lK{HWjp|!Yp8Y+g_ut3$broR$QWXWumZ{CKQftCpW zK$BzH$>F`@+*}+ihI4-+YOR2i*|UEdR8GvyE#7g@7UY84XU|R*z(*Z=er~?}rr|4_ zIdOjr^3T9oU=T{plm`3m@YFYlr+x+0e4PLPf&ZW2|EKu>IsX3v|3Aqg{osf3j@5g=7@J;q2&wPqS&Ed zi|3qfPIV_{-iAKX@j33AJqH7!|8ekk8aNQ1dNYr=^8X(JWObXGpFMl$j3gdJy9HF6 z`#W$0qy~f$FPH!0a9jSWK*u}6BBSrcLt!It@SP#5rdx5Cg)I}{SvJgK@V#MkXBccb zJ~wmzMhED1Hk&Ke+CJG*oLrq5QBV^WL2oYYV^Fy|40d~i%ItNeue>FtR=YAh8e>zE zX|AdvxN`yH9R`z`^RtU#$;5^rbe-P$g7xZ_Fb<+@$u&b-?O=G+>|JOq17FDSG8@X$ zLkAlIa$)hr?Ab7GUJ(Y{bMWk?ufJK{s`|4v#2J!mFZB(e`VM|G&_dIOV3&t(Yjryfw}zpv9q}!1L$b@O^*tJDdfRZ*WDAoOdOa|v+t+A($0i!r7_ZWJ zG1xn6hczkZQ6Svhd=yFFACT_2Ge|`}H?CmfX)za4hC8uj~`gP#*&Of8VjdeH|RDDsNWkzTjAe5BE`6PrY_e zN~!+)T%GqFtJ$~uKDy7Ez1O#-qILfz$>jcf4Tkvs`=}cT?#mja##WY(Cm$Q`<9*6m z2L}7$eTA~Ti{4$|xyGPfKRD3As6CH^+y8E^X`DdY`+E@Ox%ptkHp;>VoOW;tA6`z^uheDC0uLxpo*VJ!!1FusFVtyJ6r5v;Nv9vogk zGh7cWeg|!B9EsZ%wzO74)7o1;K{RqNg+W1N(B9bqW?yVOJwZIW%(}LGR%&pqjh~=8 zY8^jVEcQXoK{d1rA1p1_Ls>GunrGofP#dvk7K_ip5o^{+f+?*T2S+V7D;3INT2@HF zt-W`3Q0zXaug2Q7Z0F#=N*#m%UN?9^)7PZUKh``_ZLmVWsv(trCQ*|KEN zY&C;s6ot(w48lfGCC~C7GWee&YB-|`>Jkyt%Z(9~MN4=4(gIF22Lto@J;T>S(Q-RGynibJ9r zSR>;IZM;mv3b~rZ6;&ey3ib_J8{%# z){v9qu+gjtnUvc>joN^3G^r(yBhvJbc9S@Xg%qncj1dF*z8Xg{&x9|mv4mt2C2=KS zv#mkRd<+_mMo6WMsQn5Gx>BvxYSn7B9yBVopi&`6RdCX3HQ|>}Ad+o#Xs-zkGtfXw zD@ili*=mlDZw+_F@veAMov%P`)oL@DP|*+sM9~!hwmY0iCiW!Bc&Jg(xM(nto*1)g zb#HhjpHw5*%6^jMs)T*g@F)1K#CQ@^e=556{;sW7((Q`31}Ml>Pm*{eAXL4&-Njo}cy4C;jvHEZ1l3@6u;o`3L>;IbZhT z54k#)mL9i!k68a7`i+m;r%UhqMSFhG-hMc2)s7^7PdW;A9LL7>59ZD-EdB#*KqpRP zoMxvfczvt<={UA9a{97{NeDk(T|1yK6*5~7``WbcliGB z2g3gl{UpQs{o&7t|04Y7;m4zY6aG;6cOQ)35$Wv{GMd`ccZ7`r{f2bwg1tXOb@^k0k#k`R>a1 zRDLhHPxMZd3)s_Rem&Cs(dYZN9B)`&nJJ7{9*FJM9E>(V^@{5&^Rz6nwx0TIbSnU;UoyPgj4n z+WeCryy~Z5@U8!Xx5@q2Eb$ZcJEwZdK1-%?KR6WC(>RUyN89`1$wSfhH0dWN`J&CA z&ez^Vfi&44)w^+YBs!vG(yR2NSC0j0(0@0zoIeziOyfgQ(hgh7(C${1m)wieh~Fr`e~dmC|zO zDFLoF^Ka;HzjM+dv-JU%Wn}&&!*hD77rCzl!r*Tj?vH|A4EF(p3;i7$iCgyc?>{*{ z0XuX2T-tVpg-J;3Uw!hL2gn=rA9?ztOTo#`_v=G9=9}=wAOJiYfSUDIO{L*fko4kl zI6F$W%T`ym4%HV?^miD!O0V?B~?MMx@s z^|IZo_W$pbDBr4=CsBO%eV01qhI@4{H6zwQk5VU;7}^nBqyf^tJ!bW{FbkVPYYI! zY z?vP_hW5$m_Ox1O=I)E6D6!!?)G?wAUgIM6xC)g~0x`W{6! zLy>A5Mp0!Y6j8h8kOp9ohC!Rr3bV|k!N{cZ%ff9MiygspaRTvXFAijF8z@39~NeK(Geu0H{MjjSmx0XW)?&*peLdiB{_Z5;vs3wyiSdHN?1lTw?0R<&)BjVwG_ayOk#KD~*GEt+i$? zzzxna;09+IaD%f9xWQQl+*YG9;5nxi4U7IOj8^yFX5OnCSY);$x-v!Q0Qp;}vWK3w?9>M~V(cs{@$B9jE%JPF!1W6ES@ zlvpM!qr@^<86{Rq6T~Qy#Xig`9h@=cSyT11ChOr^^Jc9MT7Sy(#`;s9H`bqW2dzKl z4qAW89kl+GJ7_bj+`*M*)~{x>V%3>txmKK6zm(On>ddlS-;guwlhv@2t^7{~g&K$y zcq4)Y^1gJbL`vb_G&!o3d?`GPPAG|_Yb+{3?oIla&=wL@#LjP`lmwz8qoiaiI3gjx z|3q-ITkE&G^*H~c?*txKwt_||O|chtzK=Fc#e5aA2JeobkC;q!^H^e)#!lP1Srwly&1+PY27;D8|^Lc(08 zMG6K0dzGs#1&RG`T{KuzcW*cx*Inx1;aC;(E98G-<0Hi*epYT&qX1K>#ge=5kt z>NjIDMS{CVvnJ>tEKAlfxsJqYO|RHZuedb5Vu+9T%%`UZ=2JWsq+VXi3DW;Cytk7vyQO#)Ta05~EB`g_ z2UpU#7Pgx4oq-nx>WyZrJvKhk*|K%p_8kBt(+e~dN!+E+DQjMzPnTetQU7aEX34qu zMM(%=&kEDv=(wgPlM_o;dl%KbglfDEM%7>_xn-l(?4+8Pg_m8;4yt)M>e^*Ovz=-X z(J!}}ZB%p6a8vo2 zS8SYc#;NC<87f)1d_C1oR5M03((`RL9Eu*+oL>sRZLG=jTi?)Mx_=&fP}r~D)vd!Z z_^$IEFz6_)-57j73p>m3rEvbn;9(3i-LQ?!9QNCHbsN0*rCl+sY2Tie`vEKG%QyN@ z`SP(VZ&m-G&th2{zA9fL>~FoRKj+#6?lvoc^#Cx@)4H-)l4u`_n2TxsQ1mJggg^?S zH#EW1+D+hwl|45fsi6a&j$DPzc&mOxj6+hkhtQdZBKU?`m$0vOp3~aYM)1y?LUwi& zXyZ+pxQ;-u(Y~Ac*s$6YJ218;#t=xXVf2N_&gTUw!`;c30h9e9j14y@9bv8JE|_>$ zZq6wZ?OuZwvJ1V6jExrRy}GyKH6%^cwU&ixUDmH!wgS@&y@5!A2KI3DNhNEC zq9-*Q9*(pcJQ?!W{dO)|k1+ziCt_0Yd^qa0-KrxyAXOfSo`?=JPb~R3$s1hq63K%u z`54LlF8L_Q)FmGwIq8xYNp?sAN1F!_LL5gMT6!D>2mOab?X9HE{@lraa#yz*9Z4Jg zP@-_NTVsgA&IJ?HoJA87W)-hVRw84|P?EMv#;uks?d`9i3bDdiK@;)9V!+XUYZLkN=@c0m8-4l!~J^=Bp|F6pyHW2^Q> zJ&7q+?unU*r7s)=B9x>Xc=$~sL>5P4jYHA3qIkMIYz;Do#~n5p}2(87LE{+e&4& zjVQw)4>dBSMgnUO^Lzh~vSif%QkIPS&t}P_|NB|8Qe>zqg8_tbx|QyrkJ+w^YtMlf zq}z(tNM@~(%vvLvwMH^)jbzpu$-FfPCd%|$(U0akRGaQ(s6w70WY!bO`i(32PCD*6 z6eS@dG#@vUGpyu-!gHI;CAzT4o)%6rSo;-FJR*WQkepR||X4^Q);mIO?{boj!^G zou*-k=gQBAnXZ4~_e=bF_*NZw@FekL9^YH)Nzuv*`&@fD5g<{$4OaD z37(C#DLBT9ajohN#Bgs)%H+;PQq12uAgZ6_&od0{qZ-(>O3v=M9`)Wt#>GQq9^|A5 z0T6Pu20Fb?)&RZ(`=d^Gi)uHMjBf{%jN0j}+|E|l&bF+b7@cmrt8GU=!CoA6#XDR5 z$V5#nIM_ZF-Cg|eNEFD#z*hgFQ65DH#6Wb|0Yrm?q}`!&UVL1s9EO2` za$8jnZOwXoZI0P}uE(vR9zR}Sw&r@gE9-I7^|sTGyW2Gz8)=)x!F}7EOxxX7{{c#$ z?>6LeBOcf#n=)#QJ!eyiuzN*TO6;|&41UNP`H)Wz0rqPJV7sZ)27S^ss3mHk%e&QO zRLMyXdiM%ME*2C&UVF4!VH~Gn(TGMxLkAR{-rkH-ATPbaY9|r_E>9x7m`nG%ie;3~ zDxL}!yE{F3dY_5B5oo~>CNrmRKzJx&%i8jTLtB1utgRB+mvFGxVW8nE5xr-Ch`z7T zimVw44ca7RmPp-Cc|b=L)?*TI(F6AL@OnU9Uw{?leH0EPt9It#)fp^VD|vOU+N^qr#&m`@dd9faDKujV6|kv>(+IjOX{}63> zCBl91>V+@_m-q+_GDRPHR8MI4LwYio9${r0{$*ktC}OkF+KP^H)&3)Blx4pA@MlYW zmx-bc*7x|9E`}Oo2GGB1OY2|^0Ha_1wmmlnKNQNhtH@A25I-9Bt~^>z`Tx2-uT%z> z#%ljPv^th{I-dYaRBA1zV-mm$wivP&*PO!~U1EWMtZky^Z@{%-$wN-ij$MmZ94JRF3U#E*8@w|O!cy4z5i)ivSh<>V9BO|9WxJF45D!CVE8_KFe~zk=U|!x zMe3XtiL85;j7>vTZ|!4JG*Rh)E_+*bO{kWdE6QwBYp!0EMn;lmC&XJawj!2HYz!<} zvDP%G_OWV%YDqUxY~oNa`+o!sg~%} z>U1it)hRF_a)GTa()?rW6=S|}pj)Pj_05u1L%tNRy`yQK=!;{ zlF%^FPI~Eo^unbjxce5b;;z&Owr(~LRKbjLc@cCON-n`t{jExk{h0Hn`q9`9ANn1B*U)>2r^r z1^S%JhRZSN$N5vP&-FF>%%<4$(dS%x9T;a1;NJ*c)(7DPu z51DUPqHP5A=cCWLP&m-%l4v|k+oSuMiBD|@7Ja^kKJR0zH zm0n}dDahZUr~L$WmXlXP#sO!Qim+sn%!-IXehRnqa@bTa3w zyxwP7;JKtzs=6w@;=>Ofj;_yhZ_j5ZIb{26-Wa@+$Cc^64?ozuQby2f8rT-lL6>5m z_6C>gXrC4(=x}_idoigkT+5DU+G|}3Z?$7Cg$LUmq|l!5R(nXw7fCW+@JUK9`6M+< zBjS5nn`4*6S%%xu5v6bLH6?j=TQtA2p|~YOF{V*lUh9(j#?yM)O3c_`O-of5P3?~! z3bg}*R){%9x)%(?kNG8M&~$r}#>UA$SF&#uQX9l}(^xx0Z(-P@zJuQ6E%ybJ^Z2_0 zlifRHdBS{=q*tVqH_hV>x0?aZR7$L0uvT>b-5%6j8eEw?!!BmY?R_>_m(Jfp=eG+t zX4i3gs#}`#uP62OzM@TiT}Uf@*rE)3(f7NJ_z0gH{Y9ArdbS1J=%3dIR-BCd8M4Qy zoGIeWSw5SY5Xkm$cskqZ*Tmf`85q%hJzHP)_G~+X*3ELi0V<=8v}eu$q$YvByeGk{-0F@& z5KBq!XIi%Si6_WF$pallJb~?02rAv;cmk#*@dRv<+I@guzy;D>#sE?*eN1>+RW#R` z(AV;SvuGoORZF`Z7<}H~W;ireuz?1^x<%U9z*9?a!b$q9cf@^~m zjC|P8GU0i6z80`TY!h_oiADPpdNZyyu%LfTZz6gghUa0LWeyeWFdd23djI~vMGf`7 zIa8{#{KQ%n=c|)`&*b}SQFvc8bmXh753^Xr9RK=2|32)DDFLef&YtmB$wSh1`~v2J zygVUq4Avxqyz($}?zH2#*fbp6FHg-?tfz8X; z7MZ?Yh)C2JoNpu|ex%&}jZ=?mEzu79^*Xo7%M$Hm{hG{?ySM1HNAAA-JN$YEuy(M( zTBzDX)qZ`E>3OTYp!-tot=4-9;=#mJt4u&o9ZbO-NcUYk{|;aO{5zG}n^AA-s5cT% z3~fyh{hFSr+R(4*Sa34J4)ZmgO_HGJvpJra4D!TKYdY@T;)${6i8MaxPfk+%;L^@_ z`?BMt#+H1~_Lrb{Jv%rm+x4Yvt^as+E%ONM%OB-+tS1q`A9@$_mLtf#VtYye4e3P{d`Gjw>mLZq$P1Z8x5x&VUxTwEqnI=J6Cv9u>vRVH zbt`vcVCB6tIOStk9*46VgWvF3ENkHmZY6SOaLTOe3=S%^JN!x}BHk+CXCt$EOWVsXgMjK;`ajnMQCY%g3h8PZfm+bec`z^e^+Y*+%H>?cLZ5b*<8K#a#U5lgS+`Dnu z76ck|Lpy_9~$=O-s1eZx9$&ef9|dQQU2V`!k>Gq z^XG1s$}~rmDg3#!MyAw=?P71uHpx-7wW00&xk)WE}xQM5)f^XDd+wMH^)jbzpu$*eV!d23Ps!k|AlmP_c8A=G}hR2shLZy`=T zR*n}1D;aR|H2$*8Yr9kS+U{T?F{;0LtdUNnEx&_Js_Yld^fs1O_S&|};X!h6EWzjv zmP1>7Ly&n7?Uf-J&mkdIhQ$2G9dorKHnh)gr_5cqMMvO_p1*#l%n3sv7FfatXTI5F zUy`>XeE6$jcZZ(Og?Ov+d^YUv(({=R7e1a}z&TIPr^D`UJwFq6_vral*kvKu4@2&< z_0Vx%+6`@JG?~rJ|IuFBn#;x=WG5=Tv@@J)&_VQf5>9Oxu`79F)BC;BrS@RF%@jG_ zU7>fA=@t1qY=*GlrSm1@>JHfv3zH4Kxt2pUJkZpnquI$Cyq+OyT`r1w>J& zhh^I-U*?Xd^5@1W-1$hboMXV&js%B5%%CYx^@DuEB&ziglc=|TYDvTvn)1NLsZ|jR zDBC9KmCmBd5O?^r^1?Xa%H8SKqswyTZleL0x(N3Usj!vpGoc|HH8P>_ywy~8>*c^! zL@1j?Zd8VTbHqR*qg=W36JjjgpVODBga5d|x$F=O0>+Be&M*wH&wKk#7M#H{`f| zG;$&|2rvG_Y;1((NcyI~eAjSegm)7$Me|9J3!@ z(J?ziBz>&Q+qt>i-O3^BO%3SwMp$`I!p`)ydULAu55YRF+O$XbFk)a@*o zCtvoQMnZ??otFVM^%TQ-}7qjf?OELG!KUTj;!~CPl(Ufs#QFMtBPS(?D!ie({NP#PjiZ- z+Arq(Bc&i|IIl|<$$Wzo8cM!x559+T9X=4x_4XW1QvSbg&j)x}%?F238}HwHa=ac8 zf0{_jk^H>VG0CdWe)$(ZSVigwk>(6)FF*5(zxFm>Mqt`JImZqJ!O<`N$~%Ac-Cy{= zfAi4fa^TtWmY1vZ)fFxsgcbxmjIy{^&Rz9AIPq=?h(lfe2?d!%@J|Q$ zlGv!~)1RXWRGdGnJg6<{+;A&4LK=NZ>pI}+9jWC{EH}o&b6Kv{<_R2{d#ZOAmWQ)< z7na}ia#vyb{n}gSb8E?(L1f9g`Dt6S;p(+PXAuXYps(u93pg}yY)eLk*Kln9v-UPI z=q*_>7i>!o_z_F>vA6ZY8`-%_+uNqO=@JXVk*BRqH$X@_(aeJhDuxUL;V%vkf5mLX zy<#djuT#{+vknT}AKeRw%=xUAQxMv~DRO{BZE`x9uQavFb6<_zGftdxuBNeiSMtA2 z*iFsOoRac7L%x;(v=c4O&!cd3SO&aXJ^H`-#!E|Lmxb$Ew$9{zS=~Kx4R~@?=Bxg_ zU9k`_cfRMrlj&d>w*^k&mi4ytRa>(4nZ?Qc@;tkiz&>2sckXxA?wqT5lgM7Ra~{04 zJ7)x=b6NW%I(T*3>Q2Iz!O;%4KCRNhPq_{@R~fz4oetMKdbwzFZ#T_G&x5ym2jvQ8 z9n43tYs*WzTQhJ@=d-uVq^4bJ zb{n;2rie@o6`=*`)v}401wcB#_msjqAe|p&U&e4$0qOjE%@~d$B*o`(j~UWkRd^d_ zJY-LgwQWCK6)S?xy**L@%|{Wpr`Ng^-i&g9gc5O&I^t6JGK$WfFXKTR?VMZLScIu1 z^JN^{urDKaqapJ_g9>wB#@EV~@jRx3F)hSQ6OY|XqdXaXU_@h+TD1M4OB~ti#VVrJ zj26s$sf=^pS(Jy#lD{2umpNLp7}e7~A1KL}^~^?%t_%%Y4@oVVBLFL#r z@}+Uz^X`};=wS;iXWXPb<95Yz#?5)<%!AVa6XQPLqPY^|&U#J@2jcVM+S=bD%>vG$ zVw<%kD;_%(Og%aKX-13?=9Kaww9W_ zGbBAz{g#{3o%7UI%Fen;oDRNQ-pnXqzRvE159RKJe{$`BLr#8YhAeoov+kA2cfDz6 z+e->sPeki$hiZ&<2w3-^V6;@M4J>Yv=bVqbIMI1D{A>#lGE=zRpgoDbN0A!sDgakY zMe$TOR*T|2Dp2F))gs3p#c8~}TIATnbid46=MhQjV9@FK?^1!=szTIATH z7>$=#iyV70yllQla_murMy}6LWJTenic8K$2}Yhh3ed>)Bw01Jjn|?O8SDtiQrYukzxMb4E1N7!HM!J+6FDWdM)sz>TW`W}i-L`Twy^&sg+S~mEOeWD&r>$?;JIp{UB zja}U!!yBZCS1M&v#Py_b060iL3Bz*-4^Alam*xJ00+YJqsbJ1U^(Hv)BH#>$v=(zj zlpxm|fH=n0OXZ(Lb^DO@lR`^j20|$6P;BWyv5PN+y^5|jA>Oe>gTujLdmkKBq-pVy z;9K2`8x&@micYYf#N)eY2*1&MYmD;pOGgwZ2DVZhD^jiURhOg?rjdyTjOPC64x}v} z$As7Y(XDn!hz#!3QC$wP>?6CFRN8RUD9D#J^uDa-``Q4< z&yIcNH8rIZuAOCpcu#S@gm^w+neitE7yhK2f^!_dS>O zLH7!Y)+&3m`)HNjmr*nCaOH@a2l98g4nO4ULTG>~!aUu51h(;C=9}s;BqY{K zTz$kPe`}o<3wL83XLxEDDiC|>JeY_H59S_s3LH%@uwj%|K7a$4=||RA8`bai?IS3x zt9{{N#rCV~X#byNNML=)H8@*W;VgGBp_seJcmVl#xrY{-CKN}IGL{);?GSGV`>@cZ z5GG;1LqZiV9wY>*`|3o`nCy4;>_j|kma*YvIg3riViU30(kO^a_gL8!p-QaUmb?MT z^F<^+52)6358KZoA8^y1)0+YWYkRBS-GPe_k7Mu{9=Dpo9pG-($uvl0RrqX7yE{a9 zQt7m#U0w-xWW&vFZoRZ7pxZ8mY)x>I&7pBW;~M$S~4-hY$zJ2^WCN_4qF|16#Zs<*RcSN1Q`}>^F;G8q*DnJJzh0EOae3EWEowo_S(!wn^Uy54EVTvt zjBIZjgS9ka!`|S*%Rcz%{64E0VIv3AX*0ida{xvW9R1T|X7+$EuC=_N#P)t?5kgR8 zqWU8JA~8RI2+ostU=p0FPrWPX)ub6<%VOPNe~QzSl(G}PgFO-%P)}(-V_Loy3k+j! zVhsaUw!QWSdf{fgw*^L8eJtXV8<%WQ^3h^)%?ROB=hYz{?zjU^@A!;Sksf(}P}!}r9n~U=FEiORkx$H`nlE&Vd-$#u-6opY$3Ym1U=rxCff540QKya&5!PL1!!R_wd zF?ZPy-`;Lon;j_}P{gGDI7RG;OQDF};8JExMy5CJV#-`9*qAYD%_U!ii6g&F2)9A{5$S z>HLbb^5f@HPwN>Yqb8#aJE=+PwcW^As=M^kTsgDk5KDC>Ii%}XwbwEgOC3|S`<&#$ ziB3T*q(L4Ta@WhcT{|3__3c2M!|mj_fI4zqND>}E1OVB*7G#Bnt_6qK)1LT7SPdL` z8|a~z42MAL#7c_CfX6!7@WI_5N$Os*eU|Zh?&Z6vs z5!q9?o}}Wwv+GF^h;Iy}e^=arv^HKj6xZU+Qguzf$_zE`$telLk~N7=O`?Y_)8ppbVe&3w1CY58)n?UWXyn3EpjDl#L$F{1uj z)@*X-*TlXl45JKLrF&v?>}II9cU{YqzrL;VN{ID22Ni5?EI&PG`Fm%48&lU>HyKFy+ zdE+?gefi()e%ZEMw<^_|U8BWzBy=G2Dl3LvRj#R6x~lxqFMV{$?2wOHqPVI&u!iOD z6lgO?^u{9~-ZLp`%wJ$Q58PGdK8J;g#uA_BkO{nRw@&&T2tt9VJI^6wcHeHD^f_2t z1DS>M90(UEW2F~pY(bw<&2wN$4*Eo0%3q*W1$`Vd^B0*<<8s_ZtpBL|oDXdITRR7^ z2zSV_$hih>jBW^thO;sf4QFK}8qVN9Ijn}WG7=4EWh5HT%1AVvm62#TDh@?Q@|$+w^#JNdN_72Y_kmiz#4>j*mv`x2UdNlVIuO_3 zf0;sKvXdHhbyzN-$OVRi#eO1=8pV}?Fxkkly==r)L7R&mRaq~Q6dBo4l}5RdR;LBM zk(L!irVQ=@Tw0=6`ir_>#SFo#Ox?$6gE~wf*P_QfVO2nR(594Ul!!`$C8*YmViQ=L`QV(JpGVjQAO~n`YQp| zgsq(@kQHg-X65aXnT3wb^rtYVNYaoSg)9q_7O*aWA%`FKt^5UtR!q?Aco#l96iDsh ztAKTOI0jCtHuoL2)l*>T)sO+HN#v?p*%WCjWt$=rR*FjpC9ApdnvjH7$t1jgfwZ8j zyt#pJcea2oeIwpL_~-^gO&ozl;3c;}ftO&w<%joW~^g3@KTos(5m=gkLg&PQ0t%ik3 z+_M|fD;VCA?P9Gj=)?m$I_iT?EP<2t2Az1LmAf&p@;>MU<>OM&i8uKymbC~vVI}gQ z6O;k&%ejGYHO#CTb;1!q^ePayp&8G*ZYBsgrwkd|<2SD?>OpIMqt?s~+(5Wm7i3SQ z($QA}u38X5!eGo|Pr%kh{MaInh}o;doj+h(1X-Z8Ct`MQ1Qal{F_dT<-!0;rVWTfR ze1{1W<>s8|a@pZ0_R+SxfKY)b%xx~}FGb?vSSdw_{C0laddq@$!EPVadFMR*gzVN) z|LxJq{>KBm8qmD&Bv$%Uvo#s89Zvq5;z=y&dKwF%p`NT6PeSAQUiEM=pv0xWnSc_U zh8HiofrmFd%Km3U9+T!;(meL-p_c;zC04jTaCATk$uBo_bt2Hml=wKiMG7bZ&J^QI z_XYZ^1DUDz)(-uNFtiKr!%XPvKyB}=9%jP2;C6cs$Eo_`!%VQ_F>q}ldmK-R=Hy}| zc@nYk`MEZ5S(u5ZqM>U8x4AGA+xG`~mq z8p*6Rl38mcv(`vvt&z-ITXt#v5R=ymNxW$mY z)Dlwu1d4I4CFn+E>J6)*WdCxQi4u(0z)qKf;%+*(&4IpJhRk*{Wocgdn!*ecZrdwE z^0bEpc*++6mbs|5f-P=@_-Gzxq6A_A3y#Dt%tShY0(OYl5+HlxK*Un5;!b=ha&adf#W7s@J`!~;?!;q}i#zeNQTIx{zZ7xih3Ch(`A5&k zqpoHv7Z8E*r_|cGIvPKT6~W&ncsWrjtiPg}jYp~2DMfMDV8{uJQn9-fr2-`TyQFf5 zX+^OvLR%318%ii0N(j~DArAq_`V9YqU3(U zdi!L`HmpzLg0&Q}%M`keoybScs57RGjRXj>3lQaPTEZxcQAmZVi`FzJdVtUT2(^X7 zl+5Edls8|y#xKaeRNBHU@e?$KQNc3`*#UvfcH~NP*s&Sh9M*`QvC$$J+ki3zWSXRJ z(z`L99dYeRo8bkVaQQumtI54o)?do+LHzQ|37=89ubtT_)_D)2WRkT4XcY0ypBuVF zqI@Jm^1BH*&tCuyg$Zy0G)|CWTkjZlA9pL*Q^;aqd}V(%Jy@`*@#FQ)T}TIy5MYi7>&n92C9$|HbW;p ztxKy~BaDUznJ^jxnH}drkn_{3fNFs)0CiL-4Yq&%8;Bv2!84VkuxkZ1>N3- zX1pwHhQDs_ycX#Sdlh5bf2EDb{F3SILae!=qd)? zENN@Ju*b=%_k&QKHJYpGF1{Zv?&7ly92vV95`3dZ@Qs@8;v)m>+0FH_bfWW5 zACUGw`Yt~41Kq`EdE6m4J3nSfXgeI#eO-21?`x7J$vBJ*Eo%LLNUVu4BGN>)QcG&F zg`5a5Fj$NRU5^G%C6en7HA}jCwgC)0EoCr&9wMY|8J=~MBe(Cx@3zgw6 z8}tD*6y*YgI{7e_SpOAP!lGs~N zbSSf=LzyKV$}H(nX32(i*pkg6*hZ@ew&CuW<6I3;K~(ubS|kkD5lcGV%963&P-jV} zTUpZGerL(53!tIOYDF{-cQ>JBa06+{rpti!uejAuSP30ggh)%e_ATk!x1?*|lCFJA zy2}VH>F!3fWZi}qX_c1Pwz8h;UP5M0MzWTS`d`T2x*@Z-iPvw*>uzDRw=S%RCEfLe zY?V?y?pU?_vOYKOHLX3T@S9^ysOl74L4vd`sY_xI4H&b<1KJOx7(UIY zKN^nL{a$mRX65Ir*7KDlzpAgca;Oloh0OPdOPhh$F9ve7Sq8CJLZ4c?v;^zlHaglw z+uaMdNno6814w1jMsPVn!$YSLT?SpTHp7AIZFXuX!-CvPTisplaaH2io7Fn^71z0V zh0d{6vUyCe(K!#@Iyxtwlr7ptN8SBwcHg2G4_52wpSq5&5b?#|`gXaaSeC$wQS{KQ zx1%h590JjS20x-NU_@W&E?L}eE`YVwI{M}Zj9g2%vSl)yf&uuDGMCv=sL_5q|6WUL zHtn5n=yF1Zf<{byMMlxI?rwK?Ddv$;5H-tu!_UGmy~VnWmY&mDFfE3g4}1^?T`E@*LqXNJ(S z0(Ko9z;4%P*zfL#yuu0kt4+%{zSZcrbV206v_uNNz-38tsUj8aqZy{TK`9xXz}ZW1AoV&`TuPEj>T{C;eRX) zP`V$F_^Sx3qfkIEB5ju~=0ho2E*G1;j+u)Zx+Baj)f`>RmSyfe#IxD2T?DH0OebHd zM#hnWe0An&(DwU6-etC5ZGngBKAO&4x56w!O)k}ptXb48NAdJ1_$apER2-XtC>WRdI2^o)1HSQot--`$(SDt! z=G2m5wQz&sz zgq>HC;R>CtA;X=c zT&&rN3HC~fUYyPBvwaU_2V$1gJ+#F>m%iG=#-0Hw6IkB|D8eG>ZL*F2MQ4Ne_n1o2 zZz$R_1n=NWGx+Fd)<+#N0GmX3&q4K8<5fZR5uMmyD?sIdIpA?rk!CLi)~+f(SZdX= zukICQ!_$|q%v-(Q*{YYdT9#A@#$;yoj19uoGYCwh-C+)+mXwq6FmoVslHw-qO!F{H z7B|T(BIu3ATDw<>p~Ih#@WB494K~74iJ{}rpba*bH*K(S=u-?GzRm`ltkz=am>0#+ z_c?|Rdw&g|V0Q3uMWGXWccH7q<%3a-J!9)RABR25p*7$rtoh`$pVzxK%VDkvsCO*A zIvp2Jeu_Q8<#%nC5o%y{enV=Vu>K7Zc&fO^^6gjVzPaPszSl#4W4FK^(dOQbL7#0o zCI)}skzWJ%#1}rg#D)eQLNuCto8d~?@X&04;> z>#Sxi-`sUpvzBk}I;&aBH+P-YtmT`#&T7{3&0S|TYx?G{v!1nlbJtnTkZD`+@0G3a$yM&$C0AkoH{p@HK74H4BX@oE zDoh4_FcE2lNA6dRRkJQyG%m3e4&^wOf-12=&a~Vkcbzb<$0PT1f|S81qLeK6$X#c1 z>+#6_9v1$=<`55sM(wsPqK}ehi^jPy)a8*9`5*y`@6Zj?y5!XS`Xu?P@CG?`pHg<9 zq~wwNoO!cOW`=y+RJdsfzchLN^n74d;?^T6mnONfnEce@K_UG7i zzRMrXv0HkY%iM2(wWi1oH6H@QbPNJjm5$s2Y0BIM`upG7cQQ}n{~o{^c&6z z7r{(UxIE;9YZrN}?1amhYr86wrSk!o&R|MGsEt#v=Z#COj{1zX908gnou@6gJjo@1 zyS;HTz|%I%ZkzlaXiQ*~FLj&bO*qQi7Mb)}28S=}=3R?8YA%GCw=GiY7xX=p^R!W{ z05AVS2~4x4^V;CAS*TbpQ(O+GvSc8?UueB$+An`p8b_i zzpusxaFi?G;6{d+drSgu#qb%B89_n~tY*ch^sVhaa_^ekMn}q)XX|s>Tk~wS%KdF@ z7_o+LV>4JfMP($j%1CCFk<2SIKeF7_6r8b5mickXQUQ&~(~Q3b(|PQ(Cbl^z2PSJ- zY$IHmVK#SE)OC15y^EyGv|h;(waFQNT)fRCmXzOputAiosOTw&lj?jsQjM1b?M*80 ze$yr7%l!U=WuT7L>~zKEVfv(DMv%FkNxJuxi~mIGlVQ&TscEttNNLn$h0mH`(SRO2%6O-yvS&1B^S)e0S`?009{>8brbi0 zD8In$m*c2Xoc|>vMdZ$tvIN9so@`74X@eWq3+8OGjk%vazx+g;;Ofj|`gqyzlG*Lb z=^Sj}&gPqe+6jdajtPHM3!x(hi}tuRh5*c~Tz zD=y#)=DakKO+pqEVne8UPS_^yll-Uv6w`&ZV~3mDy`=LdLKg;+%?Ngp#1{BoDy|($ zl!HEEX&vIK9fshof>L)s{;cucKO|^kfHbqLyMj#7K#QLArEUd4ZH4O{vt7c8Z!uqON z)3C6%Y*9-X{S5WKh9vfjBRl|7GAyUw__lwkeEjI^YsR>k zpC%T5HfzXu>>JLIfzXSRDsfzf3~#B@*|Z+E1YO-19VvbbGX`fK2h12PQ>73}y&0oqNW8b_a}6l)j+PGI>Nre~Z+5O&-qwq-^pSK484VoyOfMi^lK$?oYq3 zw!rblQ3s5-7gms+?jHNK4kZ_ck*tv^HDV@^ow8-P1I8q?BfligmVqcuGCT51GCT51 zGC%UW-7F_N@v#}QoEUg^L^*4XbIDn2B(v5?X04ITS|gdYMlx^BPR-^f3&sWxAkzx_ zt720OCGjjpy-v529aI{wjv2c1wtW^0hV8lXv126p%-mv}BsYUByWxH%J4*vA+g5L9 zAyu}sTvXH0a}G2dD{laHmG!_wIna~5AH&CyhamP=kCma@l0!$T4Bc}cI$&g?0ge3* z@ODLrhULQDa~ntjcBUQLInEsU1wI)UJD}XOjeBi$lR6OXKMZ5BRC<^VZc9xbmQ`5a zb`E2puvGmpjNMWJDi#Kvjt8EUL$?epb(y#E9OS~`)vP??3dWEItvsHBP}IfpGD330 zk@pyF6VEMg0{e(_=&t!llxU3^`Pe3Fc3SkosAo5wfjrK(=DuPwVT}6&zwd%Nvw7n! zPttK=ik%Oe9|vWz;vX|{(WbDpn1`CwaD%h|xR2~Ha`nwDNX;P&lER`d-%VJ{f~1(7%PmOM zy(TMLh7qit4?l>TUP5gLIO$+)O!R&`Kj^Z%NV?8)SCEHh>-ZxVi**(*5+{ z`Cg@LgRD6DiC>B0;OEC&lI}QEkKa#_o#2(tXaFK6ziAyTG<;u^!pdO2SN2hssu zwT)kvZyUcXwas=3r%so*-O1YC>gM#awka5x*RyK(_l&Ti<^uA{`^o@G!I1Ag&R&Ax zT7Mjq?&EJ&oWyh>@b_Vu%EvUe4uUyt+NlqVUCeD6AJ=%We98j{by9Z&8uk#zdI)zM zlhw`V1hjxf>q}Cw=(bD2q7zaqp#kcvyK(*+xY$xK6|k>@q1%1x2c0*{IJUdxKI1Of zB7{Ok2Ik~6I<$QHo+s@=Lf4w&o8nthkQXU^)R%vlJ4lVmPk9>j3bb!i>S$tWJQsB0g?NyBT!JC;@kXeGrUI; zdu)xzM)9yny0i%%8fzfg^IjOtjs4J84o}7$H zd8>kvv8JKun7<&!iOfPQa(7F}BG!sYn9!FiD`sikJ#tj_w z(UYYi@49DjuB&UG{S>G9Z=lGIl#6;oeC9B2ZTxIy&(2mVK7+ImLy41P2Um}rOPlXV znL#lxwYVS?UF6u`C7*tn^kby$Jkd3JJ`#5KApvIW}6*?)SXM| zRi&e7E`PUXF*-J)L+;CTNDqov8Jnh931-X#(k>Y^#d6p(Wy(hT~$xbMTc^=QC?mmTArD;Yovxx;k9${=g` z`e>aMX`Q$%4Qidxg$WPcZJq*ifoA2&AcsQ6Os10aw}6dZLRObIgKF`^P69MTj%l$a zX(D$Gg;oYl%?+m2;EB!hL}Nhdz+Ox+JhorXiOtDeY45;zOJ-Wt>~D9{QNF6*2tl%* za72OgauHLr?K~oRoRatXFo~g>7j%`5sRHhf9dI|V_wWB(ysogv_8oE!W4Egt zrjcZSx9@mk@W9HgGzvsQ=wg>c9C+u;&y(cKvQww03(&S&q9Mfteo zjrBV|i)Agmv8+VyjYXMNy|IK0UCt=~1I1?*k~TE&*L2A8l;B#}5F=Rq)YqS}N-cBA z+I(Y~Q`UwNAY(22{kZz}+}#LeXXi@-mqA|cm$mU0haIymlUx?;4sJzx(TBF33qxIbS7M*tIWY{754q3ztqgD1XIvpJBt`#< z6ywYox^rUTD(A%VTNy%gZ#fV@l<1t8%=OsTKp%pwU16|>0~D%dek%hPd^@M9HEv}H zozqmgk*VPMa$^!e<}|hJRt66qGHuZs$;@erWaczQGWLhtNM`0|l9|&KN#`_0Yf=9K zmz%jeW%W@z?sA&K|LrLVi=oA7O8zfSW#N#Ng#}=}wUYcr7G>3ZtQ?3Jj*)r2*zq#E zPQfYc3{B>yGAYW<%*Z;Y?KiEPx}m{&@C@A502CQt&H<*n;kz0#$YEK6(t41?KkO_O z7LZcC;!itol$9Xk#s&{r`R)daJ`7!ZhRZyoS^FJexH|w=;rET@4&2?4Be{TvUevYK zKb}r_X1v192`W3t71;GCdow9Rw>-2OO~4z(lij3{2N@djVj6ZSLqBu1(q}Q=pESlx z8-9&I4Ry;0HiDz$-^tC;bRB;mDO{P`kKF{Ad-de!+2kh%T@0KY9u=*BRTreFf2r)} z%TU-g0}pZQpORcNaKR_{W7qIW3?H^=lL5mAl$Jr-@WA)JF4qh^6l;1(es$LjfKP87 z7_e*@3gPq53|un+ANLzU>pJ4*=eEW!pAl;~C~cyM?XZdsn-IpSJ&^9?SkFT=D~)ix zjH?CS@sY5OT=EOdS4~3oODZyjwmkwb&9$7 z7La1zuvO>u$?+(Rq=#c``uRM5%7xFCHj|r7*2o#PYKGI!sW;2n&Ca6d=GGhGJmC^9^^Y0KyHRFFxGLq zE!sTK8^_Q)a9hDj0HZqRWx$r*R^UVa-|QSYK>ZH61C*&oU^mMt4;qPuhX& z3_4=ZP2zd&A@H`vEX!-wOyjD8tc9aN_iCTsMLPBAWi5GX$)XeNAa9rdJ?G2D>RfAP^tJ(--D54lHehQLqNx7y5R5h)AL#xSsF!&#dPe zeBMl+V``iFCX`^;9>;aOgB`lp^cU2QGX^H{a)7((WMPp#>0C(Ghg^KNiSrS;CgS?> zsGiKZ59tY6Ju>1r0W&0*iNQwExQ^HuVdZ_4o+?7d(!wrMGL!fjn%C_G(YsvqG4CenLh9m!%e*#jmKgE7(P0 zVHN7#6=RMe4Kr>>>g5@;R$5KIB^qy)thaJL(m{AZn9t7KGeWZ47B4uGDsL$Z^Fo9d zX(yR?MI2M{dE3}ou}@xB$fH8KE6IktNNlgoL+9bk*Al!^fRXxwo}W!_M|X8V3pQx1 z^D2X=-rb+Jx(A3oMva~YIOm7eEAzen%F?AJ973`%@x#5==H{lyWR+ffKsVlN_)>P- zsou^o@J(Rgn~5I}20Sy`00#2y)Y0RPx&5*+kgtfParc035(A#|wiUovqSCcsV2Z%< zU?2~+xNge-3r7Q41omMXV4vXYf&mZcWntio_Fj*H7lVQ8xU&Ha>@COAU)LvHkD_}I=w*%iWP7(C^@EK1OSt0!3}n0VCiAb3QP2Cn*SR`nH%AO(*m+W29tNJU zW!VN$Fp>y*13o;f16qu^IKJ^dH}iM3iF-6=oO>d`z@F?fgw@gN$N!(X-Z#B7dYvBSKk87v(o*9t zOV|v`9mfHib_PcPO8azE4*m105nMr<;DBtKGguR^fMs9$0`E#_DRU*X6u9OfPxnRc zHl8dR{)je)tB&NFad&FbS-aGt}C^RSbIMi_Pp ztKMxZ(yeiy8`^?36R^FK;5xa66QOVZe-y#$kcG;s7T1*$(%GElI*%y+l2oJru90j+ zho-$<0n26E#$j{Z?cF)Q%|Rmz;S4b@n4v{R5D{6IefJy8VY8cFZ_g{!m)i5{^q@V{ z^o4q+hqxG{S4OxqAH8TmBlc<=Yi)_yXX;mP9CMLb8}LTX? zcfs6Qcy8Lw<1Zh)d3@Nh^JRmMos&iBNy#ZMVUlD-M5CND<0kGRkKOEX9dAl`%nfpP zk!QEcOfiE?dE|RL=muJ%O&HL!dp%0Hm6Q@_n@_@-L&Wtxly?pt_i#@SQYg)$4g?rZ zn|{AfrqDbo?#9Rxbs+GOf+>8k$2PcCLoA44kJ0?zb2x!^Io$3wTesE0eLw2CUcAs? zaaYag!$G$dvY%)&e44Z)-}Dl0MvLzLnUw)8JWT1V4FJr#44|h(>T=wN&e`e*gQ~eJ zXk;vH4#uu*3XS=hb^4umOJ5i&a`{dQwCkzgcctShaS2P9+jqriOEJ}$$_b*Mc$sFU zJqpmD?Ww48v%CXM=VXtlH;JoBOH|WYHdSTVopg-5+w|G<-Kg<=H;T#zc879p3h7pn zL%=@Ltv_)peJTgTLW5xb^^ErB?xarQp3Jw6_FDgn#j8kh;fBA5aGZ6s#|4qe43HPYjXd+oo__cHXnvEQ-{y36I=DV{-8g*4DPePt7KgJSpwa^-YJ07-c=d?bp-M%|J_!d zQpiSlJli%KAKW%u0kozLLK`+%20S+WzSvgog~>318NG~&;)pxr_#!`wTfImyRtJgJ znVW@C+vIyWqczxXU6`$gMYKI+^t_`Pu?^hglHtacv3p!Ru-xOKO+2_+4lHOJ05&&b z0PKOB=t}0hoak8D>XI<3KEwhWb1xI*hi$|%h@Svrme71!9LXq4)51ZzGoKQ#$t@OHr=${HsdYXSNZe^wYVo2z_a&TT(fa9ng zjb;FCc2$tj#dVL7bnm|{c8sGHmk`SQ^;#aB3^XuVWd2ly#6pEosu^A&xA%Md`Np1Eyuoi1~-cLd+G4$ zkT4g8q!izznwbp()b7tnu(3TgQe3G@!-fORyXFcm#&wAb# zY}eYrcIhYV)d5unBeS2>^P1kNNW~pf>Wl6(WtKtYvw}!~0)tq^JehyTi-{&%feJ8` zX2@|9a1MrEC8lyqm793Vv;yU8zS0Xu=>u^M;`Hf7Tmwg?M(c3D>QahM8a~7BI*n5B zOdK1CDvs^ha#N2|mWdcQ)|nW$=UkH9K*IS#{2Ku*c9K9_uOoqqmpDy0#;bVAZx**nmhD9wbe15zW^#ag0fxcmsaF zS7ZpfzDiH!r{WxznE6HRWA0S!?#vo0=6c$e&SBB-9#f6LrAH8l_CBy1yR zx^HUMYC2q#7Ftb*W70ya>2OP0Xf+*9Neiu}!zF2<)pR%{Ewq{rccg_@)8UMyYUGQw z1LHurN&YV`&X^T(I$V)d51vT#uBO8g=|94KQ?r3}PTwOp?OiW{nyubBf7QMcyy2{3FPg^$x? z-!*AxxAj;%g*Ob62t%_BqlbEws+eCf-}xYN3>uUZKRWe=6*#e7s|vm z@ga?$yl)D&U?KuG^2F(O-_&djlKgP?0i-ghaJrKrHeYG{x z$36^3vl0bknR~BdMGOp;&O=}{XODmZ03-yyjO9oC3!pNeRXFDf)J?h7KA**13VqSg zSfu9Us1l_~^Q?$z=jn5S&4k0}JV!k(XY=Elq!$hi$m{%#OJ$kDJr3All?&WpjTJmrkSt)A|HU zlvOUrr4!;!s9(GZBK-!e@Q^#6f;dmm=~VuG8kEL2XA}%3`E7=al^__WzA-^CICtuB zsxzwhh$8fa&Y^W(6D0WoNvfoJIE4$`pp$6vx41=URCWknttqa1v@H&g!>M=S%@#|O zNN57*H>g+_phSpu(X?D=tP6Lb$rhV$pKcRSkII(>S-0Vm^57T53JUQqCWUL^k|1zo z!u`(UT|DYh^jusFr^unVV!GzO7del2q0^RJ*e5zK3G(qSM#iK3lAw`xZI^zY{j}~3 zy1A`8hR{hBmjvM-DV5_K@h%3CHehcskmNx%h-4h%U62hCD9DBg6l7~Y0tML+fr4y^ zKtVRYw#UW0kRhbk`S|uQtFaIsM5_p`kqxbp4Xu$4t&t6_kqxbp&0DM9Ys}msAOS;} zLb(Wnlx^9nm-)v1vz>+)yZJ-=r-*ga?gVOz@)X#g>*Hq%aS>=4IP0yNq@pGSgkg!y zsX9CC)y^IMFrA0?#dJ-=NvSPo>QVR57W4?qB~MtjtmuLLEy(M`T((6gsr*=6zBZ$? ztA!u|t*iyNP&|V;Tdq$}$Jq*{SZ=OV%Bgvk(i1V^>q!YDd!EwcSa{A52}4s@B@e^4 zkc~u$EV$SbfFYlB&A12di%0Vqr8zL2m0$-jrH~!ttCEJ&jOqx}n?J*h&t|Not6xzy z?PDk#kOPveYgt?j69)FZYCcyd^YOS-jFL+kUOBQNkD9^!L+5VPjLuHZ&fO^Er2|K^ zzm5b&y>>sl!oxKC+5s_xk}Uulb?t&onRY=Lz;@pS7x@dJhcu>UCRrzsu4oN|wDZ5t zXEg10oey<+CA+99E0?yuvmIu&|8f7%I$>Zm{AX)xO9+wCACBe| zb6w=4%{Xd#YjB0cXiN`@@$XRxQgU#H%X`EaGeLEkDJil+%i>{lyS%5^4PDMfzUX?H z4@Mgb;=ne0nQ~@mNYDexjR=*13qJpCDn-2*!OY046mkNZ$GjL4Rp`98$3kIx`ExN^E5#3_Az0c_jhO7Dlj)UHYOu<1+9%cYk zd<)mvQg90lntVhH-%Bj_)JqI(%Dv2uE=OFriAHNUnCfxZ)KXopC>aXAQN%j#-aU@a zmF@wLy69@?zMA}o~2c&;7kxQ%6#v7F!s*eN!2F?`NWciqaUOoiQC?EZ$%I{B6{|5s*O3nOm>?okW#eTZzYgkk5wx5m}h1tH5pDdmm!gxDV;BDKMRy~)& zbPF>v&}Z&EAQ=#)WpPb{bbtGlg~cyXhEhZ@%)-Qfj9P#R_)==Gy#$94v$R}a+ud5Z zy_WLtn$_>*;Q&03`%6eR?mHQX68+&m59VWuv9w?L{de~>8$fVGSYQuN{=uLAA`fL8 zJEBc`$@(bT{>4wf>5qT)^jkjif1*1<$HuN_^X$y0<2xs>21OBhj4;pOKXGLM&DQ8g zgl7YU9m@@*^4{=yXJ1CA^@Y>BJ*@OaBk%0XXr(V$=BX19ON~3U)TckOd&bjIF~au{ z_+v>lQ2i6D1@Xpz7vW%}BZB?m?LtzJ1uKXU8mEI^%7#^H)3l{ z%zS6(-LBvM%pFq`81o0$?wQ8o94!~vryQJlSdX3A2)$#J*R?8_oK%;(>pZ(!1-=Z_ z!PoWadU*Gd>?t53HF}aZ%Xt5)xAAI?E_e=R9k1`A$b09M`04s<2Xj<0&T0pe<10C641y-{)bEN2hRsj^LV zxc^~OIQg=mbMo7H{X;mS(1B&*SIZFQncdI3THd<;1_Bs)7WTxR{7~PQu6$?V*2(Jk z^AfIi$m#gZ6K&T!EG^vgFwe()zA(=(5zxF@S;7~+qNmfZap)KdWMo~7txLB#42lrj z6$(t*Jm!dx)BGBTRjh!rut9VxfSgkWyp7-S;A@N;>LRm9AxBdZ_ID6aNGSmWrw9oc z*zF}?0DXnGFz&)z#0|`n11t9Q;pDQq#kXuR{vrs8CUrN2$r=Wx4XSdEps1O^S5FdD z9hN7h@dMMwJ&-PS2TBH8vE1*nVyU*<31|8_$diWBWR8^p{RJe3PFznF4e_7oGOA2djsq@;*TCRcbv9TUB@V~^) zTW#zyGW}j7Kwv zDTPyCsv6p{FjYiyXf0A0gAM!9w9lx(r9lylsu9uESxjKI7TD9FjHsZwm!E*`OdN8x-VZgMyrFP>_?&735AGuUo-8)bZ9mba_Qpo&LkQ^y;9=~)Xuh-+-P&Vig4+Z7f?9a{MEMHAE1e7#*PF_ZRv z5b;9j3Au7l@GTG8cUBM(vcDDTx(v+C>1%$mM5&7q$VJXpjdN2&l6W&AK_LVke$;E= z&v^~}S+9XdGjAGrH1no`M>B64cr^2?_1y`=cPd3QPaxN3+E8q(LCH~u;B1hOG+21k zHS(3aDY|Rq?JkNuX3rejcr%b#qhs@GB2k62IuNmFIIBw0aJo3qFRwb#Gn#LtQhrQw z6+0zsGej}9i3Hue_Cw@3<0Qy)v?ZdO*FhTHymm#TbW+%#@6OXqag$#dC(f`j22=Wy zrk+O&)zpvt2h!BTHP4}{hh&s=^-wL=z`j`yz~cT6<+}RAPp^&LRx5P%`{vTsn~<;v zef=BrOGS=!=<7q^AB1a07<$*$e{1RKy=ZQUgkU2s@>8R=)ID`qQY_P}CYJ})(1?o` zPQR6cOMsmzWf#4D03?ztvu*0Cv#%pt!hm5zZdov=&VHro0I)28nU|wc0xc(Mg=Cjd zhRPKbGxbht`|u6IwuU5CeQY?n36pFzg6RU|HZZK>d?>?!Ws)(MmF!K1;FauDCe<%V zcO|>K>{Uv3kuBk>`&&}8FZXJ8L*%ZPO^Aa$4|V%$FGyUj{*7neq5~~FaN$Oa0g9}V zXkPN(aAm|fY0+-Z&Nqpqa!Z`{T^B^4-_CTA#d)OR(h46Jk&7%`r39%mE_4L7J$AU# z!5b%u8gLL9Cn^GT244Ga)(0JGdC#ym01{{+-8@S8-6ZhW(#0cizaapnng&_3IMEp{ zeCT(YctoTnRScB|O?uBPK#S|dW-%Y5WIw2hE z(!>v&0xxt7%Lnw|ji{zS+bf*G*Sn8p&0z3iI!_w-`p#Uc;4MLD%kPV#krBD9j zr+)F1@A>S9z7*YwI=;4ReH3l~yHjufm(PFkGavkFga)4&C}63Duo~VPy;|YefG}eV zoU8PY?k^Qg>%7^@o-Hg1hGmUl554x@bsoTssOhpL!AM1WA39GMLUrCsmpd;*-07W1 zT|nmnSha^v>7V3Ue@ta((faeYr1iI7PU}xLX#L3+T7S)HYA?@nVyG)yX#Iyp33w8; z{$yjPop+x&t-pQp1=Hk{4O)M)LF-R8X#L3stv}hI^(X7K{x<303{N66D&WwmM*o@W zEt|Bz60)wJ_Oxy`3zqFO0|d+Vx4#_94z#}%vJJCEumXc-&0yJ~_E%Lv6XuK#SfEmB zu|Q=+#R8S)9sT$cOZlB5^_lM!QP*>$1Jj9lGU=4?)y{i8TCS{e5%s%`#YNR$V1IH@ z<^~71W+EYsa1T-Cjo|7CYj@+6DOwIwsAYp#@%m!I_GdPyhN|%7dX~ZED%Y6@=PG_Y z2Isps_uvd+oCO5~T$>JOtIYa{_?x!-&oaR2CI!ZcJUB%y&GD((C#zO-1&K^dvdvL4%oEEQC?ehUUysU zx8+dx$BhgCgN|_bx3)%12QI=vjyNA$d9F0;&&9eQDM@?9pD5P-wbfAfqBFS_lD6S% zTR_D{yRSk;Q)t#r)27!!;d&QzaVPg@LCQPVQcs2&)ds(nKj98l$t2)&cfqj@NhVR) zc6d4p(n@&I#kM0e+m6-g1r}GkMf_>q)*_X(Q77@fIaNhw6eyTCqHX6SCs+Pgrpzon z(v@byxKe!UvXXy*XFuh6lM*S}o%xXf=**AYTW5Zx13L429Xdw=o%z?eXw0kA6f=+2 zCP;TxrOR^~vbx3IHqK6k6d~2xBiOR2E?74&tBIhBi^djMmG@h8%$DUyI!7^Ex)kmYcGtr^Xxj=a-RzRY1j%aFU&obs~0IxLW}A}N1MogN?oGBAed4O*W57< z;I^)1)ZUwWVEJMeI`fzu>gdhfgClKW?ooGvhaFv|qa)fA2$<+>rkcB8Wh}f*>i8Lq zbwT`0cm5G!)NUPQ+9?5wjwxqKCJa|n?H&cS1_-HxlIV46l6ui=P9@@*Jllp5icw%Q zSrgUWqqD4`)kteFC*0&b;Rd}NBxvQ|gZ;a3n> z(me9WUm*kebRZjKo!8O&ZrMr&?Gnb`~WIWl`$KBL)7nS@bg zH8>CJTOjI4r$ zf!L6N%K19l0V$3}?KEjmfzwI*%eghq znOTI9x)>#XW>nAWe8Tst1~KxE`Bu|mj2sDk z+LxZ72pX(^Z>mGkDkgUhQyqHNe4AlxC5XnkZ%hykwD>bcctnD+fmtLOS2E33XLE&N z&f_~`XHW^DF|2cOENzHmsk24m)(uPAKMmxb+~NQgQ)>ilV6vuAI_-}p7dd%YKXDH!S|T^ z?TmWhrH|@2=yl}lGU^$#i&qsSLn3n7Q*;{jEO$mdE5?ykf9`J-R0H#YT9+6bV?~UO z70#${U#%bSh(&vyUL-x?fMm2(nnxno1;*xAzHW>BrG#K@e4Msg=gHaFJ+^A;^ zclTkOGf;f_8>d`*^xtA^j1{es4Xu$4t&t6_kqxbp4Xu&QTT9wUszyCSlC39FSc(cU zmE6oC?Dao61wfywF7Vg`~se)NJht@50OTP|j8PG_fGQ5) z`Iu@c>ck*)nbR=H)$VCo^JAjvb`a*w40J|yjK&8h?l?E+M`*~RI>-?^_DATN#5I6< zJgr2h(4$#mTtZ58rG&OZGrP(+$aCCVH}0OEAuCw`1-QGUCP4sMU2-QW09oB}H`DeR z40=YaBNlqY40<^EBYc@}Y|P>6;Go`dg3u}VkI@Qdvb3w}Y756y%o%zkg#84f-9m2^ zn=W)J;H}MHzP#p$y%F#S)avyUj=1pKaUOgF7R|V7PnI-L8=d2~=^{Txnd0_rr)c5Z zncSr3>@#Ys@X+ZCLV**#nDit;Yu$rs&)?>zJ*Q5V$R*O0uoQoixm%AV)N0`YVhrtU21O?5YCvAeCe@`z> zMyrXyShemZ0#WBqE-0k8GE|~R8>8#l>>O&CiLbO7^b-G3Q8jYfI$X}S6O)W?-~m%3 z!iZ#4$cxlev=NNa(F4h-$Rkqvo3=*i+frRWC_-vEYV^;%mnv_)=PJ3>d9#umcOI_f zHldEoC4@p-2@TDaaM40t35TjO)twkynM_r4Iv8Zowa4=3xBwoL{`Ae^31C)&mx(}v>HC3pb%=`z&z4>9g(1;R-J1Vj2##{;x zUtBf?Bl3xa5g31IrX;W@0qb;NNlCoT0yB-NLWsA5DcG8Jzs$_q{ad5Ges3|!ys*6; z-|T{)q)UZ+rNApojW)3AhWOmf;?mp-tT5c7W?`@a%~^ceB0>y-^|E_PmP=x_z|{{{t>)F875Y+={&X6tYD7^ElcP;8qYH__Dv7wvs*V(`M z*yq0XPsb0Pj%3c&*!7$!+J5@uZ~vWsi^Wm{(j#U)ww~u!_K*MS&=-B#XY%atW%9+v zxlD`22{AJ>e+-j(*f<9!AL`JI$t(tpwfu$8|LeQn@h6Xb=qnK_b?Cdre5vO#Up_@p zD6I$>!u)ttgrH$Gqt($2=RNeJ8J=M@1PCa~`q8NDw9&98ht+*Q8qCPLj7GDemW$DR zkHW?<4pjXCJ&L{#3`F`u2Rm7Mfl%RKXW7I~yE;|c&szI*$kxqX%AWSw3DmOv7Tm?M z14UWxjg4laItBn()}3rE>rS?obthZPx|S^Ktl8=i0>RqV=?vhkPFa%G z^fd8dZ^$}}J9}C;6E@5C8N6iCW!EAq!8F8>HR9I)xsH%Yhv1bd-w!AzjUh z+o9&#V~@a4ixH@?uS-J)~vo%VTyEuxT7s)nYwU@_O&X{U3{+JN1$W}TlyHy4)JJZaCpB`H8aTwH? z1B0Bs;>^&RdFcZhZ(q~P9Oj@cF)U?1cqYQ8{^=kh7lne-1mid@B+nDMVb2l8s7hqN zl|LyP$H}9WgLh#ZSFqiRwcEuwj;nlc%5*r%w;jh-R!Lq?%Eje-YQS8J6W~E*0WVu0 zE#4BnR~#={Q23zFa2wj)~~>E&|62NOGFB||vBNe)QD z68UvD9|;S-7!&;2lP&i{&6$Lmy96KP8EJ=`iw4ZoCBmpojE`vZU9qgKguwiZ?4+s3 z`{EX7-dmYjws9t1C*WK7MIspq51Dm6yk2~%zPLNibS_s3!xxGOMM!pfsK3FE>#|sL z%}O+9O+ht-7ROGyIyL7f_q;F@qkd7iIKXS$^vf`}#$q)|mf~YvQDBf+sG1j+=f#0x zjf*BV_Vq-QT9rqWa(-e#%3uSI0uEwc=$S*#k&NhiWbzdplswI@wN}8{rTIX*+NO1w zgLOu~V&5&+)&k7IPUPlb2a`An65ma)XvHoTGq8(v-FG520y*5L^ZvSb>+*M~bW86X z-;X(Mz=);U4EDNTHYwRK-eINR69z`S8?lD-7pW z1&Dp)qF8N16oa?cg4#PQ=^*tNT{UxLWF2t^qIvyh_i4~9v z((x*&YNP$ao7xk%-JCTbb1}P1?Mf&hHXB=`&)RoeBm3SP&hdTOaPF^s5i434&e<2a z;T+#A$Z)PV$jskxuD5#2A+5R?&Q0EqjHmNnDMwsNHhMiaHQjKo#}AR&K^2R*R;Wp* z8P0tkUMpg1XEU7ZvAJFh=iVT|RGNc3po`e`b4@M>8Fd#MA&Z{hPAX*4^P@L06S)x*`q4Ix@l9tb zhYVFlB_yy_$oUN|LR2dhFBMC<1QG?N7|Qf|-IR}QB?CDi=k!j&6mH(vP0eTuhqG5D zz7=>P!zCu_36F2J%-M~Zx4hR{JbB%e`J5wC*cg>e;ikv8k|CMwo(w6c#kV^6`9cc^ zp~{Wk?DFOO9+U3ZP0eImHp_KWW)=+2mM!1%Rzw#$AeG zWdW}o$t?%GD$`tl-4s;UcU7X*&aM`q!$dS(H#M1LcHPuO;;x$__?2BZMer+@-8kU~ zU9hV&KG@Z1AMA?YR~GDw;8*9X+=)25;4!&|%3v!@$Vam9YpBlG2Dc_XzjScqPAU_F zl#cuJzNc!lyQgZy*K<$Rru?2N&Yfjh)f~2SNb@$cBQ7)+)&;_-UD$Le@TTr9V2B;PdSxw`CT zjBPO9a;|%<79Ja`+dUO_iPd$1)``P3b2+X7ri^+G&5h5F;T|hG?&;3oG@H<9csGR*3p(a zJ9C7ryxVykik;3D=(%UA|R~jD;eJgXV0Jv34*5 zzP;QfNR1_jyPCu+#y9B&N+);y6y{J@IeZ0UF|uvnZc^(ppRPizX2?84Y^;d*RsIW2 z+SR#5ln+gi4TOre<6~{|YH_DnZuu}-J|Y&86J12CP(0M?F=N1|!Qg5B5wR|1*tx-z zU;r9OL=IEFn*?4~l5(WEHw18Wrr?$BWkrk*?m}7|M^O^FKehTmp9$3=^Z?u|_B0V8 zCRTAf)v1cHlif}=Jth`w9!J?3k7hgH#EW8o`NuvnV*uo49e zQ1loD3vq#~)Abj-vAn-nZ&_D!gQdJb{rrhTxGonB%H4G31Mh|;RzGCN<8HcATl%G# zSKyE=aL$CRv%rzrkp(xim$HQFQ#lt)N>)N4AeM`HWdX5V%qz>fo34DAESdaR*2TQC ztc!VNS#Kqz>Rrq$d+K6dS=Pn8k{MAEXiGh+xk2P1wcL$d_S6|cS=N~*;kQ!`S=9i! zSwW(jtRQqg?w+jX|27E)if?~OR&_;~CS_;Y%ph!Y0K_iID&tdcF{|7~S#_WxiQU}qcRw0I{ zkjm#2Wkz*6d8*dJyXtm}}X z5)Bir%j|ai)|T6Rlq&OsogU1}wNh|nhNjN)}EJigW1jP+RtYh>_fjW1<%y(n{H-PL1UTbEp%OZG%gKR9u79xRj%`M50-;i^@g`B z4=^=t0_V1&`8N4v&0`W^YOMvDI*$p?37y!-f&wyG#R}{s2-D0Na-1eGfst0-tJCE!y3}}B4g@IjcyxtDmy%J= zc@~4}yz*6JW4NEX)v&t7o`pe{xT~w>_-45s@_Vz~Ht)Q$zIJKu(1M!=;jg{DjNCMG zdznR9XIz)6y-X3}g|B)EIz`|kch%S;SqY53L25e+>p*4cXucLdBELd4umUE_~dWOkb(>=qC zP0NyJcgv7NyIy{5H+zG8JeYOYbw{ zc;!G+S$BY-p3D+S>YF2uRB^G}pN-XPy4da3;b06ig~Mp#jAb~=)H)Bvz5jOdu)3oR zIz!hLH*L|1h^T4xzW#y6;Lz~M=%U5VB}|s<((6B1G@o{=&WV zU<4bN;{CJYY40&!Dc*0!NUGy|t*^X(+U$Ifxrqgy)&3V>l+-`kA`%fDXeVvf%Ez|L zLeBm~uiGwK%%5}r9(~_xz@iwn#kbF(?%qF@U5QlJF%nS3M%N-JorqsDiqJ8bP}Irx zzeeUhA}qU8vP(i&DmdEYvvcSEs^=f0FS--h5INszu>DM(pT=|f%b{`=B?4S zW_!WMR_hPD_bN8vYjLG>!;5Q?J;Wm>2sM-T z@8zD>2fivfXY(J_7p5vwaa#>g8+lnr?MRJ=s7T>Lu$=}RS^i!tK&V>p+*>xBU>o+| zBkQnE&-VRsb)AVW3@?z0XrKgfBWZ98i?0KoMwS~cdX$oF`UOzywvE9(_$i<=53yupbS*C8xPS0g*ONn#-eFle zMa1_i>q7Ek18Z6Kw*6$;aI+;EyIIwm^~PkZ^H0*Ar1rnloHCSg*-H;#wMe(pShfO(`-YC1@%*(ZCr2Dulra>-Nidc#aUV{7GGPkeG z%>?{~w`j#oSZB^FuHfUN^!??2^^28%|U) z!c5vz;NR;F^%d|&Eo`C+FaV`wkXEA=6&}@Fyxrgm$%euq7pxVdlY-8)Q=e5`G%PdK zc0Vw6o!Bf0>Si4-rx)y^JnTl~8TAMNdfTEt1g)_Qstr42 zR_?sUWdk;zT&`IF$m38FNU{$lBtQ$73M2GT6iVuf7RJAl8h}ySXJSyB=%3K_=7~GO z9JQL(47pQAdb#KzTa=A{J%p%pPoeI+;>xDN_il29wQj;wmjemLQwZZL6gYK=(UPBO znmXsk*^R+B*Ka9o)CWd=+y_cqudgK>N=@^k&0@OgRC$Y_5W3vohh>llf!pBtBy4Wb zTFtwRkXUEF!mz~w7##vwjmDYS$=tR$n}lG1z8G~EsOQ0YChl&H?kE1+(5n3ZFRaS* zaIE`kIV0@`KA-tjc?=W-hDp9;=zls=UW)=CUg9v6{K8%6qJ4F01k$s}bkVk;NEUxL=`P zH5_Y?^}y7}9A7eRtG=E}IMyDk>98t4z+z}mR1Y-+JmOH(5ZTKQHCTtgP3Dc>s{Ft_ z!m<9gedoXTR^@zOW5+>|ZieT- zti3^I{#NC^)tetYx}SEwW*ZO347h9;>zU(=kc2osu$bD)`M<}ebVg}6%Asg`{7@W) z<17in>N&H{aIF8Ro`Mm@;l_9k5QFZ{ub;hGmH)!+oz1~{Gvi;kRryIaf^Okh4`2z! z{$2{l`Ux8`oXr6rj`etFIM#8(VdfVnAH~Sr*zfhpLu4iN!<_s0QCwZTR(TNboH<_Wh;_Kvc&%qeT1)X-&$!=tyw=nD4SJnQy0KuKf>{I; zdnBTq?Gol`@mkL$+oSI#dMjajtD#iKqdc_Elkr-;k$N>=>#0)cQ|1N=a488&1(}C4 zm0jbtTF_L!1c{*e%+-<48#8P746;||rt3yY@^Grtu4kUfMqKe)2?Ay!A#!Dr&;c+= z)WKVfn5}Hy0OTr+02?#&kSLFYRp`&n9aiieJ)SdO>yo^8enkzPB3ybFJF^FS~NL~QHn~r?MNYCaG&DB9{GHIE@6yWVO z@mk@yzQ{|u@R3;*gcDF%Nn6CEu+28_H)$!~o{OQpMrKSV<-a5Xv5w*UOa@4MLG25n%4{BBO*y%f+mxrvoW2fQ z^?)r*RlYh~N&9`TpPE3d1O~NFm}PPE;LpRMnVe_=pCEKPkgzqNT7ae*^FBS?E2A?( zYr$QD60@Dsg( zMzG=Y|9*(dLz-vmqw?J*`6Y7;Bqvuys|S1*Dad=B;Mg8|$V*!=cc|yF15g}QNVHy( zgG3W0L^(*bj62yvW$Wv%OTG{)Hi6WxX+d+{uXfbDLuBi`VfPy=iecp3trgqjTb+8J zpB%HK%3oo+eZL7#7@yoy;c~eKt&O2aTyEy)jII~62(Q0D^54xYLUZZNA~ctbh`AI1 zJuc;T5%7;((mB#^@68#6&=c6ejjE5=1SB^lX9fyKdS^rO+aiT9oC&+4#rrW+oU4lR zPPr+4b+&p75Q7htfL zvX_KgBPy~#PRf1~%TbQ$5FySbY87`nrcEO++atoZ-mdQAvJq+Najo5S9|>ujZAHsB z0w)+@C7Us>fbOXe9t#Jd0ty~$J`_XY$Yghk=j|AHvH=H3E-0s6brEII{Jkb*-<^9+ z0BPa&!y-eR7mq20J|FdD=?I|d{&!xU6HEe47GYwK*a|Ino#PeHiAO9<>`bl`$6^Rj zJ>{RfPrUF%V9LyQVlb8~0{#9D`F2iAwS<&vp>F#zh7|ZJ->Fzqpgu4O6vgN3(<~X51vGyiJW#y?06l+gIR95qX$jcUiVy%!16l+-*DAuwr zP;8&gaOje*@iylPxqSF+(pI!rd+MA#WSK0N3Zn!zlHbBLXi;Q$sr&z8B!Ik%EzYWa zo3#I-s74o#+tYecd7p)nwWs}dY_{xx#nZKH!!8joJ9M~Sn!kqYrDa1fSlX}KC-$_@ zd{r##fM(eNi@9rA7s?iL3yu@yVPiCM*VV#>U_h zD>lYLSajJ>0BFbEzS-up+HFQ7?IdbUQv9d!;dnxh%zmEo`9~ zHv770RL4jH&GKwb9`0?L!-NY34ucrI z#kyB%a89Lbwh{ZKuER9m0%B4*b`g!WHs$je$<+>%Utc>EXBR3x7-#F?=sgY3>D4r^ zg?xo}@o~AJxYZ)W0F_Q4**2eJ^SW?suAY*I?3AoNBNEVIlA-8aO-$+pxrRro;r4!~ zeClw7?q5wiN@?qT5S&%0CZ+9-vB-$t3#9hW(^34#~8 zi>B5&%jy+rcK@U)4js8I-x=?YX63`LO(y1XSXWw>L-bys7e>wI~kQ3C^5<9v5~SlEU6?(`NG7U`tt4>`zv18)946D;k9 z|6!)B?M zx2fz!&DwTDkAy77yaV}PpCe?BGGV7GV~ebx>B909bJHo?>a&Cbz~=PhSP4`B=rT>Y5Z{ROo26iRf(9_1Ey7^Yw zhtj`TN;8?ZlQb|or?*6x#G00m&4iZVl2rfbFggOWua(n4?N#l88%YOu&@*{EQcJs# zXB_!lNoy&G0e~;+iHW6^MrF|4UvA%QW^`zvb@0%c4dY&d&9J_`o`-z9lZ3g&eL!=I z?XYe)(g50roEXVANfQg9CNz^9DBmBl=%ZvC!(oJbTHae{*P^5rA)^8&SfWxP|cUq_6*k>Zc=*=r_-iOj)}~rtJAq31)1v9*i^dW}wh1UkLu#3LjBn zgiJ+g!r{F)q}aTdUXo7A152Jj)g(?5|p@kGIS>h1ybjRWj*T1Npsn z8aFk$W3B-hlV$eNtAPtSwo#dvIWuOurX0H{iurOU3r*m-vqDcZrb;&H*lf_4v#Cq# zK|L7PSo(SIXB~(wsOh&s)`1ht(^C{?S8|9wK|3f+uD2^2tt7OVml!bJ_yN1ohc2M@)xbUyEzODegqnL1aZw$DlAIYuvom{PC$K+FLJLk5wi$w~Um& z^P28&R?aT6le(H5(;%u#j%jF+V?{%z+iSFnh$FIuTd;Cdjt0mef85+CsD(ls+`7-JW_n`qvsPXkA^^BF zh}ygx4P@&ofkfG!S}=*P?3V3&%80D08*M{4n4nE&)~ zpZ^IdHi4Jszt#5yU;uP8$19udezh(8UqCu0``^-}4~9v%`!;%;^udlvAF50`$S3s} z?3B#&dYSY-P4YlWJR|Ru<7anmnB7x7%x)T1_rE@&tNY)cT;20r-5>3~y4T!@XxD6) zw5?PY_qiUXKVRIht0jHduv{q0619uv4I@}fwpITcxesh=?o^I5XMQi7`jMmI@$!vL zU5E_F-qdXx-oMHAWOM7+7D7XupwLXvI;uK|_7E zrE33)9Oc(`)zw4zGXlAt9q5thfc&|{7Yu0UOmy@q4Rc@FKUhbK5|O4cS(Q6(Ktwex zJH)9aSGa#^B>lxCoPpozFE-gQ2M+pP&X(KD67%wwUG0SP8enR)mx~|jrx_akV@;|3 z@S>_l`;biu<$TOW=hO^zg7-hvh&|17FR0~gFXvDupetAd!X(pDYY#%as?sD>$Z@qb zq#d4^E0PBd(g(R2*JZXCVSnd2LVIU`zh0$m%C945$0mCKH>RkHyn-9km~hy|g4+2H z=GvuA`o^Sk<1WY6oif-dSW=C@`Ri`<BRi670O+2~AUAWl4K7khs z{jlPBj{z*8^#+83JLl&etJ|*>);mo$_3T9JO1m7q!dyowjuI55-{BPnz8nQ2eYm_j zI4)5hXo2M{XI6z=%0Zp}_VCt7<4XN^j9|Z;#5hdiKWfK>-N+}=xX}LQFB%$iQq*h! z7kQLwy3AB)#6{w9c0vLnIp8g@l4A}oO-J78CJioEbs*Qf)@+HC<50z}CE!~1@Wc`} z+N>$`C;%IRH3An&?9|d=Dra_&ZRkt|A2mG8muB)gh(M}*ma_KgAh1yTbkI62(aDUH zt`0u&Q_ehCKmY;WPU3UVx}Q3cH^bede&sHB?ls*G5nUfb-r5ACD}YNFYA$k%bYdKC|J{>WVpSKC+*=3dIG=Cui>YO>Wfnj|{r+;#ll#kmy#1v*=}+!26L5hA(}SAEc#i7+@+9$j?1|6*CyCF0 zJmOQG^e2f=FU044NnGzPy9^v{cV3o|O^e`*xeV5w_hU}~_P6;bT6%C@2<+`>3ddy< zLdOc)xy|GnyoYnssmx9ToaLhSME~>RLij+KC1XCNdmzUF78!$NfKI|tZm*5-LmgJD z1nHEp-CE_;C7rB(Y>b}b&YRY{m`%jsg)hAdx%Q>xc@u-MLvR<-#PB{*Fy9z2*N zfN@}=OnVG4g+}O-M715w;6@;}J8#B;$xf=i8BVIEI_`v$@7NOqC{r%E5j=i?@{N3> z0ClXEB!zsd8%`D2<#IFN7>3@BA(o{=STc1nh>(jLPkrGN6P$XTLvqcoaN#tG5rBeU zaEK&_n8i#Y>gK1SK$G?Dw|&2YT%v-qYHeQi>S6I&UOslV8i5{oXBO*;Y+X> z-En!lY|P5y%i-6@aeN!-#Mv`-w>^!$NXMLq4^rPG{p`QWl?#V+qw zY)7fs@O$MERs9P@UcGD4a1DQlxW5m_2YEfj>+!}obMj;V^z(1{>-T;ox|2E@yY%eu z|Gz)SE#$A?`-SMvNgg(L$${h~Kdwfgj07){cX;LNFWJ{M`}&I~{`6n|{{G`1^u8n# z;UyAn+Xr9#^#AkgZ#n&!@B3=>y*REUys68=A8qJ(l*cK+%x0H&{yp9I-e)Q?LK{O{XY4re|g83KBJcDcAxd;Z$JAhzy6{39Dl#{ z2Ekps-^2yB2{E6bc|82(r`DFS9Q(q?c%blFEx_njV5hjbeIw>6M&uFXwtp)(wmV$* zPKCTWTI@8Hl55(CB-A&@un~oZyhWbcCRpaC?#yJgYTboSv}vFH9@ru}#&Fj7?P|lM`;3lTT_y zg7F491ZqaYHA|=p7PXU`H6&TF=mH5jy=f?_VL~Z>o;I3dxEdBzs)35y4J+mPDz9It z@df?Ce!}+$S6=l8r_sDW|F7$hhc8xL>RA`0rT>pI^G?6MM0$;8x};ODUV#aHreoB% zyM-RaYQBYj21PC8dvR$76-O6gcCs5jgxu)X=qV(NwnkD2QwiKrLpd|^8nLpH9NM#S zfaIe1BMrB34^U6v7bcerx3IxV6#B3sF+7Rswuhuq@fJabsSSLyk-ekN#)gW^gt%Cd zfb)fof+;UV=3%FH(is;m{92d8;`eHogL&OSBSY4RFq8ZhtzlXo%_RCC@@<;;IjC5c z_tPp)I+a#gW4V%C!?mF3qv>K+INw&xBD%~Q%>%t1ngZY}Q`>wBB${|QpHtplqf($5 zF*GUFX)^)E0x^t8dF_{`#@oY_fX*^+Nl6UK_|R9li!XP@xUvh12<_1$eNZ8UTJch^(KEVTc(|_cAA8IEY^ft&M=V&UR-SWs z9hM_2hX}g@OuI>?;f^|1n9ZVGS+UJVMdL1?#oVmH-U-4H^|#R;u^boIkzcjTtY|c8 z!Y}*5BxqINt^k8xYr);mM{JUoLWxUqf7D*sT z$fD+77!?6|>y`Vr@-idos4L@^5CG6xVfPR z9CNBh^LP9z?u!`W$-nV)A)B;Mg{*Txv;xk*(XxH!Qfb-#_TPrG1MRPctaCuL0)yr^ zY1tujVYKXUTbEW@b_7MH&yJe2WWp{oq)pyB_!VB_KdTO^oMn@?{6j74Iw*6|A;HEz z(LmVsKy~|Uzc~Fku|JV3lUPH(0^Ou#S$9#EW&4T(?shIQT%m=;hEKe+jZw|YS+c zyGgVkn@i0&J6yMohZX}Clv7pzn%vfcwGYhcRG>VdvEKyi+Nk`2nw&dO(=dxoSSNgQ zhNwf@B63e2tyAxBIa|M-H9`BgC#E=WwHJ8@!!C4KC$EL~QtQn1HFvlKT0?Z0I&baj zZ#i4917WK&F+ju)h*UYLTYrbL^XWBhEHljLGjoc00OgPaO7Obu1{C(qHvq2INx*K{T%LG7e&?gWP-}(GhQ}3nXm)qyBshpwY}(OvZFUKoQ?_2up$3 z-B)6TiNnQ8hq$2;=Trz@oR?leAc&>h;AW1e%MCZoWPCmg=A#Zb24-0>oHHK_Xc&Vl zC_s)L8=O$bgcHkk6wU|j37I!9>MvqA^>@Z_`ZoH+Py;Be(s?g-3_a0d#E`k87Ic~O zt&w6-PfjbtyNq2EKPHgLJE8!HbFS<{x+-)TNw3%kya2^M;QqJ6c3f(7G}0YU!P-FQ z2wdl8A zcv`==3Qqpu#7guBr%PRqgVQFL;F)QpjOAdoewN*A4RjxfRKVxmL0vdqk>ueAp&I;T-`4d|a>? zV7w5dLSQ91a4;RuVY&gB2H7FsZw}~1#hdd&mr|&7L>tCN%!H@|&IdtTR4&}?Zy(6L zN&YZ4X=UK0(nJ^Ic5v45dW3#o{p8!;{TKHifA80#JCUgMJBixqU-{(^z2~zZLgpud zU;>bwfKDR2ul&vLhL|)an?tn6$YQRFP;q)Mg_ac)6!qmEA$L{Vn>qRF&N`UH8)kL6!*jtZ=9)R+Xd3J{e+NFouzlJ)Tm*{kJjiC( zOe?moTe0y{vEfHzX2IiDo#3odMg zKq`G!q-CRw^kt4_;@bmlNcGsPH6eD&K^x3{#_13NpHl%MjIhrnp`a=yY&<2$j$`pm ziary0^fJosBV3b@P>w(JpTQ$J;6HrOEChrbV%=%y#Gu(oMHuKPM`5QK)Cv#;I4!eT zNizu0{Ge|4g(_-^F$jPVpVEBQ{7prW9R6Cx#9iKGHD~&W2{!WZCJCan5g9Q`R8!kd z%@Y!$j`3*9Nl)abw25dcH# zAXiWO{ArAwKcQLk+klAN(^~z_Fb1HSkC0{4L$8oKH>!cLR-({C`i`>2mZyV0kIf)> z+~uwoUQ+OK?B#i}BhyC0?=`J{t4VWQLu=c7SaNg&38FOWWa*|Lu$k9u`pr>{uBjG6 z+uE{OFPFAeG=wyVrL@L7e5!hf87wEK%&nFD)=u>$x?az_HdL2!K3z^1Cw!lMIfGYS zma@>>EoJ-y%(X34T%@Ssg-;gZS}qN%0r8^7NaWO*x;E_M7?KUM_R>*mk-Q3Mr}6!Q zsJ90w9Jid3)0=!tjj83~zt&Qi&(|=qq9eGC|9kuch_U+K2|-kcHbknDt#yzZSt8aS z^pkV;2jQ0=%>*t9GG_>1-~WjoJ_oJ*aP+wxFT&@&G;X_tAJ9Z{4G5QikKgZ{eP=Bh z00fl)#vZ;?>5{*vHZQlCxU0N)M}f}yb4qG$J0d-1^)E>nMjRvBjEWjnU~7bLqIzWB z1on-Xf)XKz>_P7!ouYhk^V3H5j(oB9ol6N;pf!+?)=lN7JXcQIzl3*vuWb;%HnD&4 zYX?Yc{6n|v|Ea8dKYUw1D#JSO>(r(9b^b|>So3A|EPn0EQvEUC{6!q}jEB~~q7^&R z+x%;h0d=?u)gh7NsK$WfC<$Ab7LYC%<3dGk527Rk?SWge1`rfMGK__83t_TdK*;o?uHa2fzu^aN?5C{b*Y!_*WU zEUhZ=<0#Z~2zHpznL+A3=q2HcizUCXTspWhiWUtId;12i=+xn{y>1te5#J9&J&9Lo zno;xvNZNTmy4rRt;@pm`;lyV@cE2uNbbo7X53j#O8!GdGa&OEa`?~;t9=Q#>3^r?aNijpe3_i@pYJL$-SrBy45{l%Na21Z`Y&vA! zz((_#Lk~EJ_Imm>YeHP%MR{QaW^)x@oEJudH5*P4@ppu~l~kYRXNk>^Fpe3XL>juJ z-FQ_-9V_gGn`bwPT@VaaRk{Z8DWTc~4u>QyP4yaA-vxRBKLsqCHdePUW@~+V9!qVB zWEMIwhCZe0LdMSx*2PxFy|jbvqtvn_U8!0WT)$a-b(s~jTh=LescZQ{eS>cn2)0Du zEHTo6cp|>ZR`xpepq@Qd9NM7Hu7bwX)bdmL{gD06o}NA}Tc`KPKJp=0Mb>F55{~Z4 z#$7FF8CA=;hbfGl0d>7%dkzB+?EzXwchT4GO9gFVL`7OEzi|$euoe{jQtN6Kjg$A*$4ggCX zqCC$b%F1+wpt8#F4Lr}kUze6|RZ-FS)#+u1Akg+_rq8qN z9Mocvjz1sb`dm}EP_NsTUO>*_$P2B77ldk`;!&^*;TfW;7ozQk84lH;F!$B8tr}Y+ z5DX1sX~){CpxSnaYL4)w+d}K-Gtb)cG|NxfK1_q(Y4BmIZQnoHdS>>W>2@|J*x%5D z%kv(*XnsAoqSAxQyZ2y(9=tg8;6>Jh7pGUy1DMFrgB^JfUQ+a6rJH^?)1JPR_bi)i zM`{CNj=Kf$T{#}@2z{DmPDbhAOF;JTVtT1lBw=y3a+t-+?G?;|j)QUY^c{8<)EUfp zs&u*R5eM6U#i{qH?dGe*<_@<1Qoo*O**_wCQU2l&^~=4uco&;M_+TSH++*nwFLt#+ z;?nQflBhxmc)C$%WTU|8de`Vlo{qbxC&)g{Wgj8?beClVN!Pi`AQI^&_Zb920rylH z^elab%d%&rx*nXT5Tg|7zOt9@N}uVzLUwSz%Wc?|E^!61Uo$Hup>FyV_m&Ve$olmy zcHx-dDA(MTE^}`=TZ}0eV=EBvYGGS|sJ6`o^B8Chq@$*N7&1;b+;YMMz#OL#%Nj9% znHhd%Vto~QuW`%GtVm{~fxU+&g;~>NvAnsU%y0{N`3Z%`f-{9BAEO+}s@8dwOej|4 z^q*wjG1LT@mq_RW#S|eSBJmM)FLhkJhrMDLq_+@{-;_z*v&;Y@MxoLKz|M z8>J_u(?C7L0Fh`4?zZt!odzz-PXl-1z$~MUy-Kn8nH&Oy7r6sLGkvO!YLr7kI1h*$ zb(S%URH>0o*f>vk9QIER8Tz*;hjumpDPID#z|GJjZoWt031s<(ErRA7;}PW$!~18< zyx|3=S{`c(9H7fW63#eH@v6=AB997B1?E_G`M41_gJ;W=lTV2q#m;C-2C-TV0E4yw zs4)NjC^aka_Iu#n?SZ%NF#z5^54?R9;L*Zk13bFi8}NuSbV+ir21a0s14{XQj|RBT zGhDYDJ7{6$K*38v6~|~eROHcyoM&|MUAu7Fh->tmWE1=ZQ?xO<+^rhAW$dag+VNyt zYh5UL8GDkIygF~aoo3TcwkqFvb)P5IKBJxz?M!$LY zx{Md)%lOEcTgDG5Wf@P6sdBfBxo(WJeZ$W7T2ET5O=9$u=x|{c%Tr@+$xb1`e6t9k znJ!b?=&sfhXY9HJrIK5eOWJ$bfwW|4ON5yQ>42j7Luy7@#+c1P9*jOOWsF2wEHBFY zYS^=0=D~p`^tT_{yM)Mzg7m?fBWU;3vMYrg4=34GO7BXt7b$%p&WyrO#Muk<`eXvX8Q>{;nrL^d#r{Hnw8lSJ7I z5v(xg=cjA4v>j(zZJYIJk$8aZNc10C37{ZxQ!Al0n?Z@3whRKCksDjgil@$-xo|`c zQX`_8Bl>XjCFKg3MmX*fDDsR`xzbPN%F0x(?0qU%&M=kFs!pZ#GsU(z0P9L*>HHoC zO6kUEO?J5hgGSi0jj7eSF%71zeCTFr2u7zPS<8#64vFlvE=lh5^!4CS39E(CbAaNY zr=r3F?~Td!D62fAZ*Cn(%<_lEdclWtxnLZFjA5Wy_n=tugTWMneiBtmm}>GitS zXn37+z!*-SpT_HxgG1ux$c`t6FyoTgj_eV(Ma!-Sown4D3wvFhm=ivWPW_ZtKZPG2 zW4|Rm>R0zsR`(b34H?WIVHvV~(`T#F!Ssa^3)(OEFjadTtpk(}u8ydJgW?l5kr0V* zoCFH6j)Y<-D27Z-*;8cEw^Fk-87#6aU3vww_qA!(-b7nKL#qh<&?@4WRuPF-QN}k} zreEdthu|vR9Cqb0-L2%oq`b^pXv(G=Dm7X|4@+=uAS4qhO z7XVvG$)O8+Atk6|0x3DH42zlJ3yu$H`>|(5RG@7 zVcVKNjK!XjLa4%_3%1mZ4DP5f8l&=X(PWnwkM>l4kCowenGxd4C_7r+h%PG=VlJHo zIYzR}N;-qfoX!9m5Oo4=$E;Z755olk#-O}Mu0qGXZcN3F3i^n|F&_Gg0zG@CJIEE} zU@*NbkOsq_%t3B@l6?mta7ec?+GK)AJxNo-j%MqTQy?w`ewVYelWfag!nm22D&#M> zFHl{#oGb$xf0bywn>tbM{e;vKX@sEH_PM;#?g`5~lD-!=2o2*=PqEK>ihUM}{iU2@ zLtaKi#YD0>o{JV=u0Z*N>GKs@b1;30Bgyu`5ym%qM21@%xrXyw=yMpeSfF8+AItz^X9tU*p4j)5CN4KZgXYA7#xKI5j zT*Nn>7YCineE8ilqMc}Cv@d2Hq?2*xdNF^-+~_VKGr9}KIV9`dKyi9t%pX4z+=Vee zP=-wblUSg>2bdT*dxL=}NMN;%>H>qVRJR8hJbqNS3k(i+hk+?^@0}kEY9$yL{RReI zS=k<7@c6N^U0`r1ZmrElTIpIZ$TF$PeR1~ed7uG|UycT1TY*4H@w_(3nV!8MIg{U5 z?#|Dv_=P+s5+-=zihyaY7cg<&D+`zm4y_7ddcLPJcg3ye2gHsk{~SZv1O73FWe3Twhsw!F0&WUITwmri zd&ql|>t=XcWn`%z*@^P{5Cp>Cf?hwi{66VaoVE4-!C16aLHM&5 zstQDLy~!>Mx^iemY8Z8&QfTWzy!qxsTMw>q?Crr7W%icZdSy@OKzIJ6%bGe>hFbxv zSTHy_Ke|$;t*1N;PI?%em?sQQco>|hz<}C%g#jJt83t#%!@y|k>G{E+Ok2OD_SkdX zVPM4d?EGL*FYPbiQhV$~QsTjiwP2=EkIoMUeI*!tOYO0HlhzB0J@y5DkL?pVm`pMV zV$US8KYhV0d+ZCs9=q+EdyjpA-($C>FDUP^en)zN!J~QzX!{-MNZe|Nz4jsY+84XM z)=T1jA#SjAx^~n84^!wn!vPJS)q@hVO5(;{;s<>G1o?fUscfdFD6`-4?)-gVt$VI> z`9aIO&3m7JE*af^pVx+c%5T`($WIhI_HqB1ef*gDo%OIm_p+fIs9sb#^gL8vaM~m* z$DOIf9d{l`+*W-sK_3}hyfg$Z%8 z+?RO7*`Kst60qy;WHP(JVV7r)e4%@j){8=+0~ES2TzKY0;LombQak)dhmw{vH2@{= z;9GXG?bs`&e0(sJ?z3LmEMb6hvP(#-dH*w=h4X$^<&9HsCjUNnMWR+5ru zt5^xy{MR%}vsYF6`chxxU9}R$o`yhsYYu@P^$2t($zH0loJ_LsR(d?iUS^elyCTq4 zHoBKpK=(2ax)Vw3WnnTNVKOdtGxyz<1$sJZT@?zQqR?{#0)4mD{L)IzSNodJC9SK& zM`!tHyCKj%EFbKnDgsT`THhTCPt+!}E4m@j_xPIk*IM5b3g1oP@A3$=#UYSkMG1kd zxhpFJy0_MH)IH^^Zalq&+|k6`6=2x=%1gU-2?vlarN0hKc=xb8>Vz5h$NAy<#~gnw`cS- zF;(lZF3F7;?3DFjS!WRp8A=L9CMlQec36IN<(6{G5034U5!X1Ec}wtKEI-OpisD#~ z%;T{+m(%6y!8}-gtetN8aik2m9B8rltB2+Xv?MLeWv1_EUnj(#Fd4>7pfb(}79=n) zh!VuRtVdFneV>;5!J+ITiPTH$y_`3keOiS+UE^{0bam61wr!m9+r}f_nCTeCO;+9f zw~4OEJP%f*jJSoO;<3;acW@lSzlnM`@tUFLqu%b}OfCEVd4K{<<19yjT+DXmf)@1? zhN3~tc4bw}=Gl`gyI|T6cuYH9YyCjjuO4Q%`A=?htCG@_wbqrP&k55;1$TeY z*Lbeh`oU26EQNo_c0ks|5BoK7jrJJxa12KP$>Y|}wNftjttHvT*3!o4Zf>6OLwtq} z5r%26E(CIX=ykqB`|7RhLWl0EPi8-2oBaXo(;S{ucHevIt!IS7`zidNt?+%cWQD6% zPCx2fI9PA}X!z(}KH6y?JM%FkP!_gL-vQ@AW*`3(ogqin2=6C6a-68Qej;GgBVf~i4NLkb{gS>u zU(zN`V-I9WPp$NvtDWkd6o7}44$QUI$NgHrot3;NyTQ7_X}Ujqx#r+#U$)Dyag@t! zjyyY4tb11+?Qmadv!GPa#u)qeP+#^6eRsGoyHV+Vec4YbJe9aH^wSFpmbcm1syQ}%eO3hP!tyhLZ4^n84$9b4R6Rx|Z zaOeWnS>gk&*I?&_ZfUHA59y9^EcvR5lT)0h3M(vyAJ&nZ3Lm)$C~e7H|m@8ij_ zzU(!RJ$hm}zJR066V3(UcF{!;dZWSP*2+}h>ZkfdU+dN|i;pmiuXR)XbCn4?-PgJ? z6gowr+X52(oYnlZm70In*L<$8^|RrlvwSq+?PgwGsd}=%^>d-rME_)VJJ=GKqzXmt zf<{obmsgtJ<(qz}j8HlCvmJfPQ_=_evpaaOF*?|vz0NZC^kE&H9>E#BGNd;z*m#E`5ClxV9OYh$P)?^$e^8k~1r=RlI`44=#L;bCn zhhq0p?B{*4JF4~G5$b)gzja4m?_oapA0;F)KFiUXgE&GP?nuaPN=^hiGJZQz+JX;y z0ll_nQwDo0D<{NvcC!apcEL76N!qBf0o&_(hN*eK=Z-<7WBB9~IaZ-Dg;#%yH;!da z_E*_$=*a8B$WHWUzhF4`NPqTv%N*~|ev#ls6tR!b(0~WCv!tnV$ajT`PWQL&%0YAr z5dD(-{?U%c{S2Jz$3tDtdq~>OQu+<9^j4nL^mLCijVhtfe>^y71Ov_S5jfgH75F)(YO_4Lr+1GM{Q z*WU*Qs-X3wJ2c>3RV5^+^ewdw%rAil$547HzmwT9U_b|;Y?RxebwydMfO+g1Y z!3?aZAlGlYhBroM3+&ox6ToboDzM9!8&A#*w0<*h_%sdwmcy?J47lCIx%xeRJv!i> z#KEs~ls@1}-$e)w4-gD0=v5Wt-WI^Ix6yiA-r8hiGW%`!{r>K)-Q8%pSOc`SkJ7)> z1)J{;bsuQ7?#=7IhthA?o3LHK#m!Trg4WLEaXh)NK`86#=I)KMx%bO(lq;k zrDNF#Ni~ljvNXyL`#^lzhh3-nSg~pxoAD9NoVv|e`KsA^M_g#6c`%ZF)P2QsyoVli zBYjXKwNFcv&cn)3na+=3ot!-w#wvlugNi~#`VrEIHs7JpfNI}t{y#c920VtOmnnB% zaUYH8Ly7uI0->>a8u>A!0fY2AsEHh67;=2jD6JoWz_+FZK7;-$X2Xg)@O|8Y@8bgB zhdb5`4@=-nv>HAv@O``tzK?tGeYh+5KKR6g4`k~Cz7GfR{T|5W5RHN#a2r!V#}CBL zKj7mzXoUv?<9H_+*Hy7_%7Jl8V5F7`>EU4~7+Dx2*;E;fQyz>T=nBSf8Z7)k0ORl5 z?({%FH^FFg>w|7fT4Wx)e~<(E0f%t!_gsJnzkEyrc(;|ni$IaPkf0?cEd7K7?G5bzQ02674cLt;8!g+BQKK(T}LA%4r91CS8eB>e+^%8T!V zyq4?8@AF$uBSJ?x^8Fsa*N|$J%Wvdk{FdX0h$d`W`XN#*qtnBrb^kkps5(y&wiNaY zoPmEgW-Gzvk115xg{gd<@#7QaABTz`Y&L9aY&OiZbN*n*+Zf^SZRzDqw|;NKZ&~Rk z0s(19+>nh}Ixa_MhI%kyU)|koq_TtDx??Zj8D2Wd@D3xfs$~qsv;)bzxkjxynSD}_ zr#2tDC^9E#8d+ltA`+T&*nY|}(?ke{6yg4TNQ^I1= zf1P6YdNVT#m?wN*W@Bf(*_h19&MkMRGzF%SrzxKvzMy4sN6OwJh1-xE_QVYeNB*au z+;@@6WmCU3$w#;XO_Tdax(bbylk~(2hY!@h7b^*&2v5&o#2~EU={am0gh<5q_#qN5 z_W41Q=GN$1l21|5)uivC0U_ll!KTYe-@$JoB_Zg9l#E%>IHDFkDf6RpIl8|p$|mOu!Cn06by|C84#O1*zi%{VDJyavL9h`DS5QvV{-Xpgph^OM;hMq zm(mYWTByL|hiS{ipAGJ~;`bqLveMZJ95q!iz;`;^>Zg(`Qj$sYEsS0Y6}QuoHWp2mSK{v^C|QALRKF<%wJKh*CT=k1NHu?U>T+ zlH{n;&0C|p!S6IB5`|Lyl<)Ds_bXk$CAmu}es6n~(*8c$KdEn-#Qn5$+VUVZ?Vs__ z_wxLle|`p-JnEmHTSgods|VXp^7r{VPf+Gw|NJ=55BulG2v>8+KR?3rhy3#?%0K9z zALjWn|NIc;kND>YdH#rhet_~1`{(<4e#$@JNBI-VfA5y$9wOJI+py74`dw(DlztDF z4Jo~1OERhSySZ9S>GmzPbBjm`M1NN4bGIa?m0m^NN-w2er7xooO1IFy(wEX#rI*l8 zrNfL#>2ny5Qk)JCE8W1jmF@sWN-qRHN?*i0DMehjUny>hcPm}PJSlxP^Q82$t*bEsxEZY`UxrmM^mPQvN4dA9{a1>qF^}gYQa- zp>$B`e_?$n{W0)T=}y*%(jR4gDE-e{YI~IuJZVztk1$?^G`NoOD*b-uN9o0vBxjVW zf7qf;sjTo9>U%`_8yJVy+s(|6Qhpy*`d?WudjAvr_TR{6`Gnq4mu#7jD?d*Emyo`O z6QELl-=}mB^QQEb%$w5x&b%r8DdtV-tC%;XyO}qo|BcN_=}%vhJgRzc*^-=9`U?85 z^k)b`p!B9ol9NhBNa@d%zM(&|vp%dmwEj`0uLchO_gSU4vsau}=I20Er9`W)olxdy z!BnNUfvHMw1QJSL3#KZ)6--r1i?aCJyHt5*RokiZ|AUGSDsu1>hn4=4s#oSNRjC4sLbog99HH{RAa~Zy<`sR z$=k{7m8s#ql$liKcc}SM^#i%KV0^QRY{9azYvWvCaq=weO!*hAsY4r9>~75TWJwq*6_j zQaF(0E@fa>?^Y@fV!tv3nmC~JEz~_Je1bEr?NtWO<6gaCy6=KB3P$>qa7@m4e|@r# zFdFxGJ;8p;+)MsKg7Z#9?f2>bUE;+EbQrhq8QOLKB;1d`1ma8YpW-c1+&()=G2Y`Q zrvHg0^0nLD6P%`avJcm(f7TQ61@Jcg9L?zyG0`5DGXXbF9aBnlv!hD6&5X>-dXaI9aQ7Hh3WHi7>A{q+6kA?L~9Xa zX`4=}I_r)I_ea|U=?LR}SnT|WpX6f?*d*)uF&_qiSwe0nK76E|l!7M(%zeLMuAt%S zkc^XF9b4`|GT46m`#j>%-GDgb$pb4i`GPXtdw@|N*Qm)KthHEDH4v@qgKS8)&VDXFHT%wsxucfBFLHwZJPsFv9JXVK2pzG^ zp7apzS_Rh7bXJ+H8f;)J(4AN6DO1xgA)V}_eo7nBL;^EyuHF`?Hcnj;QGEJ-eUkSzYKo+Mi`Y(CS`uhwnqX zp1P#HN1Mz&%Uj(CT-I0`!fpT5iTY`D1q4$)kq$t^F`ImBs8Y#VJk`9gJJYVxt+dsO zQ!>50QoojZz0@6pZ1hR(SIGSdRFZF(dQVc+8@%f_?2Ow_~ws^`tAbP`{nbT zz7+r2qpoIu=01~<70YJ5{3%`55|qB~B%SuA$t2b0eXA)(DP~6}Kef)OtV6@6qtgn( zbw;m0;OR%NwH(388~oz0HX;9-A1a<>`B;T|&N;@033oRaHCmVBtQSt{`Xra*?XGpY z6DTEvUG8g58A`{}AJElYTcIQE@Mh(^!ndef`4(Dt5d3uz|Jefm`h(!F2mkX0e5ahJ zre_2AcNOqA9wh&b;NMxm-*gcCP2m4a0e|yB@Hd11xdQ%{gWzug|C0s$tp~y13jXE- z{>Ao5@Z~*xyXPX#mH+e#?Np&E(q#a7OQa2JCJo-!WiUGx@Fo_cfF68Nm6y zf1~_x(e1K??akzV190&@V8GU9@}L1*n#pbh@Ety6z@}#MumKyJ$@dM|&`kCifRFEX z1J*T@9~i*;{0|LS(@cJ3z+^MoYryJe@?!%CGx&)CE1St92CQf%`wUp#Onz#>vS#u# z1C}7X}bz@k;~tHIf4c>}@2!GGI?5xx)a?EdI3tyBf)F z3?NMAF#~oslE)3$(MW!40Fg6K7_hC8OmOQDg40N@GJsf`*Bij$$5$J$sgW!-U}Gb> z#sH#%t~FqNBYA@X>l(>20|+;|&VV(I;bYj%?X+v}{i>!)q?dB-5llA1i2CS|p|7^gj zdUA^aE9=P`16I@%Ge(xz2`49)W%cCcdM>Re?=ysndh&h)9;>myub&Rol3Oihe=S*S z071RP2Jq9~TJix)*;7kCXaI4(gc0!5u3FO7^FGC^^Ca;1Hp3wD_qj?S^7mDG5+wf$ zJ-619b%sF1`|Sp7t|=6OpElK!J1m7D_zxMdp_Z&SV0|t5umQxyf5d>bd}Y8Is5F33 z`HvZ}x|V$0fK|2R69%lLk_N1(C3hOIyq0{@00QdoGGJ*f*<`>(EqSleLyY~WEQKKZ z&shpl_Mf(tebwYM2JEdSn+@1gP5#9Ic4mCmfL(lLz$B&QBD5U0JgGx z!GLYmWUB#NtI59^u%()O(SXg>7G3+|7SZ(0hw-o9nP-iX*F{lpf*Z(GXlD7nvoT~TtQ{lu=p z?^w#tDEY1dJECNz{lrGX?^z1RrSCFeYn0q?0DC_lFko|}I1%c;DM}u+6wXxNZNP>o zdB}kEQSz_>>mtSfLC@MK`M#yBiIP1AOh(D|_S5Pp`GKXZijp51urf-P+fOT^bt|k{?^jM5M?kQrJZQ6H7S|CXX1fKU7o=Ke4a=`FidR-O2TP!epOe z><&4jkRNu1GRf&zVe(V^ft~lPt1(`MMBek<9wskUrMHF2&kTX%1|BtFOPK68U~`!K z+<;AC@(TkthRH7t;DCq&2CNU0Um372Ox|Rqtqqf3TgsX+`HcaSVe;21l1cjC>A5OQ z{#MVGAxAg!ToJN2h3E1xdCWdo7IH!q2}{G|ay=))dBT8w zLGpepes7RWL_*#ZBv%=*TQOux*%c(Ox0L$=0;rU-laO3X*%2g54cHze*BG!ZNUk+t zYmmIbfGq^R8n8J?t}|d$ki5}=jY0Az12zQ7as$=}$(s#W7Z5w5vepL4TP$TwkX&!T zWRR>dV0DnZ)qqt&@>(^@${=~0C9DXNw;QlLNLCuKEJ)sAz|tUjrvVc|a)SY{NmkkO z%H&3SUXk2n&&!f`+4It5wLM>vyxX2HPj0s7CCPhua>pT;d+=DCoE8UX1ixspLkwDe z&bqmt`&$ZTrXu@V$)D=EwJ%k(QDyhn9*JFmM!KzorkaT z98H1C|KWMC2_RRD`iZCeK-a)=JgA#7<~KX%KQ`k^e9{X?!}ug7^zFEkIxv})wwdIX zGpmr9j_x+R_@wq*WsY~+-1%5WWbqsyGe?)p8LcJtG46`M`F=SkQns(SoS$1S8l?{L zC_Xt|^W(jy6l!N_SW-Od^Z!%%JWGVI?#_~zmfP`d&*EIR(yzLiwJG*cej5iXK$~*7 z#iCG-ThUcg-1g;ExXGcM6NIt|&!O!xo>EnZTG2T2S#!VipW;yMjJCfkCa9&@*nI{+ z$}Kw-2O^FM@CJc!!Il>AyA}cjsaHf`AeddQ;IN-sr$v>{Qx=WA7^k9LYcj7=Nw_ZT zjLOTn=Yq#PlFwK|s`$ul8ZB;YPoAS0_my0L!Q+Y~*5k4y)Z@~mrpGIijvg=P&O9EM zBy~M5P7-~7Q9_=&8}ylaEatil9v3F3>$mf`G=sN=9Ane^GbTa=Cum;O(Btfc0EW|| z`P@>(8#grRF`qj<_-?Llg3#QkEtHtMU3i;A2jCGW&sO5BTi3m5qQpF%t0i?4 zoaRdeL}RP`%=aWZXBA!G~o+m~{q1B!|PdIS-cjgJ((aSs;<*udR zRp;Q^qoS=YcPvRusdrW`PlH2i^`%9g727Pb0-&s;7x8vuJ zTA8e-r*aDi#^OINN`)?iK#>%3dbV2BlHxHh6`Uo)!O^3NbC6WfuruSQlM5)DGXFO4V96QkmY@Z`)Rz>_~aC{HBaIZtN5lXf3ZxWAM2 z=_k(Po1>MAnJ4GA+b2)Tl>XF!;#ia6ssuJCPfu$@t|j4e3T zlCq9FtOF=Oe1nv*gx|j_z8-e4Z(;k7FJ?uJD{0y(NPd=Dj} zGn+3K)Th&}LD1>5DH)v}OKuE(7Gn&JoT+%5?wnJZL$jY$RPhc=Y95n2k=e*s*jNZ5 zLpb02u~`}0Yi46%Po}IJr*x!ZZdUpnapF1WROVaur>EV{6LBU*oE|^Z*{;u|;Ah3B zr{z$t?PRs2_?ey)&pLn9nENb+Gmk1T@Z7AdGhI#3VP~Tsy5>PrWWA?g(O3kU(EgFT^ywcw}K8o*znx7nwu0#J!o3?9f$#Av3?{Lht<4 zj`dPc_d{iz;#%$$TCTzEf_cj|B#@RApW<7NyXtM+LYJIL*2G_)J@Oa64)aH7)V?{A zSsqwY(m=FRJR%;bp=XWIWo-b;Go`4@4re2@z%DTE@BxJnWgtC{w3i$z#R zP=mZ%8+6kij!g+pF|*MEAt$A<^L|>mprUE7w)mWucNsfhsrI1}4L=Kcek1#2d)>q! zy%C~pK-9#(#}6tgj%^6k@ZSGleA=Su#RELgCzqV3Q-;`Kl4ehg;$4ad!Gu5Cu%iB^?0&rmemk* z9*y@dp0Y>!i1MZhdj&l+`xgLYrm!l0qbKS{5yf)3wijl8n463!p9-oc>L!S)l?#Qa zn;>Q}MBT_!_Ddg87dm^1t-@0hCF6xv{2M$`H;5>f_xU6zYLyY?Q$h7at%9g}xlo8& z1u+*x)D1i@8AO!Mr)0C{)Fi#!evjYbNqUD!dS|Fla+2O@B>7ZOJxT8zNYXnYsxnDYUwTNc26^m+oCOhQ)dc_i6 zMlM~VMJZdkI5FlJul}BGd2@+sB|ed5C_{-D8x`O;BP|j0GQGIk(6fV$JMEGKj71ns zFgd`onXfv6y=G2W9AGsmMrKvAX}GN#8$%G+)?q+!b}~&pCgm7uJFM=BrGw*>du4az z@4*0~UqMRe^V=W#Gwg0zU?bW*RgRDN>2uqQJ(&w`CSFu#&W&b)I z58;KM2mxNp;{W6Ho`CCgmW7GCOFHWE-aKCN{z0oL%s@`27x~p{CGDb`%o)18z)+$w zcr7#ZM=3*>XAE^oN1dTI%R3$lWG`Y~iDAax@E{CbBZg{!+S;6<*B2N{~zMvjG*C z4;oqHr#yDbawHp5+ipyqr7`u?d`z8XV=7Dduk6m!m1JnAa% z@~FGV+L-p)6Vg6Y4!rDcBMcJrjyB#Hj5K>^Mhu;~am2urzZBiM`@%?`Ezh9$2p>lka526p)=I^k|;CR@x-!g{6jBg!`p=?)a*;xe>#1Z=$*Ubfn5~|K?nW6Wl481vH zs7pHP43$B2Ts#arjiE4O=fN1tz8yT0Na(Vhp=%2EEZYKjE!(qSPZ_$#7%F?#C8Z2K zPWG%Ki4=o%r0iKtl;2}}_8Zp5w9hvVX3w&xs%3|ZtO$uP!w-l7n47IwU-=8xP5zP@ zsC;s`@L?Ic@#bj^+?8RxEi>Wz9Y}a>*(UQvvp~Nn3zXe6;OFMs-2*Mqm10GT%je#x zyBntVxcsq*@5YwHkKO_g*M!W$lC>#+*s9REqW>);wcpxYql0$;EsVe5t<5jU+GHOP z-{jYdm59It^j#MHpL@ws^v}h%ojp@l8(H z=Z!F*3aTgU^AOf=27exUHi)n-02>Dqw%!x=xoHU7B*NHM^)H^Ve-UBqx8j?eu+JJ{ zJ{43?*k>WEe{%C#=-Dj7{smymK*Ba@&%Ga8ZYqo|UkW)lr)O;WQmP7X%ElI#l&Zp` zW}hE7b8@rQ6lTzAr}yR|#H`^kwro=~Zl0!{w>yOR)4rWQt#F4;9Fh+mTZ+97fF8+57)Nl8he)G6|=$UmF%<9)~cfljJ z&~aoaz@CAfVwdl&cV?va>$kmX#XDJe@U8d>wc>o(jUQgJ^ry=Zs2>W;#Z}5a|5Mdu<#jQx#$4tC@DyW{Yk3m?!e)|~oJSM_6 z08B)KMuY>Nu#Zkd*wP5XKFSiQC+x!_j2*9hlQ-~3j4+=HsweCt5Z15XJ_0@LduxHm zDQE);TWV*}Xm3%K^ThJ$@~1+*mTl)xrTXo?nSOIg|9f8B4v~!R4Lce{8=4F2%=d=P$MuYU* zs>ta#I)ARX=y3T9LY}`MSoup)zd?!rp>wZObJC$9lTCW3GH&D9Q6r}qvy7MnB?VB5CU}u7B`{qXCzv!OJ*}C)m z=DDAerR|>MC@R}M$CLB<{(@enNI(Q$M<$=Uq(jgZzuUn~?X>b752meZr|WShTikeP zj~b7;Y*pbg&TcjybC@gf(B?EAv)Nh4V^%UpkC{05d9+zM;4#8xI3~MnJ?juq1I46O zvt1v&G&^>~K(_B=BwO~*f-~i%cl$m@JX=}zhCgh3gEL$Z;3lVII@7Oe&QiaF6E}Q3 zrZ|%(f=zs+i!#v;sRmP`Tx5NqA4%D|0)c+)+#FNT@CXmq0xafyOd~EnMR&wfl|5yd zo9Mm#~Eem!JV>lZW9sAJaM zS+-^UKVKpcXY4iG{Ylg@Ce?^`u@RW0fB^P<3L+pt$7fkUfUXoM1q87AU;zOmF;Hpo zf?J!lG-34RJj((CbZk+FCbNJ5UG&Q8$0S0Vkdj&y#>OESwHi zmh02(s&MNmd3rIwUw4VZp7r# z701Ji+~(Yup!j%OOh@K%q4wd9hZkrQvCckXr@?r*P-~gel(q|azS~4RSKD#5V|S)$ zS`;5*isA!tT>FHjJ?$+{=IM?5PxUxkyN0!0_|)?7q(oLm^=P^+a-+8Wg=)Dia%{vZ zM+n(#7E{dHB3&aw1Hen^N(&HTUDnRKw3FhdQ)8Vow4w>qiptFT3A($|DJU^18pH62wY#^BX--DMt=wlj zjH$WzjNA7cu*KHEaW_=XcoyV^pybw>S=N>m zKg$~cSk+wWHKr4DFXX|FlEp}qqwfMNn;y9;4oS~ShbPYXt)fEGAn0stdg7{y37I@D zx|%u4^EvxkV-p9~ZX9T$KFUzV`cB`TxHm74+H9j{FvbmfWElE7vhuEANv!^(f zSv})()rq8i?T6Gj8Q_c)!7k25QJ*f(MzJ>|rjk#x%z6S%3B;knD8$9Z5C&5oM(j0< zi@}CPx<_QidAm~Mh~>-PO4+Gn>9FMN)HdxhAZ{l;rG#M#6)&^Y4M^<}LgW@n80|@IQF<|CedYTEcN|Sq6zC+OinuzsD{67}}!}|3m!PvJI!|dwy`BUv)>Mbt8|>8*7ce=`P_ftx0 zzh8B?{3rya-Rk`)x5|&gvJc>Wth?t&lX zxw&=rs`$CNb;e0VWAUqe$9%4YV+;jk32DbH*tth`vTj}Pcudw`p?baw$t%nUd-=;w zzC?Z*iuX2C zy#FJwJy(h; zZvj{}up_LfIxF<9X?o~tyrVG(11^VHY&V|tP)-cwo1EM?OOK|hpm@UG3}O8%TW^M* zNfEXjV9h|nR(rzUl<}~i#cM?vhZ$by3A;{&dz2l-YOW83pLdw+2awA%2tZ-)~a?CWex4a&nhj|Msb%dcu}NSpS&19C}uYur~p$ z8c5g*PuLr$A#Ak>dt(?c^MoxEVb_Hu6$rb|2=l3+dcv+7*n-zV&!h-j2C!xzVXHl1 zZ>3evZAemquxpJlp9-oc>{MZ8i4f!30vz4TRIJ4 z8$=i(&ad}`y}n&-r-JGUyBflpeX9Cu=-DX3UJtNoAYmIkVOLE<*k%!S z6=O!=49X`02qUJQZ*m!%Fv5H)sGhJ12?B0}0!VBQhOB))!*D z-%22gi}B7Ks&7pLf7aU=Dp#sYO2?4nH1Q*({)l?qG-Cb!`cmIUQ&(Vo{I-M5{I;p- z*VFXdYi?LWcjsdciZg#fS>-P%i2Q|4nZM8x@)v|Je>u!4V*K08fbCbdJJb=la^x}J z5gt=V;4Tur$veX1+DDhBg6ccMK zg#A>6adQpdRa2@J&wGBSx4{1=SPw2!!>IN{>L#G7dEd4+CQ$VM|$@NpaA8ie*`e;3fMCZ4QtfN*nj)5r{G~xz#Un9lT+}0qrj(v>M8g> z6!fd5?^EzvQSdOpx`CZ+P2E}X4^2bZdJ*RQB|w)n(k5E-wm)^z|8== zlvB_2NFh1*)#G<%-rasnaIb!jZ^XCDp#fTq;4&WYa~*cmz{QB|VtImFPxB{u5Wx^S#)%qA}1gnZnD9zz7hN7Z4JN1-(?8kSO!fzxTf-N3S9c$z4gL&VXX0%$bHsw~$Z~M{e+ZwGn;Dm4THoebA zE1wFgC+t25>le2Bpl5>!`!>MFfrPEMHvQH#WNi{z-wNWLo~)fB>ze^d1+u^%Ux1`@WDUXf0^7Urv9 zm&OZwRz@!M6Rw3aY_tMq38`Ik)E2;wTdc~BDbGAKn{)ookqT2kOJU4V}8{Q2E%4(Y*up;k|V0 z{i^3T01gZkqkUe!zdnuPc}(*Cb!^@QyhsCafj&qNc# zz6P+gIcV^G%oBFcG=weFR;_!2c)KTTy9oPgKvIFQuNq-K6;w~yS0Su_@ckjQDog6=$o9ZyNxWL3aTgT?tx_84K-^-))xWRHi^I~ zStGKdCY#RuMppAag?!{Mv`PK~AMzJU_b)bR|83fr`gQQSrq_ne_^%5af&Pv+ZTm?U z`mpKe$A6ccW10OmdZJrN5)$x8Ngsl_B;hK+)9j)^ta~;)&#U}5t}Es7GPm>QCEWYT z?wZBi3(Did&ZlvUqUDRiLfo8Mq(Qe_h ztNG4NIzl;{TZMU?$z`ZKX6n{2-6Yt}Z7`*`J47_IjdIEzGuee~awE^s?I_tUAGZCG z_WU?;yL_5%{~xW$E+04oH{#LkK-jn$kDgP(np8YBIR5@ea7MD$h?_DtHQ^a4-U_GK z?_91bBE*uiIB~Z#v+1sRX)3mRrjVKv8x}p%;cYA*-gKu{apC{j?8bv$Yf$c(Sx=cNgeTHbfv0+As${3ww75Zr5tZPt*42c8u4!-M_6S+pYy)>E3=2 zos;VjV>jC%{V6mjCeO;5e0n^qkIARQM=E??fk2Dj9SLnP+IIVXV9AL+BT6WZz%our=hs#w8GWJ z0b)ULJ6p`-IUJPWM%?(!xT8&b z&t+q$4YM7;o%6X|bm1$m`q*pS@nXz&PSVV(9Ou#D&;S{i{9lG*wvV)#?jyzEYs&pS zpI`sN%+ETNAnh;CsdNkGr!)5cBySWmKGi6H5})h3*|~i8moay{WmmBLrRcMy_*_pP zo9fW#P3_sbfCt<1tWnJPq)`$zO2QA6&4s*+<8xr_Iq^a@kGnglQ;?l=Q#f82pYuv4 z5^4rO*9_;hud`-2C!Jvwo8fQO41X8@t>!7+rlYt#i^8Y-C^EAVUTS~odOiOdha$7N z^uPF5)A*-0Y1;QIqCLFK{!&7FAk;TGhYncal}`oLbLqf9$JqhM*(@@C39x10Y;=Uk z(rDow#?JURVf>~*(*A3r!JN3iDe&_C>yV#Q(j-wnfiH5Q?{zaW86-Fd(FO~zUeoPl zhr#(YmrCU?{xS;6X_r{~_wm(!hJQ7r$7_T5?^CMjNNa6^?-D`u zl&@ue-xC{)+^vP=-Qwrf0DGE)0)uvm%V;HX@^nim;|r#yabIp!pP%o-x-dS!XHoXT z)b#Lb=zR4QMdjHKnykonE(~7jZo3Lr>wj!;hFSe*@z63)W`pwuV*Q0Q>h#ef`z0#p z<7>=Y_`JM@|4V#cPYeGSZ;qUowyY%_=ax0k^%2}>_xruU`G1M^|0Vuk2Wi>I)UxN{ zwQV_XUeN~09OON=NtRyLQq2DLIYpL^UimZrqrt1{%beNzKjRllHLt@N923Xe8GJr$nGf&6| z20FDrfbL$?qWzWvtQF__8f3ENri%X4&r}Ii4ud|d;H>@1HG$Q z#({O>z>Be29D^@QxzO*x-2g^>S^RoGaJ^mw*R4T(Sz0Jm+-d`tO9asueJgeD}6`Sk#s2G)6l>Ky;k6szcewIJHc_cHi5w+8vONwo{+UYHJhNdJopFpZKA>Mt(9 zP+k})Az^rGu51@qhU3M~u3ZdIUI2P794}B?SW>*$^Wz1BRytq6`lh(C7*k{*Gqxj5 zL3jNrOXg_$S~)=@`r?#M+A!AdITDdZXE*5~9b9luljb2L47L zLV5fgfTUSVAZ2uPCzR0IE3*uj{uB0Ws#9~DdeGzx4n7T z9?khqtZLn}q3%qmv(&8>mt4isPWrYAKj`QWE63zeVm~)6UoS>XUI&MKrprdZpIbye zyG*P@-s}q%HUwyf;Jd>Xd+W`7TLGV?j{^Sof%tn%ID2|=D0QFDG3p*rwfC#qcxT6f z=KvZ@=eR0Sdr~OKMZg!R zDBo!*M7m8sm?o58J6&kFUG^kphWuW;E2=CB2~GJHAkb2_2n|w9oaI}svFBCY&f(MXbg6X7lgnMbzWsnincCCM>H>J4&z+LMHzxL!`aOXtt7w&3X(DvtO+yK#w zCRevFWUy=dc{_^c_LRct+HeK2RMx|_)3s-5dz=Wt}l79bgah3eLs9qLKW%kjli#Nq7?YvAbWV56a93wdw`1&r+FfJ@&^t3B&6)U`Nt@M+1dekI zX-NfaIW!f(w@1jF5)IqSG;BN8G&rX0NSQLPvNP2(yK;5Jd6s!=xG#36XnS+CJ-ujX z^m6s~9a{C&XubC6)bo-9DJc`aOTtzTLd+=^pO;ljZN0R5Nb3H`wYB5gayjc9xWf7E z#yO$Ri7G^LYF$pXLtCAqt;x|Q`_a~>XzOz{PIT+dZBvT2B}d!bkG3^M+n%FwXjz_{ z%z+(35|QH^pjBe-&O>7^`5i_p?@DRflhefcSw)(#-^+yTJsO1UPwPFeav%lAISMIj zMUF?xsdBBj)MnpsYI%;vsYShLD^j#oIT~jO^`fm#(bnW>ll^FGQ?&It8t3cu=C&b4 z+mxem+D$Lo<`ivfj<%(UX4A^h*U3`IkB7%(p#rpX`S}uXuJE-_NHk2 zb2JXo=*{gwiZ)TtWuYH!sk!4M3pv^{87bLtCrwZ*!y7&B5Ge3b}wY@EzV<)DC^ zR43hQ(i*MH(bo2(txwT5=4kA#@6Byfinb+3W2bwLCPCX)=Fiq+%^z3s?J0-mRraP- z@6V}b-)~N}`{F=~Hqppsq=+WQFSYd+Nl1>wj@aJ3CWYo(ApK%p{skLBd%swpqHWC4 z*p`{2sq{@L(v}>FO_Dj1D{^aI`nL3o9r+h*EA0JZXNtBfM`M#;j;4m)(`AJL>r}Q$D^_-4X94JnrY?d1_bFvs*C$l4+RucT~)1u-8(Umk)+=80f=>&dz^(SHRm2JYR>Z|U6@gGL4lfdD&MrJd{ffl@MQTHybcJ`+)SXy3mH%y zgx(4P#}@)rDuit`x;|;h3K>#2a(QW4uspBal=jyxd4DBDH19x^N4MUZqHWL72-wWg z)P3*EepxgzMx;j^OPn+Rhw}IF=l3_#_Bve@`JRrI@DSi-RzYQi-!Dl{oA+wWW*468@aB zB>m>c7K!rxs}+e7#g#>43nKE>4h$r#{gAQ;KVIU3U52-ZK}7UP@jtPn((!w5U)_Bw zG*&J+dF-k6!oQ#%*6UHdTEq7SdNt7%y#UP)AsG~qL`0xO?T=Zkctku%2F6Q7mR2b% z3f_-dF{%+@6Lf1=q}@=<{mN8c(yHZ(f6otIC*(ho4FwMP$=8x%(;Ty}ruwOwR$#B;$>u6Fw zc{C}XJerhG98JpCA5F?v9L>x(A7QyB)yRTU?)~JLL|AO>Vq&Osc zsJr@gOU7nYYUO_cYLH)p?){bL*-jnz_A4%cj0A>+=XfycJ|qwuJ5s!o{kWf$P+&v` zfL-m-BTR~vO83X41Ofy5(!j90J57zcKS;r-CCeU5cURxW=&Zz{9HVT08${Nj_ z&TWRT2^i3PP-kJdv<87~))4IKmEKlNXV|R|8b(V!jWH-4M%nhJT`76lCFgWuT>bO_ zF(?W_$iE;Y&P)F}_BbJ^mj4B=Ia48U?H3r!t#O?Y_;!{MgeC+I&oS59!wCU=Dzt}$ zz_*9K^z9)b@a>Vck`n?;bwXgE69NOB5E$r$z(6Mi6%ztB*3g>AxEWUzb=s3@#v>z5 zrLpq(kx?4>jvpDNvEcZTQ5vbA3>mvx71w>se^Du~MVkjT_>^_>NM%_nJu-g533fA(^>fHy^)P|`^%b{ty*}ztgJCq{~cYm6GHj?K% znj?(mG@M{c*)nVA`OYw{ZK)^PP|-lu1OyeUG8M}c_owQmDjFEtCpywG;OIM3b%uL0 z=JFjE{D9d_fT53 zQm+R(MOB6TB>f_^&AHjD*7j+8dtE? zEK5qFS`BVsWbJ8S)i`Hh&9XPJZlkJ!4Wr7yW{%LZ@-219%CDko0}Gl?1&(1V6*vQ3 z`3Aa58|VbiKqqhpI)O9L37mmW;0$aTRR#{($YkKKH%};d#H5UQ5>)xCnnGas<~%QMYELbu8CbI}V4y3>Kqoc^x{?fRxgxrR2ocHM9ZnQ!of=u} z6$*wwQk~Y89Sda2nmx<2;%ttJ)yfPKrwF{6qNem_inwK9HOF-YRAA~Lrmn6^U_5Bo#tOAx=%}b~`dC+H02o1qCqk&DUVNfuy zogr^#my>Y=qwX&#$v|&YD^J(CE!8pGK&R0RY#LLJqy1f)(mk2M`N$Y<+l!OGCqu?J z>VtYvD4XAAg#Wl?Y&Z&QwV+mI@>g*)KG%iVzoot}p)t@6bOuHyDF(VOWT5Lp2D(-? z&~+gLT^BOYbs+;?7c$UwAp>0(GSGD)16>y~&~+gLT^Fj_#G_aPo29xM_zA1i=hVy9 zz*1ce40JUx(AB`ehF9%;*0iD6Qd`CY1D%xg z&Ll9%Y&?zMYTUJf6#a@T3-3%NyrW)wXf(iq*jt;_XJM`({zeI!j zRW`wHRx~n!^Z!RTOE!TIY-}-Bmxct5X_o4YTmzlGX`quy16|iM(Aj$ix~^xSGjgBQ zKyhG7)fi^#(M_b7Ce&+|7dMe&qgiU#8tD49fli1Gbat(QZs;@6*|i2b zAvVy}(?BXO{5HT6REnX&a_^d^syw8 zDt`mrM2c-bbQLBOR)0-fO#ixBlY!1kG0^Fss_CgzE31c_K6BQBfhAR#Dd==6KuPIT z05F{j0H#v`z;r4Am`()%)2RSpIu!s+rvmcqIqm5h(v+jCh*zYlo`I2R3^`N-WcKd+@WZJ1E~?X_j2r-4Iep&K}C#%R&G z?MSlrbR-$*2C>3CL;2b-jwA!!;Ao&D$-ssg69)QuhV`^`MsuWw;i5A<^Q{IZz1FHvN&AC6!idpUf=j zy~Cx0IHLR@ju2Cz_+K1q5pB5AXf|=MM%8*Pbw5fd%{S>(tAQcg)_tlol^b$!JSV8w zNeIf1ZT9j&z>RhI-RC8^* zQVq;YwO3cFfv!{oo!Mfbo4pz6W^V>I%{(@+WkZsI&OktlyAyTMf$2okJ0rzBjj8Em z0-=WUUP(6=lA4-MfT`&On3_(2sp$lmnofYuNU_ZPgjNL)cOSMQAZJMAIL~P6WCI~} zvH>~|f%k1XCmX4$lMOI+vH_+}Ho(-$2ADe808=L$VCrN8Or30isgn&bb+SR^h=@#` zrCOg3oKQL&3tUH}r8*~ofsRN69gzk)A`Nsz8t8~L(77-TbVM3BY^`bFi1)lg*_bHH zR$QUPIqaU6`!8+I=>7z*3W2u^jBe>(&s-UV2Bron`M2|$JCCgqkS?l|gB#Kd5V@VK43dLfHTL%XP z-R-sv+8t>hc)h~H0y4nAOFl6tO0k8~f{DAq1)3Ftp01^nd3sdQ5n5I+g*!c&k>bb< zSf!Yb<3~m*;^xVaF;&2pZZ?R+p2)zGFO%50G^Amw$iTzOytJ5lq%$wATOaAnORLdG zI`dKsAL-0X!3{?|^P=bbNM~MB14laZk~MmyGcRe5Bc1u=(d7BQ!_0hXwNf;^i!iFi zIp&6jszdcwV<;NZzX}%*2d7mki+=XSFa72lU%v0d4^^&VG-)nT+V^(9<4yP8@y$E; zSFTw~;;<$D^oQ?w*B5?r?}ptZEfz2^tF{o?MOn_T(LC4VYn9}ZM7GsocsKUKB@9BKvlvsGqh#kMcQNPnzwgnH-+t34zF+yT*imMU1lI7X`~L5K z>cL<9Xy^L91&#tQT?Dcwa3ahEAVa?UzK_6^jH7!${cpE@{El~jT(YK*G?n3!QWvVC zk8%P1uXs=^-Ku`nG^hNdliok$1Elw(b(fj&Yr>aQ2!G%1PyOiHw|wVAKQjvIebO!p z&=iU@bTJwIA2|`--$S+-)6hp+({fz>!MCott1dyUF6l(-7Z1@du-Yox3{XV;#XWDm z4Mf9vB*noj5RS|0{vW>eZl9GhG9)$AkdS2kip<^Lee-Si-hSgJ_E%o46Y#MY?Eu5awUR|+l@#P5Ic@teMI$0}2LD4h_clk?Vp@Dchw zA2gRpad<^2r?CqJQO=Kk@x!-$eED}i@iPPoG2$2cDv=^7gVikJ_lOzv?GJ0~$H14{ zr~IY#&6-0)D}8hGp?z~(5$z+zmyZ@-N?Dg)J~$=X2d4aGa>`%UPx;IHr~IWvPU+>= z;y3RrzLe1Zx%kU_i!UE4zPzXSQp(_#;x8MDFD10KQ~vVq;xFrpFL&6>WDr!n($^N7 z%2-IiHPWUzCEIn-5=ah6AcGO5yDNdUyW~HMe7MNgGXH4*~ zVt=IBZ2Fo0W;56P)L3qrN?$77nc2&>S`@u5_@_o1=$)V5tMZq!N~Y&*uJ@{N10L6e z@pfgxI4~Yw$}zD#E=ihtyo3X{d0dbTnTmzTVJMCH)9j3>I@)ndO{wc6nmgMs+;pvH zH6B^g{fz~shni21IOHYTX~lT!BE>^`w%3>+c1Hm}1^qaGNqe=j>kNw{Im#A?qT%A) z0t&PZ6;310@xS0t7Y3gLLfb2Sp6&v%fnVJ1zL4(Vh|t0pMKZqOkVZjT5iDunl9CD? z&4IZ-=swWsysMNeUu4&im1_FKoN|sKZLca)ULRP-Puv%>C=T?NQPXkK1wQ`L;SRB( zZcQ;~CuKSRuKM`q3C?jAH`t=~wjydYkcj2(166b>oXHu^uw%=z+Ij8TJAop^Na9rK zu6G0l?T@PrFf-3eJUqNNq`&>4^@qYa)RN0MiQu5^HmE8i{xmyWtD`ej8N>>5sb3O& zl|1>WM=lL{O1Zq_6UKxQ$ef!BUA4^5f}EW_1NOD%{ejHWUsRnv)QH2egs!6@5oS53||r(I3gjwjWWg*D~LANDGXg|(RY z??P+omjvUPCkF-hu+WgFWZ`#QiIF!RSZPo7TZ=W4BWryV|UE zwHeN8)0(cpuiW8f+eLwW(#b!P=RsHaGd&q?V_@)@ZuH z#$Ekz%v8YlduxL&H&tzJ_O(gPJ@2oZt~Oc}KZgBgZ*Ogg>YA!H@A0*$qfNU^{Hy}USAtfOsAzY#i*8yl1OS2B5sSmLt7Ww@N2_$wkd zl17BvBxW(HiSdqizSHXdze_qp-IHE`4>^d3uIRpfVxsygJjyYl9-K;ZRydP+R-_ZI zq9xssX%{E!gx%U%;Uuyp-w*~kt?{`0dn8N-92%tavG{Xg`(dS8-01XGDF4cZV((*v zP0||9{zV=N>>0YZAtvCh?*Fx9Y@}h!g!aeQiH!8lkcc0OVabqc!yDSDNu{(b;R0eJ z{jUGN;N;HG^G5NwySj24>CCt(JQqUUinCus;|Ys}md##H&`>aMmoJPX?Im%&{o67( zI1_><%D6Gp6~mD~(^BYSa}Q3TtI3oUKGaKLJb=Pj6wb*ge9ECw=we0>PT>F^b{vO2 z7hOndp$d^_vRk9=tHS&NRhX|ToS9YOL>eUyPYW+v5N$_N1uo{cIteXHU)EG6tKF?Z z>PFrAgTe_{L)`u~Wey6rJd`Y0vYLtrf<+G{hq1IJB#>d3i;d`SmG)P!vhH?TG!b-$ z7ey~dat9XVI#xV<>sZ7DT@I_$qh&#&2{P-JA|M|5^JuNOau9B)DyQjNp7KfCx5En# z>02t1f6F54;e4w;+2xWv0$#0GJ3j1!52&>E6Rx9;hr8vOM*wyOXtVHnqoaD+6X@zx zJluiYMFSZ`Dv~G$NI|R-BUpnKfd^yAR4jk;IpX{e6rwjvr-89rIt$9#3vs>zgY&w} z|G!!E(Fj{|Iyd7q-Xp097wSLR0h2h#tm5NTEE!@o#ihSDO?OosC%aD6{*e=@^fS1X zedbJ~%;nO4RMti^G~9g$uH`Gmcn5h#qSYpCNgNv9OTqlC_AnE_-#*~st-$XE)Cl$8GS`?PWRisjd_-DfZL64n$#s93?|36L2xXqqVf4ouZc|6Y?tagPK#EULSa2vpRE!jMI68MX;ROL zl5XLHC!t$Z!f6rTiVcUUTi{Xt?P2N`VtsN`hSLuF25uZ4SWVfOQo^4#03T^#aWKPY z2SJ|y_yGKI;m^(RwNI}*%Nr7#5y`&BG{HMB`v6C8x$BYLZiH?U%RX?zp9}FleL{R# z;;Ws0CdvKc%d*8F@x?A0B);0Rmeum?0hK&k1)ZB!QsxzRGe{Kqv_P}}T`3>71els)yvGt?Wt#8_OQ7~?!Y!7d^>Rkkd?Zm#r9Lgo&beIS#Xn9LC?vq?xyA{_)3r)9PGDQ7i` z-2oIX6xdFcp+=mHw_2;#D!w(8je zcrP^(@1+*vz0^Rwm)eK-QuFX$Y8~E7jl+AXZFny=4ezCv;XP{@dbMwu_e4Q0ZJ2!B zC1wF31}}^i zys@lBx@ox9_9s-go^ zQ9d@=!hv~_se9~8y#H1ht*A%pr_?@ffh7}ONOIbglB*7+j4ZD-o*_xla#6(s86fvZJD(WaoP9A=%BFdr3~ckC0rPSf?c0nLtj{B~dp>nh0*I zM0&}W=SlDJ>8eI|SS=-Ysd)@owstq;7_wrcV9GrHYN< z(#_fe6R$9n^fg+j#>gjbnoP-wuC^;pBt7~pp=T3GSzo;ph>JGMp)|GhIh3cCgL0Tr z$ib#zsn^=)aJFd3reT9}(58#Dlc`;yNZ*zmloxe|#f5@T{U5!Pmip{&A9Q{kRtvX(1-ecv&)WG znv&4fe|i;SU2@>DZ(8Q$A%p@{TZmF_&NOJRQCA3a5S3I+_H?TtNS8M-o*R z2lQrs09fDtn&$Ya|Hx=%+S+LEC??wum&`L(B-3fIBERJmyN{|?`pI-YF-K-UiFWsb zq+tL_a-(?K&qopVl4nsc1KeBcE9Rt0*P6VXTElxMO+LXN&}B^VI{^)x2c7oCd1N9g zkC%<0m$)4#X@A2J;O8WVBDx4v%kj6--gOOpv)MQ<9T6`lWA4%M)Grnc=~Lga@is+gt*Ip>R@TlR3oViXE~m{k1n zOyH?bJ&ffHZPW~9wrDS&c}ftS5m=5E#Jth4ss^wQ6Spp>NU5iN*>w5+)v%r$E%RU5 ziQGKe#&1mxtl!&GD7sqyh&oH5h%M2~OKjW-7up(Vr%4nu<8r6gozG}%pEY7brZd?v zoO@Qyw-UtRqDuQg^&#;j>W-cj#yk+ls`s`=8i5tUt3Rp3kXY_Tk-H>?x;AMhNT_vR zQ)hX6 zY)*xfga;fZTuiEaKOM3b%1mHbbF5;Yshnxrhh^n>feD!2NgU}gxxi>kCli+M=vD?A zqWd)aG83}&8@Qi&^lcj^?GYm7luO?V9gCD;q);0?c~mo8h;*bCYiQlSu62J@d|c1g z{R5v@u@;Pw3?h(4jiBnOu0Gb%P8Fww%@Sd0OB)?&9hmO)RfCJ;a`{Gvf~*na@Zaa{ zK{h%gtOITjn&PJ;cze*~@BQAZ&0$baJ)*xKU6(;gT^Hds_h(t!%kt~G?&IaITO13T}MHCUj z?^fR#An2`5%b;z@uZq^BQ)f%?WFj+Tj*Rg@dFfI;40;ZKQJFD*vTl&nQBRq@9cgs- z_BPi!v9M-%10!TA6}pETH8W8@O;ro)K+EGQ${IXHAB^Dwd@m zrl?u>Omm5hTYEY?RmP$Dx0>uK;d@zi^bod0X>V~#E*m$Yui2eT3(@ecV!8AsxeuIX$}2icW%-eq4~E5mnQd&;7g8>CZwd2NC@wV$oGfNCcj%^)45dFg z1VgEOCqqPc6hQ;bCggD@Ez@f@%^YMl4OXIbU~(wf9qwB+L&1EK22JlQ#P0a?)9{Uk zsc&?B9<`aWZZR&3ss|jkEq(rR9X)MiW<$GNL-XS^M#}N^)(|{ANz`Y8$g&t36v1}quGw+@aC!yRH8dUNy&C5FG1s&^hP_Jx`E0c7X98zdz7;ye! zh-)P5auC3xC(?hFs~5?D;NO8KD*wmH#@dFweZz~MyX;m}I^{KF(omS@$Qd25K{S-y zrB^s#>t$@v!$KQTov!rmQpRUDj0YhePczx%_#Dj)wW4FRrBE6YQY!}<^fPuI{(+nDUiEUIqbxO%z}YF$x@|c)2#@o9CpA;J*o92ap;&7EHDR{^>I60 zz``MAC0?e$LhbFBn^}zM2A3zTXrXkuet(rWgpFsgWkt}=EX>O=VP#|D)#H$rh|Y>Z ztb9(x`%0(9|BOdiF3q?#uR;LY9nclGI3wmc!8(Br&WTt+aGL|03!+KD@$d>;&IY&y zhm>;B){OcHnx=7GYvc~I)t4{>2(NyTOl6l7rEcsIfSu*mZ`2#@QMY| zga6#zF? zuwg{f0`{F&=M%!r9^vi~NbGQQr4tLS10j5v{{&=EwHSrkU|mIJD@jufr!1=ZyCeN?t0I#3smExyS~VnN%5_Y)bi}kzvY$sLjLs=dq*YpVx{2kJ@trJg2eFHRAzO zZGmHb7H+t!FrO$)VyW;1%8H;usqC4tQ`yYC8HQvZlY1=aw6L-uoJG#WATDWtThsh- zp&YXnH2~Z)<@dzhp=pQ) zLfS`$Y%H0FD5FizSf*RqY$BZ(n=0@VLdHQ)XTbv@gQR^|sy@d3#j^NDC~x+q*}+3p z6JkfQpYbly#Z3`OT=E&)b+wX%J1oxF`lcFWf_uOgL`IT2T)@5ONdHQWs1zn0 zwJeU^710)Z($$bbMVL_%cF#z6^GrVSxe1mZzb_do$-B%c`bH)8vlSXyc9VJFD4XKM z`N#SPxu9*D#o0`*y=LPcv-~8Oj3JDBI$H;c6ELS^>Y%1Jh$1l{>e{Mz2WJGx5uN|Nmrn)3i9EGuV*X1M)3%prZp005aBq(yOWERNpO1F5A;E# zQ$py21m(qf@P+ld0h~d+PYdG(*7q?pthZ-tEuTA*o&7t7&JLaQZMs9&%d7Nq6Cv?; z>YhHJpNHMz`i9(Jef?ZM9MqB>dwtmb9rYkl;(@wRUpK=-gFmDmLVtd|5jOIL&CR_Y zhojr2(V;zE8qTv0Bif6dn#wWa=mHSmkAHK}eypqu{W#CJ*pt(I&4D8Y@R@8k-iiVJ zH%0I1vWzUTQ`Ax(wB%g(&~Ss|q?g#vIqW!vE81w_DoGb=f5XY0Yp?k@AgvcH84Dv$ zJ`^9$Oq!|W&FMtu?Ti?L<;bQ`5XL)rnl1!J)iHF7fb)1GQQ__+GJjO!nPQkE*7TEC|=Y&Vh5#~b~}YyDG`A(WY~sdjb16cz7{aO^e=9H$dSM?E*c z9acpj7*=ixjxbI^yyiaPI944Pa};P0xUnWT58XV4))CiRZW(|YXmD2>gpNxzbd36i z{W!q=WWjvirjzTb7PSMXz)baMK|=AiF33zLQ6}4p5+J_IZap_{58ql?ve}-8VuJG| z3?`ekQm@Lq3g*8zmbAhVA%Yl75z>N?2|9*mlR85hC=qsvV^!N&6Bg|zZ^BBC`X)e7 z$HzF2F$&zcGj206H*a$?GuNFONp|yX(021SNiK@GG;K3XD^r-c)ErDtGokSfbx&ci z+tt@lUgMyD7D<0#*<$2McOzNjg3Pjwk$|u}b4hzwVX)JvY)vw2L4^8HJNG3#F!b^^<3LB1+0>sjx+U;~ zCROrdAFAtiSudWaGc#M3xPY`ft7&#+Jl)VNj(yvY?bvsbt7XZY9S~B6MnNgML7v|0 zR=((C4NmCPWGa}(mQ@jjG0?>@!o#*0Su|#g`Flh5E7?dmF<>MdKoyiknH9^BD1}DL zpwRx1R+&I2w7UU1!M)*~e7y#RXAHY{d`ucwI;};)YZS8-5?Z72%a3M025!5BZ`z0^ zbfj}JrL;_NHPF~Jo?WHoq1L#nk*ze=DsHoPti70}%&^NzkS0h{nzpVcoqq8GH=m~s zmeK>{$1+s@a9}2`m(MxNIb2BNpgL9z8sk>7(>WW#^D%Cfjd5GZRtkEvvZqBh={+6$ zNYBSU4bE!9)%h5=)qiYb9mpE%Y)h|==6;M*@qUbB45YONj&Tl)CeQ|4uySpsF%Ft= zkS$yg&2rEM3!>OToNqt}##1dOj)!M!#0iR{pWn!=Mv`@6wuD6uY6QR1Wr7mA#6j_b z=oJnc$HoX@D~yGFd;xA;q*+}%p+ES^<~rU3#+a?Kx9n&r3OPEO3Z52`wN3X=izrTB z2ht!x9tS9t#{pj2tYdpNWbP_x;@{cF@IXNmJ8}Hq)yI&zD9b*ETzU>R)?q69vF^Rq z|L`#!ijQIaRGQiJT4~InC+6DO`xvg00xow^RJQXn$N@9%I~~C6bYdduq!Y?6W%Aoq3^d*ZndHsmi!Y2LpXRpPf!HoN>?Yp$ke04i*$wp^Cnyz^3!RfhG`_i zQWXc&j9W_*e5^Y2XoM{^^;e?iYTehpnv-CYQAQFM*a?RDzs3ljCH_(Iv8}F-P=H4@ zvZ*S#_e>tFK{mV#O0ls7!4Us6OMkMkiaDe+z{YYK;~7Z6{v5y6=QL0!;3fuBwV(bKo@b^o`?jL!?3 zxj(O_^72M=Auk5ND!H|9io=utoN?R2>|zp)+43j>6`3uf884GaJh!Q&8zzS=0=ioC z5Z4vMl6FcWDQm7zVzn?&b^VZiE^yBRWaOP>MM#6SBBBWvm-E3KC2>m=wWbBd|MikF zO~fpQ-?|}-N-hhe+InI$*0+?PCC5_8J(hsw;R5SXWp-#uO?_<+%|sitd>ho{xnO^9 zTYpjuxq$^vM-x*p$1Lcop|S!*1s`TH>k$RJqeQigm(rlRb?+wby>cPsHMx{Z7RgqWYWPX#m0`*kh!{S$ONt< zAWpP)Xib8bS%>HGaEIrSi^k~=4eiDkei`Yc%O+8Gz74?XCXOcO=Eb<&oh%Y(T9CzV zOFxPgK0BJ;)Hc-Gw8p-Z@2K}HM9*~%3x5lz+cKatQIwx2!V81X5)sk>3SVqM)$@;Z zv%9B>zLafUC3K^YbQ?<4e2|h^!4*kWj{9IutSxRN4<~ z0Cd%+3-G&?a8^qcKyNKe(baN1=h$?<(5s6ysJgWCK*MJy0&z4vQHU$gV;tdvwSB1O z!Pp|8S5i?88KRvyC_a@p^e{4E+>s8f1o8_rQIY!7cdl#G0#bfndTb9!QF2l)ArlpA zFZh@WjXUJdZ#wcqV^mo#@EJD)?D8D&L6PY$n*eu`6t`*b)Hh=s$f6pf;k84#JwJh$ zB`WpsO3G`X(nG3;_|#9?E^ zqO5+tYwh3TJT6{*NQp!c`0cZQ`?nu!ueJ8tYp=C-8MCov>zUz|ElDkjbRA$R49wlB zVUsoo?r_}5W)$R2Mbfa4HIV?p$PlrSI`RTF!^ix4MQr2*K6L}ZF^?r3z;nf>VY8LZ3P@us`y?$Oujp2un& z>7F}9=kUv&q6_f4n3_X>y#LtKP4s`S`oFK}KSnL7ZU=jYhbAC~ynoNOMp4+H)MZ?g z>hiDamBIZsRdC1am4S-TIuX9{dWAs8RJVCwuZ)>f3uVPQQb}L%kKE!9)!34}o(Xhd zZ#1I+cWgxRfs~d>s*bXrx2XpP9lR<4)6+0o9#0Li*`qq8k4sqtzmZ`}s&IwZ>nH1C zD`!4PU9c`-En}TA|HXj$RR>18@o%J4>(ynllA3W3f>>d9#3&E%Pi1vPr;b!hO&hQF z0}`LqGW>o5DY~j9m-yhFCL0$gW`Q5p z+XxJW69$rV;xC--iOqhP9vJ2*D8m(oQpBXtLZibxF(v5%d~_VeTh#FWu#Xqy{k=>5h4Krx7h`)O2;ZS!R*sc;aC-- ztl4}DqFq8{sj9bCrTgC{UEvs%^0rZDMM>D>1Ug;J{0 zQtqM_%4#0#poUu*6I2d~aq6sxYC-@6)Es`UiAbjf8+l0lOoe!$!8Mx-n#zrZZJM2^ z+J9$yxbd3=GXmu_Fq4~SGinBNi=dYLCm@U8MxO3ys&X$jywHGXomng(Nh1X$@i&Vz zuCjxt!^dNXqXESDyu(F%Spk+03%Ep8=P;lNyA(wPP`zC7YDR>eU`r$MzYT#tD5Edv z&+048u_UWYA})y=C>vp$^egl*uGDm2s+eJXEVs$Jx{N*Nuk^p;j^R`!5EP#)z+XP!%>k2`Xd)Z3g8=5ayt{* zcaPy@j*-%gN@rMLq+tLpzZD1c*+eseoGK6#VwhibC^ZL?GnCh~PSkqy+_&4!-{keA zOC@v*oYi2!NaK5*tW$~vK9UYHL4R}B2~vT>Hwbw-6$>IzNm#h}@p^2RXs0tX(Tx3< zuxn`#qT*h1E)Ylar;Knfxx)5%fr~G3(>Kxjc=v~wfA)8D>vt=gHyiRAfx6+IcLbY& zGiDPoYd6~$;IPGXgzs`tLcivw z4dNHZA*;*VHW);e1Kbq`5h_*VclEH2)2wI!K7>A&K37CDI1(ExKn1%l5plyd#_4=? zNFrS^;ody5jn$FQO#co+JG;}&2picJGo@%`X#Zae>=jNH*ek;;=AO+=c6R}qe)E3( z9$ML58402YWhkTX?{$W0HeVp-{k?m9aJ_}Rb9%leJ~5jIaw^cseT779wfKMhzqr*Xvt~@4X1Ap@xbHmtR}0U?o~i$8pb6;E9rxx^$IyE= zj}E1hCVpN1vd`kTh3;^S(zNn~*pt?ad6^jrXxLIA{UxAbTjAGKIne=8E^MJ_HJ~BI z@&c|Kp7GmfP}Ong3h}u9w@e ztI}(UXp635?KIidr`6TtMOSeRz3HyHX6~lC>N>iSuI6B`1}-cM_>}m?HyQ}bKsk_W z(*|-v19`p}2%=nxJDa=TlqAFz(_KH(2J<7Kf2-d-mfbt#Oua3+moH%W+8g-|*DAp| z4VcuH1jtVUVjj&FB>M{+b%w(?+Co-WKq%@SIa-aVvFJaDW;i1ks?Foe{z3KsP|<%> zj79%Rc7OMaHUPH03ffmn)|h)vPAW%$rT<#nIA|e?uTT5nNqz8C(YT>aF1t(5Hi6;K z=!55q4{$Y&d-nh*!BU`Fz<~^@%0aB7cnt=>4~Lb*ksSrnQ^{UbTKf5DKH2zOf_xlsyK~s(2iGY z(k9+xBSRc5-^#dS!IphF3Dwk9|0z35c4n2L8P0&>PBQ|6IjG(1%KzSiAP3%Qz^r(Q zvtF+V@yho#9W&jG5ozh4iNJX&4vEI!*PE zfGLh9F47h>izd(HfItx=GXun+1#~zDj%*4vGC?EPx&~4pR*IEJZ#e-yTZMzp5;5Q9 z7sfV%o8lFlVIjS?P_QyM1r$kB{vZEuN>(UR8$%SsxY2Q;9-ipuPK_&950`?{5Dk`% zjZ>J3ZwC4%KZWscp!5osD(_y$MR}99*}#q98!C;7O1r8w*Mhf2DPbA39jI+Ie0u>~ z&}@axup~PUg5v_Qt{66fnpYjI{&kSeC8QP1i?f-D4(wH(`*M757t$AkFkcqCxol2+ z=`LPZ_i{l@ZZeCTh6-ZPH1>6e%5EVmNL6$^Cma+h;cl7LQpb)2`{k98 z#Jv7w41#8}VlA_7DVL8&{{4;NIhLrFs@8C{jSa&Q#LmUqUblR!n}0@G-QKPD^tv+w zs%#c|u-OP(SGS)S+r6^ohE0dr0C^@AI>IPNfjI0Qa&uE+ zcqh=$P60jBH34!)e=hEuh(Qv-w?_fbs^{C2qN|$C+!nH}b;qXc6a$lOazmFQ?ZG0+ zk|#+rZKh6>$`WxIRc)hklJm^b_-F!_~#^|7w~=6!crA{H>`}CN2Hw%veN}fOD*vZfU&{9&=1E1&=4d zUaeDyln+M_81o;S^s!<$G3gVt#)y&N1CJM?RqL$IxQ;-IV;IMl3G%CaJ2K<{G>vES zBMW9c9yzgaJ1DpKkyjbIi}V;Li#mzJ;LGfy>Uy2Cr3882l>zV&gwUyR1~rp^)%2;V z3C5D(7KE}vYmh{cj0cl35?K?O6XYAQWl|hj@S`>SL$(28Evhv)3L=t_q(^0d0PnC8<)WgR;p(#xfzC4nbUBVw11{n#!DSP6uhm4 zMpM~T07ltq+YRx+0$_C^KMu5%2h!OGxc2qY? zo)U{=$`r>AaSzKCWl=!CuVXdF}3O)hlzcp%B{1G0<<6t~NA|4=RltjRpa8^}F zV}A4tOOtvWu z4{;M`s+xld$>|nY^OU*OvISZd%=+RjAO$nK&`)K36WdIucE~ppLs~YLxgC=QY%pD19e*;b)oz-T$XCvFN;x|%!33S zV^roxtY6E37H*Vb+tS@w@vs};i3Pv(+zcgXoIM&M~4J*E|CKZ^`74H;mEZoKfbn2u5W! zv4{53wZ55`NxbVdfK{9tEZtbDVSX>Sg#rq$`BMD1zr?FZjpGBNzA)pqK5h;1JVl_^Vg3=QT78N58PlYLALzr6 zer(p!lUP1`0+aCZw>xUc6M=RVUM{WS(%~Qe!7u+#6}?URW=$7%cHefyWCrQ9|4@5awWn9kfr^YVvH2D?yo}!sO1zh-|=sYC;9t zQM9jQ3ES^021mnQS!DuRkOVE4!YZ`#unIYR|CN*t{TU0yg3^~*3c4sgBLk%*M492& zH{ijq43sgZW=p>jIeEx@jOw)8Bw3sSqBY1Ud@m1xpzseoc2aVuz{Z7x?`h$*QD4vR z-uZ&f<`!^{A?nUY9-UrVjM88m6V_zovDL%aDC1)zW9rxpV&WEXFNJ>?9T?1eVnmo$ zV)AnwL}JVv3~#Rwl&}IjJw;f7fu~@58Q-nt{_gWR)-S)SS?T`Pi-z@Kszx*a*?&AI zpJ#>ax+`Ew%2Rpq7ixLn@a5S8W1tRFBfkX zi#K>i^X8P^SRL@P%likO30*dFH1U3roT*7X86@j6AY)0poWkT7%3G-;*F~tAbB(C-KFP+q<6QT=MxSFd9EechVXnN;c$lMxukcWp06jp`}I7V^tS4`n)Dvf z^K{bNrst`o_c1+RNqU){D+=Yz^W~)1)APlo*Vpr8(t8lnBlQ{!;yJCfjN;W~PkJ__ z-y^*WzS~!z_Zds?NKbNE)Jn)-A^%iJpC*0M%OtN*W;x`aA^%!z^{1=kUyVhNXVczx zYd!5f^Vk2Hq{*6$Sp?{TtX=~JZ9`dfOH^qSgB z-cEae&PGz}eO%9zwcbF_mutNpUiNj$o(kW+L3%Z$&yzkA(w9gthx9t>lOcVDG)FYF z^IEO<3Dx^%t@lYiFV=dCdcIZbJ*;*H$u-#B32*W$pFNW--BjS4N3uu6#U9N$w0^>; z&ak1%9;LTD11C6Zi(+5J>6PAl(NnEtDSHqpC3}#?wI`MG`?8$`j_mK$I!;q|OG=*w zxl_gur5YIb89IKA*k?+wlD-!dk}%efhPNLCO7H* zfIzzrQmf$`#1N{(3`RidEdP=Q2`De}90jGX5saLb%b4I0_z3R))eVA(ko2o3kG=Y__P!TXNW#*dx4=GjbJg?t1^Q zxmzvWmEAoZHdiXJS+Bt6QUx{_mBu8}rsfVzA~i*KWSd>CdBc8jB8#D85G2+;S8Cx4tpx5G)Tgyy?%Fv>e}{#%Ta z9GIF>C8I1kH@w8ofs^4NXUE##Iy(-c^H!{q9=RVGBFQj_J+1L#n0iW2PQYIY&lR4- zm6=dWkfaS_Vy8BDKu>Je=99G8@0z|QEw+3iq-EK@%mJ+SD6(unX=%zM6DlOYfbkq0EiIS6kgACSkA}Gi3%SX!q0 zpn52Cmi*O_e~$b!q5K=hrFDvf6b!viGhpW)F{aoeo;pcV(Yc z*B7%-j&*&NW=;mY=JJGe+0)6Zq_2jqyhfR8-bG{-W+3FBB>#%%CohwKG338O{v{4N z3^sTIULheYc!p1tQ9@uJGTaBsKXL<<-#UTvlrJ=>N&FkFuqA3n?; zUa*j|Mj2O@k554bjd77fg;U4(Ksmk#!ubAfF+O%bM#CQquG$; zmGPadjBlkfzEdH+PQ=5tkiJ6tLP%dFeKDl3k-lVU_89c)TEK#{aMG)!ZGMc02JY$L zy})N@0;^afy&BTzNE=gP3eJ+&U^E@eP0FC~5FMVx1m}#Ucfd+06Y{U2`8*Z!*U4WF z`B%t48Pbd`@|kXt)hk7ogC)z7ZgPkn@vTJwqopqh|y?m zUg6`XJb8&EKJ=>RBK17&xfjTN-gDRZjKBcu>N#@RNm=2u6lRB&X1kamJ+WMRIlMaq zLb1!Ua;v1zg!C!Wt0BEYnq6EfH3LRfX@<NKI1Z3KgeOSx#T6%nD!Cg*iX4CpoR>gW#v0z;cahU~87XKpM7Y>5HV#h4dxTYay)- zH)L;R&X9)eEq#_WWN+znq}N0G6lv(r@>fVhYnIlgA6m2YGHK|GG@DtLcj(vhud=*D zr+SJ=v4IVs{wcfrjZ3(2K3w*|?wi>KGI-Z^SGwyjY7}k#Qam9sR~+(G(o7 z-~k$ zWA%Qm())8E?OUY4k1pWg8Tip7KeTBkd!*z*?1GW~Xug$MQcG~xlUjinJgEgZ^W<5Q z15chKxzm$tB=>sq0?8$*62OD^YW-~^2(!$6HF|Nf?uhboUCIDN!MbbmUaW&Srg1q9 zT3pg#P!+twAZ<@WTHNxowJ6lX7`7gu!s9Fa$F0@Wc!(bM5BrY z7buv(67LwTNe&Naf2ebG>~_d*{sb3Wisisc1m0OnYaERoYl4!^+-y ztMm$6gI-~qWRzC6ZTkN3rqLSSY0Z~JX-76#L2LP+Aj;YAKD54CqBUmV?d*;ct?!T} zgLCLBk)74T#kerIx6KjrV1IrnXIA0fAzc4_H`(-;FUS3$y$U6Wnw&xMy=_$R0Mqo1 zuYBVZKTL=yAuh(*%9Q~=Uu$jx(X}YLt)M#$yXUg3_W*LTj&Y6HS)9)ciypwrS*(iu zSA@Z+_!RgEO_QCyAgJQABd*=ueef02Eo*5OG0;@Bjz@lE{7!#kj8R}X`9gCeR zg8DHJW5GP-#|gk^6QVoccX)t;;Bk4JpvG~N>RVlyY|%ffYJpU&D<{I6t;L(|_6C2s z@MciF*=cXy)f=m0pWfIp#V%3^8i#o>1Ba{}VGlyNgGD(Y7?>lM|B*ybPrsk^`5WJv)+k zv*`@-RI~Sx%Dp0oAD%1C-p5t$WV7e0@upTC4aVD zzmkG-KkA<#jiG$U9VQ%6T)-645m^8}6Yv=(DPB&sWt^3GVKEPfnp02#S8J+>?I- zX<4|2(7nnco^^dsj$N#8Y-P$hqGsbjdw24VpvunNjv^q=x5Fy7&U3iFk#;TL>PqZ6 zWQxF;_n7iFSKC%R>-GbI1mtzsW-WcslTYxnumPq&Gbk@(L>CoOH+k$0xnl3uzZ|h zY69un>)|PBT~ZYP@^MwMD)8E>#lWgEe|0b5hctoOw7AosQvYn)!mL*TcEKDT4Yc@Z zNsDDCep`fXbul+*XE0zk(ZlF*2R-j?4>b4sOwI&)0z#j*CTI?|J@Kcr2S)+_iRm== zkO3ZYFxB>>fQlRdZv=zl{+gi11YG6(&QBbJO^Ar+L11`wX2dE4z`raE{UlhZClv8w zcrNoCE~f%KA6Ef?ZKj{kF`&OX(+?*G!pJK#eKhQW0db7j{{h1j!`9ViA05WJF)HN# zref$`(J#u_ri?Fb`l)X%4CI2Ie05Pz%3snGsGoywY|z4L=w=-cJbQ5o3;cJS7xK}b z%6Qtnm8aYp5Ij>NSy`v&X!B_Nz_Rlb1BIH-~M7ouPg?`4; zkgN1ImWEuhjqo(&D&36buakZyq_2=Z?P(@u4>lMo0|!UXgBVapmn89|E@AWy{9HW> zQU(nm4QX4L{D&N2qt7s+MLzMHZY1-IQpwoLj*kb9^m=URVe}b4939|ngBn`BxiI+` z%iRiM`W;!8ZI!nbhQspv$khH;9)Mwi2fga@Kz3?nS&1eYa&UIE|A5i*K*deC-AO7Q zF6xDh&UO_q_SuWQ`4>yYi-V-Vjzd9iJ=y9%Fe10Uj3s60_+lZwvx&JAM$e?;imaXz zK&+JBkxz%OwDLYE{Ihp%E2&s3cIaJJycr1G5Ae4oa7&lXwyfZX$Zc^Ya+St@(m^I5+v z09Cq{t^*+TnJ0%>1@mtN=HDopzbrL;1^O4%UNC=2hPJ#38&87-f%75$Ip5RSo`i9o z_x0w+MKK^|d%I3`S7*(zA=rxP;!TwBa*(a+Cp->l-mzzN(Vf^eehe9 zu)rf8o&$q?R(GjT*wyLH`Qi;g32zSCHy8EBNO(wP3E(FL*U{yl}Fz! zaqd7A{>ze2+FJT>v{vU9h!v5TNzxA?qvw)Dn6b5T#^!%$)7{y8IaTvvs$fHwbp4#=GDX!8`qG<+niN0LgM5?*5L3 zTZ$jAFEnMAp7J*_gX0ISw&B|N&51sWoBP*gsPxOY!Vc-j{`fz-f3BmEr?q;c*_xR} z$HBEex)jhj0ecGJvg9TNpUypc%fZ$3w|BD)xx*TK^@53$fk>AHNcWz>ei^Y}v~ngE zp!`}{j_vY2^>9^DY~F}{Koot$MAX*N?#trPeFSwNjLmLrZVXDkYOV(5{d$ lMvR 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%0D*9O9SWVV5ykYu@Kj;(~Vx%f* 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

i{7aL|GV79@=mDSS$0ptE!i2mlB2$``GHze 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 lMvR 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%0D*9O9SWVV5ykYu@Kj;(~Vx%f* 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

i{7aL|GV79@=mDSS$0ptE!i2mlB2$``GHze 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