From 89777789710eab27b8f6b43f90b48071eb71f32c Mon Sep 17 00:00:00 2001 From: Manoj Prabhakar Paidiparthy Date: Tue, 30 Jun 2026 15:37:52 -0700 Subject: [PATCH 01/27] feat(models): add optional Pricing field to ModelInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a Pricing model (input/output per-million rates, optional cache-read/cache-write rates, currency, as_of) and an optional `pricing` field on ModelInfo. Providers can now surface their existing internal rate tables (e.g., the Anthropic provider's _RATES dict) through /v1/models so HTTP-bridge applications such as amplifier-app-opencode can display cost estimates without maintaining their own hardcoded pricing tables. Backwards-compatible: pricing defaults to None, so providers that don't populate it (local providers like ollama, self-hosted backends like vllm) are unaffected, and existing ModelInfo construction call sites continue to work unchanged. Fixes: microsoft-amplifier/amplifier-support#295 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- python/amplifier_core/__init__.py | 2 + python/amplifier_core/models.py | 33 ++++++++++++ tests/test_model_info_pricing.py | 85 +++++++++++++++++++++++++++++++ uv.lock | 2 +- 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 tests/test_model_info_pricing.py diff --git a/python/amplifier_core/__init__.py b/python/amplifier_core/__init__.py index 13303a8..1de1d22 100644 --- a/python/amplifier_core/__init__.py +++ b/python/amplifier_core/__init__.py @@ -77,6 +77,7 @@ from .models import HookResult from .models import ModelInfo from .models import ModuleInfo +from .models import Pricing from .models import ProviderInfo from .models import SessionStatus from .models import ToolResult @@ -118,6 +119,7 @@ "ConfigField", "ModelInfo", "ModuleInfo", + "Pricing", "ProviderInfo", "SessionStatus", "ApprovalRequest", diff --git a/python/amplifier_core/models.py b/python/amplifier_core/models.py index 218d649..81a7634 100644 --- a/python/amplifier_core/models.py +++ b/python/amplifier_core/models.py @@ -5,6 +5,7 @@ import json import re +from datetime import date from datetime import datetime from decimal import Decimal from typing import Any @@ -322,6 +323,31 @@ class HookResult(BaseModel): ) +class Pricing(BaseModel): + """Per-model pricing information. + + Rates are per million tokens, in the specified currency. Surfaced via + /v1/models so HTTP-bridge applications (e.g., amplifier-app-opencode) + can display cost estimates without maintaining their own pricing tables. + """ + + input_per_million: float = Field(..., description="Cost per million input tokens") + output_per_million: float = Field(..., description="Cost per million output tokens") + cache_read_per_million: float | None = Field( + default=None, + description="Cost per million cache-read input tokens (None if not supported)", + ) + cache_write_per_million: float | None = Field( + default=None, + description="Cost per million cache-write input tokens (None if not supported)", + ) + currency: str = Field(default="USD", description="ISO 4217 currency code") + as_of: date | None = Field( + default=None, + description="Date these rates were last verified; None if unknown", + ) + + class ModelInfo(BaseModel): """Model metadata for provider models. @@ -342,6 +368,13 @@ class ModelInfo(BaseModel): default_factory=dict, description="Model-specific default config values (e.g., temperature, max_tokens)", ) + pricing: Pricing | None = Field( + default=None, + description=( + "Per-model pricing information. None when pricing is not available " + "(e.g., local providers like ollama, self-hosted backends like vllm)." + ), + ) class ConfigField(BaseModel): diff --git a/tests/test_model_info_pricing.py b/tests/test_model_info_pricing.py new file mode 100644 index 0000000..006b3e8 --- /dev/null +++ b/tests/test_model_info_pricing.py @@ -0,0 +1,85 @@ +"""Serialization contract for ModelInfo.pricing and the Pricing model. + +pricing is optional on ModelInfo: None when a provider has no rate data +(e.g., local providers like ollama, self-hosted backends like vllm), and a +populated Pricing object when a provider can supply rates. Both states must +round-trip cleanly through model_dump() / model_validate() so HTTP bridges +(e.g., amplifier-app-opencode) can rely on the field without special-casing +either branch. +""" + +from amplifier_core.models import ModelInfo +from amplifier_core.models import Pricing + + +class TestPricingRoundTrip: + """Pricing model dumps and reconstructs without loss.""" + + def test_round_trip_full(self): + pricing = Pricing( + input_per_million=3.0, + output_per_million=15.0, + cache_read_per_million=0.3, + cache_write_per_million=3.75, + currency="USD", + ) + dumped = pricing.model_dump() + rebuilt = Pricing.model_validate(dumped) + + assert rebuilt == pricing + assert rebuilt.input_per_million == 3.0 + assert rebuilt.output_per_million == 15.0 + assert rebuilt.cache_read_per_million == 0.3 + assert rebuilt.cache_write_per_million == 3.75 + assert rebuilt.currency == "USD" + assert rebuilt.as_of is None + + def test_optional_fields_default_to_none(self): + pricing = Pricing(input_per_million=1.0, output_per_million=5.0) + + assert pricing.cache_read_per_million is None + assert pricing.cache_write_per_million is None + assert pricing.currency == "USD" + assert pricing.as_of is None + + +class TestModelInfoPricingField: + """ModelInfo.pricing: optional, backwards-compatible, round-trips both states.""" + + def test_pricing_none_serializes_cleanly(self): + """Local/self-hosted providers (no rate data) omit pricing without error.""" + model = ModelInfo( + id="local-model", + display_name="Local Model", + context_window=8192, + max_output_tokens=4096, + ) + + dumped = model.model_dump() + + assert dumped["pricing"] is None + rebuilt = ModelInfo.model_validate(dumped) + assert rebuilt.pricing is None + + def test_pricing_populated_round_trips(self): + pricing = Pricing( + input_per_million=3.0, + output_per_million=15.0, + cache_read_per_million=0.3, + cache_write_per_million=3.75, + ) + model = ModelInfo( + id="claude-sonnet-4-5", + display_name="Claude Sonnet 4.5", + context_window=200_000, + max_output_tokens=64_000, + pricing=pricing, + ) + + dumped = model.model_dump() + rebuilt = ModelInfo.model_validate(dumped) + + assert rebuilt.pricing is not None + assert rebuilt.pricing == pricing + assert dumped["pricing"]["input_per_million"] == 3.0 + assert dumped["pricing"]["output_per_million"] == 15.0 diff --git a/uv.lock b/uv.lock index 652a841..c288645 100644 --- a/uv.lock +++ b/uv.lock @@ -4,7 +4,7 @@ requires-python = ">=3.11" [[package]] name = "amplifier-core" -version = "1.5.1" +version = "1.6.0" source = { editable = "." } dependencies = [ { name = "click" }, From 1a94515993f9fb44177e7e937577136ad1bd20a6 Mon Sep 17 00:00:00 2001 From: Manoj Prabhakar Paidiparthy Date: Tue, 30 Jun 2026 17:51:55 -0700 Subject: [PATCH 02/27] refactor(models): drop Pricing.as_of and tighten schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Applies triage feedback to the Pricing model added in 8977778: - Removed the `as_of` date field entirely. All Pricing fields are now float/str only -- no dates. The unused `date` import is removed too. - Added ISO 4217 currency validation via a field_validator: currency must match `^[A-Z]{3}$`. - Added a docstring note clarifying that Pricing rate fields use float (not Decimal) because they are display-only estimates for /v1/models, distinct from Usage.cost_usd which is Decimal and rejects float. Tests: removed as_of assertions, added a JSON round-trip test (model_dump / model_dump(mode="json") / model_dump_json all succeed now that there are no date fields) and a currency validation test covering valid and invalid ISO 4217 codes. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- python/amplifier_core/models.py | 17 ++++++++++++----- tests/test_model_info_pricing.py | 27 +++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/python/amplifier_core/models.py b/python/amplifier_core/models.py index 81a7634..9d7c204 100644 --- a/python/amplifier_core/models.py +++ b/python/amplifier_core/models.py @@ -5,7 +5,6 @@ import json import re -from datetime import date from datetime import datetime from decimal import Decimal from typing import Any @@ -329,6 +328,11 @@ class Pricing(BaseModel): Rates are per million tokens, in the specified currency. Surfaced via /v1/models so HTTP-bridge applications (e.g., amplifier-app-opencode) can display cost estimates without maintaining their own pricing tables. + + Rate fields use `float` (not `Decimal`). These are display-only estimates + surfaced to consumers via `/v1/models` for UI-level cost display. For + per-turn cost accounting, use `Usage.cost_usd`, which is `Decimal` and + uses a field validator that explicitly rejects `float`. """ input_per_million: float = Field(..., description="Cost per million input tokens") @@ -342,10 +346,13 @@ class Pricing(BaseModel): description="Cost per million cache-write input tokens (None if not supported)", ) currency: str = Field(default="USD", description="ISO 4217 currency code") - as_of: date | None = Field( - default=None, - description="Date these rates were last verified; None if unknown", - ) + + @field_validator("currency") + @classmethod + def _validate_currency(cls, v: str) -> str: + if not re.match(r"^[A-Z]{3}$", v): + raise ValueError(f"currency must be a 3-letter ISO 4217 code, got {v!r}") + return v class ModelInfo(BaseModel): diff --git a/tests/test_model_info_pricing.py b/tests/test_model_info_pricing.py index 006b3e8..1f93e88 100644 --- a/tests/test_model_info_pricing.py +++ b/tests/test_model_info_pricing.py @@ -8,6 +8,10 @@ either branch. """ +import json + +from pydantic import ValidationError + from amplifier_core.models import ModelInfo from amplifier_core.models import Pricing @@ -32,7 +36,6 @@ def test_round_trip_full(self): assert rebuilt.cache_read_per_million == 0.3 assert rebuilt.cache_write_per_million == 3.75 assert rebuilt.currency == "USD" - assert rebuilt.as_of is None def test_optional_fields_default_to_none(self): pricing = Pricing(input_per_million=1.0, output_per_million=5.0) @@ -40,7 +43,27 @@ def test_optional_fields_default_to_none(self): assert pricing.cache_read_per_million is None assert pricing.cache_write_per_million is None assert pricing.currency == "USD" - assert pricing.as_of is None + + def test_pricing_json_dumps_survives(self): + p = Pricing(input_per_million=3.0, output_per_million=15.0) + # Verify json.dumps(model_dump()) works without needing mode="json" + # (since we no longer have date fields, this should be trivially safe) + json.dumps(p.model_dump()) + # And explicit mode="json" for consistency + json.dumps(p.model_dump(mode="json")) + # And model_dump_json() directly + Pricing.model_validate_json(p.model_dump_json()) + + def test_currency_must_be_iso_4217(self): + # valid 3-letter uppercase + Pricing(input_per_million=1.0, output_per_million=2.0, currency="EUR") + # invalid + for bad in ["usd", "US", "USDD", "us$", "123"]: + try: + Pricing(input_per_million=1.0, output_per_million=2.0, currency=bad) + raise AssertionError(f"expected ValidationError for currency={bad!r}") + except ValidationError: + pass class TestModelInfoPricingField: From cb28489d89a1168d5fc9d4c405bcc6b427101ae6 Mon Sep 17 00:00:00 2001 From: Manoj Prabhakar Paidiparthy Date: Tue, 30 Jun 2026 17:52:39 -0700 Subject: [PATCH 03/27] feat(models): propagate Pricing through Rust bridges + guest crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Threads the Pricing model added in 8977778 through the full Rust ecosystem: native crate, WASM guest crate, proto wire format, gRPC bridge, and equivalence tests. - crates/amplifier-core/src/models.rs: adds a native `Pricing` struct (input_per_million, output_per_million, optional cache rates, currency defaulting to "USD") and `ModelInfo.pricing: Option`. - crates/amplifier-guest/src/types.rs: duplicates the `Pricing` struct (this crate has no amplifier-core dependency) and adds the same `pricing` field to its `ModelInfo`. Updates existing tests to cover the new field. - proto/amplifier_module.proto: adds `string pricing_json = 7` to `ModelInfo`, mirroring the existing `defaults_json` string-blob pattern rather than a nested message type. Regenerates src/generated/amplifier.module.rs via `cargo build` (protoc installed locally). - crates/amplifier-core/src/generated/conversions.rs: extends the native <-> proto ModelInfo conversions for pricing/pricing_json. Proto -> native intentionally returns None (not a synthetic default) on parse failure, since there is no meaningful default price. Adds roundtrip and failure-mode tests. - crates/amplifier-core/src/bridges/grpc_provider.rs: adds `parse_pricing_json`, threaded through `list_models()`, with unit tests for the valid/empty/invalid cases. - crates/amplifier-core/src/bridges/wasm_provider.rs: no changes needed -- it deserializes directly into the native `ModelInfo` via `serde_json::from_slice`, so `pricing` is picked up automatically via `#[serde(default)]`. - crates/amplifier-core/src/generated/equivalence_tests.rs: extends the ModelInfo proto equivalence test to cover `pricing_json`. - tests/fixtures/wasm/src/echo-provider/src/lib.rs and crates/amplifier-guest/src/lib.rs: update existing ModelInfo literals for the new required field. - bindings/python/tests/test_schema_sync.py: adds a Python <-> JSON round-trip test for ModelInfo.pricing. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- bindings/python/tests/test_schema_sync.py | 19 ++++++ .../src/bridges/grpc_provider.rs | 19 ++++++ .../src/generated/amplifier.module.rs | 4 ++ .../src/generated/conversions.rs | 64 +++++++++++++++++++ .../src/generated/equivalence_tests.rs | 6 ++ crates/amplifier-core/src/models.rs | 43 +++++++++++++ crates/amplifier-guest/src/lib.rs | 1 + crates/amplifier-guest/src/types.rs | 40 ++++++++++++ proto/amplifier_module.proto | 3 + .../wasm/src/echo-provider/src/lib.rs | 1 + 10 files changed, 200 insertions(+) diff --git a/bindings/python/tests/test_schema_sync.py b/bindings/python/tests/test_schema_sync.py index 2388287..f25df8a 100644 --- a/bindings/python/tests/test_schema_sync.py +++ b/bindings/python/tests/test_schema_sync.py @@ -148,3 +148,22 @@ def test_hook_result_json_roundtrip(): assert restored.context_injection == "Lint error on line 42" assert restored.suppress_output is True assert restored.user_message == "Found 1 issue" + + +def test_model_info_pricing_field(): + """ModelInfo.pricing and Pricing are importable and round-trip via JSON.""" + from amplifier_core import ModelInfo, Pricing + + info = ModelInfo( + id="test-model", + display_name="Test", + context_window=1000, + max_output_tokens=100, + pricing=Pricing(input_per_million=3.0, output_per_million=15.0), + ) + json_str = info.model_dump_json() + parsed = json.loads(json_str) + assert parsed["pricing"]["input_per_million"] == 3.0 + + restored = ModelInfo.model_validate(parsed) + assert restored.pricing.output_per_million == 15.0 diff --git a/crates/amplifier-core/src/bridges/grpc_provider.rs b/crates/amplifier-core/src/bridges/grpc_provider.rs index 609dff9..7f51c98 100644 --- a/crates/amplifier-core/src/bridges/grpc_provider.rs +++ b/crates/amplifier-core/src/bridges/grpc_provider.rs @@ -47,6 +47,23 @@ fn parse_defaults_json(json_str: &str, id: &str) -> HashMap { }) } +/// Parse a JSON string into an optional Pricing, logging a warning on +/// non-empty parse failures. Empty string means "no pricing available". +fn parse_pricing_json(json_str: &str, id: &str) -> Option { + if json_str.is_empty() { + return None; + } + serde_json::from_str(json_str) + .map_err(|e| { + log::warn!( + "Failed to parse model '{}' pricing_json: {e} — pricing unavailable", + id + ); + e + }) + .ok() +} + /// A bridge that wraps a remote gRPC `ProviderService` as a native [`Provider`]. /// /// The client is held behind a [`tokio::sync::Mutex`] because @@ -125,6 +142,7 @@ impl Provider for GrpcProviderBridge { .into_iter() .map(|m| { let defaults = parse_defaults_json(&m.defaults_json, &m.id); + let pricing = parse_pricing_json(&m.pricing_json, &m.id); ModelInfo { id: m.id, display_name: m.display_name, @@ -132,6 +150,7 @@ impl Provider for GrpcProviderBridge { max_output_tokens: m.max_output_tokens as i64, capabilities: m.capabilities, defaults, + pricing, } }) .collect(); diff --git a/crates/amplifier-core/src/generated/amplifier.module.rs b/crates/amplifier-core/src/generated/amplifier.module.rs index d510a11..5f76cc5 100644 --- a/crates/amplifier-core/src/generated/amplifier.module.rs +++ b/crates/amplifier-core/src/generated/amplifier.module.rs @@ -452,6 +452,10 @@ pub struct ModelInfo { pub capabilities: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, #[prost(string, tag = "6")] pub defaults_json: ::prost::alloc::string::String, + /// JSON-encoded Pricing, or empty string if pricing is unavailable + /// (e.g., local providers, self-hosted backends). + #[prost(string, tag = "7")] + pub pricing_json: ::prost::alloc::string::String, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct ProviderInfo { diff --git a/crates/amplifier-core/src/generated/conversions.rs b/crates/amplifier-core/src/generated/conversions.rs index 39b06cf..6599f8e 100644 --- a/crates/amplifier-core/src/generated/conversions.rs +++ b/crates/amplifier-core/src/generated/conversions.rs @@ -95,6 +95,11 @@ impl From for super::amplifier_module::ModelInfo { }), capabilities: native.capabilities, defaults_json: to_json_or_warn(&native.defaults, "ModelInfo defaults"), + pricing_json: native + .pricing + .as_ref() + .map(|p| to_json_or_warn(p, "ModelInfo pricing")) + .unwrap_or_default(), } } } @@ -112,6 +117,19 @@ impl From for crate::models::ModelInfo { } else { from_json_or_default(&proto.defaults_json, "ModelInfo defaults_json") }, + pricing: if proto.pricing_json.is_empty() { + None + } else { + match serde_json::from_str::(&proto.pricing_json) { + Ok(p) => Some(p), + Err(e) => { + log::warn!( + "Failed to parse ModelInfo pricing_json: {e} — pricing unavailable" + ); + None + } + } + }, } } } @@ -1029,10 +1047,54 @@ mod tests { max_output_tokens: 8192, capabilities: vec!["tools".into(), "vision".into()], defaults: HashMap::from([("temperature".to_string(), serde_json::json!(0.7))]), + pricing: Some(crate::models::Pricing { + input_per_million: 30.0, + output_per_million: 60.0, + cache_read_per_million: None, + cache_write_per_million: None, + currency: "USD".into(), + }), + }; + let proto: super::super::amplifier_module::ModelInfo = original.clone().into(); + let restored: crate::models::ModelInfo = proto.into(); + assert_eq!(original, restored); + } + + #[test] + fn model_info_pricing_none_roundtrips_to_empty_json() { + let original = crate::models::ModelInfo { + id: "local-model".into(), + display_name: "Local Model".into(), + context_window: 8192, + max_output_tokens: 4096, + capabilities: vec![], + defaults: HashMap::new(), + pricing: None, }; let proto: super::super::amplifier_module::ModelInfo = original.clone().into(); + assert!(proto.pricing_json.is_empty()); let restored: crate::models::ModelInfo = proto.into(); assert_eq!(original, restored); + assert!(restored.pricing.is_none()); + } + + #[test] + fn model_info_pricing_invalid_json_becomes_none() { + let mut proto = super::super::amplifier_module::ModelInfo { + id: "broken-model".into(), + display_name: "Broken".into(), + context_window: 1000, + max_output_tokens: 100, + capabilities: vec![], + defaults_json: String::new(), + pricing_json: "not-valid-json".into(), + }; + let restored: crate::models::ModelInfo = proto.clone().into(); + assert!(restored.pricing.is_none()); + + proto.pricing_json = String::new(); + let restored_empty: crate::models::ModelInfo = proto.into(); + assert!(restored_empty.pricing.is_none()); } #[test] @@ -1122,6 +1184,7 @@ mod tests { max_output_tokens: 100, capabilities: vec![], defaults: HashMap::new(), + pricing: None, }; let proto: super::super::amplifier_module::ModelInfo = original.into(); assert_eq!(proto.context_window, i32::MAX); @@ -1136,6 +1199,7 @@ mod tests { max_output_tokens: i64::from(i32::MAX) + 500, capabilities: vec![], defaults: HashMap::new(), + pricing: None, }; let proto: super::super::amplifier_module::ModelInfo = original.into(); assert_eq!(proto.max_output_tokens, i32::MAX); diff --git a/crates/amplifier-core/src/generated/equivalence_tests.rs b/crates/amplifier-core/src/generated/equivalence_tests.rs index 87b4775..def3cc9 100644 --- a/crates/amplifier-core/src/generated/equivalence_tests.rs +++ b/crates/amplifier-core/src/generated/equivalence_tests.rs @@ -194,6 +194,8 @@ mod tests { max_output_tokens: 4096, capabilities: vec!["vision".into(), "tools".into(), "streaming".into()], defaults_json: r#"{"temperature":0.7}"#.into(), + pricing_json: r#"{"input_per_million":15.0,"output_per_million":75.0,"currency":"USD"}"# + .into(), }; assert_eq!(info.id, "claude-3-opus"); assert_eq!(info.display_name, "Claude 3 Opus"); @@ -201,6 +203,10 @@ mod tests { assert_eq!(info.max_output_tokens, 4096); assert_eq!(info.capabilities.len(), 3); assert_eq!(info.defaults_json, r#"{"temperature":0.7}"#); + assert_eq!( + info.pricing_json, + r#"{"input_per_million":15.0,"output_per_million":75.0,"currency":"USD"}"# + ); } #[test] diff --git a/crates/amplifier-core/src/models.rs b/crates/amplifier-core/src/models.rs index 266c62e..7c4e45f 100644 --- a/crates/amplifier-core/src/models.rs +++ b/crates/amplifier-core/src/models.rs @@ -304,6 +304,11 @@ pub struct ModelInfo { /// Model-specific default config values (e.g., temperature, max_tokens). #[serde(default)] pub defaults: HashMap, + + /// Per-model pricing information. None when pricing is not available + /// (e.g., local providers like ollama, self-hosted backends like vllm). + #[serde(default)] + pub pricing: Option, } /// A configuration field that a provider needs, with prompt metadata. @@ -352,6 +357,36 @@ pub struct ConfigField { pub requires_model: bool, } +/// Per-model pricing information. +/// +/// Rates are per million tokens, in the specified currency. Surfaced via +/// `/v1/models` so HTTP-bridge applications (e.g., amplifier-app-opencode) +/// can display cost estimates without maintaining their own pricing tables. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Pricing { + /// Cost per million input tokens. + pub input_per_million: f64, + + /// Cost per million output tokens. + pub output_per_million: f64, + + /// Cost per million cache-read input tokens (None if not supported). + #[serde(default)] + pub cache_read_per_million: Option, + + /// Cost per million cache-write input tokens (None if not supported). + #[serde(default)] + pub cache_write_per_million: Option, + + /// ISO 4217 currency code. + #[serde(default = "default_currency")] + pub currency: String, +} + +fn default_currency() -> String { + "USD".to_string() +} + /// Provider metadata. /// /// Describes capabilities, authentication requirements, and defaults for a provider. @@ -727,6 +762,7 @@ mod tests { max_output_tokens: 4096, capabilities: vec!["streaming".into()], defaults: Default::default(), + pricing: None, }; assert_eq!(info.id, "gpt-4"); } @@ -740,6 +776,13 @@ mod tests { max_output_tokens: 8192, capabilities: vec!["tools".into(), "vision".into(), "streaming".into()], defaults: HashMap::from([("temperature".into(), json!(0.7))]), + pricing: Some(Pricing { + input_per_million: 3.0, + output_per_million: 15.0, + cache_read_per_million: None, + cache_write_per_million: None, + currency: "USD".into(), + }), }; let json_str = serde_json::to_string(&info).unwrap(); let deserialized: ModelInfo = serde_json::from_str(&json_str).unwrap(); diff --git a/crates/amplifier-guest/src/lib.rs b/crates/amplifier-guest/src/lib.rs index c738506..6be07cc 100644 --- a/crates/amplifier-guest/src/lib.rs +++ b/crates/amplifier-guest/src/lib.rs @@ -1289,6 +1289,7 @@ mod provider_tests { max_output_tokens: 1024, capabilities: vec!["chat".to_string()], defaults: HashMap::new(), + pricing: None, }]) } diff --git a/crates/amplifier-guest/src/types.rs b/crates/amplifier-guest/src/types.rs index ada17d4..eb86aaa 100644 --- a/crates/amplifier-guest/src/types.rs +++ b/crates/amplifier-guest/src/types.rs @@ -171,6 +171,27 @@ pub struct ProviderInfo { pub defaults: HashMap, } +/// Per-model pricing information. +/// +/// Rates are per million tokens, in the specified currency. Mirrors +/// `amplifier_core::models::Pricing` on the native side (this crate has no +/// dependency on `amplifier-core`, so the struct is duplicated here). +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Pricing { + pub input_per_million: f64, + pub output_per_million: f64, + #[serde(default)] + pub cache_read_per_million: Option, + #[serde(default)] + pub cache_write_per_million: Option, + #[serde(default = "default_currency")] + pub currency: String, +} + +fn default_currency() -> String { + "USD".to_string() +} + /// Metadata about a specific model. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ModelInfo { @@ -180,6 +201,10 @@ pub struct ModelInfo { pub max_output_tokens: i64, pub capabilities: Vec, pub defaults: HashMap, + /// Per-model pricing information. None when pricing is not available + /// (e.g., local providers like ollama, self-hosted backends like vllm). + #[serde(default)] + pub pricing: Option, } /// Request for an LLM chat completion. @@ -473,9 +498,17 @@ mod tests { max_output_tokens: 4096, capabilities: vec!["chat".to_string(), "tools".to_string()], defaults: HashMap::new(), + pricing: Some(Pricing { + input_per_million: 30.0, + output_per_million: 60.0, + cache_read_per_million: None, + cache_write_per_million: None, + currency: "USD".to_string(), + }), }; assert_eq!(info.context_window, 128000); assert_eq!(info.max_output_tokens, 4096); + assert_eq!(info.pricing.as_ref().unwrap().input_per_million, 30.0); } // --- ChatRequest tests --- @@ -638,6 +671,13 @@ mod tests { max_output_tokens: 4096, capabilities: vec!["chat".to_string(), "tools".to_string()], defaults: HashMap::new(), + pricing: Some(Pricing { + input_per_million: 30.0, + output_per_million: 60.0, + cache_read_per_million: Some(15.0), + cache_write_per_million: Some(37.5), + currency: "USD".to_string(), + }), }; let json_str = serde_json::to_string(&original).unwrap(); let deserialized: ModelInfo = serde_json::from_str(&json_str).unwrap(); diff --git a/proto/amplifier_module.proto b/proto/amplifier_module.proto index e12f988..35bc2cc 100644 --- a/proto/amplifier_module.proto +++ b/proto/amplifier_module.proto @@ -415,6 +415,9 @@ message ModelInfo { int32 max_output_tokens = 4; repeated string capabilities = 5; string defaults_json = 6; + // JSON-encoded Pricing, or empty string if pricing is unavailable + // (e.g., local providers, self-hosted backends). + string pricing_json = 7; } message ProviderInfo { diff --git a/tests/fixtures/wasm/src/echo-provider/src/lib.rs b/tests/fixtures/wasm/src/echo-provider/src/lib.rs index 4650eca..f4c46cf 100644 --- a/tests/fixtures/wasm/src/echo-provider/src/lib.rs +++ b/tests/fixtures/wasm/src/echo-provider/src/lib.rs @@ -31,6 +31,7 @@ impl Provider for EchoProvider { max_output_tokens: 1024, capabilities: vec!["chat".to_string()], defaults: HashMap::new(), + pricing: None, }]) } From 4021c6ad8907bf0c3b0880c9200659787744db02 Mon Sep 17 00:00:00 2001 From: Manoj Prabhakar Paidiparthy Date: Wed, 1 Jul 2026 09:52:18 -0700 Subject: [PATCH 04/27] style: apply cargo fmt to equivalence_tests.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI's rustfmt (stable) wraps the pricing_json line differently than the local rustfmt run that produced the prior commit. Applying the CI-preferred formatting to unblock the fmt check on PR #92. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- crates/amplifier-core/src/generated/equivalence_tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/amplifier-core/src/generated/equivalence_tests.rs b/crates/amplifier-core/src/generated/equivalence_tests.rs index def3cc9..098c6f3 100644 --- a/crates/amplifier-core/src/generated/equivalence_tests.rs +++ b/crates/amplifier-core/src/generated/equivalence_tests.rs @@ -194,8 +194,8 @@ mod tests { max_output_tokens: 4096, capabilities: vec!["vision".into(), "tools".into(), "streaming".into()], defaults_json: r#"{"temperature":0.7}"#.into(), - pricing_json: r#"{"input_per_million":15.0,"output_per_million":75.0,"currency":"USD"}"# - .into(), + pricing_json: + r#"{"input_per_million":15.0,"output_per_million":75.0,"currency":"USD"}"#.into(), }; assert_eq!(info.id, "claude-3-opus"); assert_eq!(info.display_name, "Claude 3 Opus"); From 6b563a494b13e1c9247274f6d350f2862531eff7 Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Thu, 20 Aug 2026 19:09:02 -0700 Subject: [PATCH 05/27] fix(session): honor config.session.metadata on session:start in the Rust kernel `docs/specs/CONTRIBUTION_CHANNELS.md` lists `session.metadata` as a runtime passthrough channel; `tests/test_session_metadata.py` specifies it; and `python/amplifier_core/session.py` implements it. The Rust switchover shipped without it. Production does `from amplifier_core import AmplifierSession`, which is `RustSession` (`python/amplifier_core/__init__.py`). Its lifecycle emit built the payload from `{session_id, parent_id}` only, so configured metadata was dropped before any hook saw it. The pure-Rust kernel had the same omission. `session:fork` was unaffected -- it is emitted by the shared Python helper `_session_init.py`, which already honored the contract. So one contract had three payload constructions and two of them disagreed with it. The metadata tests were green throughout, because they import the *pure-Python* `amplifier_core.session.AmplifierSession` -- the class no runtime consumer executes. That blind spot is what let this ship dead. - `session_metadata_passthrough()` in `crates/amplifier-core/src/session.rs`: one shared reader, so the Rust emit paths cannot drift apart again. Mirrors the Python kernel's `if session_metadata:` guard, so absent *or* empty metadata leaves the payload byte-identical to before. - Merged into the bindings emit (cached at construction alongside `session_id`/`parent_id`) and into the pure-Rust emit. - `bindings/python/tests/test_session_metadata_rust.py`: the parallel suite against `RustSession` that the existing tests could not provide. Additive only. No new event, no new top-level payload key, no schema change. Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- bindings/python/src/session.rs | 17 +- .../tests/test_session_metadata_rust.py | 228 ++++++++++++++++++ crates/amplifier-core/src/session.rs | 175 +++++++++++++- 3 files changed, 409 insertions(+), 11 deletions(-) create mode 100644 bindings/python/tests/test_session_metadata_rust.py diff --git a/bindings/python/src/session.rs b/bindings/python/src/session.rs index 699e575..b6233b4 100644 --- a/bindings/python/src/session.rs +++ b/bindings/python/src/session.rs @@ -44,6 +44,8 @@ pub(crate) struct PySession { cached_session_id: String, /// Cached parent_id. cached_parent_id: Option, + /// Cached `session.metadata` (CP-SM passthrough), `None` when unconfigured. + cached_session_metadata: Option, } #[pymethods] @@ -112,6 +114,12 @@ impl PySession { let json_str: String = json_dumps_safe(py, config.as_any())?; let value: Value = serde_json::from_str(&json_str) .map_err(|e| PyErr::new::(format!("Invalid config JSON: {e}")))?; + // CP-SM (docs/specs/CONTRIBUTION_CHANNELS.md): cache `session.metadata` here, + // alongside session_id/parent_id, so the lifecycle emit below can include it + // without re-entering Python. Read before `value` is moved into SessionConfig. + let cached_session_metadata = + amplifier_core::session::session_metadata_passthrough(value.get("session")); + let session_config = amplifier_core::SessionConfig::from_value(value) .map_err(|e| PyErr::new::(format!("Invalid session config: {e}")))?; @@ -180,6 +188,7 @@ impl PySession { is_resumed, cached_session_id: actual_session_id, cached_parent_id: actual_parent_id, + cached_session_metadata, }) } @@ -386,10 +395,16 @@ impl PySession { let hook_registry = hooks.extract::>()?; hook_registry.inner.clone() }; - let pre_event_data = serde_json::json!({ + let mut pre_event_data = serde_json::json!({ "session_id": self.cached_session_id, "parent_id": self.cached_parent_id, }); + // CP-SM passthrough (docs/specs/CONTRIBUTION_CHANNELS.md), matching the + // pure-Python kernel. Unconfigured or empty metadata leaves the payload + // exactly as it was before this change. + if let Some(metadata) = &self.cached_session_metadata { + pre_event_data["metadata"] = metadata.clone(); + } // Clone references for the async block let coordinator = self.coordinator.clone_ref(py); diff --git a/bindings/python/tests/test_session_metadata_rust.py b/bindings/python/tests/test_session_metadata_rust.py new file mode 100644 index 0000000..52148d1 --- /dev/null +++ b/bindings/python/tests/test_session_metadata_rust.py @@ -0,0 +1,228 @@ +"""CP-SM metadata passthrough, exercised against the class that actually runs. + +`tests/test_session_metadata.py` covers the same contract against the +*pure-Python* `amplifier_core.session.AmplifierSession`. Production code does +``from amplifier_core import AmplifierSession``, which is ``RustSession`` +(`python/amplifier_core/__init__.py`) -- so those tests were green on a code +path no runtime consumer executes, and the Rust emit shipped without the +metadata merge. + +This module is the parallel suite against `RustSession`. Keep the two in sync: +a payload contract that is only asserted on one side of the switchover is a +contract that can regress silently. + +CP-SM: Kernel reads config.session.metadata and includes it as optional +'metadata' key in event payloads. Pure passthrough - no interpretation or +validation. +""" + +from __future__ import annotations + +from unittest.mock import AsyncMock, Mock, patch + +import pytest +from amplifier_core._engine import RustSession +from amplifier_core.events import SESSION_FORK, SESSION_RESUME, SESSION_START + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _config(metadata=None): + """Minimal mount plan, optionally carrying session.metadata.""" + session: dict = { + "orchestrator": "loop-basic", + "context": "context-simple", + } + if metadata is not None: + session["metadata"] = metadata + return {"session": session, "providers": [], "hooks": [], "tools": []} + + +async def _make_session(config: dict, **kwargs) -> RustSession: + """Create a RustSession whose module loading is stubbed out.""" + session = RustSession(config=config, **kwargs) + with patch("amplifier_core._session_init.initialize_session", AsyncMock()): + await session.initialize() + return session + + +def _mount_stubs(session: RustSession) -> None: + """Mount the minimum required for execute() to reach the orchestrator.""" + orchestrator = AsyncMock() + orchestrator.execute = AsyncMock(return_value="ok") + session.coordinator.mount_points["orchestrator"] = orchestrator + session.coordinator.mount_points["context"] = AsyncMock() + session.coordinator.mount_points["providers"] = {"mock": AsyncMock()} + + +def _capture(session: RustSession, event: str) -> list[dict]: + """Register a handler that records payloads for `event`.""" + captured: list[dict] = [] + + async def _handler(_event: str, data: dict): + captured.append(dict(data)) + return None + + session.coordinator.hooks.register(event, _handler, name=f"capture-{event}") + return captured + + +# --------------------------------------------------------------------------- +# session:start +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_session_start_includes_metadata_when_configured(): + """session:start carries config.session.metadata verbatim.""" + metadata = {"agent_name": "test-agent", "run_id": "abc123"} + session = await _make_session(_config(metadata)) + starts = _capture(session, SESSION_START) + _mount_stubs(session) + + await session.execute("hello") + + assert len(starts) == 1, f"Expected 1 session:start, got {len(starts)}" + assert "metadata" in starts[0], ( + "Expected 'metadata' key in session:start payload. " + f"Got keys: {sorted(starts[0])}" + ) + assert starts[0]["metadata"] == metadata + + +@pytest.mark.asyncio +async def test_session_start_passes_nested_metadata_through_untouched(): + """Passthrough is verbatim -- nested objects are not flattened or rewritten.""" + metadata = { + "invocation": { + "schema": 1, + "mode": "single", + "stdin_isatty": False, + "launched_by_session_id": None, + } + } + session = await _make_session(_config(metadata)) + starts = _capture(session, SESSION_START) + _mount_stubs(session) + + await session.execute("hello") + + assert starts[0]["metadata"] == metadata + + +@pytest.mark.asyncio +async def test_session_start_excludes_metadata_when_not_configured(): + """No metadata configured => payload is exactly what it was before CP-SM.""" + session = await _make_session(_config()) + starts = _capture(session, SESSION_START) + _mount_stubs(session) + + await session.execute("hello") + + assert len(starts) == 1 + assert "metadata" not in starts[0], ( + "Expected no 'metadata' key when unconfigured. " + f"Got keys: {sorted(starts[0])}" + ) + + +@pytest.mark.asyncio +async def test_session_start_excludes_empty_metadata(): + """An empty dict is falsy for the Python kernel -- the Rust kernel must agree.""" + session = await _make_session(_config({})) + starts = _capture(session, SESSION_START) + _mount_stubs(session) + + await session.execute("hello") + + assert "metadata" not in starts[0], ( + "Empty metadata must behave exactly like absent metadata " + "(matches the pure-Python kernel's `if session_metadata:` guard)." + ) + + +@pytest.mark.asyncio +async def test_metadata_does_not_displace_existing_payload_fields(): + """Additive only: session_id / parent_id keep their meaning and values.""" + parent_id = "parent-session-id-123" + session = await _make_session( + _config({"tag": "some-tag"}), + session_id="child-session-id-456", + parent_id=parent_id, + ) + starts = _capture(session, SESSION_START) + _mount_stubs(session) + + await session.execute("hello") + + payload = starts[0] + assert payload["session_id"] == "child-session-id-456" + assert payload["parent_id"] == parent_id + assert payload["metadata"] == {"tag": "some-tag"} + + +# --------------------------------------------------------------------------- +# session:resume +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_session_resume_includes_metadata_when_configured(): + """session:resume carries metadata too -- same emit site, same contract.""" + metadata = {"agent_name": "resumed-agent"} + session = await _make_session( + _config(metadata), session_id="resumed-id", is_resumed=True + ) + resumes = _capture(session, SESSION_RESUME) + _mount_stubs(session) + + await session.execute("hello") + + assert len(resumes) == 1 + assert resumes[0]["metadata"] == metadata + + +@pytest.mark.asyncio +async def test_session_resume_excludes_metadata_when_not_configured(): + session = await _make_session( + _config(), session_id="resumed-id-2", is_resumed=True + ) + resumes = _capture(session, SESSION_RESUME) + _mount_stubs(session) + + await session.execute("hello") + + assert len(resumes) == 1 + assert "metadata" not in resumes[0] + + +# --------------------------------------------------------------------------- +# session:fork +# +# fork is emitted by the shared Python helper (`_session_init.py`), which both +# session classes delegate to -- so it already honored CP-SM. Pinned here so +# the three emit paths stay in agreement. +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_session_fork_includes_metadata_when_configured(): + metadata = {"agent_name": "child-agent", "depth": 1} + parent_id = "parent-session-id-789" + session = RustSession(config=_config(metadata), parent_id=parent_id) + + loader = Mock() + loader.load = AsyncMock(return_value=AsyncMock(return_value=None)) + loader.get_on_session_ready_queue = Mock(return_value=[]) + loader.clear_on_session_ready_queue = Mock(return_value=None) + session.coordinator.loader = loader + + forks = _capture(session, SESSION_FORK) + + await session.initialize() + + assert len(forks) == 1, f"Expected 1 session:fork, got {len(forks)}" + assert forks[0]["metadata"] == metadata + assert forks[0]["parent"] == parent_id diff --git a/crates/amplifier-core/src/session.rs b/crates/amplifier-core/src/session.rs index 6598ed5..68dcc77 100644 --- a/crates/amplifier-core/src/session.rs +++ b/crates/amplifier-core/src/session.rs @@ -112,6 +112,34 @@ impl SessionConfig { } } +/// CP-SM: extract the `session.metadata` passthrough value from a session +/// config's `session` section. +/// +/// `session.metadata` is a documented contribution channel +/// (`docs/specs/CONTRIBUTION_CHANNELS.md`): the kernel reads it and includes it +/// as an optional `metadata` key in lifecycle event payloads. Pure passthrough — +/// no interpretation, no validation. +/// +/// Returns `None` when the key is absent *or* empty, mirroring the pure-Python +/// kernel's `if session_metadata:` guard (`python/amplifier_core/session.py`). +/// That guard is what keeps the payload byte-identical for every caller that +/// does not configure metadata. +/// +/// Shared by all Rust emit paths so they cannot drift apart again. +pub fn session_metadata_passthrough(session_section: Option<&Value>) -> Option { + let metadata = session_section?.get("metadata")?; + // Mirror Python truthiness: null / false / 0 / "" / [] / {} are all falsy. + let non_empty = match metadata { + Value::Null => false, + Value::Bool(b) => *b, + Value::Number(n) => n.as_f64().is_none_or(|f| f != 0.0), + Value::String(s) => !s.is_empty(), + Value::Array(a) => !a.is_empty(), + Value::Object(o) => !o.is_empty(), + }; + non_empty.then(|| metadata.clone()) +} + // --------------------------------------------------------------------------- // Session // --------------------------------------------------------------------------- @@ -313,16 +341,19 @@ impl Session { events::SESSION_START }; - self.coordinator - .hooks() - .emit( - event, - serde_json::json!({ - "session_id": self.session_id, - "parent_id": self.parent_id, - }), - ) - .await; + let mut payload = serde_json::json!({ + "session_id": self.session_id, + "parent_id": self.parent_id, + }); + // CP-SM passthrough (docs/specs/CONTRIBUTION_CHANNELS.md). Absent or + // empty metadata leaves the payload exactly as it was before. + if let Some(metadata) = + session_metadata_passthrough(self.coordinator.config().get("session")) + { + payload["metadata"] = metadata; + } + + self.coordinator.hooks().emit(event, payload).await; } // Get orchestrator @@ -754,6 +785,130 @@ mod tests { ); } + // --------------------------------------------------------------- + // CP-SM: session.metadata passthrough + // --------------------------------------------------------------- + + #[test] + fn session_metadata_passthrough_reads_configured_value() { + let section = serde_json::json!({ + "orchestrator": "loop-basic", + "context": "context-simple", + "metadata": {"agent_name": "test-agent"}, + }); + assert_eq!( + session_metadata_passthrough(Some(§ion)), + Some(serde_json::json!({"agent_name": "test-agent"})) + ); + } + + #[test] + fn session_metadata_passthrough_is_none_when_absent_or_empty() { + assert_eq!(session_metadata_passthrough(None), None); + + let no_key = serde_json::json!({"orchestrator": "loop-basic"}); + assert_eq!(session_metadata_passthrough(Some(&no_key)), None); + + // Empty / falsy values must behave exactly like "absent" so the payload + // stays byte-identical for callers that configure nothing meaningful. + for empty in [ + serde_json::json!({}), + serde_json::json!([]), + serde_json::json!(""), + serde_json::json!(null), + ] { + let section = serde_json::json!({"metadata": empty}); + assert_eq!(session_metadata_passthrough(Some(§ion)), None); + } + } + + /// CP-SM contract: `config.session.metadata` must reach the `session:start` + /// payload. Specified in `docs/specs/CONTRIBUTION_CHANNELS.md` and already + /// honored by the pure-Python kernel; this pins the Rust kernel to it too. + #[tokio::test] + async fn execute_emits_session_start_with_metadata_when_configured() { + let config = SessionConfig::from_value(serde_json::json!({ + "session": { + "orchestrator": "loop-basic", + "context": "context-simple", + "metadata": {"invocation": {"schema": 1, "mode": "single"}}, + } + })) + .expect("valid config"); + let mut session = Session::new(config, None, None); + session + .coordinator_mut() + .set_orchestrator(Arc::new(FakeOrchestrator::new("ok"))); + session + .coordinator_mut() + .set_context(Arc::new(FakeContextManager::new())); + session + .coordinator_mut() + .mount_provider("test", Arc::new(FakeProvider::new("test", "hi"))); + + let handler = Arc::new(FakeHookHandler::new()); + let _ = session.coordinator().hooks().register( + events::SESSION_START, + handler.clone(), + 0, + Some("test-handler".into()), + ); + + session.set_initialized(); + let _ = session.execute("hello").await; + + let recorded = handler.recorded_events(); + let (_, data) = recorded + .iter() + .find(|(name, _)| name == events::SESSION_START) + .expect("session:start must be emitted"); + assert_eq!( + data.get("metadata"), + Some(&serde_json::json!({"invocation": {"schema": 1, "mode": "single"}})), + "session:start must carry config.session.metadata verbatim. Payload: {data:?}" + ); + // Nothing else moved. + assert!(data.get("session_id").is_some()); + assert!(data.get("parent_id").is_some()); + } + + /// Back-compat: unconfigured metadata leaves the payload exactly as it was. + #[tokio::test] + async fn execute_omits_metadata_when_not_configured() { + let config = SessionConfig::minimal("loop-basic", "context-simple"); + let mut session = Session::new(config, None, None); + session + .coordinator_mut() + .set_orchestrator(Arc::new(FakeOrchestrator::new("ok"))); + session + .coordinator_mut() + .set_context(Arc::new(FakeContextManager::new())); + session + .coordinator_mut() + .mount_provider("test", Arc::new(FakeProvider::new("test", "hi"))); + + let handler = Arc::new(FakeHookHandler::new()); + let _ = session.coordinator().hooks().register( + events::SESSION_START, + handler.clone(), + 0, + Some("test-handler".into()), + ); + + session.set_initialized(); + let _ = session.execute("hello").await; + + let recorded = handler.recorded_events(); + let (_, data) = recorded + .iter() + .find(|(name, _)| name == events::SESSION_START) + .expect("session:start must be emitted"); + assert!( + data.get("metadata").is_none(), + "session:start must not invent a metadata key. Payload: {data:?}" + ); + } + /// Regression test for the Rust-port regression: session:start must fire /// exactly ONCE per session, not once per execute() call. /// From a55d8b9b60aca2b5021ad1576667cb54f6e33333 Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Wed, 9 Sep 2026 07:58:50 -0700 Subject: [PATCH 06/27] fix(validation): reuse canonical module during path checks Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- python/amplifier_core/validation/base.py | 47 +++++ python/amplifier_core/validation/context.py | 31 ++- python/amplifier_core/validation/hook.py | 31 ++- .../amplifier_core/validation/orchestrator.py | 31 ++- python/amplifier_core/validation/provider.py | 31 ++- python/amplifier_core/validation/tool.py | 31 ++- tests/test_validation_canonical_module.py | 176 ++++++++++++++++++ 7 files changed, 278 insertions(+), 100 deletions(-) create mode 100644 tests/test_validation_canonical_module.py diff --git a/python/amplifier_core/validation/base.py b/python/amplifier_core/validation/base.py index 6a3cbdc..4e84e24 100644 --- a/python/amplifier_core/validation/base.py +++ b/python/amplifier_core/validation/base.py @@ -11,9 +11,13 @@ ``context/release-mandate.md`` for the v1.4.0 regression that motivated this. """ +import importlib.util import inspect +import sys from dataclasses import dataclass from dataclasses import field +from pathlib import Path +from types import ModuleType from typing import Any from typing import Literal @@ -62,6 +66,49 @@ def summary(self) -> str: return f"{status}: {passed_count}/{len(self.checks)} checks passed ({len(self.errors)} errors, {len(self.warnings)} warnings)" +def import_module_from_path(module_path: str | Path) -> ModuleType: + """Import a Python source path without duplicating its canonical module. + + When runtime loading already imported the module from the same source, + validation must inspect that object. A same-named module from another + source is deliberately not reused: path validation must validate the + requested file rather than whichever package happens to be in + ``sys.modules``. + """ + path = Path(module_path) + source_path = path / "__init__.py" if path.is_dir() else path + module_name = path.name if path.is_dir() else path.stem + + existing = sys.modules.get(module_name) + if existing is not None: + existing_file = getattr(existing, "__file__", None) + if existing_file is not None and Path(existing_file).resolve() == source_path.resolve(): + return existing + + spec = importlib.util.spec_from_file_location(module_name, source_path) + if spec is None or spec.loader is None: + raise ImportError(f"Could not load spec for {path}") + + module = importlib.util.module_from_spec(spec) + module_prefix = f"{module_name}." + previous_modules = { + name: value + for name, value in sys.modules.items() + if name == module_name or name.startswith(module_prefix) + } + for name in previous_modules: + del sys.modules[name] + sys.modules[module_name] = module + try: + spec.loader.exec_module(module) + finally: + for name in list(sys.modules): + if name == module_name or name.startswith(module_prefix): + del sys.modules[name] + sys.modules.update(previous_modules) + return module + + def check_on_session_ready(module: Any) -> ValidationCheck | None: """Check whether a module's on_session_ready() function, if present, is valid. diff --git a/python/amplifier_core/validation/context.py b/python/amplifier_core/validation/context.py index 5440783..2f15059 100644 --- a/python/amplifier_core/validation/context.py +++ b/python/amplifier_core/validation/context.py @@ -8,7 +8,6 @@ import asyncio import importlib -import importlib.util import inspect from pathlib import Path from typing import Any @@ -16,6 +15,7 @@ from .base import ValidationCheck from .base import ValidationResult from .base import check_on_session_ready +from .base import import_module_from_path def _implements_context_manager_interface(obj: Any) -> bool: @@ -95,11 +95,7 @@ def _check_importable( # File path - find the Python module if path.is_dir(): init_file = path / "__init__.py" - if init_file.exists(): - spec = importlib.util.spec_from_file_location( - path.name, init_file - ) - else: + if not init_file.exists(): result.add( ValidationCheck( name="module_importable", @@ -109,21 +105,16 @@ def _check_importable( ) ) return None - else: - spec = importlib.util.spec_from_file_location(path.stem, path) - - if spec and spec.loader: - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - result.add( - ValidationCheck( - name="module_importable", - passed=True, - message=f"Module loaded from {path}", - severity="info", - ) + module = import_module_from_path(path) + result.add( + ValidationCheck( + name="module_importable", + passed=True, + message=f"Module loaded from {path}", + severity="info", ) - return module + ) + return module else: # Module name - import directly module = importlib.import_module(str(module_path)) diff --git a/python/amplifier_core/validation/hook.py b/python/amplifier_core/validation/hook.py index e3e68ee..5f2b41e 100644 --- a/python/amplifier_core/validation/hook.py +++ b/python/amplifier_core/validation/hook.py @@ -8,7 +8,6 @@ import asyncio import importlib -import importlib.util import inspect from pathlib import Path from typing import Any @@ -16,6 +15,7 @@ from .base import ValidationCheck from .base import ValidationResult from .base import check_on_session_ready +from .base import import_module_from_path def _implements_hook_handler_interface(obj: Any) -> bool: @@ -86,11 +86,7 @@ def _check_importable( # File path - find the Python module if path.is_dir(): init_file = path / "__init__.py" - if init_file.exists(): - spec = importlib.util.spec_from_file_location( - path.name, init_file - ) - else: + if not init_file.exists(): result.add( ValidationCheck( name="module_importable", @@ -100,21 +96,16 @@ def _check_importable( ) ) return None - else: - spec = importlib.util.spec_from_file_location(path.stem, path) - - if spec and spec.loader: - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - result.add( - ValidationCheck( - name="module_importable", - passed=True, - message=f"Module loaded from {path}", - severity="info", - ) + module = import_module_from_path(path) + result.add( + ValidationCheck( + name="module_importable", + passed=True, + message=f"Module loaded from {path}", + severity="info", ) - return module + ) + return module else: # Module name - import directly module = importlib.import_module(str(module_path)) diff --git a/python/amplifier_core/validation/orchestrator.py b/python/amplifier_core/validation/orchestrator.py index 09a6e98..0a722f3 100644 --- a/python/amplifier_core/validation/orchestrator.py +++ b/python/amplifier_core/validation/orchestrator.py @@ -8,7 +8,6 @@ import asyncio import importlib -import importlib.util import inspect from pathlib import Path from typing import Any @@ -16,6 +15,7 @@ from .base import ValidationCheck from .base import ValidationResult from .base import check_on_session_ready +from .base import import_module_from_path def _implements_orchestrator_interface(obj: Any) -> bool: @@ -86,11 +86,7 @@ def _check_importable( # File path - find the Python module if path.is_dir(): init_file = path / "__init__.py" - if init_file.exists(): - spec = importlib.util.spec_from_file_location( - path.name, init_file - ) - else: + if not init_file.exists(): result.add( ValidationCheck( name="module_importable", @@ -100,21 +96,16 @@ def _check_importable( ) ) return None - else: - spec = importlib.util.spec_from_file_location(path.stem, path) - - if spec and spec.loader: - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - result.add( - ValidationCheck( - name="module_importable", - passed=True, - message=f"Module loaded from {path}", - severity="info", - ) + module = import_module_from_path(path) + result.add( + ValidationCheck( + name="module_importable", + passed=True, + message=f"Module loaded from {path}", + severity="info", ) - return module + ) + return module else: # Module name - import directly module = importlib.import_module(str(module_path)) diff --git a/python/amplifier_core/validation/provider.py b/python/amplifier_core/validation/provider.py index 3626f8a..8066f63 100644 --- a/python/amplifier_core/validation/provider.py +++ b/python/amplifier_core/validation/provider.py @@ -8,7 +8,6 @@ import asyncio import importlib -import importlib.util import inspect from pathlib import Path from typing import Any @@ -17,6 +16,7 @@ from .base import ValidationCheck from .base import ValidationResult from .base import check_on_session_ready +from .base import import_module_from_path def _implements_provider_interface(obj: Any) -> bool: @@ -96,11 +96,7 @@ def _check_importable( # File path - find the Python module if path.is_dir(): init_file = path / "__init__.py" - if init_file.exists(): - spec = importlib.util.spec_from_file_location( - path.name, init_file - ) - else: + if not init_file.exists(): result.add( ValidationCheck( name="module_importable", @@ -110,21 +106,16 @@ def _check_importable( ) ) return None - else: - spec = importlib.util.spec_from_file_location(path.stem, path) - - if spec and spec.loader: - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - result.add( - ValidationCheck( - name="module_importable", - passed=True, - message=f"Module loaded from {path}", - severity="info", - ) + module = import_module_from_path(path) + result.add( + ValidationCheck( + name="module_importable", + passed=True, + message=f"Module loaded from {path}", + severity="info", ) - return module + ) + return module else: # Module name - import directly module = importlib.import_module(str(module_path)) diff --git a/python/amplifier_core/validation/tool.py b/python/amplifier_core/validation/tool.py index e4bfe02..a10c7c4 100644 --- a/python/amplifier_core/validation/tool.py +++ b/python/amplifier_core/validation/tool.py @@ -8,7 +8,6 @@ import asyncio import importlib -import importlib.util import inspect from pathlib import Path from typing import Any @@ -16,6 +15,7 @@ from .base import ValidationCheck from .base import ValidationResult from .base import check_on_session_ready +from .base import import_module_from_path def _implements_tool_interface(obj: Any) -> bool: @@ -91,11 +91,7 @@ def _check_importable( # File path - find the Python module if path.is_dir(): init_file = path / "__init__.py" - if init_file.exists(): - spec = importlib.util.spec_from_file_location( - path.name, init_file - ) - else: + if not init_file.exists(): result.add( ValidationCheck( name="module_importable", @@ -105,21 +101,16 @@ def _check_importable( ) ) return None - else: - spec = importlib.util.spec_from_file_location(path.stem, path) - - if spec and spec.loader: - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - result.add( - ValidationCheck( - name="module_importable", - passed=True, - message=f"Module loaded from {path}", - severity="info", - ) + module = import_module_from_path(path) + result.add( + ValidationCheck( + name="module_importable", + passed=True, + message=f"Module loaded from {path}", + severity="info", ) - return module + ) + return module else: # Module name - import directly module = importlib.import_module(str(module_path)) diff --git a/tests/test_validation_canonical_module.py b/tests/test_validation_canonical_module.py new file mode 100644 index 0000000..96d6184 --- /dev/null +++ b/tests/test_validation_canonical_module.py @@ -0,0 +1,176 @@ +"""Regression tests for validation sharing the runtime module object.""" + +import importlib +import sys +from pathlib import Path + +import pytest + +from amplifier_core.loader import ModuleLoader +from amplifier_core.validation import ToolValidator + + +class _Source: + def __init__(self, root: Path) -> None: + self.root = root + + def resolve(self) -> Path: + return self.root + + +class _Resolver: + def __init__(self, root: Path) -> None: + self.source = _Source(root) + + def resolve( + self, + module_id: str, + source_hint: str | dict | None = None, + profile_hint: str | dict | None = None, + ) -> _Source: + return self.source + + +class _ResolverCoordinator: + def __init__(self, root: Path) -> None: + self.resolver = _Resolver(root) + + def get(self, mount_point: str) -> _Resolver: + assert mount_point == "module-source-resolver" + return self.resolver + + +class _RecordingCoordinator: + def __init__(self) -> None: + self.mounted: dict[str, object] = {} + + async def mount(self, mount_point: str, module: object, name: str | None = None) -> None: + assert mount_point == "tools" + assert name is not None + self.mounted[name] = module + + +def _write_tool_package(root: Path, package_name: str, token: str) -> Path: + package = root / package_name + package.mkdir(parents=True) + (package / "marker.py").write_text(f'TOKEN = "{token}"\n') + (package / "__init__.py").write_text( + f""" +from .marker import TOKEN + +IMPORT_COUNT = globals().get("IMPORT_COUNT", 0) + 1 +MODULE_TOKEN = object() +__amplifier_module_type__ = "tool" + +class StatefulTool: + name = "stateful" + description = TOKEN + module_token = MODULE_TOKEN + + async def execute(self, input): + return {{}} + +async def mount(coordinator, config): + await coordinator.mount("tools", StatefulTool(), name="stateful") +""" + ) + return package + + +@pytest.mark.asyncio +async def test_loader_validation_and_runtime_share_canonical_package( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """Validation must inspect the package the filesystem loader later mounts.""" + module_id = "tool-canonical" + package_name = "amplifier_module_tool_canonical" + package = _write_tool_package(tmp_path, package_name, "canonical") + seen: dict[str, object] = {} + original = ToolValidator._check_mount_exists + + def capture_validated_module(self, result, module): + seen["module"] = module + return original(self, result, module) + + monkeypatch.setattr(ToolValidator, "_check_mount_exists", capture_validated_module) + loader = ModuleLoader(coordinator=_ResolverCoordinator(tmp_path)) + monkeypatch.setattr(loader, "_load_entry_point", lambda _module_id: None) + + try: + mount = await loader.load(module_id) + canonical = sys.modules[package_name] + + assert seen["module"] is canonical + assert canonical.IMPORT_COUNT == 1 + + runtime_coordinator = _RecordingCoordinator() + await mount(runtime_coordinator) + assert runtime_coordinator.mounted["stateful"].module_token is canonical.MODULE_TOKEN + assert canonical.IMPORT_COUNT == 1 + finally: + loader.cleanup() + sys.modules.pop(package_name, None) + + +@pytest.mark.asyncio +async def test_path_validation_does_not_reuse_same_named_different_source( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """A canonical package from another path must not substitute for the target.""" + package_name = "amplifier_module_tool_collision" + first_root = tmp_path / "first" + second_root = tmp_path / "second" + _write_tool_package(first_root, package_name, "first") + second_package = _write_tool_package(second_root, package_name, "second") + seen: dict[str, object] = {} + original = ToolValidator._check_mount_exists + + def capture_validated_module(self, result, module): + seen["module"] = module + return original(self, result, module) + + monkeypatch.setattr(ToolValidator, "_check_mount_exists", capture_validated_module) + monkeypatch.syspath_prepend(str(first_root)) + + try: + canonical = importlib.import_module(package_name) + result = await ToolValidator().validate(second_package) + + assert result.passed + assert seen["module"] is not canonical + assert Path(seen["module"].__file__).resolve() == ( + second_package / "__init__.py" + ).resolve() + assert seen["module"].StatefulTool.description == "second" + finally: + sys.modules.pop(package_name, None) + + +@pytest.mark.asyncio +async def test_path_only_validation_imports_requested_package(tmp_path: Path) -> None: + """Standalone path validation remains valid when no package is loaded.""" + package_name = "amplifier_module_tool_standalone" + package = _write_tool_package(tmp_path, package_name, "standalone") + + try: + result = await ToolValidator().validate(package) + assert result.passed + assert package_name not in sys.modules + finally: + sys.modules.pop(package_name, None) + + +@pytest.mark.asyncio +async def test_path_validation_rejects_malformed_module(tmp_path: Path) -> None: + """Reusing canonical modules does not turn malformed source into a pass.""" + package = tmp_path / "amplifier_module_tool_malformed" + package.mkdir() + (package / "__init__.py").write_text("async def mount(:\n") + + result = await ToolValidator().validate(package) + + assert not result.passed + assert any( + check.name == "module_importable" and not check.passed + for check in result.checks + ) \ No newline at end of file From cbc20bcb64be05d5f08e3d2e3bf3b19955d16d0d Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Wed, 9 Sep 2026 08:37:08 -0700 Subject: [PATCH 07/27] fix(loader): isolate source-resolved module imports Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- python/amplifier_core/loader.py | 83 +++--- python/amplifier_core/validation/base.py | 111 +++++--- tests/test_validation_canonical_module.py | 295 ++++++++++++++++++++-- 3 files changed, 403 insertions(+), 86 deletions(-) diff --git a/python/amplifier_core/loader.py b/python/amplifier_core/loader.py index ef6f064..694bada 100644 --- a/python/amplifier_core/loader.py +++ b/python/amplifier_core/loader.py @@ -73,6 +73,7 @@ def __init__( search_paths: Optional list of filesystem paths for direct discovery """ self._loaded_modules: dict[str, Any] = {} + self._loaded_module_paths: dict[str, Path] = {} self._module_info: dict[str, ModuleInfo] = {} self._search_paths = search_paths self._coordinator = coordinator @@ -204,17 +205,43 @@ async def load( if module_id in self._loaded_modules: logger.debug(f"Module '{module_id}' already loaded, creating fresh closure") raw_fn = self._loaded_modules[module_id] + cached_source_path = self._loaded_module_paths.get(module_id) + source_resolver = None + if self._coordinator: + with contextlib.suppress(ValueError): + source_resolver = self._coordinator.get("module-source-resolver") + if cached_source_path is not None and self._coordinator: + if source_resolver is not None: + if hasattr(source_resolver, "async_resolve"): + source = await source_resolver.async_resolve( + module_id, + source_hint=source_hint, + profile_hint=source_hint, + ) + else: + source = source_resolver.resolve( + module_id, + source_hint=source_hint, + profile_hint=source_hint, + ) + requested_source_path = source.resolve().resolve() + if requested_source_path != cached_source_path: + raise ImportError( + f"Refusing to load '{module_id}' from {requested_source_path}: " + f"it is already loaded from {cached_source_path}" + ) - async def mount_with_config_cached( - coordinator: ModuleCoordinator, fn=raw_fn - ): - return await fn(coordinator, config or {}) + if source_resolver is None or cached_source_path is not None: + async def mount_with_config_cached( + coordinator: ModuleCoordinator, fn=raw_fn + ): + return await fn(coordinator, config or {}) - # B1: propagate __on_session_ready__ to fresh closure - if on_sr := getattr(raw_fn, "__on_session_ready__", None): - setattr(mount_with_config_cached, "__on_session_ready__", on_sr) + # B1: propagate __on_session_ready__ to fresh closure + if on_sr := getattr(raw_fn, "__on_session_ready__", None): + setattr(mount_with_config_cached, "__on_session_ready__", on_sr) - return mount_with_config_cached + return mount_with_config_cached try: # Resolve module source @@ -304,7 +331,9 @@ async def mount_with_config_cached( ) # Validate module before loading (Python modules only at this point) - await self._validate_module(module_id, module_path, config=config) + package_path = await self._validate_module( + module_id, module_path, config=config + ) except Exception as resolve_error: # Import here to avoid circular dependency @@ -320,26 +349,13 @@ async def mount_with_config_cached( return mount_fn raise resolve_error - # Try to load via entry point first - raw_fn = self._load_entry_point(module_id) - if raw_fn: - self._loaded_modules[module_id] = raw_fn - - async def mount_with_config_ep( - coordinator: ModuleCoordinator, fn=raw_fn - ): - return await fn(coordinator, config or {}) - - # B1: propagate __on_session_ready__ to closure - if on_sr := getattr(raw_fn, "__on_session_ready__", None): - setattr(mount_with_config_ep, "__on_session_ready__", on_sr) - - return mount_with_config_ep - - # Try filesystem loading - raw_fn = self._load_filesystem(module_id) + # Source resolution selected and validated this filesystem package. + # Do not let an installed entry point for the same module id mount a + # different source after validation. + raw_fn = self._load_filesystem(module_id, module_name=package_path.name) if raw_fn: self._loaded_modules[module_id] = raw_fn + self._loaded_module_paths[module_id] = module_path.resolve() async def mount_with_config_fs( coordinator: ModuleCoordinator, fn=raw_fn @@ -457,7 +473,9 @@ def _load_entry_point(self, module_id: str) -> Callable | None: return None - def _load_filesystem(self, module_id: str) -> Callable | None: + def _load_filesystem( + self, module_id: str, module_name: str | None = None + ) -> Callable | None: """Resolve module from filesystem and return the raw mount function. Returns the raw (un-configured) mount function so callers can cache it @@ -465,7 +483,7 @@ def _load_filesystem(self, module_id: str) -> Callable | None: """ try: # Try to import the module - module_name = f"amplifier_module_{module_id.replace('-', '_')}" + module_name = module_name or f"amplifier_module_{module_id.replace('-', '_')}" module = importlib.import_module(module_name) # Detect on_session_ready lifecycle hook if present. @@ -524,7 +542,7 @@ def _get_module_metadata( package_path = self._find_package_dir(module_id, module_path) if package_path: # Import the module temporarily - module_name = f"amplifier_module_{module_id.replace('-', '_')}" + module_name = package_path.name # Add to sys.path temporarily for import path_str = str(module_path) @@ -603,7 +621,7 @@ def _guess_from_naming( async def _validate_module( self, module_id: str, module_path: Path, config: dict[str, Any] | None = None - ) -> None: + ) -> Path: """ Validate a module before loading. @@ -643,7 +661,7 @@ async def _validate_module( logger.warning( f"Unknown module type '{module_type}' for '{module_id}', skipping validation" ) - return + return module_path # Find the actual Python package directory within the module root # Module structure: amplifier-module-xyz/ contains amplifier_module_xyz/ @@ -664,6 +682,7 @@ async def _validate_module( ) logger.info(f"[module:validated] {module_id} - {result.summary()}") + return package_path def _find_package_dir(self, module_id: str, module_path: Path) -> Path | None: """ diff --git a/python/amplifier_core/validation/base.py b/python/amplifier_core/validation/base.py index 4e84e24..2e72853 100644 --- a/python/amplifier_core/validation/base.py +++ b/python/amplifier_core/validation/base.py @@ -11,7 +11,8 @@ ``context/release-mandate.md`` for the v1.4.0 regression that motivated this. """ -import importlib.util +import _imp +import importlib import inspect import sys from dataclasses import dataclass @@ -67,45 +68,91 @@ def summary(self) -> str: def import_module_from_path(module_path: str | Path) -> ModuleType: - """Import a Python source path without duplicating its canonical module. + """Import a Python source path through Python's normal import machinery. - When runtime loading already imported the module from the same source, - validation must inspect that object. A same-named module from another - source is deliberately not reused: path validation must validate the - requested file rather than whichever package happens to be in - ``sys.modules``. + Validation must use the canonical module object that runtime loading will + mount. If another source already owns the same package name, fail closed + rather than replacing entries in ``sys.modules`` while another importer can + observe them. """ path = Path(module_path) source_path = path / "__init__.py" if path.is_dir() else path module_name = path.name if path.is_dir() else path.stem - existing = sys.modules.get(module_name) - if existing is not None: - existing_file = getattr(existing, "__file__", None) - if existing_file is not None and Path(existing_file).resolve() == source_path.resolve(): - return existing - - spec = importlib.util.spec_from_file_location(module_name, source_path) - if spec is None or spec.loader is None: - raise ImportError(f"Could not load spec for {path}") - - module = importlib.util.module_from_spec(spec) - module_prefix = f"{module_name}." - previous_modules = { - name: value - for name, value in sys.modules.items() - if name == module_name or name.startswith(module_prefix) - } - for name in previous_modules: - del sys.modules[name] - sys.modules[module_name] = module + import_root = path.parent if path.is_dir() else source_path.parent + parent = str(import_root) + _imp.acquire_lock() try: - spec.loader.exec_module(module) + existing = sys.modules.get(module_name) + if existing is not None: + existing_file = getattr(existing, "__file__", None) + if ( + existing_file is not None + and Path(existing_file).resolve() == source_path.resolve() + ): + return existing + raise ImportError( + f"Refusing to import '{module_name}' from {source_path}: " + f"it is already loaded from {existing_file}" + ) + + package_dir = source_path.parent.resolve() + for cached_name, cached_module in sys.modules.items(): + if not cached_name.startswith(f"{module_name}."): + continue + cached_file = getattr(cached_module, "__file__", None) + if cached_file is None or not Path(cached_file).resolve().is_relative_to( + package_dir + ): + raise ImportError( + f"Refusing to import '{module_name}' from {source_path}: " + f"cached submodule '{cached_name}' is from {cached_file}" + ) + + try: + original_path_index = sys.path.index(parent) + except ValueError: + original_path_index = None + next_path = ( + sys.path[original_path_index + 1] + if original_path_index is not None + and original_path_index + 1 < len(sys.path) + else None + ) + if original_path_index is None: + sys.path.insert(0, parent) + elif original_path_index != 0: + sys.path.pop(original_path_index) + sys.path.insert(0, parent) + try: + module = importlib.import_module(module_name) + finally: + current_path_index = next( + (index for index, value in enumerate(sys.path) if value is parent), + None, + ) + if original_path_index is None: + if current_path_index is not None: + sys.path.pop(current_path_index) + elif original_path_index != 0 and current_path_index is not None: + sys.path.pop(current_path_index) + next_path_index = next( + (index for index, value in enumerate(sys.path) if value is next_path), + None, + ) + if next_path_index is None: + sys.path.append(parent) + else: + sys.path.insert(next_path_index, parent) finally: - for name in list(sys.modules): - if name == module_name or name.startswith(module_prefix): - del sys.modules[name] - sys.modules.update(previous_modules) + _imp.release_lock() + + imported_file = getattr(module, "__file__", None) + if imported_file is None or Path(imported_file).resolve() != source_path.resolve(): + raise ImportError( + f"Refusing to validate '{module_name}' from {source_path}: " + f"Python imported {imported_file}" + ) return module diff --git a/tests/test_validation_canonical_module.py b/tests/test_validation_canonical_module.py index 96d6184..9f6604f 100644 --- a/tests/test_validation_canonical_module.py +++ b/tests/test_validation_canonical_module.py @@ -1,13 +1,18 @@ """Regression tests for validation sharing the runtime module object.""" import importlib +import importlib.metadata import sys +import threading +import types from pathlib import Path import pytest from amplifier_core.loader import ModuleLoader +from amplifier_core.loader import ModuleValidationError from amplifier_core.validation import ToolValidator +from amplifier_core.validation.base import import_module_from_path class _Source: @@ -77,14 +82,39 @@ async def mount(coordinator, config): return package +def _clear_package(package_name: str) -> None: + for name in list(sys.modules): + if name == package_name or name.startswith(f"{package_name}."): + sys.modules.pop(name, None) + + +def _write_conflicting_entry_point(root: Path, module_id: str) -> None: + (root / "installed_entry_point.py").write_text( + """ +async def mount(coordinator, config): + await coordinator.mount("tools", object(), name="installed-entry-point") +""" + ) + dist_info = root / "conflicting_entry_point-1.0.dist-info" + dist_info.mkdir() + (dist_info / "METADATA").write_text("Name: conflicting-entry-point\nVersion: 1.0\n") + (dist_info / "entry_points.txt").write_text( + f"[amplifier.modules]\n{module_id} = installed_entry_point:mount\n" + ) + + @pytest.mark.asyncio async def test_loader_validation_and_runtime_share_canonical_package( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: - """Validation must inspect the package the filesystem loader later mounts.""" + """A source-resolved module wins over an installed conflicting entry point.""" module_id = "tool-canonical" package_name = "amplifier_module_tool_canonical" - package = _write_tool_package(tmp_path, package_name, "canonical") + source_root = tmp_path / "source" + package = _write_tool_package(source_root, package_name, "canonical") + entry_point_root = tmp_path / "entry-point" + entry_point_root.mkdir() + _write_conflicting_entry_point(entry_point_root, module_id) seen: dict[str, object] = {} original = ToolValidator._check_mount_exists @@ -93,10 +123,14 @@ def capture_validated_module(self, result, module): return original(self, result, module) monkeypatch.setattr(ToolValidator, "_check_mount_exists", capture_validated_module) - loader = ModuleLoader(coordinator=_ResolverCoordinator(tmp_path)) - monkeypatch.setattr(loader, "_load_entry_point", lambda _module_id: None) + monkeypatch.syspath_prepend(str(entry_point_root)) + loader = ModuleLoader(coordinator=_ResolverCoordinator(source_root)) try: + assert any( + entry_point.name == module_id + for entry_point in importlib.metadata.entry_points(group="amplifier.modules") + ) mount = await loader.load(module_id) canonical = sys.modules[package_name] @@ -106,44 +140,168 @@ def capture_validated_module(self, result, module): runtime_coordinator = _RecordingCoordinator() await mount(runtime_coordinator) assert runtime_coordinator.mounted["stateful"].module_token is canonical.MODULE_TOKEN + assert "installed-entry-point" not in runtime_coordinator.mounted + assert "installed_entry_point" not in sys.modules assert canonical.IMPORT_COUNT == 1 finally: loader.cleanup() - sys.modules.pop(package_name, None) + _clear_package(package_name) @pytest.mark.asyncio -async def test_path_validation_does_not_reuse_same_named_different_source( +async def test_path_validation_rejects_same_named_different_source_without_cache_mutation( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: - """A canonical package from another path must not substitute for the target.""" + """A conflicting source fails without replacing the canonical package tree.""" package_name = "amplifier_module_tool_collision" first_root = tmp_path / "first" second_root = tmp_path / "second" _write_tool_package(first_root, package_name, "first") second_package = _write_tool_package(second_root, package_name, "second") - seen: dict[str, object] = {} - original = ToolValidator._check_mount_exists - def capture_validated_module(self, result, module): - seen["module"] = module - return original(self, result, module) - - monkeypatch.setattr(ToolValidator, "_check_mount_exists", capture_validated_module) monkeypatch.syspath_prepend(str(first_root)) try: canonical = importlib.import_module(package_name) + canonical_marker = sys.modules[f"{package_name}.marker"] result = await ToolValidator().validate(second_package) - assert result.passed - assert seen["module"] is not canonical - assert Path(seen["module"].__file__).resolve() == ( - second_package / "__init__.py" + assert not result.passed + assert any( + check.name == "module_importable" + and not check.passed + and "Refusing to import" in check.message + for check in result.checks + ) + assert sys.modules[package_name] is canonical + assert sys.modules[f"{package_name}.marker"] is canonical_marker + assert canonical.StatefulTool.description == "first" + finally: + _clear_package(package_name) + + +@pytest.mark.asyncio +async def test_path_validation_rejects_a_conflicting_cached_submodule( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """A dangling child cache cannot be mixed into a requested package source.""" + package_name = "amplifier_module_tool_submodule_collision" + first_root = tmp_path / "first" + second_root = tmp_path / "second" + _write_tool_package(first_root, package_name, "first") + second_package = _write_tool_package(second_root, package_name, "second") + monkeypatch.syspath_prepend(str(first_root)) + + try: + importlib.import_module(package_name) + cached_marker = sys.modules[f"{package_name}.marker"] + sys.modules.pop(package_name) + + result = await ToolValidator().validate(second_package) + + assert not result.passed + assert any( + check.name == "module_importable" + and not check.passed + and "cached submodule" in check.message + for check in result.checks + ) + assert package_name not in sys.modules + assert sys.modules[f"{package_name}.marker"] is cached_marker + assert cached_marker.TOKEN == "first" + finally: + _clear_package(package_name) + + +@pytest.mark.asyncio +async def test_source_resolved_cache_rejects_a_different_source( + tmp_path: Path, +) -> None: + """A cached source-resolved module cannot silently mount a later source.""" + module_id = "tool-cache-collision" + package_name = "amplifier_module_tool_cache_collision" + first_root = tmp_path / "first" + second_root = tmp_path / "second" + _write_tool_package(first_root, package_name, "first") + _write_tool_package(second_root, package_name, "second") + coordinator = _ResolverCoordinator(first_root) + loader = ModuleLoader(coordinator=coordinator) + + try: + await loader.load(module_id) + coordinator.resolver.source.root = second_root + + with pytest.raises(ImportError, match="already loaded from"): + await loader.load(module_id) + + assert Path(sys.modules[package_name].__file__).resolve() == ( + first_root / package_name / "__init__.py" ).resolve() - assert seen["module"].StatefulTool.description == "second" finally: - sys.modules.pop(package_name, None) + loader.cleanup() + _clear_package(package_name) + + +@pytest.mark.asyncio +async def test_direct_cache_does_not_override_a_later_source_resolution( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """A direct cache cannot silently satisfy a later conflicting source request.""" + module_id = "tool-direct-cache" + package_name = "amplifier_module_tool_direct_cache" + direct_root = tmp_path / "direct" + source_root = tmp_path / "source" + _write_tool_package(direct_root, package_name, "direct") + _write_tool_package(source_root, package_name, "source") + monkeypatch.syspath_prepend(str(direct_root)) + loader = ModuleLoader() + + try: + await loader.load(module_id) + loader._coordinator = _ResolverCoordinator(source_root) + + with pytest.raises(ModuleValidationError, match="Refusing to import"): + await loader.load(module_id) + + assert Path(sys.modules[package_name].__file__).resolve() == ( + direct_root / package_name / "__init__.py" + ).resolve() + finally: + loader.cleanup() + _clear_package(package_name) + + +@pytest.mark.asyncio +async def test_source_resolved_fallback_package_is_mounted_from_validated_source( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """A fallback package name remains the identity used for runtime mounting.""" + module_id = "tool-beta" + package_name = "amplifier_module_alpha" + package = _write_tool_package(tmp_path, package_name, "source") + installed_root = tmp_path / "installed" + installed_package = installed_root / "amplifier_module_tool_beta" + installed_package.mkdir(parents=True) + (installed_package / "__init__.py").write_text( + '__amplifier_module_type__ = "provider"\n' + ) + monkeypatch.syspath_prepend(str(installed_root)) + loader = ModuleLoader(coordinator=_ResolverCoordinator(tmp_path)) + + try: + mount = await loader.load(module_id) + + runtime_coordinator = _RecordingCoordinator() + await mount(runtime_coordinator) + assert runtime_coordinator.mounted["stateful"].description == "source" + assert "amplifier_module_tool_beta" not in sys.modules + assert Path(sys.modules[package_name].__file__).resolve() == ( + package / "__init__.py" + ).resolve() + finally: + loader.cleanup() + _clear_package(package_name) + _clear_package("amplifier_module_tool_beta") @pytest.mark.asyncio @@ -155,9 +313,102 @@ async def test_path_only_validation_imports_requested_package(tmp_path: Path) -> try: result = await ToolValidator().validate(package) assert result.passed - assert package_name not in sys.modules + assert Path(sys.modules[package_name].__file__).resolve() == ( + package / "__init__.py" + ).resolve() + assert sys.modules[f"{package_name}.marker"].TOKEN == "standalone" + finally: + _clear_package(package_name) + + +def test_standalone_path_validation_prioritizes_requested_source( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """The requested path wins even when it already trails another source path.""" + package_name = "amplifier_module_tool_path_priority" + installed_root = tmp_path / "installed" + requested_root = tmp_path / "requested" + _write_tool_package(installed_root, package_name, "installed") + requested_package = _write_tool_package(requested_root, package_name, "requested") + injected_path = str(tmp_path / "added-by-module") + init_file = requested_package / "__init__.py" + init_file.write_text( + f"import sys\nsys.path.insert(0, {injected_path!r})\n" + init_file.read_text() + ) + monkeypatch.syspath_prepend(str(installed_root)) + sys.path.append(str(requested_root)) + original_path = list(sys.path) + + try: + module = import_module_from_path(requested_package) + + assert module.StatefulTool.description == "requested" + assert sys.path == [injected_path, *original_path] + finally: + _clear_package(package_name) + sys.path.remove(injected_path) + sys.path.remove(str(requested_root)) + + +def test_standalone_path_validation_does_not_expose_temporary_import_path( + tmp_path: Path, +) -> None: + """Normal imports block until the standalone validation path is removed.""" + package_name = "amplifier_module_tool_blocking" + package = tmp_path / package_name + package.mkdir() + entered = threading.Event() + release = threading.Event() + synchronizer = types.ModuleType("_validation_import_synchronizer") + synchronizer.entered = entered + synchronizer.release = release + sys.modules[synchronizer.__name__] = synchronizer + (package / "__init__.py").write_text( + """ +from _validation_import_synchronizer import entered, release + +entered.set() +assert release.wait(timeout=5) +""" + ) + (tmp_path / "unrelated.py").write_text("VALUE = 'must-not-import'\n") + validation_error: list[Exception] = [] + unrelated_result: list[object] = [] + unrelated_done = threading.Event() + + def validate() -> None: + try: + import_module_from_path(package) + except Exception as error: + validation_error.append(error) + + def import_unrelated() -> None: + try: + unrelated_result.append(importlib.import_module("unrelated")) + except Exception as error: + unrelated_result.append(error) + finally: + unrelated_done.set() + + validation_thread = threading.Thread(target=validate) + validation_thread.start() + assert entered.wait(timeout=5) + unrelated_thread = threading.Thread(target=import_unrelated) + unrelated_thread.start() + assert not unrelated_done.wait(timeout=0.1) + release.set() + validation_thread.join(timeout=5) + unrelated_thread.join(timeout=5) + + try: + assert not validation_thread.is_alive() + assert not unrelated_thread.is_alive() + assert not validation_error + assert isinstance(unrelated_result[0], ModuleNotFoundError) finally: - sys.modules.pop(package_name, None) + _clear_package(package_name) + sys.modules.pop(synchronizer.__name__, None) + sys.modules.pop("unrelated", None) @pytest.mark.asyncio From 113e38dfc0f0fead724bf2d73fa83d1e10e15621 Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Fri, 11 Sep 2026 22:54:44 -0700 Subject: [PATCH 08/27] feat(core): preserve rich ToolResult content Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- Cargo.lock | 1 + bindings/python/src/lib.rs | 26 + bindings/python/src/module_resolver.rs | 31 +- bindings/python/src/wasm.rs | 29 +- .../python/tests/test_grpc_integration.py | 17 + crates/amplifier-core/Cargo.toml | 1 + .../amplifier-core/src/bridges/grpc_tool.rs | 8 + .../src/bridges/wasm_orchestrator.rs | 1 + .../src/generated/amplifier.module.rs | 5 + .../src/generated/conversions.rs | 95 ++ .../src/generated/equivalence_tests.rs | 2 + crates/amplifier-core/src/grpc_server.rs | 26 +- crates/amplifier-core/src/lib.rs | 2 +- crates/amplifier-core/src/models.rs | 307 +++++++ crates/amplifier-core/src/testing.rs | 4 + crates/amplifier-core/src/traits.rs | 2 +- crates/amplifier-core/tests/grpc_tool_e2e.rs | 37 + crates/amplifier-core/tests/wasm_e2e.rs | 14 + crates/amplifier-guest/src/lib.rs | 2 + crates/amplifier-guest/src/types.rs | 10 + proto/amplifier_module.proto | 3 + proto/amplifier_module_pb2.py | 322 +++---- proto/test_task03_module_specific.py | 5 +- python/amplifier_core/_engine.pyi | 4 + .../_grpc_gen/amplifier_module_pb2.py | 322 +++---- python/amplifier_core/loader_grpc.py | 42 +- python/amplifier_core/models.py | 57 ++ tests/fixtures/wasm/echo-tool.wasm | Bin 163753 -> 167110 bytes tests/fixtures/wasm/src/echo-tool/Cargo.lock | 861 ++++++++++++++++++ tests/fixtures/wasm/src/echo-tool/src/lib.rs | 11 + tests/test_generated_equivalence.py | 8 +- tests/test_pyo3_proto_bridge.py | 47 + tests/test_tool_result_autopop.py | 100 ++ tests/test_wasm_integration.py | 12 + uv.lock | 2 +- 35 files changed, 2039 insertions(+), 377 deletions(-) create mode 100644 tests/fixtures/wasm/src/echo-tool/Cargo.lock diff --git a/Cargo.lock b/Cargo.lock index b8ae9b1..88ed22b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,6 +42,7 @@ checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" name = "amplifier-core" version = "1.6.1" dependencies = [ + "base64", "chrono", "log", "prost", diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index d241903..0529e37 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -102,6 +102,30 @@ fn json_to_proto_chat_response(json_str: &str) -> PyResult> { Ok(proto.encode_to_vec()) } +/// Normalize untrusted ToolResult rich content through the Rust core. +#[pyfunction(name = "_normalize_tool_result_content")] +fn normalize_tool_result_content(content_json: &str) -> PyResult> { + let content = serde_json::from_str(content_json) + .map_err(|_| pyo3::exceptions::PyValueError::new_err("invalid tool result content"))?; + let normalized = amplifier_core::ToolResult::normalize_content(content) + .map_err(|error| pyo3::exceptions::PyValueError::new_err(error.to_string()))?; + normalized + .map(|value| { + serde_json::to_string(&value) + .map_err(|_| pyo3::exceptions::PyValueError::new_err("invalid tool result content")) + }) + .transpose() +} + +/// Return an image-safe ToolResult payload for hook events. +#[pyfunction(name = "_tool_result_safe_hook_presentation")] +fn tool_result_safe_hook_presentation(tool_result_json: &str) -> PyResult { + let result: amplifier_core::ToolResult = serde_json::from_str(tool_result_json) + .map_err(|_| pyo3::exceptions::PyValueError::new_err("invalid tool result content"))?; + serde_json::to_string(&result.safe_hook_presentation()) + .map_err(|_| pyo3::exceptions::PyValueError::new_err("invalid tool result content")) +} + // --------------------------------------------------------------------------- // Module registration // --------------------------------------------------------------------------- @@ -134,6 +158,8 @@ fn _engine(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(resolve_module, m)?)?; m.add_function(wrap_pyfunction!(proto_chat_request_to_json, m)?)?; m.add_function(wrap_pyfunction!(json_to_proto_chat_response, m)?)?; + m.add_function(wrap_pyfunction!(normalize_tool_result_content, m)?)?; + m.add_function(wrap_pyfunction!(tool_result_safe_hook_presentation, m)?)?; #[cfg(feature = "wasm")] { m.add_function(wrap_pyfunction!(load_wasm_from_path, m)?)?; diff --git a/bindings/python/src/module_resolver.rs b/bindings/python/src/module_resolver.rs index e86325c..ddbd537 100644 --- a/bindings/python/src/module_resolver.rs +++ b/bindings/python/src/module_resolver.rs @@ -15,7 +15,10 @@ use pyo3::types::PyDict; /// and artifact-specific keys ("artifact_path", "endpoint", "package_name"). #[pyfunction] pub(crate) fn resolve_module(py: Python<'_>, path: String) -> PyResult> { - let manifest = amplifier_core::module_resolver::resolve_module(std::path::Path::new(&path)) + let manifest = py + .detach(move || { + amplifier_core::module_resolver::resolve_module(std::path::Path::new(&path)) + }) .map_err(|e| PyErr::new::(format!("{e}")))?; let dict = PyDict::new(py); @@ -78,7 +81,10 @@ pub(crate) const RUST_TRANSPORT_ERROR_MSG: &str = #[cfg(feature = "wasm")] #[pyfunction] pub(crate) fn load_wasm_from_path(py: Python<'_>, path: String) -> PyResult> { - let manifest = amplifier_core::module_resolver::resolve_module(std::path::Path::new(&path)) + let manifest = py + .detach(move || { + amplifier_core::module_resolver::resolve_module(std::path::Path::new(&path)) + }) .map_err(|e| PyErr::new::(format!("{e}")))?; if manifest.transport == amplifier_core::transport::Transport::Rust { @@ -92,14 +98,19 @@ pub(crate) fn load_wasm_from_path(py: Python<'_>, path: String) -> PyResult(format!("WASM engine creation failed: {e}")) - })?; - - let coordinator = std::sync::Arc::new(amplifier_core::Coordinator::new_for_test()); - let loaded = - amplifier_core::module_resolver::load_module(&manifest, engine.inner(), Some(coordinator)) - .map_err(|e| PyErr::new::(format!("Module loading failed: {e}")))?; + let loaded = py + .detach(move || { + let engine = amplifier_core::wasm_engine::WasmEngine::new() + .map_err(|e| format!("WASM engine creation failed: {e}"))?; + let coordinator = std::sync::Arc::new(amplifier_core::Coordinator::new_for_test()); + amplifier_core::module_resolver::load_module( + &manifest, + engine.inner(), + Some(coordinator), + ) + .map_err(|e| format!("Module loading failed: {e}")) + }) + .map_err(PyErr::new::)?; let dict = PyDict::new(py); dict.set_item("status", "loaded")?; diff --git a/bindings/python/src/wasm.rs b/bindings/python/src/wasm.rs index 79cf647..2127c11 100644 --- a/bindings/python/src/wasm.rs +++ b/bindings/python/src/wasm.rs @@ -762,7 +762,10 @@ pub(crate) fn load_and_mount_wasm( coordinator: &PyCoordinator, path: String, ) -> PyResult> { - let manifest = amplifier_core::module_resolver::resolve_module(std::path::Path::new(&path)) + let manifest = py + .detach(move || { + amplifier_core::module_resolver::resolve_module(std::path::Path::new(&path)) + }) .map_err(|e| PyErr::new::(format!("{e}")))?; if manifest.transport != amplifier_core::transport::Transport::Wasm { @@ -772,21 +775,23 @@ pub(crate) fn load_and_mount_wasm( ))); } - let engine = amplifier_core::wasm_engine::WasmEngine::new().map_err(|e| { - PyErr::new::(format!("WASM engine creation failed: {e}")) - })?; - // Use the real coordinator's inner Arc for orchestrator modules let rust_coordinator = coordinator.inner.clone(); - let loaded = amplifier_core::module_resolver::load_module( - &manifest, - engine.inner(), - Some(rust_coordinator), - ) - .map_err(|e| PyErr::new::(format!("Module loading failed: {e}")))?; + let loaded = py + .detach(move || { + let engine = amplifier_core::wasm_engine::WasmEngine::new() + .map_err(|e| format!("WASM engine creation failed: {e}"))?; + amplifier_core::module_resolver::load_module( + &manifest, + engine.inner(), + Some(rust_coordinator), + ) + .map_err(|e| format!("Module loading failed: {e}")) + }) + .map_err(PyErr::new::)?; let dict = PyDict::new(py); - dict.set_item("module_type", loaded.variant_name())?; + dict.set_item("module_type", loaded.variant_name().to_ascii_lowercase())?; match loaded { amplifier_core::module_resolver::LoadedModule::Tool(tool) => { diff --git a/bindings/python/tests/test_grpc_integration.py b/bindings/python/tests/test_grpc_integration.py index ee784fb..79baefe 100644 --- a/bindings/python/tests/test_grpc_integration.py +++ b/bindings/python/tests/test_grpc_integration.py @@ -35,6 +35,16 @@ async def Execute(self, request, context): success=True, output=json.dumps(output).encode("utf-8"), content_type="application/json", + content_blocks=[ + amplifier_module_pb2.ContentBlock( + text_block=amplifier_module_pb2.TextBlock(text="echo result") + ), + amplifier_module_pb2.ContentBlock( + image_block=amplifier_module_pb2.ImageBlock( + media_type="image/png", data=b"\x00" + ) + ), + ], ) server = grpc_aio.server() @@ -76,6 +86,13 @@ async def test_grpc_tool_bridge_full_roundtrip(mock_tool_server): result = await bridge.execute(message="hello world") assert result["success"] is True assert result["output"]["echoed"] == "hello world" + assert result["content"] == [ + {"type": "text", "text": "echo result"}, + { + "type": "image", + "source": {"type": "base64", "media_type": "image/png", "data": "AA=="}, + }, + ] # Cleanup await bridge.cleanup() diff --git a/crates/amplifier-core/Cargo.toml b/crates/amplifier-core/Cargo.toml index fadbe06..db3add7 100644 --- a/crates/amplifier-core/Cargo.toml +++ b/crates/amplifier-core/Cargo.toml @@ -10,6 +10,7 @@ repository = "https://github.com/microsoft/amplifier-core" tokio = { version = "1", features = ["rt", "sync", "time", "macros", "net"] } serde = { version = "1", features = ["derive"] } serde_json = "1" +base64 = "0.22" thiserror = "2" uuid = { version = "1", features = ["v4"] } chrono = { version = "0.4", features = ["serde"] } diff --git a/crates/amplifier-core/src/bridges/grpc_tool.rs b/crates/amplifier-core/src/bridges/grpc_tool.rs index aa7eb83..6eb1a47 100644 --- a/crates/amplifier-core/src/bridges/grpc_tool.rs +++ b/crates/amplifier-core/src/bridges/grpc_tool.rs @@ -28,6 +28,7 @@ use tonic::transport::Channel; use crate::errors::ToolError; use crate::generated::amplifier_module; use crate::generated::amplifier_module::tool_service_client::ToolServiceClient; +use crate::generated::conversions::proto_tool_result_content_to_native; use crate::messages; use crate::models::ToolResult; use crate::traits::Tool; @@ -156,11 +157,18 @@ impl Tool for GrpcToolBridge { Value::String(resp.error), )])) }; + let content = + proto_tool_result_content_to_native(resp.content_blocks).map_err(|error| { + ToolError::Other { + message: error.to_string(), + } + })?; Ok(ToolResult { success: resp.success, output, error, + content, }) }) } diff --git a/crates/amplifier-core/src/bridges/wasm_orchestrator.rs b/crates/amplifier-core/src/bridges/wasm_orchestrator.rs index 73aefa3..dbf34dc 100644 --- a/crates/amplifier-core/src/bridges/wasm_orchestrator.rs +++ b/crates/amplifier-core/src/bridges/wasm_orchestrator.rs @@ -544,6 +544,7 @@ mod tests { success: true, output: Some(serde_json::json!({"prompt": "hello from test"})), error: None, + content: None, }], )); coordinator.mount_tool("echo-tool", echo); diff --git a/crates/amplifier-core/src/generated/amplifier.module.rs b/crates/amplifier-core/src/generated/amplifier.module.rs index d510a11..e0c8d33 100644 --- a/crates/amplifier-core/src/generated/amplifier.module.rs +++ b/crates/amplifier-core/src/generated/amplifier.module.rs @@ -33,6 +33,9 @@ pub struct ToolExecuteResponse { /// Error message if success is false. #[prost(string, tag = "4")] pub error: ::prost::alloc::string::String, + /// Canonical rich tool-result content. + #[prost(message, repeated, tag = "5")] + pub content_blocks: ::prost::alloc::vec::Vec, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct ModuleInfo { @@ -403,6 +406,8 @@ pub struct ToolResult { pub output_json: ::prost::alloc::string::String, #[prost(string, tag = "3")] pub error_json: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "4")] + pub content_blocks: ::prost::alloc::vec::Vec, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct HookResult { diff --git a/crates/amplifier-core/src/generated/conversions.rs b/crates/amplifier-core/src/generated/conversions.rs index 39b06cf..bc1bcdd 100644 --- a/crates/amplifier-core/src/generated/conversions.rs +++ b/crates/amplifier-core/src/generated/conversions.rs @@ -38,6 +38,7 @@ impl From for super::amplifier_module::ToolResult { .error .map(|e| to_json_or_warn(&e, "ToolResult error")) .unwrap_or_default(), + content_blocks: native_tool_result_content_to_proto(native.content), } } } @@ -66,6 +67,12 @@ impl From for crate::models::ToolResult { }) .ok() }, + content: proto_tool_result_content_to_native(proto.content_blocks).unwrap_or_else( + |error| { + log::warn!("Invalid tool result content from proto: {error}"); + None + }, + ), } } } @@ -257,6 +264,75 @@ fn proto_visibility_to_native(vis: i32) -> Option { // ContentBlock conversion helpers (private) // --------------------------------------------------------------------------- +/// Convert canonical ToolResult blocks without using the generic Message image +/// representation, which redundantly stores base64 data in `source_json`. +pub(crate) fn native_tool_result_content_to_proto( + content: Option>, +) -> Vec { + use super::amplifier_module::content_block::Block; + use crate::messages::ContentBlock; + use base64::Engine; + + content + .unwrap_or_default() + .into_iter() + .filter_map(|block| match block { + ContentBlock::Text { text, .. } => Some(super::amplifier_module::ContentBlock { + block: Some(Block::TextBlock(super::amplifier_module::TextBlock { + text, + })), + visibility: 0, + }), + ContentBlock::Image { source, .. } => { + let media_type = source.get("media_type")?.as_str()?.to_string(); + let data = source.get("data")?.as_str()?; + let data = base64::engine::general_purpose::STANDARD + .decode(data) + .ok()?; + Some(super::amplifier_module::ContentBlock { + block: Some(Block::ImageBlock(super::amplifier_module::ImageBlock { + media_type, + data, + source_json: String::new(), + })), + visibility: 0, + }) + } + _ => None, + }) + .collect() +} + +/// Decode ToolResult-only protobuf blocks and validate them through the +/// canonical ToolResult ingress path. +pub(crate) fn proto_tool_result_content_to_native( + content: Vec, +) -> Result>, crate::models::ToolResultContentError> { + use super::amplifier_module::content_block::Block; + use base64::Engine; + + let raw_content = content + .into_iter() + .map(|block| match block.block { + Some(Block::TextBlock(text)) => Ok(serde_json::json!({ + "type": "text", + "text": text.text, + })), + Some(Block::ImageBlock(image)) => Ok(serde_json::json!({ + "type": "image", + "source": { + "type": "base64", + "media_type": image.media_type, + "data": base64::engine::general_purpose::STANDARD.encode(image.data), + } + })), + _ => Err(crate::models::ToolResultContentError::InvalidContent), + }) + .collect::, _>>()?; + + crate::models::ToolResult::normalize_content(Some(raw_content)) +} + fn native_content_block_to_proto( block: crate::messages::ContentBlock, ) -> super::amplifier_module::ContentBlock { @@ -999,8 +1075,26 @@ mod tests { success: true, output: Some(serde_json::json!({"key": "value"})), error: None, + content: crate::models::ToolResult::normalize_content(Some(vec![ + serde_json::json!({"type": "text", "text": "details"}), + serde_json::json!({ + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "AA==" + } + }), + ])) + .unwrap(), }; let proto: super::super::amplifier_module::ToolResult = original.clone().into(); + let image = match proto.content_blocks[1].block.as_ref().unwrap() { + super::super::amplifier_module::content_block::Block::ImageBlock(image) => image, + _ => panic!("expected image block"), + }; + assert_eq!(image.data, vec![0]); + assert!(image.source_json.is_empty()); let restored: crate::models::ToolResult = proto.into(); assert_eq!(original, restored); } @@ -1014,6 +1108,7 @@ mod tests { "message".to_string(), serde_json::json!("something failed"), )])), + content: None, }; let proto: super::super::amplifier_module::ToolResult = original.clone().into(); let restored: crate::models::ToolResult = proto.into(); diff --git a/crates/amplifier-core/src/generated/equivalence_tests.rs b/crates/amplifier-core/src/generated/equivalence_tests.rs index 87b4775..79de978 100644 --- a/crates/amplifier-core/src/generated/equivalence_tests.rs +++ b/crates/amplifier-core/src/generated/equivalence_tests.rs @@ -42,6 +42,7 @@ mod tests { success: true, output_json: r#"{"value": 42}"#.into(), error_json: String::new(), + content_blocks: vec![], }; assert!(result.success); assert_eq!(result.output_json, r#"{"value": 42}"#); @@ -52,6 +53,7 @@ mod tests { success: false, output_json: String::new(), error_json: r#"{"code":"NOT_FOUND"}"#.into(), + content_blocks: vec![], }; assert!(!err_result.success); assert!(err_result.output_json.is_empty()); diff --git a/crates/amplifier-core/src/grpc_server.rs b/crates/amplifier-core/src/grpc_server.rs index 54d7573..615365f 100644 --- a/crates/amplifier-core/src/grpc_server.rs +++ b/crates/amplifier-core/src/grpc_server.rs @@ -226,31 +226,7 @@ impl KernelService for KernelServiceImpl { // Execute the tool match tool.execute(input).await { - Ok(result) => { - let output_json = result - .output - .map(|v| { - serde_json::to_string(&v).unwrap_or_else(|e| { - log::warn!("Failed to serialize tool result output to JSON: {e}"); - String::new() - }) - }) - .unwrap_or_default(); - let error_json = result - .error - .map(|e| { - serde_json::to_string(&e).unwrap_or_else(|ser_err| { - log::warn!("Failed to serialize tool result error to JSON: {ser_err}"); - String::new() - }) - }) - .unwrap_or_default(); - Ok(Response::new(amplifier_module::ToolResult { - success: result.success, - output_json, - error_json, - })) - } + Ok(result) => Ok(Response::new(result.into())), Err(e) => { log::error!("Tool execution failed for {tool_name}: {e}"); Err(Status::internal("Tool execution failed")) diff --git a/crates/amplifier-core/src/lib.rs b/crates/amplifier-core/src/lib.rs index 3390987..fd693cb 100644 --- a/crates/amplifier-core/src/lib.rs +++ b/crates/amplifier-core/src/lib.rs @@ -52,7 +52,7 @@ pub use errors::{AmplifierError, ContextError, HookError, ProviderError, Session pub use models::{ ApprovalDefault, ApprovalRequest, ApprovalResponse, ConfigField, ConfigFieldType, ContextInjectionRole, HookAction, HookResult, ModelInfo, ModuleInfo, ModuleType, ProviderInfo, - SessionState, SessionStatus, ToolResult, UserMessageLevel, + SessionState, SessionStatus, ToolResult, ToolResultContentError, UserMessageLevel, }; // Chat protocol models diff --git a/crates/amplifier-core/src/models.rs b/crates/amplifier-core/src/models.rs index 266c62e..eabd725 100644 --- a/crates/amplifier-core/src/models.rs +++ b/crates/amplifier-core/src/models.rs @@ -6,9 +6,12 @@ use std::collections::HashMap; +use base64::Engine; use serde::{Deserialize, Serialize}; use serde_json::Value; +use crate::messages::ContentBlock; + // --------------------------------------------------------------------------- // Enums // --------------------------------------------------------------------------- @@ -234,6 +237,14 @@ pub struct ToolResult { /// Error details if failed. #[serde(default)] pub error: Option>, + + /// Canonical rich tool-result content. + #[serde( + default, + deserialize_with = "deserialize_tool_result_content", + skip_serializing_if = "Option::is_none" + )] + pub content: Option>, } fn default_true() -> bool { @@ -246,10 +257,43 @@ impl Default for ToolResult { success: true, output: None, error: None, + content: None, } } } +/// A fixed, input-safe error returned when rich tool-result content is invalid. +#[derive(Debug, thiserror::Error, PartialEq, Eq)] +pub enum ToolResultContentError { + #[error("invalid tool result content")] + InvalidContent, + #[error("invalid tool result image source")] + InvalidImageSource, + #[error("invalid tool result image media type")] + InvalidImageMediaType, + #[error("invalid tool result image data")] + InvalidImageData, +} + +/// Deserialize ToolResult content through its strict canonicalization boundary. +/// +/// Decode as a JSON value first so type errors cannot include untrusted image data +/// in the error returned to callers. +fn deserialize_tool_result_content<'de, D>( + deserializer: D, +) -> Result>, D::Error> +where + D: serde::Deserializer<'de>, +{ + let raw_content = Option::::deserialize(deserializer)?; + let raw_content = match raw_content { + None => None, + Some(Value::Array(content)) => Some(content), + Some(_) => return Err(serde::de::Error::custom("invalid tool result content")), + }; + ToolResult::normalize_content(raw_content).map_err(serde::de::Error::custom) +} + impl ToolResult { /// Create a new ToolResult with auto-populate behavior. /// When success=false and output is None, auto-populates output @@ -263,11 +307,132 @@ impl ToolResult { success, output, error, + content: None, }; result.auto_populate_output(); result } + /// Strictly validate and canonicalize untrusted rich tool-result content. + /// + /// Only text and base64 image blocks are accepted. Unknown block/source + /// fields are deliberately dropped at this boundary. + pub fn normalize_content( + content: Option>, + ) -> Result>, ToolResultContentError> { + let Some(content) = content else { + return Ok(None); + }; + if content.is_empty() { + return Ok(None); + } + + let mut normalized = Vec::with_capacity(content.len()); + for block in content { + let object = block + .as_object() + .ok_or(ToolResultContentError::InvalidContent)?; + let block_type = object + .get("type") + .and_then(Value::as_str) + .ok_or(ToolResultContentError::InvalidContent)?; + + match block_type { + "text" => { + let text = object + .get("text") + .and_then(Value::as_str) + .ok_or(ToolResultContentError::InvalidContent)?; + normalized.push(ContentBlock::Text { + text: text.to_string(), + visibility: None, + extensions: HashMap::new(), + }); + } + "image" => { + let source = object + .get("source") + .and_then(Value::as_object) + .ok_or(ToolResultContentError::InvalidImageSource)?; + if source.get("type").and_then(Value::as_str) != Some("base64") { + return Err(ToolResultContentError::InvalidImageSource); + } + let media_type = source + .get("media_type") + .and_then(Value::as_str) + .ok_or(ToolResultContentError::InvalidImageMediaType)?; + if !valid_image_media_type(media_type) { + return Err(ToolResultContentError::InvalidImageMediaType); + } + let data = source + .get("data") + .and_then(Value::as_str) + .ok_or(ToolResultContentError::InvalidImageData)?; + if data.is_empty() { + return Err(ToolResultContentError::InvalidImageData); + } + let decoded = base64::engine::general_purpose::STANDARD + .decode(data) + .map_err(|_| ToolResultContentError::InvalidImageData)?; + if base64::engine::general_purpose::STANDARD.encode(decoded) != data { + return Err(ToolResultContentError::InvalidImageData); + } + normalized.push(ContentBlock::Image { + source: HashMap::from([ + ("type".to_string(), Value::String("base64".to_string())), + ( + "media_type".to_string(), + Value::String(media_type.to_string()), + ), + ("data".to_string(), Value::String(data.to_string())), + ]), + visibility: None, + extensions: HashMap::new(), + }); + } + _ => return Err(ToolResultContentError::InvalidContent), + } + } + + Ok(Some(normalized)) + } + + /// Produce an image-safe representation for hook event payloads. + pub fn safe_hook_presentation(&self) -> Value { + let mut result = serde_json::Map::new(); + result.insert("success".to_string(), Value::Bool(self.success)); + result.insert( + "output".to_string(), + self.output.clone().unwrap_or(Value::Null), + ); + result.insert( + "error".to_string(), + self.error + .as_ref() + .and_then(|error| serde_json::to_value(error).ok()) + .unwrap_or(Value::Null), + ); + + if let Some(content) = &self.content { + let safe_content: Vec = content + .iter() + .filter_map(|block| match block { + ContentBlock::Text { text, .. } => { + Some(serde_json::json!({"type": "text", "text": text})) + } + ContentBlock::Image { .. } => Some(serde_json::json!({ + "type": "text", + "text": "[Image omitted from tool event; original retained for model request if unmodified.]" + })), + _ => None, + }) + .collect(); + result.insert("content".to_string(), Value::Array(safe_content)); + } + + Value::Object(result) + } + /// Auto-populate output from error message when tools forget to set it. fn auto_populate_output(&mut self) { if !self.success && self.output.is_none() { @@ -280,6 +445,24 @@ impl ToolResult { } } +fn valid_image_media_type(media_type: &str) -> bool { + let Some(subtype) = media_type.strip_prefix("image/") else { + return false; + }; + let mut chars = subtype.chars(); + let Some(first) = chars.next() else { + return false; + }; + first.is_ascii_alphanumeric() + && chars.all(|character| { + character.is_ascii_alphanumeric() + || matches!( + character, + '!' | '#' | '$' | '&' | '^' | '_' | '.' | '+' | '-' + ) + }) +} + /// Model metadata for provider models. /// /// Describes capabilities and defaults for a specific model available from a provider. @@ -655,6 +838,7 @@ mod tests { assert!(result.success); assert!(result.output.is_none()); assert!(result.error.is_none()); + assert!(result.content.is_none()); } #[test] @@ -663,6 +847,7 @@ mod tests { success: true, output: Some(json!({"key": "value"})), error: None, + content: None, }; let json_str = serde_json::to_string(&result).unwrap(); let deserialized: ToolResult = serde_json::from_str(&json_str).unwrap(); @@ -678,6 +863,7 @@ mod tests { "message".to_string(), json!("command failed"), )])), + content: None, }; assert!(!result.success); assert_eq!( @@ -686,6 +872,127 @@ mod tests { ); } + #[test] + fn tool_result_content_normalization_is_canonical_and_strict() { + let content = ToolResult::normalize_content(Some(vec![ + json!({"type": "text", "text": "details", "visibility": "user", "extra": true}), + json!({ + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "AA==", + "ignored": true + }, + "ignored": true + }), + ])) + .unwrap() + .unwrap(); + assert_eq!( + serde_json::to_value(&content).unwrap(), + json!([ + {"type": "text", "text": "details"}, + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "AA==" + } + } + ]) + ); + assert_eq!( + ToolResult::normalize_content(Some(vec![json!({ + "type": "image", + "source": {"type": "base64", "media_type": "image/png", "data": "AA"} + })])), + Err(ToolResultContentError::InvalidImageData) + ); + } + + #[test] + fn tool_result_deserialization_normalizes_content_without_input_echo() { + let valid: ToolResult = serde_json::from_value(json!({ + "content": [ + {"type": "text", "text": "details", "visibility": "user"}, + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "AA==", + "ignored": true + }, + "ignored": true + } + ] + })) + .unwrap(); + assert_eq!( + serde_json::to_value(valid.content).unwrap(), + json!([ + {"type": "text", "text": "details"}, + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "AA==" + } + } + ]) + ); + + let empty: ToolResult = serde_json::from_value(json!({"content": []})).unwrap(); + assert!(empty.content.is_none()); + + for content in [ + json!([{"type": "thinking", "thinking": "private-bytes"}]), + json!("private-bytes"), + ] { + let error = serde_json::from_value::(json!({"content": content})) + .unwrap_err() + .to_string(); + assert!(error.contains("invalid tool result content")); + assert!(!error.contains("private-bytes")); + } + } + + #[test] + fn tool_result_safe_hook_presentation_omits_image_bytes() { + let result = ToolResult { + success: true, + output: Some(json!({"status": "ok"})), + error: None, + content: ToolResult::normalize_content(Some(vec![ + json!({"type": "text", "text": "details"}), + json!({ + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "AA==" + } + }), + ])) + .unwrap(), + }; + let safe = result.safe_hook_presentation(); + assert_eq!( + safe["content"], + json!([ + {"type": "text", "text": "details"}, + { + "type": "text", + "text": "[Image omitted from tool event; original retained for model request if unmodified.]" + } + ]) + ); + assert!(!safe.to_string().contains("AA==")); + } + // --- ToolResult auto-populate tests --- #[test] diff --git a/crates/amplifier-core/src/testing.rs b/crates/amplifier-core/src/testing.rs index 0a49a9e..5253122 100644 --- a/crates/amplifier-core/src/testing.rs +++ b/crates/amplifier-core/src/testing.rs @@ -82,6 +82,7 @@ impl Tool for EchoTool { success: true, output: Some(input), error: None, + content: None, }) }) } @@ -169,6 +170,7 @@ impl Tool for FakeTool { success: true, output: Some(input), error: None, + content: None, } } else { responses.remove(0) @@ -571,11 +573,13 @@ mod tests { success: true, output: Some(serde_json::json!("first")), error: None, + content: None, }, crate::models::ToolResult { success: false, output: None, error: None, + content: None, }, ], ); diff --git a/crates/amplifier-core/src/traits.rs b/crates/amplifier-core/src/traits.rs index d2df760..443937d 100644 --- a/crates/amplifier-core/src/traits.rs +++ b/crates/amplifier-core/src/traits.rs @@ -91,7 +91,7 @@ use crate::models::{ /// input: Value, /// ) -> Pin> + Send + '_>> { /// Box::pin(async move { -/// Ok(ToolResult { success: true, output: Some(input), error: None }) +/// Ok(ToolResult { success: true, output: Some(input), error: None, content: None }) /// }) /// } /// } diff --git a/crates/amplifier-core/tests/grpc_tool_e2e.rs b/crates/amplifier-core/tests/grpc_tool_e2e.rs index ffba314..5a2e411 100644 --- a/crates/amplifier-core/tests/grpc_tool_e2e.rs +++ b/crates/amplifier-core/tests/grpc_tool_e2e.rs @@ -38,6 +38,26 @@ impl ToolService for EchoToolService { output: req.input, content_type: req.content_type, error: String::new(), + content_blocks: vec![ + amplifier_module::ContentBlock { + block: Some(amplifier_module::content_block::Block::TextBlock( + amplifier_module::TextBlock { + text: "echo result".to_string(), + }, + )), + visibility: 0, + }, + amplifier_module::ContentBlock { + block: Some(amplifier_module::content_block::Block::ImageBlock( + amplifier_module::ImageBlock { + media_type: "image/png".to_string(), + data: vec![0], + source_json: String::new(), + }, + )), + visibility: 0, + }, + ], }, )) } @@ -88,6 +108,7 @@ impl ToolService for InvalidParamsToolService { output: b"{}".to_vec(), content_type: "application/json".to_string(), error: String::new(), + content_blocks: vec![], }, )) } @@ -123,6 +144,7 @@ impl ToolService for BinaryOutputToolService { output: vec![0xFF, 0xFE, 0x00, 0x01], // not valid JSON content_type: "application/json".to_string(), error: String::new(), + content_blocks: vec![], }, )) } @@ -158,6 +180,7 @@ impl ToolService for NonJsonContentTypeToolService { output: b"{\"result\": 42}".to_vec(), content_type: "text/plain".to_string(), error: String::new(), + content_blocks: vec![], }, )) } @@ -180,6 +203,20 @@ async fn grpc_tool_round_trip() -> Result<(), Box, #[serde(skip_serializing_if = "Option::is_none")] pub error: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub content: Option>, } fn default_true() -> bool { @@ -32,6 +34,7 @@ impl Default for ToolResult { success: true, output: None, error: None, + content: None, } } } @@ -253,6 +256,7 @@ mod tests { assert!(result.success); assert!(result.output.is_none()); assert!(result.error.is_none()); + assert!(result.content.is_none()); } #[test] @@ -265,6 +269,7 @@ mod tests { m.insert("code".to_string(), json!(404)); m }), + content: Some(vec![json!({"type": "text", "text": "details"})]), }; let json_str = serde_json::to_string(&result).unwrap(); let deserialized: ToolResult = serde_json::from_str(&json_str).unwrap(); @@ -272,6 +277,10 @@ mod tests { assert_eq!(deserialized.output, Some(json!("hello"))); let err = deserialized.error.as_ref().unwrap(); assert_eq!(err.get("code"), Some(&json!(404))); + assert_eq!( + deserialized.content, + Some(vec![json!({"type": "text", "text": "details"})]) + ); } // --- HookAction tests --- @@ -551,6 +560,7 @@ mod tests { m.insert("code".to_string(), json!(404)); m }), + content: Some(vec![json!({"type": "text", "text": "details"})]), }; let json_str = serde_json::to_string(&original).unwrap(); let deserialized: ToolResult = serde_json::from_str(&json_str).unwrap(); diff --git a/proto/amplifier_module.proto b/proto/amplifier_module.proto index e12f988..6aa157b 100644 --- a/proto/amplifier_module.proto +++ b/proto/amplifier_module.proto @@ -36,6 +36,8 @@ message ToolExecuteResponse { string content_type = 3; // Error message if success is false. string error = 4; + // Canonical rich tool-result content. + repeated ContentBlock content_blocks = 5; } // --------------------------------------------------------------------------- @@ -387,6 +389,7 @@ message ToolResult { bool success = 1; string output_json = 2; string error_json = 3; + repeated ContentBlock content_blocks = 4; } message HookResult { diff --git a/proto/amplifier_module_pb2.py b/proto/amplifier_module_pb2.py index 6f4d751..d6c0e3e 100644 --- a/proto/amplifier_module_pb2.py +++ b/proto/amplifier_module_pb2.py @@ -24,7 +24,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x61mplifier_module.proto\x12\x10\x61mplifier.module\"\x07\n\x05\x45mpty\"F\n\x08ToolSpec\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"9\n\x12ToolExecuteRequest\x12\r\n\x05input\x18\x01 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x02 \x01(\t\"[\n\x13ToolExecuteResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0e\n\x06output\x18\x02 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x03 \x01(\t\x12\r\n\x05\x65rror\x18\x04 \x01(\t\"\xd6\x01\n\nModuleInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x31\n\x0bmodule_type\x18\x04 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\x12\x13\n\x0bmount_point\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x06 \x01(\t\x12\x1a\n\x12\x63onfig_schema_json\x18\x07 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x08 \x03(\t\x12\x0e\n\x06\x61uthor\x18\t \x01(\t\"\x8c\x01\n\x0cMountRequest\x12:\n\x06\x63onfig\x18\x01 \x03(\x0b\x32*.amplifier.module.MountRequest.ConfigEntry\x12\x11\n\tmodule_id\x18\x02 \x01(\t\x1a-\n\x0b\x43onfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"_\n\rMountResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12.\n\x06status\x18\x03 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\"V\n\x13HealthCheckResponse\x12.\n\x06status\x18\x01 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xca\x02\n\x0b\x43onfigField\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x35\n\nfield_type\x18\x03 \x01(\x0e\x32!.amplifier.module.ConfigFieldType\x12\x0e\n\x06prompt\x18\x04 \x01(\t\x12\x0f\n\x07\x65nv_var\x18\x05 \x01(\t\x12\x0f\n\x07\x63hoices\x18\x06 \x03(\t\x12\x10\n\x08required\x18\x07 \x01(\x08\x12\x15\n\rdefault_value\x18\x08 \x01(\t\x12>\n\tshow_when\x18\t \x03(\x0b\x32+.amplifier.module.ConfigField.ShowWhenEntry\x12\x16\n\x0erequires_model\x18\n \x01(\x08\x1a/\n\rShowWhenEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xbc\x01\n\rProviderError\x12\x37\n\nerror_type\x18\x01 \x01(\x0e\x32#.amplifier.module.ProviderErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x15\n\rprovider_name\x18\x03 \x01(\t\x12\r\n\x05model\x18\x04 \x01(\t\x12\x13\n\x0bstatus_code\x18\x05 \x01(\x05\x12\x11\n\tretryable\x18\x06 \x01(\x08\x12\x13\n\x0bretry_after\x18\x07 \x01(\x01\"\x97\x01\n\tToolError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.ToolErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\ttool_name\x18\x03 \x01(\t\x12\x0e\n\x06stdout\x18\x04 \x01(\t\x12\x0e\n\x06stderr\x18\x05 \x01(\t\x12\x11\n\texit_code\x18\x06 \x01(\x05\"d\n\tHookError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.HookErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\thook_name\x18\x03 \x01(\t\"\xef\x01\n\x0e\x41mplifierError\x12\x39\n\x0eprovider_error\x18\x01 \x01(\x0b\x32\x1f.amplifier.module.ProviderErrorH\x00\x12\x31\n\ntool_error\x18\x02 \x01(\x0b\x32\x1b.amplifier.module.ToolErrorH\x00\x12\x31\n\nhook_error\x18\x03 \x01(\x0b\x32\x1b.amplifier.module.HookErrorH\x00\x12\x17\n\rgeneric_error\x18\x04 \x01(\tH\x00\x12\x1a\n\x10validation_error\x18\x05 \x01(\tH\x00\x42\x07\n\x05\x65rror\"\x19\n\tTextBlock\x12\x0c\n\x04text\x18\x01 \x01(\t\"E\n\rThinkingBlock\x12\x10\n\x08thinking\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\t\"%\n\x15RedactedThinkingBlock\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\t\"=\n\rToolCallBlock\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x12\n\ninput_json\x18\x03 \x01(\t\"<\n\x0fToolResultBlock\x12\x14\n\x0ctool_call_id\x18\x01 \x01(\t\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\"C\n\nImageBlock\x12\x12\n\nmedia_type\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x13\n\x0bsource_json\x18\x03 \x01(\t\"2\n\x0eReasoningBlock\x12\x0f\n\x07\x63ontent\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x03(\t\"\xf1\x03\n\x0c\x43ontentBlock\x12\x31\n\ntext_block\x18\x01 \x01(\x0b\x32\x1b.amplifier.module.TextBlockH\x00\x12\x39\n\x0ethinking_block\x18\x02 \x01(\x0b\x32\x1f.amplifier.module.ThinkingBlockH\x00\x12J\n\x17redacted_thinking_block\x18\x03 \x01(\x0b\x32\'.amplifier.module.RedactedThinkingBlockH\x00\x12:\n\x0ftool_call_block\x18\x04 \x01(\x0b\x32\x1f.amplifier.module.ToolCallBlockH\x00\x12>\n\x11tool_result_block\x18\x05 \x01(\x0b\x32!.amplifier.module.ToolResultBlockH\x00\x12\x33\n\x0bimage_block\x18\x06 \x01(\x0b\x32\x1c.amplifier.module.ImageBlockH\x00\x12;\n\x0freasoning_block\x18\x07 \x01(\x0b\x32 .amplifier.module.ReasoningBlockH\x00\x12\x30\n\nvisibility\x18\x08 \x01(\x0e\x32\x1c.amplifier.module.VisibilityB\x07\n\x05\x62lock\"B\n\x10\x43ontentBlockList\x12.\n\x06\x62locks\x18\x01 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xca\x01\n\x07Message\x12$\n\x04role\x18\x01 \x01(\x0e\x32\x16.amplifier.module.Role\x12\x16\n\x0ctext_content\x18\x02 \x01(\tH\x00\x12;\n\rblock_content\x18\x03 \x01(\x0b\x32\".amplifier.module.ContentBlockListH\x00\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x14\n\x0ctool_call_id\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\tB\t\n\x07\x63ontent\"C\n\x0fToolCallMessage\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x16\n\x0e\x61rguments_json\x18\x03 \x01(\t\"K\n\rToolSpecProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"7\n\x10JsonSchemaFormat\x12\x13\n\x0bschema_json\x18\x01 \x01(\t\x12\x0e\n\x06strict\x18\x02 \x01(\x08\"u\n\x0eResponseFormat\x12\x0e\n\x04text\x18\x01 \x01(\x08H\x00\x12\x0e\n\x04json\x18\x02 \x01(\x08H\x00\x12\x39\n\x0bjson_schema\x18\x03 \x01(\x0b\x32\".amplifier.module.JsonSchemaFormatH\x00\x42\x08\n\x06\x66ormat\"\xf7\x01\n\x05Usage\x12\x15\n\rprompt_tokens\x18\x01 \x01(\x05\x12\x19\n\x11\x63ompletion_tokens\x18\x02 \x01(\x05\x12\x14\n\x0ctotal_tokens\x18\x03 \x01(\x05\x12\x1d\n\x10reasoning_tokens\x18\x04 \x01(\x05H\x00\x88\x01\x01\x12\x1e\n\x11\x63\x61\x63he_read_tokens\x18\x05 \x01(\x05H\x01\x88\x01\x01\x12\"\n\x15\x63\x61\x63he_creation_tokens\x18\x06 \x01(\x05H\x02\x88\x01\x01\x42\x13\n\x11_reasoning_tokensB\x14\n\x12_cache_read_tokensB\x18\n\x16_cache_creation_tokens\"@\n\x0b\x44\x65gradation\x12\x11\n\trequested\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tual\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x81\x03\n\x0b\x43hatRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\x12.\n\x05tools\x18\x02 \x03(\x0b\x32\x1f.amplifier.module.ToolSpecProto\x12\x39\n\x0fresponse_format\x18\x03 \x01(\x0b\x32 .amplifier.module.ResponseFormat\x12\x13\n\x0btemperature\x18\x04 \x01(\x01\x12\r\n\x05top_p\x18\x05 \x01(\x01\x12\x19\n\x11max_output_tokens\x18\x06 \x01(\x05\x12\x17\n\x0f\x63onversation_id\x18\x07 \x01(\t\x12\x0e\n\x06stream\x18\x08 \x01(\x08\x12\x15\n\rmetadata_json\x18\t \x01(\t\x12\r\n\x05model\x18\n \x01(\t\x12\x13\n\x0btool_choice\x18\x0b \x01(\t\x12\x0c\n\x04stop\x18\x0c \x03(\t\x12\x18\n\x10reasoning_effort\x18\r \x01(\t\x12\x0f\n\x07timeout\x18\x0e \x01(\x01\"\x98\x02\n\x0c\x43hatResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x35\n\ntool_calls\x18\x02 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\x12&\n\x05usage\x18\x03 \x01(\x0b\x32\x17.amplifier.module.Usage\x12\x32\n\x0b\x64\x65gradation\x18\x04 \x01(\x0b\x32\x1d.amplifier.module.Degradation\x12\x15\n\rfinish_reason\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x07 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"F\n\nToolResult\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\x12\x12\n\nerror_json\x18\x03 \x01(\t\"\xa7\x04\n\nHookResult\x12,\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x1c.amplifier.module.HookAction\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x19\n\x11\x63ontext_injection\x18\x04 \x01(\t\x12\x46\n\x16\x63ontext_injection_role\x18\x05 \x01(\x0e\x32&.amplifier.module.ContextInjectionRole\x12\x11\n\tephemeral\x18\x06 \x01(\x08\x12\x17\n\x0f\x61pproval_prompt\x18\x07 \x01(\t\x12\x18\n\x10\x61pproval_options\x18\x08 \x03(\t\x12\x1d\n\x10\x61pproval_timeout\x18\t \x01(\x01H\x00\x88\x01\x01\x12;\n\x10\x61pproval_default\x18\n \x01(\x0e\x32!.amplifier.module.ApprovalDefault\x12\x17\n\x0fsuppress_output\x18\x0b \x01(\x08\x12\x14\n\x0cuser_message\x18\x0c \x01(\t\x12>\n\x12user_message_level\x18\r \x01(\x0e\x32\".amplifier.module.UserMessageLevel\x12\x1b\n\x13user_message_source\x18\x0e \x01(\t\x12\"\n\x1a\x61ppend_to_last_tool_result\x18\x0f \x01(\x08\x42\x13\n\x11_approval_timeout\"\x8d\x01\n\tModelInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x16\n\x0e\x63ontext_window\x18\x03 \x01(\x05\x12\x19\n\x11max_output_tokens\x18\x04 \x01(\x05\x12\x14\n\x0c\x63\x61pabilities\x18\x05 \x03(\t\x12\x15\n\rdefaults_json\x18\x06 \x01(\t\"\xb0\x01\n\x0cProviderInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x1b\n\x13\x63redential_env_vars\x18\x03 \x03(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x04 \x03(\t\x12\x15\n\rdefaults_json\x18\x05 \x01(\t\x12\x34\n\rconfig_fields\x18\x06 \x03(\x0b\x32\x1d.amplifier.module.ConfigField\"\x80\x01\n\x0f\x41pprovalRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x14\n\x0c\x64\x65tails_json\x18\x03 \x01(\t\x12\x12\n\nrisk_level\x18\x04 \x01(\t\x12\x14\n\x07timeout\x18\x05 \x01(\x01H\x00\x88\x01\x01\x42\n\n\x08_timeout\"F\n\x10\x41pprovalResponse\x12\x10\n\x08\x61pproved\x18\x01 \x01(\x08\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x10\n\x08remember\x18\x03 \x01(\x08\"A\n\x12ListModelsResponse\x12+\n\x06models\x18\x01 \x03(\x0b\x32\x1b.amplifier.module.ModelInfo\"O\n\x16ParseToolCallsResponse\x12\x35\n\ntool_calls\x18\x01 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\"@\n\x1aOrchestratorExecuteRequest\x12\x0e\n\x06prompt\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\">\n\x1bOrchestratorExecuteResponse\x12\x10\n\x08response\x18\x01 \x01(\t\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"?\n\x11\x41\x64\x64MessageRequest\x12*\n\x07message\x18\x01 \x01(\x0b\x32\x19.amplifier.module.Message\"B\n\x13GetMessagesResponse\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"J\n\x1bGetMessagesForRequestParams\x12\x14\n\x0ctoken_budget\x18\x01 \x01(\x05\x12\x15\n\rprovider_name\x18\x02 \x01(\t\"A\n\x12SetMessagesRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"5\n\x11HookHandleRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\".\n\x17GetSubscriptionsRequest\x12\x13\n\x0b\x63onfig_json\x18\x01 \x01(\t\"V\n\x18GetSubscriptionsResponse\x12:\n\rsubscriptions\x18\x01 \x03(\x0b\x32#.amplifier.module.EventSubscription\"B\n\x11\x45ventSubscription\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x10\n\x08priority\x18\x02 \x01(\x05\x12\x0c\n\x04name\x18\x03 \x01(\t\"d\n\x1b\x43ompleteWithProviderRequest\x12\x15\n\rprovider_name\x18\x01 \x01(\t\x12.\n\x07request\x18\x02 \x01(\x0b\x32\x1d.amplifier.module.ChatRequest\";\n\x12\x45xecuteToolRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x12\n\ninput_json\x18\x02 \x01(\t\"3\n\x0f\x45mitHookRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\"V\n\x19\x45mitHookAndCollectRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x17\n\x0ftimeout_seconds\x18\x03 \x01(\x01\"4\n\x1a\x45mitHookAndCollectResponse\x12\x16\n\x0eresponses_json\x18\x01 \x03(\t\"(\n\x12GetMessagesRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\"Y\n\x17KernelAddMessageRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12*\n\x07message\x18\x02 \x01(\x0b\x32\x19.amplifier.module.Message\"a\n\x17GetMountedModuleRequest\x12\x13\n\x0bmodule_name\x18\x01 \x01(\t\x12\x31\n\x0bmodule_type\x18\x02 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\"U\n\x18GetMountedModuleResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12*\n\x04info\x18\x02 \x01(\x0b\x32\x1c.amplifier.module.ModuleInfo\"=\n\x19RegisterCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\nvalue_json\x18\x02 \x01(\t\"$\n\x14GetCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\":\n\x15GetCapabilityResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12\x12\n\nvalue_json\x18\x02 \x01(\t*\xbc\x01\n\nModuleType\x12\x1b\n\x17MODULE_TYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14MODULE_TYPE_PROVIDER\x10\x01\x12\x14\n\x10MODULE_TYPE_TOOL\x10\x02\x12\x14\n\x10MODULE_TYPE_HOOK\x10\x03\x12\x16\n\x12MODULE_TYPE_MEMORY\x10\x04\x12\x19\n\x15MODULE_TYPE_GUARDRAIL\x10\x05\x12\x18\n\x14MODULE_TYPE_APPROVAL\x10\x06*\x82\x01\n\x0cHealthStatus\x12\x1d\n\x19HEALTH_STATUS_UNSPECIFIED\x10\x00\x12\x19\n\x15HEALTH_STATUS_SERVING\x10\x01\x12\x1d\n\x19HEALTH_STATUS_NOT_SERVING\x10\x02\x12\x19\n\x15HEALTH_STATUS_UNKNOWN\x10\x03*\xad\x01\n\x0f\x43onfigFieldType\x12!\n\x1d\x43ONFIG_FIELD_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_STRING\x10\x01\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_NUMBER\x10\x02\x12\x1d\n\x19\x43ONFIG_FIELD_TYPE_BOOLEAN\x10\x03\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_SECRET\x10\x04*\xd8\x02\n\x11ProviderErrorType\x12#\n\x1fPROVIDER_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18PROVIDER_ERROR_TYPE_AUTH\x10\x01\x12\"\n\x1ePROVIDER_ERROR_TYPE_RATE_LIMIT\x10\x02\x12&\n\"PROVIDER_ERROR_TYPE_CONTEXT_LENGTH\x10\x03\x12\'\n#PROVIDER_ERROR_TYPE_INVALID_REQUEST\x10\x04\x12&\n\"PROVIDER_ERROR_TYPE_CONTENT_FILTER\x10\x05\x12#\n\x1fPROVIDER_ERROR_TYPE_UNAVAILABLE\x10\x06\x12\x1f\n\x1bPROVIDER_ERROR_TYPE_TIMEOUT\x10\x07\x12\x1d\n\x19PROVIDER_ERROR_TYPE_OTHER\x10\x08*\x8c\x01\n\rToolErrorType\x12\x1f\n\x1bTOOL_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19TOOL_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aTOOL_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17TOOL_ERROR_TYPE_TIMEOUT\x10\x03*\x8c\x01\n\rHookErrorType\x12\x1f\n\x1bHOOK_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19HOOK_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aHOOK_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17HOOK_ERROR_TYPE_TIMEOUT\x10\x03*\x86\x01\n\x04Role\x12\x14\n\x10ROLE_UNSPECIFIED\x10\x00\x12\x0f\n\x0bROLE_SYSTEM\x10\x01\x12\r\n\tROLE_USER\x10\x02\x12\x12\n\x0eROLE_ASSISTANT\x10\x03\x12\r\n\tROLE_TOOL\x10\x04\x12\x11\n\rROLE_FUNCTION\x10\x05\x12\x12\n\x0eROLE_DEVELOPER\x10\x06*o\n\nVisibility\x12\x1a\n\x16VISIBILITY_UNSPECIFIED\x10\x00\x12\x12\n\x0eVISIBILITY_ALL\x10\x01\x12\x17\n\x13VISIBILITY_LLM_ONLY\x10\x02\x12\x18\n\x14VISIBILITY_USER_ONLY\x10\x03*\xab\x01\n\nHookAction\x12\x1b\n\x17HOOK_ACTION_UNSPECIFIED\x10\x00\x12\x18\n\x14HOOK_ACTION_CONTINUE\x10\x01\x12\x16\n\x12HOOK_ACTION_MODIFY\x10\x02\x12\x14\n\x10HOOK_ACTION_DENY\x10\x03\x12\x1e\n\x1aHOOK_ACTION_INJECT_CONTEXT\x10\x04\x12\x18\n\x14HOOK_ACTION_ASK_USER\x10\x05*\xa8\x01\n\x14\x43ontextInjectionRole\x12&\n\"CONTEXT_INJECTION_ROLE_UNSPECIFIED\x10\x00\x12!\n\x1d\x43ONTEXT_INJECTION_ROLE_SYSTEM\x10\x01\x12\x1f\n\x1b\x43ONTEXT_INJECTION_ROLE_USER\x10\x02\x12$\n CONTEXT_INJECTION_ROLE_ASSISTANT\x10\x03*l\n\x0f\x41pprovalDefault\x12 \n\x1c\x41PPROVAL_DEFAULT_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x41PPROVAL_DEFAULT_APPROVE\x10\x01\x12\x19\n\x15\x41PPROVAL_DEFAULT_DENY\x10\x02*\x91\x01\n\x10UserMessageLevel\x12\"\n\x1eUSER_MESSAGE_LEVEL_UNSPECIFIED\x10\x00\x12\x1b\n\x17USER_MESSAGE_LEVEL_INFO\x10\x01\x12\x1e\n\x1aUSER_MESSAGE_LEVEL_WARNING\x10\x02\x12\x1c\n\x18USER_MESSAGE_LEVEL_ERROR\x10\x03\x32\xa5\x01\n\x0bToolService\x12>\n\x07GetSpec\x12\x17.amplifier.module.Empty\x1a\x1a.amplifier.module.ToolSpec\x12V\n\x07\x45xecute\x12$.amplifier.module.ToolExecuteRequest\x1a%.amplifier.module.ToolExecuteResponse2\x9f\x03\n\x0fProviderService\x12\x42\n\x07GetInfo\x12\x17.amplifier.module.Empty\x1a\x1e.amplifier.module.ProviderInfo\x12K\n\nListModels\x12\x17.amplifier.module.Empty\x1a$.amplifier.module.ListModelsResponse\x12I\n\x08\x43omplete\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse\x12T\n\x11\x43ompleteStreaming\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Z\n\x0eParseToolCalls\x12\x1e.amplifier.module.ChatResponse\x1a(.amplifier.module.ParseToolCallsResponse2}\n\x13OrchestratorService\x12\x66\n\x07\x45xecute\x12,.amplifier.module.OrchestratorExecuteRequest\x1a-.amplifier.module.OrchestratorExecuteResponse2\xa3\x03\n\x0e\x43ontextService\x12J\n\nAddMessage\x12#.amplifier.module.AddMessageRequest\x1a\x17.amplifier.module.Empty\x12M\n\x0bGetMessages\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.GetMessagesResponse\x12m\n\x15GetMessagesForRequest\x12-.amplifier.module.GetMessagesForRequestParams\x1a%.amplifier.module.GetMessagesResponse\x12L\n\x0bSetMessages\x12$.amplifier.module.SetMessagesRequest\x1a\x17.amplifier.module.Empty\x12\x39\n\x05\x43lear\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty2\xc5\x01\n\x0bHookService\x12K\n\x06Handle\x12#.amplifier.module.HookHandleRequest\x1a\x1c.amplifier.module.HookResult\x12i\n\x10GetSubscriptions\x12).amplifier.module.GetSubscriptionsRequest\x1a*.amplifier.module.GetSubscriptionsResponse2k\n\x0f\x41pprovalService\x12X\n\x0fRequestApproval\x12!.amplifier.module.ApprovalRequest\x1a\".amplifier.module.ApprovalResponse2\xd0\x07\n\rKernelService\x12\x65\n\x14\x43ompleteWithProvider\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse\x12p\n\x1d\x43ompleteWithProviderStreaming\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Q\n\x0b\x45xecuteTool\x12$.amplifier.module.ExecuteToolRequest\x1a\x1c.amplifier.module.ToolResult\x12K\n\x08\x45mitHook\x12!.amplifier.module.EmitHookRequest\x1a\x1c.amplifier.module.HookResult\x12o\n\x12\x45mitHookAndCollect\x12+.amplifier.module.EmitHookAndCollectRequest\x1a,.amplifier.module.EmitHookAndCollectResponse\x12Z\n\x0bGetMessages\x12$.amplifier.module.GetMessagesRequest\x1a%.amplifier.module.GetMessagesResponse\x12P\n\nAddMessage\x12).amplifier.module.KernelAddMessageRequest\x1a\x17.amplifier.module.Empty\x12i\n\x10GetMountedModule\x12).amplifier.module.GetMountedModuleRequest\x1a*.amplifier.module.GetMountedModuleResponse\x12Z\n\x12RegisterCapability\x12+.amplifier.module.RegisterCapabilityRequest\x1a\x17.amplifier.module.Empty\x12`\n\rGetCapability\x12&.amplifier.module.GetCapabilityRequest\x1a\'.amplifier.module.GetCapabilityResponse2\xaf\x02\n\x0fModuleLifecycle\x12H\n\x05Mount\x12\x1e.amplifier.module.MountRequest\x1a\x1f.amplifier.module.MountResponse\x12;\n\x07\x43leanup\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty\x12M\n\x0bHealthCheck\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.HealthCheckResponse\x12\x46\n\rGetModuleInfo\x12\x17.amplifier.module.Empty\x1a\x1c.amplifier.module.ModuleInfob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x61mplifier_module.proto\x12\x10\x61mplifier.module\"\x07\n\x05\x45mpty\"F\n\x08ToolSpec\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"9\n\x12ToolExecuteRequest\x12\r\n\x05input\x18\x01 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x02 \x01(\t\"\x93\x01\n\x13ToolExecuteResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0e\n\x06output\x18\x02 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x03 \x01(\t\x12\r\n\x05\x65rror\x18\x04 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x05 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xd6\x01\n\nModuleInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x31\n\x0bmodule_type\x18\x04 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\x12\x13\n\x0bmount_point\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x06 \x01(\t\x12\x1a\n\x12\x63onfig_schema_json\x18\x07 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x08 \x03(\t\x12\x0e\n\x06\x61uthor\x18\t \x01(\t\"\x8c\x01\n\x0cMountRequest\x12:\n\x06\x63onfig\x18\x01 \x03(\x0b\x32*.amplifier.module.MountRequest.ConfigEntry\x12\x11\n\tmodule_id\x18\x02 \x01(\t\x1a-\n\x0b\x43onfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"_\n\rMountResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12.\n\x06status\x18\x03 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\"V\n\x13HealthCheckResponse\x12.\n\x06status\x18\x01 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xca\x02\n\x0b\x43onfigField\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x35\n\nfield_type\x18\x03 \x01(\x0e\x32!.amplifier.module.ConfigFieldType\x12\x0e\n\x06prompt\x18\x04 \x01(\t\x12\x0f\n\x07\x65nv_var\x18\x05 \x01(\t\x12\x0f\n\x07\x63hoices\x18\x06 \x03(\t\x12\x10\n\x08required\x18\x07 \x01(\x08\x12\x15\n\rdefault_value\x18\x08 \x01(\t\x12>\n\tshow_when\x18\t \x03(\x0b\x32+.amplifier.module.ConfigField.ShowWhenEntry\x12\x16\n\x0erequires_model\x18\n \x01(\x08\x1a/\n\rShowWhenEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xbc\x01\n\rProviderError\x12\x37\n\nerror_type\x18\x01 \x01(\x0e\x32#.amplifier.module.ProviderErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x15\n\rprovider_name\x18\x03 \x01(\t\x12\r\n\x05model\x18\x04 \x01(\t\x12\x13\n\x0bstatus_code\x18\x05 \x01(\x05\x12\x11\n\tretryable\x18\x06 \x01(\x08\x12\x13\n\x0bretry_after\x18\x07 \x01(\x01\"\x97\x01\n\tToolError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.ToolErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\ttool_name\x18\x03 \x01(\t\x12\x0e\n\x06stdout\x18\x04 \x01(\t\x12\x0e\n\x06stderr\x18\x05 \x01(\t\x12\x11\n\texit_code\x18\x06 \x01(\x05\"d\n\tHookError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.HookErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\thook_name\x18\x03 \x01(\t\"\xef\x01\n\x0e\x41mplifierError\x12\x39\n\x0eprovider_error\x18\x01 \x01(\x0b\x32\x1f.amplifier.module.ProviderErrorH\x00\x12\x31\n\ntool_error\x18\x02 \x01(\x0b\x32\x1b.amplifier.module.ToolErrorH\x00\x12\x31\n\nhook_error\x18\x03 \x01(\x0b\x32\x1b.amplifier.module.HookErrorH\x00\x12\x17\n\rgeneric_error\x18\x04 \x01(\tH\x00\x12\x1a\n\x10validation_error\x18\x05 \x01(\tH\x00\x42\x07\n\x05\x65rror\"\x19\n\tTextBlock\x12\x0c\n\x04text\x18\x01 \x01(\t\"E\n\rThinkingBlock\x12\x10\n\x08thinking\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\t\"%\n\x15RedactedThinkingBlock\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\t\"=\n\rToolCallBlock\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x12\n\ninput_json\x18\x03 \x01(\t\"<\n\x0fToolResultBlock\x12\x14\n\x0ctool_call_id\x18\x01 \x01(\t\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\"C\n\nImageBlock\x12\x12\n\nmedia_type\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x13\n\x0bsource_json\x18\x03 \x01(\t\"2\n\x0eReasoningBlock\x12\x0f\n\x07\x63ontent\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x03(\t\"\xf1\x03\n\x0c\x43ontentBlock\x12\x31\n\ntext_block\x18\x01 \x01(\x0b\x32\x1b.amplifier.module.TextBlockH\x00\x12\x39\n\x0ethinking_block\x18\x02 \x01(\x0b\x32\x1f.amplifier.module.ThinkingBlockH\x00\x12J\n\x17redacted_thinking_block\x18\x03 \x01(\x0b\x32\'.amplifier.module.RedactedThinkingBlockH\x00\x12:\n\x0ftool_call_block\x18\x04 \x01(\x0b\x32\x1f.amplifier.module.ToolCallBlockH\x00\x12>\n\x11tool_result_block\x18\x05 \x01(\x0b\x32!.amplifier.module.ToolResultBlockH\x00\x12\x33\n\x0bimage_block\x18\x06 \x01(\x0b\x32\x1c.amplifier.module.ImageBlockH\x00\x12;\n\x0freasoning_block\x18\x07 \x01(\x0b\x32 .amplifier.module.ReasoningBlockH\x00\x12\x30\n\nvisibility\x18\x08 \x01(\x0e\x32\x1c.amplifier.module.VisibilityB\x07\n\x05\x62lock\"B\n\x10\x43ontentBlockList\x12.\n\x06\x62locks\x18\x01 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xca\x01\n\x07Message\x12$\n\x04role\x18\x01 \x01(\x0e\x32\x16.amplifier.module.Role\x12\x16\n\x0ctext_content\x18\x02 \x01(\tH\x00\x12;\n\rblock_content\x18\x03 \x01(\x0b\x32\".amplifier.module.ContentBlockListH\x00\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x14\n\x0ctool_call_id\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\tB\t\n\x07\x63ontent\"C\n\x0fToolCallMessage\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x16\n\x0e\x61rguments_json\x18\x03 \x01(\t\"K\n\rToolSpecProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"7\n\x10JsonSchemaFormat\x12\x13\n\x0bschema_json\x18\x01 \x01(\t\x12\x0e\n\x06strict\x18\x02 \x01(\x08\"u\n\x0eResponseFormat\x12\x0e\n\x04text\x18\x01 \x01(\x08H\x00\x12\x0e\n\x04json\x18\x02 \x01(\x08H\x00\x12\x39\n\x0bjson_schema\x18\x03 \x01(\x0b\x32\".amplifier.module.JsonSchemaFormatH\x00\x42\x08\n\x06\x66ormat\"\xf7\x01\n\x05Usage\x12\x15\n\rprompt_tokens\x18\x01 \x01(\x05\x12\x19\n\x11\x63ompletion_tokens\x18\x02 \x01(\x05\x12\x14\n\x0ctotal_tokens\x18\x03 \x01(\x05\x12\x1d\n\x10reasoning_tokens\x18\x04 \x01(\x05H\x00\x88\x01\x01\x12\x1e\n\x11\x63\x61\x63he_read_tokens\x18\x05 \x01(\x05H\x01\x88\x01\x01\x12\"\n\x15\x63\x61\x63he_creation_tokens\x18\x06 \x01(\x05H\x02\x88\x01\x01\x42\x13\n\x11_reasoning_tokensB\x14\n\x12_cache_read_tokensB\x18\n\x16_cache_creation_tokens\"@\n\x0b\x44\x65gradation\x12\x11\n\trequested\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tual\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x81\x03\n\x0b\x43hatRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\x12.\n\x05tools\x18\x02 \x03(\x0b\x32\x1f.amplifier.module.ToolSpecProto\x12\x39\n\x0fresponse_format\x18\x03 \x01(\x0b\x32 .amplifier.module.ResponseFormat\x12\x13\n\x0btemperature\x18\x04 \x01(\x01\x12\r\n\x05top_p\x18\x05 \x01(\x01\x12\x19\n\x11max_output_tokens\x18\x06 \x01(\x05\x12\x17\n\x0f\x63onversation_id\x18\x07 \x01(\t\x12\x0e\n\x06stream\x18\x08 \x01(\x08\x12\x15\n\rmetadata_json\x18\t \x01(\t\x12\r\n\x05model\x18\n \x01(\t\x12\x13\n\x0btool_choice\x18\x0b \x01(\t\x12\x0c\n\x04stop\x18\x0c \x03(\t\x12\x18\n\x10reasoning_effort\x18\r \x01(\t\x12\x0f\n\x07timeout\x18\x0e \x01(\x01\"\x98\x02\n\x0c\x43hatResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x35\n\ntool_calls\x18\x02 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\x12&\n\x05usage\x18\x03 \x01(\x0b\x32\x17.amplifier.module.Usage\x12\x32\n\x0b\x64\x65gradation\x18\x04 \x01(\x0b\x32\x1d.amplifier.module.Degradation\x12\x15\n\rfinish_reason\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x07 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"~\n\nToolResult\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\x12\x12\n\nerror_json\x18\x03 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x04 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xa7\x04\n\nHookResult\x12,\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x1c.amplifier.module.HookAction\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x19\n\x11\x63ontext_injection\x18\x04 \x01(\t\x12\x46\n\x16\x63ontext_injection_role\x18\x05 \x01(\x0e\x32&.amplifier.module.ContextInjectionRole\x12\x11\n\tephemeral\x18\x06 \x01(\x08\x12\x17\n\x0f\x61pproval_prompt\x18\x07 \x01(\t\x12\x18\n\x10\x61pproval_options\x18\x08 \x03(\t\x12\x1d\n\x10\x61pproval_timeout\x18\t \x01(\x01H\x00\x88\x01\x01\x12;\n\x10\x61pproval_default\x18\n \x01(\x0e\x32!.amplifier.module.ApprovalDefault\x12\x17\n\x0fsuppress_output\x18\x0b \x01(\x08\x12\x14\n\x0cuser_message\x18\x0c \x01(\t\x12>\n\x12user_message_level\x18\r \x01(\x0e\x32\".amplifier.module.UserMessageLevel\x12\x1b\n\x13user_message_source\x18\x0e \x01(\t\x12\"\n\x1a\x61ppend_to_last_tool_result\x18\x0f \x01(\x08\x42\x13\n\x11_approval_timeout\"\x8d\x01\n\tModelInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x16\n\x0e\x63ontext_window\x18\x03 \x01(\x05\x12\x19\n\x11max_output_tokens\x18\x04 \x01(\x05\x12\x14\n\x0c\x63\x61pabilities\x18\x05 \x03(\t\x12\x15\n\rdefaults_json\x18\x06 \x01(\t\"\xb0\x01\n\x0cProviderInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x1b\n\x13\x63redential_env_vars\x18\x03 \x03(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x04 \x03(\t\x12\x15\n\rdefaults_json\x18\x05 \x01(\t\x12\x34\n\rconfig_fields\x18\x06 \x03(\x0b\x32\x1d.amplifier.module.ConfigField\"\x80\x01\n\x0f\x41pprovalRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x14\n\x0c\x64\x65tails_json\x18\x03 \x01(\t\x12\x12\n\nrisk_level\x18\x04 \x01(\t\x12\x14\n\x07timeout\x18\x05 \x01(\x01H\x00\x88\x01\x01\x42\n\n\x08_timeout\"F\n\x10\x41pprovalResponse\x12\x10\n\x08\x61pproved\x18\x01 \x01(\x08\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x10\n\x08remember\x18\x03 \x01(\x08\"A\n\x12ListModelsResponse\x12+\n\x06models\x18\x01 \x03(\x0b\x32\x1b.amplifier.module.ModelInfo\"O\n\x16ParseToolCallsResponse\x12\x35\n\ntool_calls\x18\x01 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\"@\n\x1aOrchestratorExecuteRequest\x12\x0e\n\x06prompt\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\">\n\x1bOrchestratorExecuteResponse\x12\x10\n\x08response\x18\x01 \x01(\t\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"?\n\x11\x41\x64\x64MessageRequest\x12*\n\x07message\x18\x01 \x01(\x0b\x32\x19.amplifier.module.Message\"B\n\x13GetMessagesResponse\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"J\n\x1bGetMessagesForRequestParams\x12\x14\n\x0ctoken_budget\x18\x01 \x01(\x05\x12\x15\n\rprovider_name\x18\x02 \x01(\t\"A\n\x12SetMessagesRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"5\n\x11HookHandleRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\".\n\x17GetSubscriptionsRequest\x12\x13\n\x0b\x63onfig_json\x18\x01 \x01(\t\"V\n\x18GetSubscriptionsResponse\x12:\n\rsubscriptions\x18\x01 \x03(\x0b\x32#.amplifier.module.EventSubscription\"B\n\x11\x45ventSubscription\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x10\n\x08priority\x18\x02 \x01(\x05\x12\x0c\n\x04name\x18\x03 \x01(\t\"d\n\x1b\x43ompleteWithProviderRequest\x12\x15\n\rprovider_name\x18\x01 \x01(\t\x12.\n\x07request\x18\x02 \x01(\x0b\x32\x1d.amplifier.module.ChatRequest\";\n\x12\x45xecuteToolRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x12\n\ninput_json\x18\x02 \x01(\t\"3\n\x0f\x45mitHookRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\"V\n\x19\x45mitHookAndCollectRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x17\n\x0ftimeout_seconds\x18\x03 \x01(\x01\"4\n\x1a\x45mitHookAndCollectResponse\x12\x16\n\x0eresponses_json\x18\x01 \x03(\t\"(\n\x12GetMessagesRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\"Y\n\x17KernelAddMessageRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12*\n\x07message\x18\x02 \x01(\x0b\x32\x19.amplifier.module.Message\"a\n\x17GetMountedModuleRequest\x12\x13\n\x0bmodule_name\x18\x01 \x01(\t\x12\x31\n\x0bmodule_type\x18\x02 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\"U\n\x18GetMountedModuleResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12*\n\x04info\x18\x02 \x01(\x0b\x32\x1c.amplifier.module.ModuleInfo\"=\n\x19RegisterCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\nvalue_json\x18\x02 \x01(\t\"$\n\x14GetCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\":\n\x15GetCapabilityResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12\x12\n\nvalue_json\x18\x02 \x01(\t*\xbc\x01\n\nModuleType\x12\x1b\n\x17MODULE_TYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14MODULE_TYPE_PROVIDER\x10\x01\x12\x14\n\x10MODULE_TYPE_TOOL\x10\x02\x12\x14\n\x10MODULE_TYPE_HOOK\x10\x03\x12\x16\n\x12MODULE_TYPE_MEMORY\x10\x04\x12\x19\n\x15MODULE_TYPE_GUARDRAIL\x10\x05\x12\x18\n\x14MODULE_TYPE_APPROVAL\x10\x06*\x82\x01\n\x0cHealthStatus\x12\x1d\n\x19HEALTH_STATUS_UNSPECIFIED\x10\x00\x12\x19\n\x15HEALTH_STATUS_SERVING\x10\x01\x12\x1d\n\x19HEALTH_STATUS_NOT_SERVING\x10\x02\x12\x19\n\x15HEALTH_STATUS_UNKNOWN\x10\x03*\xad\x01\n\x0f\x43onfigFieldType\x12!\n\x1d\x43ONFIG_FIELD_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_STRING\x10\x01\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_NUMBER\x10\x02\x12\x1d\n\x19\x43ONFIG_FIELD_TYPE_BOOLEAN\x10\x03\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_SECRET\x10\x04*\xd8\x02\n\x11ProviderErrorType\x12#\n\x1fPROVIDER_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18PROVIDER_ERROR_TYPE_AUTH\x10\x01\x12\"\n\x1ePROVIDER_ERROR_TYPE_RATE_LIMIT\x10\x02\x12&\n\"PROVIDER_ERROR_TYPE_CONTEXT_LENGTH\x10\x03\x12\'\n#PROVIDER_ERROR_TYPE_INVALID_REQUEST\x10\x04\x12&\n\"PROVIDER_ERROR_TYPE_CONTENT_FILTER\x10\x05\x12#\n\x1fPROVIDER_ERROR_TYPE_UNAVAILABLE\x10\x06\x12\x1f\n\x1bPROVIDER_ERROR_TYPE_TIMEOUT\x10\x07\x12\x1d\n\x19PROVIDER_ERROR_TYPE_OTHER\x10\x08*\x8c\x01\n\rToolErrorType\x12\x1f\n\x1bTOOL_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19TOOL_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aTOOL_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17TOOL_ERROR_TYPE_TIMEOUT\x10\x03*\x8c\x01\n\rHookErrorType\x12\x1f\n\x1bHOOK_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19HOOK_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aHOOK_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17HOOK_ERROR_TYPE_TIMEOUT\x10\x03*\x86\x01\n\x04Role\x12\x14\n\x10ROLE_UNSPECIFIED\x10\x00\x12\x0f\n\x0bROLE_SYSTEM\x10\x01\x12\r\n\tROLE_USER\x10\x02\x12\x12\n\x0eROLE_ASSISTANT\x10\x03\x12\r\n\tROLE_TOOL\x10\x04\x12\x11\n\rROLE_FUNCTION\x10\x05\x12\x12\n\x0eROLE_DEVELOPER\x10\x06*o\n\nVisibility\x12\x1a\n\x16VISIBILITY_UNSPECIFIED\x10\x00\x12\x12\n\x0eVISIBILITY_ALL\x10\x01\x12\x17\n\x13VISIBILITY_LLM_ONLY\x10\x02\x12\x18\n\x14VISIBILITY_USER_ONLY\x10\x03*\xab\x01\n\nHookAction\x12\x1b\n\x17HOOK_ACTION_UNSPECIFIED\x10\x00\x12\x18\n\x14HOOK_ACTION_CONTINUE\x10\x01\x12\x16\n\x12HOOK_ACTION_MODIFY\x10\x02\x12\x14\n\x10HOOK_ACTION_DENY\x10\x03\x12\x1e\n\x1aHOOK_ACTION_INJECT_CONTEXT\x10\x04\x12\x18\n\x14HOOK_ACTION_ASK_USER\x10\x05*\xa8\x01\n\x14\x43ontextInjectionRole\x12&\n\"CONTEXT_INJECTION_ROLE_UNSPECIFIED\x10\x00\x12!\n\x1d\x43ONTEXT_INJECTION_ROLE_SYSTEM\x10\x01\x12\x1f\n\x1b\x43ONTEXT_INJECTION_ROLE_USER\x10\x02\x12$\n CONTEXT_INJECTION_ROLE_ASSISTANT\x10\x03*l\n\x0f\x41pprovalDefault\x12 \n\x1c\x41PPROVAL_DEFAULT_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x41PPROVAL_DEFAULT_APPROVE\x10\x01\x12\x19\n\x15\x41PPROVAL_DEFAULT_DENY\x10\x02*\x91\x01\n\x10UserMessageLevel\x12\"\n\x1eUSER_MESSAGE_LEVEL_UNSPECIFIED\x10\x00\x12\x1b\n\x17USER_MESSAGE_LEVEL_INFO\x10\x01\x12\x1e\n\x1aUSER_MESSAGE_LEVEL_WARNING\x10\x02\x12\x1c\n\x18USER_MESSAGE_LEVEL_ERROR\x10\x03\x32\xa5\x01\n\x0bToolService\x12>\n\x07GetSpec\x12\x17.amplifier.module.Empty\x1a\x1a.amplifier.module.ToolSpec\x12V\n\x07\x45xecute\x12$.amplifier.module.ToolExecuteRequest\x1a%.amplifier.module.ToolExecuteResponse2\x9f\x03\n\x0fProviderService\x12\x42\n\x07GetInfo\x12\x17.amplifier.module.Empty\x1a\x1e.amplifier.module.ProviderInfo\x12K\n\nListModels\x12\x17.amplifier.module.Empty\x1a$.amplifier.module.ListModelsResponse\x12I\n\x08\x43omplete\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse\x12T\n\x11\x43ompleteStreaming\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Z\n\x0eParseToolCalls\x12\x1e.amplifier.module.ChatResponse\x1a(.amplifier.module.ParseToolCallsResponse2}\n\x13OrchestratorService\x12\x66\n\x07\x45xecute\x12,.amplifier.module.OrchestratorExecuteRequest\x1a-.amplifier.module.OrchestratorExecuteResponse2\xa3\x03\n\x0e\x43ontextService\x12J\n\nAddMessage\x12#.amplifier.module.AddMessageRequest\x1a\x17.amplifier.module.Empty\x12M\n\x0bGetMessages\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.GetMessagesResponse\x12m\n\x15GetMessagesForRequest\x12-.amplifier.module.GetMessagesForRequestParams\x1a%.amplifier.module.GetMessagesResponse\x12L\n\x0bSetMessages\x12$.amplifier.module.SetMessagesRequest\x1a\x17.amplifier.module.Empty\x12\x39\n\x05\x43lear\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty2\xc5\x01\n\x0bHookService\x12K\n\x06Handle\x12#.amplifier.module.HookHandleRequest\x1a\x1c.amplifier.module.HookResult\x12i\n\x10GetSubscriptions\x12).amplifier.module.GetSubscriptionsRequest\x1a*.amplifier.module.GetSubscriptionsResponse2k\n\x0f\x41pprovalService\x12X\n\x0fRequestApproval\x12!.amplifier.module.ApprovalRequest\x1a\".amplifier.module.ApprovalResponse2\xd0\x07\n\rKernelService\x12\x65\n\x14\x43ompleteWithProvider\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse\x12p\n\x1d\x43ompleteWithProviderStreaming\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Q\n\x0b\x45xecuteTool\x12$.amplifier.module.ExecuteToolRequest\x1a\x1c.amplifier.module.ToolResult\x12K\n\x08\x45mitHook\x12!.amplifier.module.EmitHookRequest\x1a\x1c.amplifier.module.HookResult\x12o\n\x12\x45mitHookAndCollect\x12+.amplifier.module.EmitHookAndCollectRequest\x1a,.amplifier.module.EmitHookAndCollectResponse\x12Z\n\x0bGetMessages\x12$.amplifier.module.GetMessagesRequest\x1a%.amplifier.module.GetMessagesResponse\x12P\n\nAddMessage\x12).amplifier.module.KernelAddMessageRequest\x1a\x17.amplifier.module.Empty\x12i\n\x10GetMountedModule\x12).amplifier.module.GetMountedModuleRequest\x1a*.amplifier.module.GetMountedModuleResponse\x12Z\n\x12RegisterCapability\x12+.amplifier.module.RegisterCapabilityRequest\x1a\x17.amplifier.module.Empty\x12`\n\rGetCapability\x12&.amplifier.module.GetCapabilityRequest\x1a\'.amplifier.module.GetCapabilityResponse2\xaf\x02\n\x0fModuleLifecycle\x12H\n\x05Mount\x12\x1e.amplifier.module.MountRequest\x1a\x1f.amplifier.module.MountResponse\x12;\n\x07\x43leanup\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty\x12M\n\x0bHealthCheck\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.HealthCheckResponse\x12\x46\n\rGetModuleInfo\x12\x17.amplifier.module.Empty\x1a\x1c.amplifier.module.ModuleInfob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -35,170 +35,170 @@ _globals['_MOUNTREQUEST_CONFIGENTRY']._serialized_options = b'8\001' _globals['_CONFIGFIELD_SHOWWHENENTRY']._loaded_options = None _globals['_CONFIGFIELD_SHOWWHENENTRY']._serialized_options = b'8\001' - _globals['_MODULETYPE']._serialized_start=7113 - _globals['_MODULETYPE']._serialized_end=7301 - _globals['_HEALTHSTATUS']._serialized_start=7304 - _globals['_HEALTHSTATUS']._serialized_end=7434 - _globals['_CONFIGFIELDTYPE']._serialized_start=7437 - _globals['_CONFIGFIELDTYPE']._serialized_end=7610 - _globals['_PROVIDERERRORTYPE']._serialized_start=7613 - _globals['_PROVIDERERRORTYPE']._serialized_end=7957 - _globals['_TOOLERRORTYPE']._serialized_start=7960 - _globals['_TOOLERRORTYPE']._serialized_end=8100 - _globals['_HOOKERRORTYPE']._serialized_start=8103 - _globals['_HOOKERRORTYPE']._serialized_end=8243 - _globals['_ROLE']._serialized_start=8246 - _globals['_ROLE']._serialized_end=8380 - _globals['_VISIBILITY']._serialized_start=8382 - _globals['_VISIBILITY']._serialized_end=8493 - _globals['_HOOKACTION']._serialized_start=8496 - _globals['_HOOKACTION']._serialized_end=8667 - _globals['_CONTEXTINJECTIONROLE']._serialized_start=8670 - _globals['_CONTEXTINJECTIONROLE']._serialized_end=8838 - _globals['_APPROVALDEFAULT']._serialized_start=8840 - _globals['_APPROVALDEFAULT']._serialized_end=8948 - _globals['_USERMESSAGELEVEL']._serialized_start=8951 - _globals['_USERMESSAGELEVEL']._serialized_end=9096 + _globals['_MODULETYPE']._serialized_start=7226 + _globals['_MODULETYPE']._serialized_end=7414 + _globals['_HEALTHSTATUS']._serialized_start=7417 + _globals['_HEALTHSTATUS']._serialized_end=7547 + _globals['_CONFIGFIELDTYPE']._serialized_start=7550 + _globals['_CONFIGFIELDTYPE']._serialized_end=7723 + _globals['_PROVIDERERRORTYPE']._serialized_start=7726 + _globals['_PROVIDERERRORTYPE']._serialized_end=8070 + _globals['_TOOLERRORTYPE']._serialized_start=8073 + _globals['_TOOLERRORTYPE']._serialized_end=8213 + _globals['_HOOKERRORTYPE']._serialized_start=8216 + _globals['_HOOKERRORTYPE']._serialized_end=8356 + _globals['_ROLE']._serialized_start=8359 + _globals['_ROLE']._serialized_end=8493 + _globals['_VISIBILITY']._serialized_start=8495 + _globals['_VISIBILITY']._serialized_end=8606 + _globals['_HOOKACTION']._serialized_start=8609 + _globals['_HOOKACTION']._serialized_end=8780 + _globals['_CONTEXTINJECTIONROLE']._serialized_start=8783 + _globals['_CONTEXTINJECTIONROLE']._serialized_end=8951 + _globals['_APPROVALDEFAULT']._serialized_start=8953 + _globals['_APPROVALDEFAULT']._serialized_end=9061 + _globals['_USERMESSAGELEVEL']._serialized_start=9064 + _globals['_USERMESSAGELEVEL']._serialized_end=9209 _globals['_EMPTY']._serialized_start=44 _globals['_EMPTY']._serialized_end=51 _globals['_TOOLSPEC']._serialized_start=53 _globals['_TOOLSPEC']._serialized_end=123 _globals['_TOOLEXECUTEREQUEST']._serialized_start=125 _globals['_TOOLEXECUTEREQUEST']._serialized_end=182 - _globals['_TOOLEXECUTERESPONSE']._serialized_start=184 - _globals['_TOOLEXECUTERESPONSE']._serialized_end=275 - _globals['_MODULEINFO']._serialized_start=278 - _globals['_MODULEINFO']._serialized_end=492 - _globals['_MOUNTREQUEST']._serialized_start=495 - _globals['_MOUNTREQUEST']._serialized_end=635 - _globals['_MOUNTREQUEST_CONFIGENTRY']._serialized_start=590 - _globals['_MOUNTREQUEST_CONFIGENTRY']._serialized_end=635 - _globals['_MOUNTRESPONSE']._serialized_start=637 - _globals['_MOUNTRESPONSE']._serialized_end=732 - _globals['_HEALTHCHECKRESPONSE']._serialized_start=734 - _globals['_HEALTHCHECKRESPONSE']._serialized_end=820 - _globals['_CONFIGFIELD']._serialized_start=823 - _globals['_CONFIGFIELD']._serialized_end=1153 - _globals['_CONFIGFIELD_SHOWWHENENTRY']._serialized_start=1106 - _globals['_CONFIGFIELD_SHOWWHENENTRY']._serialized_end=1153 - _globals['_PROVIDERERROR']._serialized_start=1156 - _globals['_PROVIDERERROR']._serialized_end=1344 - _globals['_TOOLERROR']._serialized_start=1347 - _globals['_TOOLERROR']._serialized_end=1498 - _globals['_HOOKERROR']._serialized_start=1500 - _globals['_HOOKERROR']._serialized_end=1600 - _globals['_AMPLIFIERERROR']._serialized_start=1603 - _globals['_AMPLIFIERERROR']._serialized_end=1842 - _globals['_TEXTBLOCK']._serialized_start=1844 - _globals['_TEXTBLOCK']._serialized_end=1869 - _globals['_THINKINGBLOCK']._serialized_start=1871 - _globals['_THINKINGBLOCK']._serialized_end=1940 - _globals['_REDACTEDTHINKINGBLOCK']._serialized_start=1942 - _globals['_REDACTEDTHINKINGBLOCK']._serialized_end=1979 - _globals['_TOOLCALLBLOCK']._serialized_start=1981 - _globals['_TOOLCALLBLOCK']._serialized_end=2042 - _globals['_TOOLRESULTBLOCK']._serialized_start=2044 - _globals['_TOOLRESULTBLOCK']._serialized_end=2104 - _globals['_IMAGEBLOCK']._serialized_start=2106 - _globals['_IMAGEBLOCK']._serialized_end=2173 - _globals['_REASONINGBLOCK']._serialized_start=2175 - _globals['_REASONINGBLOCK']._serialized_end=2225 - _globals['_CONTENTBLOCK']._serialized_start=2228 - _globals['_CONTENTBLOCK']._serialized_end=2725 - _globals['_CONTENTBLOCKLIST']._serialized_start=2727 - _globals['_CONTENTBLOCKLIST']._serialized_end=2793 - _globals['_MESSAGE']._serialized_start=2796 - _globals['_MESSAGE']._serialized_end=2998 - _globals['_TOOLCALLMESSAGE']._serialized_start=3000 - _globals['_TOOLCALLMESSAGE']._serialized_end=3067 - _globals['_TOOLSPECPROTO']._serialized_start=3069 - _globals['_TOOLSPECPROTO']._serialized_end=3144 - _globals['_JSONSCHEMAFORMAT']._serialized_start=3146 - _globals['_JSONSCHEMAFORMAT']._serialized_end=3201 - _globals['_RESPONSEFORMAT']._serialized_start=3203 - _globals['_RESPONSEFORMAT']._serialized_end=3320 - _globals['_USAGE']._serialized_start=3323 - _globals['_USAGE']._serialized_end=3570 - _globals['_DEGRADATION']._serialized_start=3572 - _globals['_DEGRADATION']._serialized_end=3636 - _globals['_CHATREQUEST']._serialized_start=3639 - _globals['_CHATREQUEST']._serialized_end=4024 - _globals['_CHATRESPONSE']._serialized_start=4027 - _globals['_CHATRESPONSE']._serialized_end=4307 - _globals['_TOOLRESULT']._serialized_start=4309 - _globals['_TOOLRESULT']._serialized_end=4379 - _globals['_HOOKRESULT']._serialized_start=4382 - _globals['_HOOKRESULT']._serialized_end=4933 - _globals['_MODELINFO']._serialized_start=4936 - _globals['_MODELINFO']._serialized_end=5077 - _globals['_PROVIDERINFO']._serialized_start=5080 - _globals['_PROVIDERINFO']._serialized_end=5256 - _globals['_APPROVALREQUEST']._serialized_start=5259 - _globals['_APPROVALREQUEST']._serialized_end=5387 - _globals['_APPROVALRESPONSE']._serialized_start=5389 - _globals['_APPROVALRESPONSE']._serialized_end=5459 - _globals['_LISTMODELSRESPONSE']._serialized_start=5461 - _globals['_LISTMODELSRESPONSE']._serialized_end=5526 - _globals['_PARSETOOLCALLSRESPONSE']._serialized_start=5528 - _globals['_PARSETOOLCALLSRESPONSE']._serialized_end=5607 - _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_start=5609 - _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_end=5673 - _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_start=5675 - _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_end=5737 - _globals['_ADDMESSAGEREQUEST']._serialized_start=5739 - _globals['_ADDMESSAGEREQUEST']._serialized_end=5802 - _globals['_GETMESSAGESRESPONSE']._serialized_start=5804 - _globals['_GETMESSAGESRESPONSE']._serialized_end=5870 - _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_start=5872 - _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_end=5946 - _globals['_SETMESSAGESREQUEST']._serialized_start=5948 - _globals['_SETMESSAGESREQUEST']._serialized_end=6013 - _globals['_HOOKHANDLEREQUEST']._serialized_start=6015 - _globals['_HOOKHANDLEREQUEST']._serialized_end=6068 - _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_start=6070 - _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_end=6116 - _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_start=6118 - _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_end=6204 - _globals['_EVENTSUBSCRIPTION']._serialized_start=6206 - _globals['_EVENTSUBSCRIPTION']._serialized_end=6272 - _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_start=6274 - _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_end=6374 - _globals['_EXECUTETOOLREQUEST']._serialized_start=6376 - _globals['_EXECUTETOOLREQUEST']._serialized_end=6435 - _globals['_EMITHOOKREQUEST']._serialized_start=6437 - _globals['_EMITHOOKREQUEST']._serialized_end=6488 - _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_start=6490 - _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_end=6576 - _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_start=6578 - _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_end=6630 - _globals['_GETMESSAGESREQUEST']._serialized_start=6632 - _globals['_GETMESSAGESREQUEST']._serialized_end=6672 - _globals['_KERNELADDMESSAGEREQUEST']._serialized_start=6674 - _globals['_KERNELADDMESSAGEREQUEST']._serialized_end=6763 - _globals['_GETMOUNTEDMODULEREQUEST']._serialized_start=6765 - _globals['_GETMOUNTEDMODULEREQUEST']._serialized_end=6862 - _globals['_GETMOUNTEDMODULERESPONSE']._serialized_start=6864 - _globals['_GETMOUNTEDMODULERESPONSE']._serialized_end=6949 - _globals['_REGISTERCAPABILITYREQUEST']._serialized_start=6951 - _globals['_REGISTERCAPABILITYREQUEST']._serialized_end=7012 - _globals['_GETCAPABILITYREQUEST']._serialized_start=7014 - _globals['_GETCAPABILITYREQUEST']._serialized_end=7050 - _globals['_GETCAPABILITYRESPONSE']._serialized_start=7052 - _globals['_GETCAPABILITYRESPONSE']._serialized_end=7110 - _globals['_TOOLSERVICE']._serialized_start=9099 - _globals['_TOOLSERVICE']._serialized_end=9264 - _globals['_PROVIDERSERVICE']._serialized_start=9267 - _globals['_PROVIDERSERVICE']._serialized_end=9682 - _globals['_ORCHESTRATORSERVICE']._serialized_start=9684 - _globals['_ORCHESTRATORSERVICE']._serialized_end=9809 - _globals['_CONTEXTSERVICE']._serialized_start=9812 - _globals['_CONTEXTSERVICE']._serialized_end=10231 - _globals['_HOOKSERVICE']._serialized_start=10234 - _globals['_HOOKSERVICE']._serialized_end=10431 - _globals['_APPROVALSERVICE']._serialized_start=10433 - _globals['_APPROVALSERVICE']._serialized_end=10540 - _globals['_KERNELSERVICE']._serialized_start=10543 - _globals['_KERNELSERVICE']._serialized_end=11519 - _globals['_MODULELIFECYCLE']._serialized_start=11522 - _globals['_MODULELIFECYCLE']._serialized_end=11825 + _globals['_TOOLEXECUTERESPONSE']._serialized_start=185 + _globals['_TOOLEXECUTERESPONSE']._serialized_end=332 + _globals['_MODULEINFO']._serialized_start=335 + _globals['_MODULEINFO']._serialized_end=549 + _globals['_MOUNTREQUEST']._serialized_start=552 + _globals['_MOUNTREQUEST']._serialized_end=692 + _globals['_MOUNTREQUEST_CONFIGENTRY']._serialized_start=647 + _globals['_MOUNTREQUEST_CONFIGENTRY']._serialized_end=692 + _globals['_MOUNTRESPONSE']._serialized_start=694 + _globals['_MOUNTRESPONSE']._serialized_end=789 + _globals['_HEALTHCHECKRESPONSE']._serialized_start=791 + _globals['_HEALTHCHECKRESPONSE']._serialized_end=877 + _globals['_CONFIGFIELD']._serialized_start=880 + _globals['_CONFIGFIELD']._serialized_end=1210 + _globals['_CONFIGFIELD_SHOWWHENENTRY']._serialized_start=1163 + _globals['_CONFIGFIELD_SHOWWHENENTRY']._serialized_end=1210 + _globals['_PROVIDERERROR']._serialized_start=1213 + _globals['_PROVIDERERROR']._serialized_end=1401 + _globals['_TOOLERROR']._serialized_start=1404 + _globals['_TOOLERROR']._serialized_end=1555 + _globals['_HOOKERROR']._serialized_start=1557 + _globals['_HOOKERROR']._serialized_end=1657 + _globals['_AMPLIFIERERROR']._serialized_start=1660 + _globals['_AMPLIFIERERROR']._serialized_end=1899 + _globals['_TEXTBLOCK']._serialized_start=1901 + _globals['_TEXTBLOCK']._serialized_end=1926 + _globals['_THINKINGBLOCK']._serialized_start=1928 + _globals['_THINKINGBLOCK']._serialized_end=1997 + _globals['_REDACTEDTHINKINGBLOCK']._serialized_start=1999 + _globals['_REDACTEDTHINKINGBLOCK']._serialized_end=2036 + _globals['_TOOLCALLBLOCK']._serialized_start=2038 + _globals['_TOOLCALLBLOCK']._serialized_end=2099 + _globals['_TOOLRESULTBLOCK']._serialized_start=2101 + _globals['_TOOLRESULTBLOCK']._serialized_end=2161 + _globals['_IMAGEBLOCK']._serialized_start=2163 + _globals['_IMAGEBLOCK']._serialized_end=2230 + _globals['_REASONINGBLOCK']._serialized_start=2232 + _globals['_REASONINGBLOCK']._serialized_end=2282 + _globals['_CONTENTBLOCK']._serialized_start=2285 + _globals['_CONTENTBLOCK']._serialized_end=2782 + _globals['_CONTENTBLOCKLIST']._serialized_start=2784 + _globals['_CONTENTBLOCKLIST']._serialized_end=2850 + _globals['_MESSAGE']._serialized_start=2853 + _globals['_MESSAGE']._serialized_end=3055 + _globals['_TOOLCALLMESSAGE']._serialized_start=3057 + _globals['_TOOLCALLMESSAGE']._serialized_end=3124 + _globals['_TOOLSPECPROTO']._serialized_start=3126 + _globals['_TOOLSPECPROTO']._serialized_end=3201 + _globals['_JSONSCHEMAFORMAT']._serialized_start=3203 + _globals['_JSONSCHEMAFORMAT']._serialized_end=3258 + _globals['_RESPONSEFORMAT']._serialized_start=3260 + _globals['_RESPONSEFORMAT']._serialized_end=3377 + _globals['_USAGE']._serialized_start=3380 + _globals['_USAGE']._serialized_end=3627 + _globals['_DEGRADATION']._serialized_start=3629 + _globals['_DEGRADATION']._serialized_end=3693 + _globals['_CHATREQUEST']._serialized_start=3696 + _globals['_CHATREQUEST']._serialized_end=4081 + _globals['_CHATRESPONSE']._serialized_start=4084 + _globals['_CHATRESPONSE']._serialized_end=4364 + _globals['_TOOLRESULT']._serialized_start=4366 + _globals['_TOOLRESULT']._serialized_end=4492 + _globals['_HOOKRESULT']._serialized_start=4495 + _globals['_HOOKRESULT']._serialized_end=5046 + _globals['_MODELINFO']._serialized_start=5049 + _globals['_MODELINFO']._serialized_end=5190 + _globals['_PROVIDERINFO']._serialized_start=5193 + _globals['_PROVIDERINFO']._serialized_end=5369 + _globals['_APPROVALREQUEST']._serialized_start=5372 + _globals['_APPROVALREQUEST']._serialized_end=5500 + _globals['_APPROVALRESPONSE']._serialized_start=5502 + _globals['_APPROVALRESPONSE']._serialized_end=5572 + _globals['_LISTMODELSRESPONSE']._serialized_start=5574 + _globals['_LISTMODELSRESPONSE']._serialized_end=5639 + _globals['_PARSETOOLCALLSRESPONSE']._serialized_start=5641 + _globals['_PARSETOOLCALLSRESPONSE']._serialized_end=5720 + _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_start=5722 + _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_end=5786 + _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_start=5788 + _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_end=5850 + _globals['_ADDMESSAGEREQUEST']._serialized_start=5852 + _globals['_ADDMESSAGEREQUEST']._serialized_end=5915 + _globals['_GETMESSAGESRESPONSE']._serialized_start=5917 + _globals['_GETMESSAGESRESPONSE']._serialized_end=5983 + _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_start=5985 + _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_end=6059 + _globals['_SETMESSAGESREQUEST']._serialized_start=6061 + _globals['_SETMESSAGESREQUEST']._serialized_end=6126 + _globals['_HOOKHANDLEREQUEST']._serialized_start=6128 + _globals['_HOOKHANDLEREQUEST']._serialized_end=6181 + _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_start=6183 + _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_end=6229 + _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_start=6231 + _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_end=6317 + _globals['_EVENTSUBSCRIPTION']._serialized_start=6319 + _globals['_EVENTSUBSCRIPTION']._serialized_end=6385 + _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_start=6387 + _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_end=6487 + _globals['_EXECUTETOOLREQUEST']._serialized_start=6489 + _globals['_EXECUTETOOLREQUEST']._serialized_end=6548 + _globals['_EMITHOOKREQUEST']._serialized_start=6550 + _globals['_EMITHOOKREQUEST']._serialized_end=6601 + _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_start=6603 + _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_end=6689 + _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_start=6691 + _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_end=6743 + _globals['_GETMESSAGESREQUEST']._serialized_start=6745 + _globals['_GETMESSAGESREQUEST']._serialized_end=6785 + _globals['_KERNELADDMESSAGEREQUEST']._serialized_start=6787 + _globals['_KERNELADDMESSAGEREQUEST']._serialized_end=6876 + _globals['_GETMOUNTEDMODULEREQUEST']._serialized_start=6878 + _globals['_GETMOUNTEDMODULEREQUEST']._serialized_end=6975 + _globals['_GETMOUNTEDMODULERESPONSE']._serialized_start=6977 + _globals['_GETMOUNTEDMODULERESPONSE']._serialized_end=7062 + _globals['_REGISTERCAPABILITYREQUEST']._serialized_start=7064 + _globals['_REGISTERCAPABILITYREQUEST']._serialized_end=7125 + _globals['_GETCAPABILITYREQUEST']._serialized_start=7127 + _globals['_GETCAPABILITYREQUEST']._serialized_end=7163 + _globals['_GETCAPABILITYRESPONSE']._serialized_start=7165 + _globals['_GETCAPABILITYRESPONSE']._serialized_end=7223 + _globals['_TOOLSERVICE']._serialized_start=9212 + _globals['_TOOLSERVICE']._serialized_end=9377 + _globals['_PROVIDERSERVICE']._serialized_start=9380 + _globals['_PROVIDERSERVICE']._serialized_end=9795 + _globals['_ORCHESTRATORSERVICE']._serialized_start=9797 + _globals['_ORCHESTRATORSERVICE']._serialized_end=9922 + _globals['_CONTEXTSERVICE']._serialized_start=9925 + _globals['_CONTEXTSERVICE']._serialized_end=10344 + _globals['_HOOKSERVICE']._serialized_start=10347 + _globals['_HOOKSERVICE']._serialized_end=10544 + _globals['_APPROVALSERVICE']._serialized_start=10546 + _globals['_APPROVALSERVICE']._serialized_end=10653 + _globals['_KERNELSERVICE']._serialized_start=10656 + _globals['_KERNELSERVICE']._serialized_end=11632 + _globals['_MODULELIFECYCLE']._serialized_start=11635 + _globals['_MODULELIFECYCLE']._serialized_end=11938 # @@protoc_insertion_point(module_scope) diff --git a/proto/test_task03_module_specific.py b/proto/test_task03_module_specific.py index 80c7b8b..85d6933 100644 --- a/proto/test_task03_module_specific.py +++ b/proto/test_task03_module_specific.py @@ -114,7 +114,7 @@ def test_user_message_level_enum(): # --------------------------------------------------------------------------- def test_tool_result_message(): - """ToolResult message with 3 fields: success, output_json, error_json.""" + """ToolResult message with rich content alongside legacy fields.""" content = read_proto() assert "message ToolResult" in content, "Missing message ToolResult" m = re.search(r'message ToolResult\s*\{([^}]+)\}', content) @@ -123,6 +123,9 @@ def test_tool_result_message(): assert field_present(body, "bool success"), "ToolResult missing field: success" assert field_present(body, "string output_json"), "ToolResult missing field: output_json" assert field_present(body, "string error_json"), "ToolResult missing field: error_json" + assert field_present(body, "repeated ContentBlock content_blocks"), ( + "ToolResult missing field: content_blocks" + ) def test_hook_result_message_15_fields(): diff --git a/python/amplifier_core/_engine.pyi b/python/amplifier_core/_engine.pyi index d3fb0e4..d3e7049 100644 --- a/python/amplifier_core/_engine.pyi +++ b/python/amplifier_core/_engine.pyi @@ -18,6 +18,10 @@ from typing import Any, Optional __version__: str RUST_AVAILABLE: bool +# Private helpers used by ``ToolResult`` to enforce the Rust content boundary. +def _normalize_tool_result_content(content_json: str) -> str | None: ... +def _tool_result_safe_hook_presentation(tool_result_json: str) -> str: ... + # --------------------------------------------------------------------------- # RustSession — wraps amplifier_core::Session # --------------------------------------------------------------------------- diff --git a/python/amplifier_core/_grpc_gen/amplifier_module_pb2.py b/python/amplifier_core/_grpc_gen/amplifier_module_pb2.py index 6f4d751..d6c0e3e 100644 --- a/python/amplifier_core/_grpc_gen/amplifier_module_pb2.py +++ b/python/amplifier_core/_grpc_gen/amplifier_module_pb2.py @@ -24,7 +24,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x61mplifier_module.proto\x12\x10\x61mplifier.module\"\x07\n\x05\x45mpty\"F\n\x08ToolSpec\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"9\n\x12ToolExecuteRequest\x12\r\n\x05input\x18\x01 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x02 \x01(\t\"[\n\x13ToolExecuteResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0e\n\x06output\x18\x02 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x03 \x01(\t\x12\r\n\x05\x65rror\x18\x04 \x01(\t\"\xd6\x01\n\nModuleInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x31\n\x0bmodule_type\x18\x04 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\x12\x13\n\x0bmount_point\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x06 \x01(\t\x12\x1a\n\x12\x63onfig_schema_json\x18\x07 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x08 \x03(\t\x12\x0e\n\x06\x61uthor\x18\t \x01(\t\"\x8c\x01\n\x0cMountRequest\x12:\n\x06\x63onfig\x18\x01 \x03(\x0b\x32*.amplifier.module.MountRequest.ConfigEntry\x12\x11\n\tmodule_id\x18\x02 \x01(\t\x1a-\n\x0b\x43onfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"_\n\rMountResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12.\n\x06status\x18\x03 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\"V\n\x13HealthCheckResponse\x12.\n\x06status\x18\x01 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xca\x02\n\x0b\x43onfigField\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x35\n\nfield_type\x18\x03 \x01(\x0e\x32!.amplifier.module.ConfigFieldType\x12\x0e\n\x06prompt\x18\x04 \x01(\t\x12\x0f\n\x07\x65nv_var\x18\x05 \x01(\t\x12\x0f\n\x07\x63hoices\x18\x06 \x03(\t\x12\x10\n\x08required\x18\x07 \x01(\x08\x12\x15\n\rdefault_value\x18\x08 \x01(\t\x12>\n\tshow_when\x18\t \x03(\x0b\x32+.amplifier.module.ConfigField.ShowWhenEntry\x12\x16\n\x0erequires_model\x18\n \x01(\x08\x1a/\n\rShowWhenEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xbc\x01\n\rProviderError\x12\x37\n\nerror_type\x18\x01 \x01(\x0e\x32#.amplifier.module.ProviderErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x15\n\rprovider_name\x18\x03 \x01(\t\x12\r\n\x05model\x18\x04 \x01(\t\x12\x13\n\x0bstatus_code\x18\x05 \x01(\x05\x12\x11\n\tretryable\x18\x06 \x01(\x08\x12\x13\n\x0bretry_after\x18\x07 \x01(\x01\"\x97\x01\n\tToolError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.ToolErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\ttool_name\x18\x03 \x01(\t\x12\x0e\n\x06stdout\x18\x04 \x01(\t\x12\x0e\n\x06stderr\x18\x05 \x01(\t\x12\x11\n\texit_code\x18\x06 \x01(\x05\"d\n\tHookError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.HookErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\thook_name\x18\x03 \x01(\t\"\xef\x01\n\x0e\x41mplifierError\x12\x39\n\x0eprovider_error\x18\x01 \x01(\x0b\x32\x1f.amplifier.module.ProviderErrorH\x00\x12\x31\n\ntool_error\x18\x02 \x01(\x0b\x32\x1b.amplifier.module.ToolErrorH\x00\x12\x31\n\nhook_error\x18\x03 \x01(\x0b\x32\x1b.amplifier.module.HookErrorH\x00\x12\x17\n\rgeneric_error\x18\x04 \x01(\tH\x00\x12\x1a\n\x10validation_error\x18\x05 \x01(\tH\x00\x42\x07\n\x05\x65rror\"\x19\n\tTextBlock\x12\x0c\n\x04text\x18\x01 \x01(\t\"E\n\rThinkingBlock\x12\x10\n\x08thinking\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\t\"%\n\x15RedactedThinkingBlock\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\t\"=\n\rToolCallBlock\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x12\n\ninput_json\x18\x03 \x01(\t\"<\n\x0fToolResultBlock\x12\x14\n\x0ctool_call_id\x18\x01 \x01(\t\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\"C\n\nImageBlock\x12\x12\n\nmedia_type\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x13\n\x0bsource_json\x18\x03 \x01(\t\"2\n\x0eReasoningBlock\x12\x0f\n\x07\x63ontent\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x03(\t\"\xf1\x03\n\x0c\x43ontentBlock\x12\x31\n\ntext_block\x18\x01 \x01(\x0b\x32\x1b.amplifier.module.TextBlockH\x00\x12\x39\n\x0ethinking_block\x18\x02 \x01(\x0b\x32\x1f.amplifier.module.ThinkingBlockH\x00\x12J\n\x17redacted_thinking_block\x18\x03 \x01(\x0b\x32\'.amplifier.module.RedactedThinkingBlockH\x00\x12:\n\x0ftool_call_block\x18\x04 \x01(\x0b\x32\x1f.amplifier.module.ToolCallBlockH\x00\x12>\n\x11tool_result_block\x18\x05 \x01(\x0b\x32!.amplifier.module.ToolResultBlockH\x00\x12\x33\n\x0bimage_block\x18\x06 \x01(\x0b\x32\x1c.amplifier.module.ImageBlockH\x00\x12;\n\x0freasoning_block\x18\x07 \x01(\x0b\x32 .amplifier.module.ReasoningBlockH\x00\x12\x30\n\nvisibility\x18\x08 \x01(\x0e\x32\x1c.amplifier.module.VisibilityB\x07\n\x05\x62lock\"B\n\x10\x43ontentBlockList\x12.\n\x06\x62locks\x18\x01 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xca\x01\n\x07Message\x12$\n\x04role\x18\x01 \x01(\x0e\x32\x16.amplifier.module.Role\x12\x16\n\x0ctext_content\x18\x02 \x01(\tH\x00\x12;\n\rblock_content\x18\x03 \x01(\x0b\x32\".amplifier.module.ContentBlockListH\x00\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x14\n\x0ctool_call_id\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\tB\t\n\x07\x63ontent\"C\n\x0fToolCallMessage\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x16\n\x0e\x61rguments_json\x18\x03 \x01(\t\"K\n\rToolSpecProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"7\n\x10JsonSchemaFormat\x12\x13\n\x0bschema_json\x18\x01 \x01(\t\x12\x0e\n\x06strict\x18\x02 \x01(\x08\"u\n\x0eResponseFormat\x12\x0e\n\x04text\x18\x01 \x01(\x08H\x00\x12\x0e\n\x04json\x18\x02 \x01(\x08H\x00\x12\x39\n\x0bjson_schema\x18\x03 \x01(\x0b\x32\".amplifier.module.JsonSchemaFormatH\x00\x42\x08\n\x06\x66ormat\"\xf7\x01\n\x05Usage\x12\x15\n\rprompt_tokens\x18\x01 \x01(\x05\x12\x19\n\x11\x63ompletion_tokens\x18\x02 \x01(\x05\x12\x14\n\x0ctotal_tokens\x18\x03 \x01(\x05\x12\x1d\n\x10reasoning_tokens\x18\x04 \x01(\x05H\x00\x88\x01\x01\x12\x1e\n\x11\x63\x61\x63he_read_tokens\x18\x05 \x01(\x05H\x01\x88\x01\x01\x12\"\n\x15\x63\x61\x63he_creation_tokens\x18\x06 \x01(\x05H\x02\x88\x01\x01\x42\x13\n\x11_reasoning_tokensB\x14\n\x12_cache_read_tokensB\x18\n\x16_cache_creation_tokens\"@\n\x0b\x44\x65gradation\x12\x11\n\trequested\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tual\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x81\x03\n\x0b\x43hatRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\x12.\n\x05tools\x18\x02 \x03(\x0b\x32\x1f.amplifier.module.ToolSpecProto\x12\x39\n\x0fresponse_format\x18\x03 \x01(\x0b\x32 .amplifier.module.ResponseFormat\x12\x13\n\x0btemperature\x18\x04 \x01(\x01\x12\r\n\x05top_p\x18\x05 \x01(\x01\x12\x19\n\x11max_output_tokens\x18\x06 \x01(\x05\x12\x17\n\x0f\x63onversation_id\x18\x07 \x01(\t\x12\x0e\n\x06stream\x18\x08 \x01(\x08\x12\x15\n\rmetadata_json\x18\t \x01(\t\x12\r\n\x05model\x18\n \x01(\t\x12\x13\n\x0btool_choice\x18\x0b \x01(\t\x12\x0c\n\x04stop\x18\x0c \x03(\t\x12\x18\n\x10reasoning_effort\x18\r \x01(\t\x12\x0f\n\x07timeout\x18\x0e \x01(\x01\"\x98\x02\n\x0c\x43hatResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x35\n\ntool_calls\x18\x02 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\x12&\n\x05usage\x18\x03 \x01(\x0b\x32\x17.amplifier.module.Usage\x12\x32\n\x0b\x64\x65gradation\x18\x04 \x01(\x0b\x32\x1d.amplifier.module.Degradation\x12\x15\n\rfinish_reason\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x07 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"F\n\nToolResult\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\x12\x12\n\nerror_json\x18\x03 \x01(\t\"\xa7\x04\n\nHookResult\x12,\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x1c.amplifier.module.HookAction\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x19\n\x11\x63ontext_injection\x18\x04 \x01(\t\x12\x46\n\x16\x63ontext_injection_role\x18\x05 \x01(\x0e\x32&.amplifier.module.ContextInjectionRole\x12\x11\n\tephemeral\x18\x06 \x01(\x08\x12\x17\n\x0f\x61pproval_prompt\x18\x07 \x01(\t\x12\x18\n\x10\x61pproval_options\x18\x08 \x03(\t\x12\x1d\n\x10\x61pproval_timeout\x18\t \x01(\x01H\x00\x88\x01\x01\x12;\n\x10\x61pproval_default\x18\n \x01(\x0e\x32!.amplifier.module.ApprovalDefault\x12\x17\n\x0fsuppress_output\x18\x0b \x01(\x08\x12\x14\n\x0cuser_message\x18\x0c \x01(\t\x12>\n\x12user_message_level\x18\r \x01(\x0e\x32\".amplifier.module.UserMessageLevel\x12\x1b\n\x13user_message_source\x18\x0e \x01(\t\x12\"\n\x1a\x61ppend_to_last_tool_result\x18\x0f \x01(\x08\x42\x13\n\x11_approval_timeout\"\x8d\x01\n\tModelInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x16\n\x0e\x63ontext_window\x18\x03 \x01(\x05\x12\x19\n\x11max_output_tokens\x18\x04 \x01(\x05\x12\x14\n\x0c\x63\x61pabilities\x18\x05 \x03(\t\x12\x15\n\rdefaults_json\x18\x06 \x01(\t\"\xb0\x01\n\x0cProviderInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x1b\n\x13\x63redential_env_vars\x18\x03 \x03(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x04 \x03(\t\x12\x15\n\rdefaults_json\x18\x05 \x01(\t\x12\x34\n\rconfig_fields\x18\x06 \x03(\x0b\x32\x1d.amplifier.module.ConfigField\"\x80\x01\n\x0f\x41pprovalRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x14\n\x0c\x64\x65tails_json\x18\x03 \x01(\t\x12\x12\n\nrisk_level\x18\x04 \x01(\t\x12\x14\n\x07timeout\x18\x05 \x01(\x01H\x00\x88\x01\x01\x42\n\n\x08_timeout\"F\n\x10\x41pprovalResponse\x12\x10\n\x08\x61pproved\x18\x01 \x01(\x08\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x10\n\x08remember\x18\x03 \x01(\x08\"A\n\x12ListModelsResponse\x12+\n\x06models\x18\x01 \x03(\x0b\x32\x1b.amplifier.module.ModelInfo\"O\n\x16ParseToolCallsResponse\x12\x35\n\ntool_calls\x18\x01 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\"@\n\x1aOrchestratorExecuteRequest\x12\x0e\n\x06prompt\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\">\n\x1bOrchestratorExecuteResponse\x12\x10\n\x08response\x18\x01 \x01(\t\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"?\n\x11\x41\x64\x64MessageRequest\x12*\n\x07message\x18\x01 \x01(\x0b\x32\x19.amplifier.module.Message\"B\n\x13GetMessagesResponse\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"J\n\x1bGetMessagesForRequestParams\x12\x14\n\x0ctoken_budget\x18\x01 \x01(\x05\x12\x15\n\rprovider_name\x18\x02 \x01(\t\"A\n\x12SetMessagesRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"5\n\x11HookHandleRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\".\n\x17GetSubscriptionsRequest\x12\x13\n\x0b\x63onfig_json\x18\x01 \x01(\t\"V\n\x18GetSubscriptionsResponse\x12:\n\rsubscriptions\x18\x01 \x03(\x0b\x32#.amplifier.module.EventSubscription\"B\n\x11\x45ventSubscription\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x10\n\x08priority\x18\x02 \x01(\x05\x12\x0c\n\x04name\x18\x03 \x01(\t\"d\n\x1b\x43ompleteWithProviderRequest\x12\x15\n\rprovider_name\x18\x01 \x01(\t\x12.\n\x07request\x18\x02 \x01(\x0b\x32\x1d.amplifier.module.ChatRequest\";\n\x12\x45xecuteToolRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x12\n\ninput_json\x18\x02 \x01(\t\"3\n\x0f\x45mitHookRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\"V\n\x19\x45mitHookAndCollectRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x17\n\x0ftimeout_seconds\x18\x03 \x01(\x01\"4\n\x1a\x45mitHookAndCollectResponse\x12\x16\n\x0eresponses_json\x18\x01 \x03(\t\"(\n\x12GetMessagesRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\"Y\n\x17KernelAddMessageRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12*\n\x07message\x18\x02 \x01(\x0b\x32\x19.amplifier.module.Message\"a\n\x17GetMountedModuleRequest\x12\x13\n\x0bmodule_name\x18\x01 \x01(\t\x12\x31\n\x0bmodule_type\x18\x02 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\"U\n\x18GetMountedModuleResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12*\n\x04info\x18\x02 \x01(\x0b\x32\x1c.amplifier.module.ModuleInfo\"=\n\x19RegisterCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\nvalue_json\x18\x02 \x01(\t\"$\n\x14GetCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\":\n\x15GetCapabilityResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12\x12\n\nvalue_json\x18\x02 \x01(\t*\xbc\x01\n\nModuleType\x12\x1b\n\x17MODULE_TYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14MODULE_TYPE_PROVIDER\x10\x01\x12\x14\n\x10MODULE_TYPE_TOOL\x10\x02\x12\x14\n\x10MODULE_TYPE_HOOK\x10\x03\x12\x16\n\x12MODULE_TYPE_MEMORY\x10\x04\x12\x19\n\x15MODULE_TYPE_GUARDRAIL\x10\x05\x12\x18\n\x14MODULE_TYPE_APPROVAL\x10\x06*\x82\x01\n\x0cHealthStatus\x12\x1d\n\x19HEALTH_STATUS_UNSPECIFIED\x10\x00\x12\x19\n\x15HEALTH_STATUS_SERVING\x10\x01\x12\x1d\n\x19HEALTH_STATUS_NOT_SERVING\x10\x02\x12\x19\n\x15HEALTH_STATUS_UNKNOWN\x10\x03*\xad\x01\n\x0f\x43onfigFieldType\x12!\n\x1d\x43ONFIG_FIELD_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_STRING\x10\x01\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_NUMBER\x10\x02\x12\x1d\n\x19\x43ONFIG_FIELD_TYPE_BOOLEAN\x10\x03\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_SECRET\x10\x04*\xd8\x02\n\x11ProviderErrorType\x12#\n\x1fPROVIDER_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18PROVIDER_ERROR_TYPE_AUTH\x10\x01\x12\"\n\x1ePROVIDER_ERROR_TYPE_RATE_LIMIT\x10\x02\x12&\n\"PROVIDER_ERROR_TYPE_CONTEXT_LENGTH\x10\x03\x12\'\n#PROVIDER_ERROR_TYPE_INVALID_REQUEST\x10\x04\x12&\n\"PROVIDER_ERROR_TYPE_CONTENT_FILTER\x10\x05\x12#\n\x1fPROVIDER_ERROR_TYPE_UNAVAILABLE\x10\x06\x12\x1f\n\x1bPROVIDER_ERROR_TYPE_TIMEOUT\x10\x07\x12\x1d\n\x19PROVIDER_ERROR_TYPE_OTHER\x10\x08*\x8c\x01\n\rToolErrorType\x12\x1f\n\x1bTOOL_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19TOOL_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aTOOL_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17TOOL_ERROR_TYPE_TIMEOUT\x10\x03*\x8c\x01\n\rHookErrorType\x12\x1f\n\x1bHOOK_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19HOOK_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aHOOK_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17HOOK_ERROR_TYPE_TIMEOUT\x10\x03*\x86\x01\n\x04Role\x12\x14\n\x10ROLE_UNSPECIFIED\x10\x00\x12\x0f\n\x0bROLE_SYSTEM\x10\x01\x12\r\n\tROLE_USER\x10\x02\x12\x12\n\x0eROLE_ASSISTANT\x10\x03\x12\r\n\tROLE_TOOL\x10\x04\x12\x11\n\rROLE_FUNCTION\x10\x05\x12\x12\n\x0eROLE_DEVELOPER\x10\x06*o\n\nVisibility\x12\x1a\n\x16VISIBILITY_UNSPECIFIED\x10\x00\x12\x12\n\x0eVISIBILITY_ALL\x10\x01\x12\x17\n\x13VISIBILITY_LLM_ONLY\x10\x02\x12\x18\n\x14VISIBILITY_USER_ONLY\x10\x03*\xab\x01\n\nHookAction\x12\x1b\n\x17HOOK_ACTION_UNSPECIFIED\x10\x00\x12\x18\n\x14HOOK_ACTION_CONTINUE\x10\x01\x12\x16\n\x12HOOK_ACTION_MODIFY\x10\x02\x12\x14\n\x10HOOK_ACTION_DENY\x10\x03\x12\x1e\n\x1aHOOK_ACTION_INJECT_CONTEXT\x10\x04\x12\x18\n\x14HOOK_ACTION_ASK_USER\x10\x05*\xa8\x01\n\x14\x43ontextInjectionRole\x12&\n\"CONTEXT_INJECTION_ROLE_UNSPECIFIED\x10\x00\x12!\n\x1d\x43ONTEXT_INJECTION_ROLE_SYSTEM\x10\x01\x12\x1f\n\x1b\x43ONTEXT_INJECTION_ROLE_USER\x10\x02\x12$\n CONTEXT_INJECTION_ROLE_ASSISTANT\x10\x03*l\n\x0f\x41pprovalDefault\x12 \n\x1c\x41PPROVAL_DEFAULT_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x41PPROVAL_DEFAULT_APPROVE\x10\x01\x12\x19\n\x15\x41PPROVAL_DEFAULT_DENY\x10\x02*\x91\x01\n\x10UserMessageLevel\x12\"\n\x1eUSER_MESSAGE_LEVEL_UNSPECIFIED\x10\x00\x12\x1b\n\x17USER_MESSAGE_LEVEL_INFO\x10\x01\x12\x1e\n\x1aUSER_MESSAGE_LEVEL_WARNING\x10\x02\x12\x1c\n\x18USER_MESSAGE_LEVEL_ERROR\x10\x03\x32\xa5\x01\n\x0bToolService\x12>\n\x07GetSpec\x12\x17.amplifier.module.Empty\x1a\x1a.amplifier.module.ToolSpec\x12V\n\x07\x45xecute\x12$.amplifier.module.ToolExecuteRequest\x1a%.amplifier.module.ToolExecuteResponse2\x9f\x03\n\x0fProviderService\x12\x42\n\x07GetInfo\x12\x17.amplifier.module.Empty\x1a\x1e.amplifier.module.ProviderInfo\x12K\n\nListModels\x12\x17.amplifier.module.Empty\x1a$.amplifier.module.ListModelsResponse\x12I\n\x08\x43omplete\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse\x12T\n\x11\x43ompleteStreaming\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Z\n\x0eParseToolCalls\x12\x1e.amplifier.module.ChatResponse\x1a(.amplifier.module.ParseToolCallsResponse2}\n\x13OrchestratorService\x12\x66\n\x07\x45xecute\x12,.amplifier.module.OrchestratorExecuteRequest\x1a-.amplifier.module.OrchestratorExecuteResponse2\xa3\x03\n\x0e\x43ontextService\x12J\n\nAddMessage\x12#.amplifier.module.AddMessageRequest\x1a\x17.amplifier.module.Empty\x12M\n\x0bGetMessages\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.GetMessagesResponse\x12m\n\x15GetMessagesForRequest\x12-.amplifier.module.GetMessagesForRequestParams\x1a%.amplifier.module.GetMessagesResponse\x12L\n\x0bSetMessages\x12$.amplifier.module.SetMessagesRequest\x1a\x17.amplifier.module.Empty\x12\x39\n\x05\x43lear\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty2\xc5\x01\n\x0bHookService\x12K\n\x06Handle\x12#.amplifier.module.HookHandleRequest\x1a\x1c.amplifier.module.HookResult\x12i\n\x10GetSubscriptions\x12).amplifier.module.GetSubscriptionsRequest\x1a*.amplifier.module.GetSubscriptionsResponse2k\n\x0f\x41pprovalService\x12X\n\x0fRequestApproval\x12!.amplifier.module.ApprovalRequest\x1a\".amplifier.module.ApprovalResponse2\xd0\x07\n\rKernelService\x12\x65\n\x14\x43ompleteWithProvider\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse\x12p\n\x1d\x43ompleteWithProviderStreaming\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Q\n\x0b\x45xecuteTool\x12$.amplifier.module.ExecuteToolRequest\x1a\x1c.amplifier.module.ToolResult\x12K\n\x08\x45mitHook\x12!.amplifier.module.EmitHookRequest\x1a\x1c.amplifier.module.HookResult\x12o\n\x12\x45mitHookAndCollect\x12+.amplifier.module.EmitHookAndCollectRequest\x1a,.amplifier.module.EmitHookAndCollectResponse\x12Z\n\x0bGetMessages\x12$.amplifier.module.GetMessagesRequest\x1a%.amplifier.module.GetMessagesResponse\x12P\n\nAddMessage\x12).amplifier.module.KernelAddMessageRequest\x1a\x17.amplifier.module.Empty\x12i\n\x10GetMountedModule\x12).amplifier.module.GetMountedModuleRequest\x1a*.amplifier.module.GetMountedModuleResponse\x12Z\n\x12RegisterCapability\x12+.amplifier.module.RegisterCapabilityRequest\x1a\x17.amplifier.module.Empty\x12`\n\rGetCapability\x12&.amplifier.module.GetCapabilityRequest\x1a\'.amplifier.module.GetCapabilityResponse2\xaf\x02\n\x0fModuleLifecycle\x12H\n\x05Mount\x12\x1e.amplifier.module.MountRequest\x1a\x1f.amplifier.module.MountResponse\x12;\n\x07\x43leanup\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty\x12M\n\x0bHealthCheck\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.HealthCheckResponse\x12\x46\n\rGetModuleInfo\x12\x17.amplifier.module.Empty\x1a\x1c.amplifier.module.ModuleInfob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x61mplifier_module.proto\x12\x10\x61mplifier.module\"\x07\n\x05\x45mpty\"F\n\x08ToolSpec\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"9\n\x12ToolExecuteRequest\x12\r\n\x05input\x18\x01 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x02 \x01(\t\"\x93\x01\n\x13ToolExecuteResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0e\n\x06output\x18\x02 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x03 \x01(\t\x12\r\n\x05\x65rror\x18\x04 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x05 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xd6\x01\n\nModuleInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x31\n\x0bmodule_type\x18\x04 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\x12\x13\n\x0bmount_point\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x06 \x01(\t\x12\x1a\n\x12\x63onfig_schema_json\x18\x07 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x08 \x03(\t\x12\x0e\n\x06\x61uthor\x18\t \x01(\t\"\x8c\x01\n\x0cMountRequest\x12:\n\x06\x63onfig\x18\x01 \x03(\x0b\x32*.amplifier.module.MountRequest.ConfigEntry\x12\x11\n\tmodule_id\x18\x02 \x01(\t\x1a-\n\x0b\x43onfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"_\n\rMountResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12.\n\x06status\x18\x03 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\"V\n\x13HealthCheckResponse\x12.\n\x06status\x18\x01 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xca\x02\n\x0b\x43onfigField\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x35\n\nfield_type\x18\x03 \x01(\x0e\x32!.amplifier.module.ConfigFieldType\x12\x0e\n\x06prompt\x18\x04 \x01(\t\x12\x0f\n\x07\x65nv_var\x18\x05 \x01(\t\x12\x0f\n\x07\x63hoices\x18\x06 \x03(\t\x12\x10\n\x08required\x18\x07 \x01(\x08\x12\x15\n\rdefault_value\x18\x08 \x01(\t\x12>\n\tshow_when\x18\t \x03(\x0b\x32+.amplifier.module.ConfigField.ShowWhenEntry\x12\x16\n\x0erequires_model\x18\n \x01(\x08\x1a/\n\rShowWhenEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xbc\x01\n\rProviderError\x12\x37\n\nerror_type\x18\x01 \x01(\x0e\x32#.amplifier.module.ProviderErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x15\n\rprovider_name\x18\x03 \x01(\t\x12\r\n\x05model\x18\x04 \x01(\t\x12\x13\n\x0bstatus_code\x18\x05 \x01(\x05\x12\x11\n\tretryable\x18\x06 \x01(\x08\x12\x13\n\x0bretry_after\x18\x07 \x01(\x01\"\x97\x01\n\tToolError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.ToolErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\ttool_name\x18\x03 \x01(\t\x12\x0e\n\x06stdout\x18\x04 \x01(\t\x12\x0e\n\x06stderr\x18\x05 \x01(\t\x12\x11\n\texit_code\x18\x06 \x01(\x05\"d\n\tHookError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.HookErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\thook_name\x18\x03 \x01(\t\"\xef\x01\n\x0e\x41mplifierError\x12\x39\n\x0eprovider_error\x18\x01 \x01(\x0b\x32\x1f.amplifier.module.ProviderErrorH\x00\x12\x31\n\ntool_error\x18\x02 \x01(\x0b\x32\x1b.amplifier.module.ToolErrorH\x00\x12\x31\n\nhook_error\x18\x03 \x01(\x0b\x32\x1b.amplifier.module.HookErrorH\x00\x12\x17\n\rgeneric_error\x18\x04 \x01(\tH\x00\x12\x1a\n\x10validation_error\x18\x05 \x01(\tH\x00\x42\x07\n\x05\x65rror\"\x19\n\tTextBlock\x12\x0c\n\x04text\x18\x01 \x01(\t\"E\n\rThinkingBlock\x12\x10\n\x08thinking\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\t\"%\n\x15RedactedThinkingBlock\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\t\"=\n\rToolCallBlock\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x12\n\ninput_json\x18\x03 \x01(\t\"<\n\x0fToolResultBlock\x12\x14\n\x0ctool_call_id\x18\x01 \x01(\t\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\"C\n\nImageBlock\x12\x12\n\nmedia_type\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x13\n\x0bsource_json\x18\x03 \x01(\t\"2\n\x0eReasoningBlock\x12\x0f\n\x07\x63ontent\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x03(\t\"\xf1\x03\n\x0c\x43ontentBlock\x12\x31\n\ntext_block\x18\x01 \x01(\x0b\x32\x1b.amplifier.module.TextBlockH\x00\x12\x39\n\x0ethinking_block\x18\x02 \x01(\x0b\x32\x1f.amplifier.module.ThinkingBlockH\x00\x12J\n\x17redacted_thinking_block\x18\x03 \x01(\x0b\x32\'.amplifier.module.RedactedThinkingBlockH\x00\x12:\n\x0ftool_call_block\x18\x04 \x01(\x0b\x32\x1f.amplifier.module.ToolCallBlockH\x00\x12>\n\x11tool_result_block\x18\x05 \x01(\x0b\x32!.amplifier.module.ToolResultBlockH\x00\x12\x33\n\x0bimage_block\x18\x06 \x01(\x0b\x32\x1c.amplifier.module.ImageBlockH\x00\x12;\n\x0freasoning_block\x18\x07 \x01(\x0b\x32 .amplifier.module.ReasoningBlockH\x00\x12\x30\n\nvisibility\x18\x08 \x01(\x0e\x32\x1c.amplifier.module.VisibilityB\x07\n\x05\x62lock\"B\n\x10\x43ontentBlockList\x12.\n\x06\x62locks\x18\x01 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xca\x01\n\x07Message\x12$\n\x04role\x18\x01 \x01(\x0e\x32\x16.amplifier.module.Role\x12\x16\n\x0ctext_content\x18\x02 \x01(\tH\x00\x12;\n\rblock_content\x18\x03 \x01(\x0b\x32\".amplifier.module.ContentBlockListH\x00\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x14\n\x0ctool_call_id\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\tB\t\n\x07\x63ontent\"C\n\x0fToolCallMessage\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x16\n\x0e\x61rguments_json\x18\x03 \x01(\t\"K\n\rToolSpecProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"7\n\x10JsonSchemaFormat\x12\x13\n\x0bschema_json\x18\x01 \x01(\t\x12\x0e\n\x06strict\x18\x02 \x01(\x08\"u\n\x0eResponseFormat\x12\x0e\n\x04text\x18\x01 \x01(\x08H\x00\x12\x0e\n\x04json\x18\x02 \x01(\x08H\x00\x12\x39\n\x0bjson_schema\x18\x03 \x01(\x0b\x32\".amplifier.module.JsonSchemaFormatH\x00\x42\x08\n\x06\x66ormat\"\xf7\x01\n\x05Usage\x12\x15\n\rprompt_tokens\x18\x01 \x01(\x05\x12\x19\n\x11\x63ompletion_tokens\x18\x02 \x01(\x05\x12\x14\n\x0ctotal_tokens\x18\x03 \x01(\x05\x12\x1d\n\x10reasoning_tokens\x18\x04 \x01(\x05H\x00\x88\x01\x01\x12\x1e\n\x11\x63\x61\x63he_read_tokens\x18\x05 \x01(\x05H\x01\x88\x01\x01\x12\"\n\x15\x63\x61\x63he_creation_tokens\x18\x06 \x01(\x05H\x02\x88\x01\x01\x42\x13\n\x11_reasoning_tokensB\x14\n\x12_cache_read_tokensB\x18\n\x16_cache_creation_tokens\"@\n\x0b\x44\x65gradation\x12\x11\n\trequested\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tual\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x81\x03\n\x0b\x43hatRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\x12.\n\x05tools\x18\x02 \x03(\x0b\x32\x1f.amplifier.module.ToolSpecProto\x12\x39\n\x0fresponse_format\x18\x03 \x01(\x0b\x32 .amplifier.module.ResponseFormat\x12\x13\n\x0btemperature\x18\x04 \x01(\x01\x12\r\n\x05top_p\x18\x05 \x01(\x01\x12\x19\n\x11max_output_tokens\x18\x06 \x01(\x05\x12\x17\n\x0f\x63onversation_id\x18\x07 \x01(\t\x12\x0e\n\x06stream\x18\x08 \x01(\x08\x12\x15\n\rmetadata_json\x18\t \x01(\t\x12\r\n\x05model\x18\n \x01(\t\x12\x13\n\x0btool_choice\x18\x0b \x01(\t\x12\x0c\n\x04stop\x18\x0c \x03(\t\x12\x18\n\x10reasoning_effort\x18\r \x01(\t\x12\x0f\n\x07timeout\x18\x0e \x01(\x01\"\x98\x02\n\x0c\x43hatResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x35\n\ntool_calls\x18\x02 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\x12&\n\x05usage\x18\x03 \x01(\x0b\x32\x17.amplifier.module.Usage\x12\x32\n\x0b\x64\x65gradation\x18\x04 \x01(\x0b\x32\x1d.amplifier.module.Degradation\x12\x15\n\rfinish_reason\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x07 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"~\n\nToolResult\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\x12\x12\n\nerror_json\x18\x03 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x04 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xa7\x04\n\nHookResult\x12,\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x1c.amplifier.module.HookAction\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x19\n\x11\x63ontext_injection\x18\x04 \x01(\t\x12\x46\n\x16\x63ontext_injection_role\x18\x05 \x01(\x0e\x32&.amplifier.module.ContextInjectionRole\x12\x11\n\tephemeral\x18\x06 \x01(\x08\x12\x17\n\x0f\x61pproval_prompt\x18\x07 \x01(\t\x12\x18\n\x10\x61pproval_options\x18\x08 \x03(\t\x12\x1d\n\x10\x61pproval_timeout\x18\t \x01(\x01H\x00\x88\x01\x01\x12;\n\x10\x61pproval_default\x18\n \x01(\x0e\x32!.amplifier.module.ApprovalDefault\x12\x17\n\x0fsuppress_output\x18\x0b \x01(\x08\x12\x14\n\x0cuser_message\x18\x0c \x01(\t\x12>\n\x12user_message_level\x18\r \x01(\x0e\x32\".amplifier.module.UserMessageLevel\x12\x1b\n\x13user_message_source\x18\x0e \x01(\t\x12\"\n\x1a\x61ppend_to_last_tool_result\x18\x0f \x01(\x08\x42\x13\n\x11_approval_timeout\"\x8d\x01\n\tModelInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x16\n\x0e\x63ontext_window\x18\x03 \x01(\x05\x12\x19\n\x11max_output_tokens\x18\x04 \x01(\x05\x12\x14\n\x0c\x63\x61pabilities\x18\x05 \x03(\t\x12\x15\n\rdefaults_json\x18\x06 \x01(\t\"\xb0\x01\n\x0cProviderInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x1b\n\x13\x63redential_env_vars\x18\x03 \x03(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x04 \x03(\t\x12\x15\n\rdefaults_json\x18\x05 \x01(\t\x12\x34\n\rconfig_fields\x18\x06 \x03(\x0b\x32\x1d.amplifier.module.ConfigField\"\x80\x01\n\x0f\x41pprovalRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x14\n\x0c\x64\x65tails_json\x18\x03 \x01(\t\x12\x12\n\nrisk_level\x18\x04 \x01(\t\x12\x14\n\x07timeout\x18\x05 \x01(\x01H\x00\x88\x01\x01\x42\n\n\x08_timeout\"F\n\x10\x41pprovalResponse\x12\x10\n\x08\x61pproved\x18\x01 \x01(\x08\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x10\n\x08remember\x18\x03 \x01(\x08\"A\n\x12ListModelsResponse\x12+\n\x06models\x18\x01 \x03(\x0b\x32\x1b.amplifier.module.ModelInfo\"O\n\x16ParseToolCallsResponse\x12\x35\n\ntool_calls\x18\x01 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\"@\n\x1aOrchestratorExecuteRequest\x12\x0e\n\x06prompt\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\">\n\x1bOrchestratorExecuteResponse\x12\x10\n\x08response\x18\x01 \x01(\t\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"?\n\x11\x41\x64\x64MessageRequest\x12*\n\x07message\x18\x01 \x01(\x0b\x32\x19.amplifier.module.Message\"B\n\x13GetMessagesResponse\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"J\n\x1bGetMessagesForRequestParams\x12\x14\n\x0ctoken_budget\x18\x01 \x01(\x05\x12\x15\n\rprovider_name\x18\x02 \x01(\t\"A\n\x12SetMessagesRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"5\n\x11HookHandleRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\".\n\x17GetSubscriptionsRequest\x12\x13\n\x0b\x63onfig_json\x18\x01 \x01(\t\"V\n\x18GetSubscriptionsResponse\x12:\n\rsubscriptions\x18\x01 \x03(\x0b\x32#.amplifier.module.EventSubscription\"B\n\x11\x45ventSubscription\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x10\n\x08priority\x18\x02 \x01(\x05\x12\x0c\n\x04name\x18\x03 \x01(\t\"d\n\x1b\x43ompleteWithProviderRequest\x12\x15\n\rprovider_name\x18\x01 \x01(\t\x12.\n\x07request\x18\x02 \x01(\x0b\x32\x1d.amplifier.module.ChatRequest\";\n\x12\x45xecuteToolRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x12\n\ninput_json\x18\x02 \x01(\t\"3\n\x0f\x45mitHookRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\"V\n\x19\x45mitHookAndCollectRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x17\n\x0ftimeout_seconds\x18\x03 \x01(\x01\"4\n\x1a\x45mitHookAndCollectResponse\x12\x16\n\x0eresponses_json\x18\x01 \x03(\t\"(\n\x12GetMessagesRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\"Y\n\x17KernelAddMessageRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12*\n\x07message\x18\x02 \x01(\x0b\x32\x19.amplifier.module.Message\"a\n\x17GetMountedModuleRequest\x12\x13\n\x0bmodule_name\x18\x01 \x01(\t\x12\x31\n\x0bmodule_type\x18\x02 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\"U\n\x18GetMountedModuleResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12*\n\x04info\x18\x02 \x01(\x0b\x32\x1c.amplifier.module.ModuleInfo\"=\n\x19RegisterCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\nvalue_json\x18\x02 \x01(\t\"$\n\x14GetCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\":\n\x15GetCapabilityResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12\x12\n\nvalue_json\x18\x02 \x01(\t*\xbc\x01\n\nModuleType\x12\x1b\n\x17MODULE_TYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14MODULE_TYPE_PROVIDER\x10\x01\x12\x14\n\x10MODULE_TYPE_TOOL\x10\x02\x12\x14\n\x10MODULE_TYPE_HOOK\x10\x03\x12\x16\n\x12MODULE_TYPE_MEMORY\x10\x04\x12\x19\n\x15MODULE_TYPE_GUARDRAIL\x10\x05\x12\x18\n\x14MODULE_TYPE_APPROVAL\x10\x06*\x82\x01\n\x0cHealthStatus\x12\x1d\n\x19HEALTH_STATUS_UNSPECIFIED\x10\x00\x12\x19\n\x15HEALTH_STATUS_SERVING\x10\x01\x12\x1d\n\x19HEALTH_STATUS_NOT_SERVING\x10\x02\x12\x19\n\x15HEALTH_STATUS_UNKNOWN\x10\x03*\xad\x01\n\x0f\x43onfigFieldType\x12!\n\x1d\x43ONFIG_FIELD_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_STRING\x10\x01\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_NUMBER\x10\x02\x12\x1d\n\x19\x43ONFIG_FIELD_TYPE_BOOLEAN\x10\x03\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_SECRET\x10\x04*\xd8\x02\n\x11ProviderErrorType\x12#\n\x1fPROVIDER_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18PROVIDER_ERROR_TYPE_AUTH\x10\x01\x12\"\n\x1ePROVIDER_ERROR_TYPE_RATE_LIMIT\x10\x02\x12&\n\"PROVIDER_ERROR_TYPE_CONTEXT_LENGTH\x10\x03\x12\'\n#PROVIDER_ERROR_TYPE_INVALID_REQUEST\x10\x04\x12&\n\"PROVIDER_ERROR_TYPE_CONTENT_FILTER\x10\x05\x12#\n\x1fPROVIDER_ERROR_TYPE_UNAVAILABLE\x10\x06\x12\x1f\n\x1bPROVIDER_ERROR_TYPE_TIMEOUT\x10\x07\x12\x1d\n\x19PROVIDER_ERROR_TYPE_OTHER\x10\x08*\x8c\x01\n\rToolErrorType\x12\x1f\n\x1bTOOL_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19TOOL_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aTOOL_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17TOOL_ERROR_TYPE_TIMEOUT\x10\x03*\x8c\x01\n\rHookErrorType\x12\x1f\n\x1bHOOK_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19HOOK_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aHOOK_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17HOOK_ERROR_TYPE_TIMEOUT\x10\x03*\x86\x01\n\x04Role\x12\x14\n\x10ROLE_UNSPECIFIED\x10\x00\x12\x0f\n\x0bROLE_SYSTEM\x10\x01\x12\r\n\tROLE_USER\x10\x02\x12\x12\n\x0eROLE_ASSISTANT\x10\x03\x12\r\n\tROLE_TOOL\x10\x04\x12\x11\n\rROLE_FUNCTION\x10\x05\x12\x12\n\x0eROLE_DEVELOPER\x10\x06*o\n\nVisibility\x12\x1a\n\x16VISIBILITY_UNSPECIFIED\x10\x00\x12\x12\n\x0eVISIBILITY_ALL\x10\x01\x12\x17\n\x13VISIBILITY_LLM_ONLY\x10\x02\x12\x18\n\x14VISIBILITY_USER_ONLY\x10\x03*\xab\x01\n\nHookAction\x12\x1b\n\x17HOOK_ACTION_UNSPECIFIED\x10\x00\x12\x18\n\x14HOOK_ACTION_CONTINUE\x10\x01\x12\x16\n\x12HOOK_ACTION_MODIFY\x10\x02\x12\x14\n\x10HOOK_ACTION_DENY\x10\x03\x12\x1e\n\x1aHOOK_ACTION_INJECT_CONTEXT\x10\x04\x12\x18\n\x14HOOK_ACTION_ASK_USER\x10\x05*\xa8\x01\n\x14\x43ontextInjectionRole\x12&\n\"CONTEXT_INJECTION_ROLE_UNSPECIFIED\x10\x00\x12!\n\x1d\x43ONTEXT_INJECTION_ROLE_SYSTEM\x10\x01\x12\x1f\n\x1b\x43ONTEXT_INJECTION_ROLE_USER\x10\x02\x12$\n CONTEXT_INJECTION_ROLE_ASSISTANT\x10\x03*l\n\x0f\x41pprovalDefault\x12 \n\x1c\x41PPROVAL_DEFAULT_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x41PPROVAL_DEFAULT_APPROVE\x10\x01\x12\x19\n\x15\x41PPROVAL_DEFAULT_DENY\x10\x02*\x91\x01\n\x10UserMessageLevel\x12\"\n\x1eUSER_MESSAGE_LEVEL_UNSPECIFIED\x10\x00\x12\x1b\n\x17USER_MESSAGE_LEVEL_INFO\x10\x01\x12\x1e\n\x1aUSER_MESSAGE_LEVEL_WARNING\x10\x02\x12\x1c\n\x18USER_MESSAGE_LEVEL_ERROR\x10\x03\x32\xa5\x01\n\x0bToolService\x12>\n\x07GetSpec\x12\x17.amplifier.module.Empty\x1a\x1a.amplifier.module.ToolSpec\x12V\n\x07\x45xecute\x12$.amplifier.module.ToolExecuteRequest\x1a%.amplifier.module.ToolExecuteResponse2\x9f\x03\n\x0fProviderService\x12\x42\n\x07GetInfo\x12\x17.amplifier.module.Empty\x1a\x1e.amplifier.module.ProviderInfo\x12K\n\nListModels\x12\x17.amplifier.module.Empty\x1a$.amplifier.module.ListModelsResponse\x12I\n\x08\x43omplete\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse\x12T\n\x11\x43ompleteStreaming\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Z\n\x0eParseToolCalls\x12\x1e.amplifier.module.ChatResponse\x1a(.amplifier.module.ParseToolCallsResponse2}\n\x13OrchestratorService\x12\x66\n\x07\x45xecute\x12,.amplifier.module.OrchestratorExecuteRequest\x1a-.amplifier.module.OrchestratorExecuteResponse2\xa3\x03\n\x0e\x43ontextService\x12J\n\nAddMessage\x12#.amplifier.module.AddMessageRequest\x1a\x17.amplifier.module.Empty\x12M\n\x0bGetMessages\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.GetMessagesResponse\x12m\n\x15GetMessagesForRequest\x12-.amplifier.module.GetMessagesForRequestParams\x1a%.amplifier.module.GetMessagesResponse\x12L\n\x0bSetMessages\x12$.amplifier.module.SetMessagesRequest\x1a\x17.amplifier.module.Empty\x12\x39\n\x05\x43lear\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty2\xc5\x01\n\x0bHookService\x12K\n\x06Handle\x12#.amplifier.module.HookHandleRequest\x1a\x1c.amplifier.module.HookResult\x12i\n\x10GetSubscriptions\x12).amplifier.module.GetSubscriptionsRequest\x1a*.amplifier.module.GetSubscriptionsResponse2k\n\x0f\x41pprovalService\x12X\n\x0fRequestApproval\x12!.amplifier.module.ApprovalRequest\x1a\".amplifier.module.ApprovalResponse2\xd0\x07\n\rKernelService\x12\x65\n\x14\x43ompleteWithProvider\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse\x12p\n\x1d\x43ompleteWithProviderStreaming\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Q\n\x0b\x45xecuteTool\x12$.amplifier.module.ExecuteToolRequest\x1a\x1c.amplifier.module.ToolResult\x12K\n\x08\x45mitHook\x12!.amplifier.module.EmitHookRequest\x1a\x1c.amplifier.module.HookResult\x12o\n\x12\x45mitHookAndCollect\x12+.amplifier.module.EmitHookAndCollectRequest\x1a,.amplifier.module.EmitHookAndCollectResponse\x12Z\n\x0bGetMessages\x12$.amplifier.module.GetMessagesRequest\x1a%.amplifier.module.GetMessagesResponse\x12P\n\nAddMessage\x12).amplifier.module.KernelAddMessageRequest\x1a\x17.amplifier.module.Empty\x12i\n\x10GetMountedModule\x12).amplifier.module.GetMountedModuleRequest\x1a*.amplifier.module.GetMountedModuleResponse\x12Z\n\x12RegisterCapability\x12+.amplifier.module.RegisterCapabilityRequest\x1a\x17.amplifier.module.Empty\x12`\n\rGetCapability\x12&.amplifier.module.GetCapabilityRequest\x1a\'.amplifier.module.GetCapabilityResponse2\xaf\x02\n\x0fModuleLifecycle\x12H\n\x05Mount\x12\x1e.amplifier.module.MountRequest\x1a\x1f.amplifier.module.MountResponse\x12;\n\x07\x43leanup\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty\x12M\n\x0bHealthCheck\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.HealthCheckResponse\x12\x46\n\rGetModuleInfo\x12\x17.amplifier.module.Empty\x1a\x1c.amplifier.module.ModuleInfob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -35,170 +35,170 @@ _globals['_MOUNTREQUEST_CONFIGENTRY']._serialized_options = b'8\001' _globals['_CONFIGFIELD_SHOWWHENENTRY']._loaded_options = None _globals['_CONFIGFIELD_SHOWWHENENTRY']._serialized_options = b'8\001' - _globals['_MODULETYPE']._serialized_start=7113 - _globals['_MODULETYPE']._serialized_end=7301 - _globals['_HEALTHSTATUS']._serialized_start=7304 - _globals['_HEALTHSTATUS']._serialized_end=7434 - _globals['_CONFIGFIELDTYPE']._serialized_start=7437 - _globals['_CONFIGFIELDTYPE']._serialized_end=7610 - _globals['_PROVIDERERRORTYPE']._serialized_start=7613 - _globals['_PROVIDERERRORTYPE']._serialized_end=7957 - _globals['_TOOLERRORTYPE']._serialized_start=7960 - _globals['_TOOLERRORTYPE']._serialized_end=8100 - _globals['_HOOKERRORTYPE']._serialized_start=8103 - _globals['_HOOKERRORTYPE']._serialized_end=8243 - _globals['_ROLE']._serialized_start=8246 - _globals['_ROLE']._serialized_end=8380 - _globals['_VISIBILITY']._serialized_start=8382 - _globals['_VISIBILITY']._serialized_end=8493 - _globals['_HOOKACTION']._serialized_start=8496 - _globals['_HOOKACTION']._serialized_end=8667 - _globals['_CONTEXTINJECTIONROLE']._serialized_start=8670 - _globals['_CONTEXTINJECTIONROLE']._serialized_end=8838 - _globals['_APPROVALDEFAULT']._serialized_start=8840 - _globals['_APPROVALDEFAULT']._serialized_end=8948 - _globals['_USERMESSAGELEVEL']._serialized_start=8951 - _globals['_USERMESSAGELEVEL']._serialized_end=9096 + _globals['_MODULETYPE']._serialized_start=7226 + _globals['_MODULETYPE']._serialized_end=7414 + _globals['_HEALTHSTATUS']._serialized_start=7417 + _globals['_HEALTHSTATUS']._serialized_end=7547 + _globals['_CONFIGFIELDTYPE']._serialized_start=7550 + _globals['_CONFIGFIELDTYPE']._serialized_end=7723 + _globals['_PROVIDERERRORTYPE']._serialized_start=7726 + _globals['_PROVIDERERRORTYPE']._serialized_end=8070 + _globals['_TOOLERRORTYPE']._serialized_start=8073 + _globals['_TOOLERRORTYPE']._serialized_end=8213 + _globals['_HOOKERRORTYPE']._serialized_start=8216 + _globals['_HOOKERRORTYPE']._serialized_end=8356 + _globals['_ROLE']._serialized_start=8359 + _globals['_ROLE']._serialized_end=8493 + _globals['_VISIBILITY']._serialized_start=8495 + _globals['_VISIBILITY']._serialized_end=8606 + _globals['_HOOKACTION']._serialized_start=8609 + _globals['_HOOKACTION']._serialized_end=8780 + _globals['_CONTEXTINJECTIONROLE']._serialized_start=8783 + _globals['_CONTEXTINJECTIONROLE']._serialized_end=8951 + _globals['_APPROVALDEFAULT']._serialized_start=8953 + _globals['_APPROVALDEFAULT']._serialized_end=9061 + _globals['_USERMESSAGELEVEL']._serialized_start=9064 + _globals['_USERMESSAGELEVEL']._serialized_end=9209 _globals['_EMPTY']._serialized_start=44 _globals['_EMPTY']._serialized_end=51 _globals['_TOOLSPEC']._serialized_start=53 _globals['_TOOLSPEC']._serialized_end=123 _globals['_TOOLEXECUTEREQUEST']._serialized_start=125 _globals['_TOOLEXECUTEREQUEST']._serialized_end=182 - _globals['_TOOLEXECUTERESPONSE']._serialized_start=184 - _globals['_TOOLEXECUTERESPONSE']._serialized_end=275 - _globals['_MODULEINFO']._serialized_start=278 - _globals['_MODULEINFO']._serialized_end=492 - _globals['_MOUNTREQUEST']._serialized_start=495 - _globals['_MOUNTREQUEST']._serialized_end=635 - _globals['_MOUNTREQUEST_CONFIGENTRY']._serialized_start=590 - _globals['_MOUNTREQUEST_CONFIGENTRY']._serialized_end=635 - _globals['_MOUNTRESPONSE']._serialized_start=637 - _globals['_MOUNTRESPONSE']._serialized_end=732 - _globals['_HEALTHCHECKRESPONSE']._serialized_start=734 - _globals['_HEALTHCHECKRESPONSE']._serialized_end=820 - _globals['_CONFIGFIELD']._serialized_start=823 - _globals['_CONFIGFIELD']._serialized_end=1153 - _globals['_CONFIGFIELD_SHOWWHENENTRY']._serialized_start=1106 - _globals['_CONFIGFIELD_SHOWWHENENTRY']._serialized_end=1153 - _globals['_PROVIDERERROR']._serialized_start=1156 - _globals['_PROVIDERERROR']._serialized_end=1344 - _globals['_TOOLERROR']._serialized_start=1347 - _globals['_TOOLERROR']._serialized_end=1498 - _globals['_HOOKERROR']._serialized_start=1500 - _globals['_HOOKERROR']._serialized_end=1600 - _globals['_AMPLIFIERERROR']._serialized_start=1603 - _globals['_AMPLIFIERERROR']._serialized_end=1842 - _globals['_TEXTBLOCK']._serialized_start=1844 - _globals['_TEXTBLOCK']._serialized_end=1869 - _globals['_THINKINGBLOCK']._serialized_start=1871 - _globals['_THINKINGBLOCK']._serialized_end=1940 - _globals['_REDACTEDTHINKINGBLOCK']._serialized_start=1942 - _globals['_REDACTEDTHINKINGBLOCK']._serialized_end=1979 - _globals['_TOOLCALLBLOCK']._serialized_start=1981 - _globals['_TOOLCALLBLOCK']._serialized_end=2042 - _globals['_TOOLRESULTBLOCK']._serialized_start=2044 - _globals['_TOOLRESULTBLOCK']._serialized_end=2104 - _globals['_IMAGEBLOCK']._serialized_start=2106 - _globals['_IMAGEBLOCK']._serialized_end=2173 - _globals['_REASONINGBLOCK']._serialized_start=2175 - _globals['_REASONINGBLOCK']._serialized_end=2225 - _globals['_CONTENTBLOCK']._serialized_start=2228 - _globals['_CONTENTBLOCK']._serialized_end=2725 - _globals['_CONTENTBLOCKLIST']._serialized_start=2727 - _globals['_CONTENTBLOCKLIST']._serialized_end=2793 - _globals['_MESSAGE']._serialized_start=2796 - _globals['_MESSAGE']._serialized_end=2998 - _globals['_TOOLCALLMESSAGE']._serialized_start=3000 - _globals['_TOOLCALLMESSAGE']._serialized_end=3067 - _globals['_TOOLSPECPROTO']._serialized_start=3069 - _globals['_TOOLSPECPROTO']._serialized_end=3144 - _globals['_JSONSCHEMAFORMAT']._serialized_start=3146 - _globals['_JSONSCHEMAFORMAT']._serialized_end=3201 - _globals['_RESPONSEFORMAT']._serialized_start=3203 - _globals['_RESPONSEFORMAT']._serialized_end=3320 - _globals['_USAGE']._serialized_start=3323 - _globals['_USAGE']._serialized_end=3570 - _globals['_DEGRADATION']._serialized_start=3572 - _globals['_DEGRADATION']._serialized_end=3636 - _globals['_CHATREQUEST']._serialized_start=3639 - _globals['_CHATREQUEST']._serialized_end=4024 - _globals['_CHATRESPONSE']._serialized_start=4027 - _globals['_CHATRESPONSE']._serialized_end=4307 - _globals['_TOOLRESULT']._serialized_start=4309 - _globals['_TOOLRESULT']._serialized_end=4379 - _globals['_HOOKRESULT']._serialized_start=4382 - _globals['_HOOKRESULT']._serialized_end=4933 - _globals['_MODELINFO']._serialized_start=4936 - _globals['_MODELINFO']._serialized_end=5077 - _globals['_PROVIDERINFO']._serialized_start=5080 - _globals['_PROVIDERINFO']._serialized_end=5256 - _globals['_APPROVALREQUEST']._serialized_start=5259 - _globals['_APPROVALREQUEST']._serialized_end=5387 - _globals['_APPROVALRESPONSE']._serialized_start=5389 - _globals['_APPROVALRESPONSE']._serialized_end=5459 - _globals['_LISTMODELSRESPONSE']._serialized_start=5461 - _globals['_LISTMODELSRESPONSE']._serialized_end=5526 - _globals['_PARSETOOLCALLSRESPONSE']._serialized_start=5528 - _globals['_PARSETOOLCALLSRESPONSE']._serialized_end=5607 - _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_start=5609 - _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_end=5673 - _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_start=5675 - _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_end=5737 - _globals['_ADDMESSAGEREQUEST']._serialized_start=5739 - _globals['_ADDMESSAGEREQUEST']._serialized_end=5802 - _globals['_GETMESSAGESRESPONSE']._serialized_start=5804 - _globals['_GETMESSAGESRESPONSE']._serialized_end=5870 - _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_start=5872 - _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_end=5946 - _globals['_SETMESSAGESREQUEST']._serialized_start=5948 - _globals['_SETMESSAGESREQUEST']._serialized_end=6013 - _globals['_HOOKHANDLEREQUEST']._serialized_start=6015 - _globals['_HOOKHANDLEREQUEST']._serialized_end=6068 - _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_start=6070 - _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_end=6116 - _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_start=6118 - _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_end=6204 - _globals['_EVENTSUBSCRIPTION']._serialized_start=6206 - _globals['_EVENTSUBSCRIPTION']._serialized_end=6272 - _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_start=6274 - _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_end=6374 - _globals['_EXECUTETOOLREQUEST']._serialized_start=6376 - _globals['_EXECUTETOOLREQUEST']._serialized_end=6435 - _globals['_EMITHOOKREQUEST']._serialized_start=6437 - _globals['_EMITHOOKREQUEST']._serialized_end=6488 - _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_start=6490 - _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_end=6576 - _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_start=6578 - _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_end=6630 - _globals['_GETMESSAGESREQUEST']._serialized_start=6632 - _globals['_GETMESSAGESREQUEST']._serialized_end=6672 - _globals['_KERNELADDMESSAGEREQUEST']._serialized_start=6674 - _globals['_KERNELADDMESSAGEREQUEST']._serialized_end=6763 - _globals['_GETMOUNTEDMODULEREQUEST']._serialized_start=6765 - _globals['_GETMOUNTEDMODULEREQUEST']._serialized_end=6862 - _globals['_GETMOUNTEDMODULERESPONSE']._serialized_start=6864 - _globals['_GETMOUNTEDMODULERESPONSE']._serialized_end=6949 - _globals['_REGISTERCAPABILITYREQUEST']._serialized_start=6951 - _globals['_REGISTERCAPABILITYREQUEST']._serialized_end=7012 - _globals['_GETCAPABILITYREQUEST']._serialized_start=7014 - _globals['_GETCAPABILITYREQUEST']._serialized_end=7050 - _globals['_GETCAPABILITYRESPONSE']._serialized_start=7052 - _globals['_GETCAPABILITYRESPONSE']._serialized_end=7110 - _globals['_TOOLSERVICE']._serialized_start=9099 - _globals['_TOOLSERVICE']._serialized_end=9264 - _globals['_PROVIDERSERVICE']._serialized_start=9267 - _globals['_PROVIDERSERVICE']._serialized_end=9682 - _globals['_ORCHESTRATORSERVICE']._serialized_start=9684 - _globals['_ORCHESTRATORSERVICE']._serialized_end=9809 - _globals['_CONTEXTSERVICE']._serialized_start=9812 - _globals['_CONTEXTSERVICE']._serialized_end=10231 - _globals['_HOOKSERVICE']._serialized_start=10234 - _globals['_HOOKSERVICE']._serialized_end=10431 - _globals['_APPROVALSERVICE']._serialized_start=10433 - _globals['_APPROVALSERVICE']._serialized_end=10540 - _globals['_KERNELSERVICE']._serialized_start=10543 - _globals['_KERNELSERVICE']._serialized_end=11519 - _globals['_MODULELIFECYCLE']._serialized_start=11522 - _globals['_MODULELIFECYCLE']._serialized_end=11825 + _globals['_TOOLEXECUTERESPONSE']._serialized_start=185 + _globals['_TOOLEXECUTERESPONSE']._serialized_end=332 + _globals['_MODULEINFO']._serialized_start=335 + _globals['_MODULEINFO']._serialized_end=549 + _globals['_MOUNTREQUEST']._serialized_start=552 + _globals['_MOUNTREQUEST']._serialized_end=692 + _globals['_MOUNTREQUEST_CONFIGENTRY']._serialized_start=647 + _globals['_MOUNTREQUEST_CONFIGENTRY']._serialized_end=692 + _globals['_MOUNTRESPONSE']._serialized_start=694 + _globals['_MOUNTRESPONSE']._serialized_end=789 + _globals['_HEALTHCHECKRESPONSE']._serialized_start=791 + _globals['_HEALTHCHECKRESPONSE']._serialized_end=877 + _globals['_CONFIGFIELD']._serialized_start=880 + _globals['_CONFIGFIELD']._serialized_end=1210 + _globals['_CONFIGFIELD_SHOWWHENENTRY']._serialized_start=1163 + _globals['_CONFIGFIELD_SHOWWHENENTRY']._serialized_end=1210 + _globals['_PROVIDERERROR']._serialized_start=1213 + _globals['_PROVIDERERROR']._serialized_end=1401 + _globals['_TOOLERROR']._serialized_start=1404 + _globals['_TOOLERROR']._serialized_end=1555 + _globals['_HOOKERROR']._serialized_start=1557 + _globals['_HOOKERROR']._serialized_end=1657 + _globals['_AMPLIFIERERROR']._serialized_start=1660 + _globals['_AMPLIFIERERROR']._serialized_end=1899 + _globals['_TEXTBLOCK']._serialized_start=1901 + _globals['_TEXTBLOCK']._serialized_end=1926 + _globals['_THINKINGBLOCK']._serialized_start=1928 + _globals['_THINKINGBLOCK']._serialized_end=1997 + _globals['_REDACTEDTHINKINGBLOCK']._serialized_start=1999 + _globals['_REDACTEDTHINKINGBLOCK']._serialized_end=2036 + _globals['_TOOLCALLBLOCK']._serialized_start=2038 + _globals['_TOOLCALLBLOCK']._serialized_end=2099 + _globals['_TOOLRESULTBLOCK']._serialized_start=2101 + _globals['_TOOLRESULTBLOCK']._serialized_end=2161 + _globals['_IMAGEBLOCK']._serialized_start=2163 + _globals['_IMAGEBLOCK']._serialized_end=2230 + _globals['_REASONINGBLOCK']._serialized_start=2232 + _globals['_REASONINGBLOCK']._serialized_end=2282 + _globals['_CONTENTBLOCK']._serialized_start=2285 + _globals['_CONTENTBLOCK']._serialized_end=2782 + _globals['_CONTENTBLOCKLIST']._serialized_start=2784 + _globals['_CONTENTBLOCKLIST']._serialized_end=2850 + _globals['_MESSAGE']._serialized_start=2853 + _globals['_MESSAGE']._serialized_end=3055 + _globals['_TOOLCALLMESSAGE']._serialized_start=3057 + _globals['_TOOLCALLMESSAGE']._serialized_end=3124 + _globals['_TOOLSPECPROTO']._serialized_start=3126 + _globals['_TOOLSPECPROTO']._serialized_end=3201 + _globals['_JSONSCHEMAFORMAT']._serialized_start=3203 + _globals['_JSONSCHEMAFORMAT']._serialized_end=3258 + _globals['_RESPONSEFORMAT']._serialized_start=3260 + _globals['_RESPONSEFORMAT']._serialized_end=3377 + _globals['_USAGE']._serialized_start=3380 + _globals['_USAGE']._serialized_end=3627 + _globals['_DEGRADATION']._serialized_start=3629 + _globals['_DEGRADATION']._serialized_end=3693 + _globals['_CHATREQUEST']._serialized_start=3696 + _globals['_CHATREQUEST']._serialized_end=4081 + _globals['_CHATRESPONSE']._serialized_start=4084 + _globals['_CHATRESPONSE']._serialized_end=4364 + _globals['_TOOLRESULT']._serialized_start=4366 + _globals['_TOOLRESULT']._serialized_end=4492 + _globals['_HOOKRESULT']._serialized_start=4495 + _globals['_HOOKRESULT']._serialized_end=5046 + _globals['_MODELINFO']._serialized_start=5049 + _globals['_MODELINFO']._serialized_end=5190 + _globals['_PROVIDERINFO']._serialized_start=5193 + _globals['_PROVIDERINFO']._serialized_end=5369 + _globals['_APPROVALREQUEST']._serialized_start=5372 + _globals['_APPROVALREQUEST']._serialized_end=5500 + _globals['_APPROVALRESPONSE']._serialized_start=5502 + _globals['_APPROVALRESPONSE']._serialized_end=5572 + _globals['_LISTMODELSRESPONSE']._serialized_start=5574 + _globals['_LISTMODELSRESPONSE']._serialized_end=5639 + _globals['_PARSETOOLCALLSRESPONSE']._serialized_start=5641 + _globals['_PARSETOOLCALLSRESPONSE']._serialized_end=5720 + _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_start=5722 + _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_end=5786 + _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_start=5788 + _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_end=5850 + _globals['_ADDMESSAGEREQUEST']._serialized_start=5852 + _globals['_ADDMESSAGEREQUEST']._serialized_end=5915 + _globals['_GETMESSAGESRESPONSE']._serialized_start=5917 + _globals['_GETMESSAGESRESPONSE']._serialized_end=5983 + _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_start=5985 + _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_end=6059 + _globals['_SETMESSAGESREQUEST']._serialized_start=6061 + _globals['_SETMESSAGESREQUEST']._serialized_end=6126 + _globals['_HOOKHANDLEREQUEST']._serialized_start=6128 + _globals['_HOOKHANDLEREQUEST']._serialized_end=6181 + _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_start=6183 + _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_end=6229 + _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_start=6231 + _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_end=6317 + _globals['_EVENTSUBSCRIPTION']._serialized_start=6319 + _globals['_EVENTSUBSCRIPTION']._serialized_end=6385 + _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_start=6387 + _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_end=6487 + _globals['_EXECUTETOOLREQUEST']._serialized_start=6489 + _globals['_EXECUTETOOLREQUEST']._serialized_end=6548 + _globals['_EMITHOOKREQUEST']._serialized_start=6550 + _globals['_EMITHOOKREQUEST']._serialized_end=6601 + _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_start=6603 + _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_end=6689 + _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_start=6691 + _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_end=6743 + _globals['_GETMESSAGESREQUEST']._serialized_start=6745 + _globals['_GETMESSAGESREQUEST']._serialized_end=6785 + _globals['_KERNELADDMESSAGEREQUEST']._serialized_start=6787 + _globals['_KERNELADDMESSAGEREQUEST']._serialized_end=6876 + _globals['_GETMOUNTEDMODULEREQUEST']._serialized_start=6878 + _globals['_GETMOUNTEDMODULEREQUEST']._serialized_end=6975 + _globals['_GETMOUNTEDMODULERESPONSE']._serialized_start=6977 + _globals['_GETMOUNTEDMODULERESPONSE']._serialized_end=7062 + _globals['_REGISTERCAPABILITYREQUEST']._serialized_start=7064 + _globals['_REGISTERCAPABILITYREQUEST']._serialized_end=7125 + _globals['_GETCAPABILITYREQUEST']._serialized_start=7127 + _globals['_GETCAPABILITYREQUEST']._serialized_end=7163 + _globals['_GETCAPABILITYRESPONSE']._serialized_start=7165 + _globals['_GETCAPABILITYRESPONSE']._serialized_end=7223 + _globals['_TOOLSERVICE']._serialized_start=9212 + _globals['_TOOLSERVICE']._serialized_end=9377 + _globals['_PROVIDERSERVICE']._serialized_start=9380 + _globals['_PROVIDERSERVICE']._serialized_end=9795 + _globals['_ORCHESTRATORSERVICE']._serialized_start=9797 + _globals['_ORCHESTRATORSERVICE']._serialized_end=9922 + _globals['_CONTEXTSERVICE']._serialized_start=9925 + _globals['_CONTEXTSERVICE']._serialized_end=10344 + _globals['_HOOKSERVICE']._serialized_start=10347 + _globals['_HOOKSERVICE']._serialized_end=10544 + _globals['_APPROVALSERVICE']._serialized_start=10546 + _globals['_APPROVALSERVICE']._serialized_end=10653 + _globals['_KERNELSERVICE']._serialized_start=10656 + _globals['_KERNELSERVICE']._serialized_end=11632 + _globals['_MODULELIFECYCLE']._serialized_start=11635 + _globals['_MODULELIFECYCLE']._serialized_end=11938 # @@protoc_insertion_point(module_scope) diff --git a/python/amplifier_core/loader_grpc.py b/python/amplifier_core/loader_grpc.py index 41b0c3c..3690652 100644 --- a/python/amplifier_core/loader_grpc.py +++ b/python/amplifier_core/loader_grpc.py @@ -9,6 +9,7 @@ """ import json +import base64 import logging from typing import Any @@ -105,6 +106,39 @@ def _deserialize_output(self, output_bytes: bytes, content_type: str) -> Any: logger.warning(f"Unknown content type '{content_type}', attempting JSON decode") return json.loads(output_bytes.decode("utf-8")) + def _deserialize_content_blocks(self, content_blocks: Any) -> list[dict[str, Any]] | None: + """Decode the ToolResult-specific protobuf representation.""" + if not content_blocks: + return None + + raw_content: list[dict[str, Any]] = [] + for block in content_blocks: + block_type = block.WhichOneof("block") + if block_type == "text_block": + raw_content.append({"type": "text", "text": block.text_block.text}) + elif block_type == "image_block": + raw_content.append( + { + "type": "image", + "source": { + "type": "base64", + "media_type": block.image_block.media_type, + "data": base64.b64encode(block.image_block.data).decode("ascii"), + }, + } + ) + else: + raise ValueError("invalid tool result content") + + from amplifier_core.models import ToolResult + + result = ToolResult(content=raw_content) + return ( + [item.model_dump(exclude_none=True) for item in result.content] + if result.content is not None + else None + ) + async def execute(self, **kwargs: Any) -> dict[str, Any]: """Execute the tool via gRPC. @@ -131,18 +165,22 @@ async def execute(self, **kwargs: Any) -> dict[str, Any]: content_type=content_type, ) response = await self._stub.Execute(request) + content = self._deserialize_content_blocks(response.content_blocks) if response.success: output = self._deserialize_output( response.output, response.content_type ) - return {"success": True, "output": output, "error": None} + result = {"success": True, "output": output, "error": None} else: - return { + result = { "success": False, "output": None, "error": {"message": response.error}, } + if content is not None: + result["content"] = content + return result except Exception as e: logger.error(f"gRPC tool execution failed for '{self._name}': {e}") diff --git a/python/amplifier_core/models.py b/python/amplifier_core/models.py index 2bf3330..7a655f0 100644 --- a/python/amplifier_core/models.py +++ b/python/amplifier_core/models.py @@ -11,10 +11,14 @@ from typing import Literal from pydantic import BaseModel +from pydantic import ConfigDict from pydantic import Field from pydantic import field_serializer from pydantic import field_validator +from .message_models import ImageBlock +from .message_models import TextBlock + def _json_default(obj: Any) -> Any: """JSON encoder default for raw json.dumps() paths. @@ -56,11 +60,44 @@ def _sanitize_for_llm(text: str) -> str: class ToolResult(BaseModel): """Result from tool execution.""" + model_config = ConfigDict(hide_input_in_errors=True) + success: bool = Field(default=True, description="Whether execution succeeded") output: Any | None = Field(default=None, description="Tool output data") error: dict[str, Any] | None = Field( default=None, description="Error details if failed" ) + content: list[TextBlock | ImageBlock] | None = Field( + default=None, description="Canonical rich tool-result content" + ) + + @field_validator("content", mode="before") + @classmethod + def normalize_content(cls, content: Any) -> Any: + """Normalize untrusted rich content through the Rust validation boundary.""" + if content is None: + return None + + from amplifier_core._engine import _normalize_tool_result_content + + serialized = json.dumps( + content, + default=lambda value: value.model_dump(exclude_none=True), + ) + normalized = _normalize_tool_result_content(serialized) + return json.loads(normalized) if normalized is not None else None + + @field_serializer("content", when_used="unless-none") + def serialize_content(self, content: list[TextBlock | ImageBlock]) -> list[dict[str, Any]]: + """Emit only the canonical block fields retained by Rust normalization.""" + return [block.model_dump(exclude_none=True) for block in content] + + def model_dump(self, **kwargs: Any) -> dict[str, Any]: + """Preserve the legacy no-content key set while exposing rich content.""" + result = super().model_dump(**kwargs) + if self.content is None: + result.pop("content", None) + return result def model_post_init(self, __context: Any) -> None: """Auto-populate output from error when tools forget to set it. @@ -123,6 +160,26 @@ def get_serialized_output(self) -> str: # Success with no output return "Success" + def safe_hook_presentation(self) -> dict[str, Any]: + """Return an image-safe hook event payload from the Rust core.""" + from amplifier_core._engine import _tool_result_safe_hook_presentation + + payload = { + "success": self.success, + "output": self.output, + "error": self.error, + "content": ( + [block.model_dump(exclude_none=True) for block in self.content] + if self.content is not None + else None + ), + } + return json.loads( + _tool_result_safe_hook_presentation( + json.dumps(payload, default=_json_default) + ) + ) + class HookResult(BaseModel): """ diff --git a/tests/fixtures/wasm/echo-tool.wasm b/tests/fixtures/wasm/echo-tool.wasm index 78afa2ce9cfbfdf439f6fdeb95c60c636ade07d7..a4024ec445314632f85cbba77aee1d0a4d8ceba3 100644 GIT binary patch delta 21740 zcmch931C!3^7mBFpZ-PP6A)pd1szfSHx zobkfScK2^hH~%m{LvBkSmKKYdCYUHDDWh{!?+hEoHi~;hYMRAlG7+Vl(WV4}Jq|5WIr;<%=-A8?esiXR0 zA5{obw#xgK9Kw{d{1jo2nNH0N*VIm`Ziv=Qo;)pV8k4aKvkQ^+ob+?rwa@fh2L}cQ z$NW{CdG6_FpLO9G6YD3%7K_J8(OPO)PdA7=sEM}FMtYF8(!F#)ZKh3hA3Z^D&@=QT zJw;E`Ji3Y=rt9b$T1eN@Vp>F3(?j$aJxUMIBOZE=w$qE$OwZB_w2hvpJLxXEhwi2; zu6&tZqN~Mi;vgNSjp8P;NvsgF&kj@|km!Ll6Qt-3>En1^oUL2s20 zbi1-cg30;1^R4Jy((whm*%G5G&9UB)3)tK(`;P3{YYW+h-Qp8Z^4shakK!G&*W>Xh z)!SCc2m17n*+sd@UYHU}JwD3>EKw|qd}0|Rntb9RyoWJGn_p~X*uv%D!yq_pJ>aQ+ zp$8G6jZ_~WU>PMw_!}@xRaq3epSy+pAn-BOht})b7yPuv?lA<(LzkEe{N{;V0+XTi z`Vy1LZ<=PbO!J%4bjdu?Z`O>Km`#4O&1g;Yo4TOC*-?-xDnq7n)9&xKVnFlAEQo@O zwnvy1tIaSQ2U`sT3N10PXUsL-c$wljmJr2p(yFqAJuUS8fvxwZ`b-!*1d&e1z-c}) z*{DrCRwpxedij&v(pox|8J&uaPDOZ`V*^cgyrB=CN$3j`%g$bJdy&}_mBxRpqerQI zni<|NOlFgn!K|t$sW1&wG!>-bUwS54nMg5IQkd=&gN@MwAPALtPy;ZD1ptdeF)Dyu zi^Vo7Nax-_0cD<=!+0@w;O9B!CsP2$7*&gnN5Uk@_Ofo};dv$@OM`7ILt2q_Q6uL~ zhVV7M)&|?j- z=<%}u{}VkZf2U0?mR?^hw6NF#1q5}G!QUD(-plA%ww=maI>I3^7oLFyz2F`RFG@+) zk~UIwv_MPcrp2tKfiWls@M6=#gwjMPexu*q>9=|2Hi` z|Lnxte&X)b#zWvBsmW4gJ7)=2n3l501|tkM*&E)rk7~xH>tav-xd| zt2K6{_htM^y&l0)ySVomT&W|tl1Ffr6n(%Gt2LJFeY}W(?f(~g{j+L~A@Kdzy)W)j z?V#3}u@1oitPp*=*6S@v5-c&T!H{Hu!2O0)aV!ExB z5Tp}VMU2A{sz1ROWkHgkJ-hR|)@46Q z{7F&nhy|=YI&Hn`hcC^4wCuM8Gvr@?-s=DFM14$2$Nu}vK4x6TSq>~LlpfQ|k1(;@ zmmn(nwI!ys$Om{vKDl0JmH#o||4&5}gW>&7YV=>pjAEQP93A*E?NXKz=8m9{yh1M{ zzp1y~5`u_KcC5A~rip$N0g%r4$RCje>BJeWjFe{}+5M)j0O;f&$#n}3@*kaRGodaz z%AjPn`Ar3&MgvU|yo66ofPP@`=Nq+i@q*lt-q=(9X3kbvVezQeNXfydgMqq*fCK~N zok*yWjGY3CkH&c0FcBrq8Q8bo&zYlb}`xqXqbsG~CZT2x>p_M!AlJp)XE87l2C1Ro<363e0$)f(& z)MC1}h0JpZ3~&k_SEx^9q6ik!@j^Cd;ar3>J3Q(AKS;zA@uZxh6cY3;z9r}rw?0pAvfZ;8X7gf0p+KV=0eoMQrKR~%=%e#hdQ zHr@~>=lB>rF6v@TI9bwcFUbkM#?)glW>WHEPmk{cEFs=-8M^Ao5`ZtmTnk~np_q;W zEs|s*BM6w+^8{f0hGbD@!?xDjEpUZGixFA5s!MFK>!~nR=dQ3wliARbZZ$E0l?J0T zW!kKv^pK-vpHHEf#daWmDnOy%D_egDCnX7piZ{ec^)rN{4Y zq2oJfQE4YF9nK}q@b$RTSm3*?WYg*(dd)m*w-7=X5tVWp=`G zg(bd2gV=JpBGX3s=@B_}RL|^Zm`pb-(6&P*rV{fOIc-!?uSa!qVFnKjTlhWp+XW2Vi%lP5R)grp|2!io zXxiv1joCx)8)KCpj;P1IpoDrn^MVFGw!8ja?F{7 zVsZ0M3-R!}0@-XyI7WxIZuonCo9y$y=jU?!oCi0Q+Cn7rleg0y&ql1sPd&;j}3*(de5ft4vL?J31r zSBQ`9P)g@VCPo->&Pkwf!#S&ZzruKWSpu>dXckmN{2Yv<1Wr#+K6fZREEk`fpZSJ1 z5gf?W+ZM@(&K+de46+qjz#)9urs)-ekggq&9mbvjA)h@1^7k7q#R>;w6ME$4?d0HWHx~3NFK zHa-GD5afr&wo~peat_$eZSjMyvO9)GIN*Vl2bi2}@9HXj6_?Owa&tu&+9_YC=CtvS6(TZgE64EZVlg?+f|09+I{SpS$lY)yuLsAG@skT)b1jA1WW%)#|Jq5X@F+vK$ z42MA-5ZR}n^JgVvLz}2))Rs=h@L1-CkTyQ$Al&=Oa&C!f@NaEABN}fCL z?}ayM=^+Z*v)X57T8Ehm+Ch)lGC?52us;CWnS(0gbI}dJg%fH6mn#SPFo_+5axsZL zgVxhpxnS_|ab^<8d5p|4;?u$B(oJ&karv~WY3y-sqE+(xA?3&5>?Zlzka;b_nK<Vzt*>%H*&@J-A;TIuD zo;bqW5+u!f2{BU96b_OAAV{tmaX79w!8U~vT84R`s#BY$7WO`<>eo@RQLww)^i{aePKgXbZv%-73d=T*`UIqy6d%8#6PMu#0wZE3`A5(}*5;&RgtnK#akmL=o5 z$uq_c)+KlBF+nLYP88D%1ivf=X2cO_(o~3=&KuJUEu3@cR1;|p*{;_Hs{6oyHN2iT zs_B}59Vf%@bMh=IK}oOt*rM6);7*09`U4zd(!_e5DaZ2a3sOWSwi2Au0>5=o{f(0B z*fz8s6wyJ$v3HLTnOYwz(?=u*-IT<51*vde9>SiAn3!p{q9;!^1^aG2RLGuU%nciX zw!vhnk`kL#3N zR#$@7r|VqwklbHq&mru!kaykAGT{gSOOw;5;hc!m%etPGz_YF z$dE(oOJaOu0K_rk9Amz^ExiCep$IUSIT`TK62Ye%z*?pYwhgS|ZWzSp^7dKeFX?obg z3@vT&a8W^qmNsM;!x`m?S;ReL%{<&xiFe6hVP`;(xMZW{od!1X2^x05y3d;oGhvk& zvNuGo4arbLK?azI^x#q~Ztdy6=NM37J*esC*bxq51QvS+vJOL=M-crV=&(hFsYxnK zi7QZh5VHvzJUcPMA9DQrt{hs-A;@-X{MHJ51$wjIFE>pHAPU(o=q2;dKMtjWXm9yy zI76o)+$s1p{mqwEXyj{M;jZ zAQPHbU@yCtr@7Pyy^)#^%UH6MEC;V@R4;={Pm{ zVof$ZcnqA<4DcUcKF)1v)adRV&PP{~*ZXCheT5IYXjj1Y`B&i)pSikN8Is3(bhg;ND2Cvk)qG(rk!<<;BnNycd_iC0=i z4baCLGCZoRM^F3ifaiua8YXn9&1?_)jyubgOhUj6kp14ZQ(b z*>D|t^_jUhMPEpgU1t=^z^v(j-94*E|CYm@l=#UG_7L;+QKUYy!N%T_*ld0~YaGa( zJi7>8XUskkx$zUTC$!|oSixEj5G_$tfrgoO2nT56|(?KKZXX zZ1rm{DUj|<`=o8t#1_fwOAF}@dDW#wbd9|8(rK-SVkvym3Pz1XG5%P?wFA<3+3eD{ zb>}8l3UbC41zB^EL4-czMmym=?GO&DWaU};kWJ_N!Wi5*_WJ;(8_sdcOE2FLKj4ht zbS$}I744VXuJGnP&!qXfn@(S0i;y5T&dX)TD<7lJn_ju{861qs>l!a8*rP22#tJ*) zNBqpAfWV1yx}Pz3JS~?K=Z>)b!ki%i3_ZCL1=|Aon5KlTV!zT_LNyx1Eyz48OzZK7~=DYFib@kJ*h>xx= z=+I(JIuG6^+b=k)M+7 z(BcQ_1^Lb5ZuCM^#`RSeSoi3q3Jw2U+C)#t)i-vPX>$G`y+4MY9C2^8LTk{Q?FZ(d zgSCUM!6^Q1)H}xOuv;%$Bob6|^0~u?zc=wo#5S9C*Oq&vN&&TKc-hG{4rS z8C~I}-E#GczO+j|yJ8Lm033Yv&iREMNIR;J0VCKTr7d7@WzNj?`UFq2D2LSwT5(0f-GFkV{}V`+^Qj6 zlORuMNM;h`MGeW(keD&p-;+QFz zNPbec{Tc#w$n`6)&;iY4Y!2D9cggZq)9HP=Zq>@{57;)bN#n#_2fq*HiK~0ikMhFR zZai17_Tafgmn~};641}d`ng^|-(C~R`IyNd$H!XJa>6xg?Mb-?96>cH%&Cr}DMvoJ z_B{GXc3D^IdGtX{+Ma^+B1E$)=+Q?|ZP*%0pT#hw46_ErVO%#Gb$RhRC%WIYZZ>@& zd)_<*v$^2rvG9lHo3HC-%*m9A+gtc@$w!f9z8!!i2ZuSf_hrrc5x~4-{ps{~d1!qV z=nTE3IL=1eG1vf1B(s5ff{oR;FdMJkawZ5C-nsj_LManl@IEDKhtI82_up%-v3-*Dowq}a4;!_$3xi9B=TahO_%%>nuH#%x+BKipV>Xp^<6SK2P!d%1LP8mi&>@`g=0{dQ}kfJxwt ziq9QUvf$DL(n9Qskn(>K@AcBADouA7^l|#;vf@v;pFVO1(sqL}BbSJGJQ(kI$L6Z` zkFq$9wQRSIjce{7EcQ04#xm+g2h{2^>L>FbnDi$<#s%c=2afg0Z!fbdke^~|$NJ>g zPu}z3NcvXpf3ORl8BHEMhct2d?54`D-#!K22bmDhVGe6zlX>UKF7U0quW3Z06<_(< zwq`yw95`bhVmcEaIu6e}9vYFho2fyC`yaXupIDYZ?7{|T`@;oz{_EkEz#(}9gbnP`Gt&dck|*q?>+G2;&rhk1e|dn z-B~)C|AJ9AHs{girstbW2$g-0_H`zxF2RQv%P=wDp1PngYZ&t8B!WnJ?F`Fgm>l`o zoKA^eyTPbA(bDwRW6zl3QSzz6ly6z`^0)W8%D0~C+~t5aHBk;(>SXFWuG_x9@#zrO zz>lA=partS3)Su8DT^M(TIO*LbuibKd_n!=O zE2FP6hT?1ywO{1kEfqNI&)+&Wtx>Q1o8%`CmPCe#wpFgy_{?Gn- z+y3U+mGrt?_}p80iJbl59b2&wyx12m7}SoDtoxTb9eGwv9vQ zuFuaC%de93w9op*&FI2wf$a}P-&3x7VbC9Ir>9){;t*^KcD&dvZ6D9rwovYWv48Gs zTH8sx6P!-U=@%EvbGG+@re|+2&x+?TA;NlhK;E}~k7%4LSG;r_UQO@3R7_kw{4!VN zkXIV`mGf#J`Q9r7jpBL86Q{n)Y38a|Pv~~95q-QsF8fTOU0i@%etC5s=Fs?B zchPv2T>DxjhX3fb;rYCUz>3SpXyM?eOYn0}zvXeS_wS^q5Nfm-=b$L?R!d&~dJ*FB zEw8(a8kvCJMHJ$b1XB^VPZZUwz67r$HGuO)=woEX`P%?Kf@dFTS-dGLR> zbu##lz#yz{@qIB>iY>`9eU_hA6m;c3b7r9Drdacy<^K@2?BEMdjngc2^QoR z?}cDd{dXNtH_C=x3$cItc~_aB`Lg)^Xkyaw&?Q&D|9AqF2yo#KRwaZQ2QgP1y>?jQ zTY(N%+YQqGVJ`NX-Vf))mLLCc6wVt`KUx}R2T50e*SEp8@8n${m9;|ksy71AJs+La z3T?ry{CF!)EX3~0zQ$xhC((nM;6rE_xNSdI@JnFd%Za;Jn>2YfQS&+RN^ zMkBsJVZtuOwuV84yq^s1)ed;O-2TaUy1uF3KQf6{OV6i6dfuGqhHuh1m-dN#yH5Yi zq5OK=&2q`7WdTDc=5m}xAt$jJs@B^#&J$sA_)yuRx+FCD{imnUcXHTgwI$baNI>$< zQ&^f*UZO)b?;81ljBHoQ*FHO?^E!qa2{?sT-p4V-g52D(XHeI_CUstxRF+Ni_q<67 zg<=M5*iDgtcH_|1)c8d&USHRLc@ARl!7n!^@DjH0;J-$l9S_&~aHDZO$7IaR*b5jZ z9!8*U1H6CJu`t)Q0Oc89O)@U|v0_?pCCboQ-4pkXP=xzs?&WRxz`avAq%IwogPT(y zaB;~w4*Bxe9XMdV|Mh8fv)Xzx<;u`E1(-_sn=AFRQWowjP9R^t?_gqnmbimo^=)t5 z$++X&B0OI~`HJi;wytKR^CEq_8RcTBrpt-0_a56mK=n z`@x%mTQ86Ob_JgP!$o+WfA|7CA3xj!&s~R4w|>Tq7T~zt_SwT;>YYk&H;+xF3)AV{ z=B@4NtyFqTE$>8ryk6^sU+w){^Rmt~z}(?)+|7;|ib@#lTWV)lszjIUZgeJhxzJ9> z)BEZ^JKc!dumajie^a@IG`>uC3LR(rroIk-ScN766Wg>Ic4DZ5w{8359+@D(8KrY-~DM! z>!Qz}r3h%RsIjHw$citN8gxLdD5W61qIQ&0m-rPvAlqJ22TJJ_TA(V+Xb1?#%BZ`s zAv&|=VwnDB1d=o7kI?Jn1Qrk~5{9Qsb3F@UDgFU`*lpy_6suf4bV1Q&j#oPJTS zxakJ^wRxO}E+hI?z3YXnHZ-sH(d9(9H|GT~1-eB|2+~;{=CP*~;<|D>yXhj^e6=k| z7lK^bKz<=Kc2ai_q`ov)?HEXdX>D`XAd(_yZahR%X#XeL^K5ff({XfT=SAGWUwK29 z^9sD>Q})J1YTOW7nba0bZrgn#<*J@T>580|&PhEt4+XP})Q>}HCPuHXr1eB=lyw-5 zO=p=CU)=9J)!kRXU&OS zbP&bidt9aJ(o@Kz2AxV(0L(p={+fVq8cklcH%Lx(?PzMB1f7*A`^He=33tTx0pCKd zdS|=_4@T`Sqo!A;UszFg_H3OqTRUmH$2K@b4FE1^UO$GGn{#gBF7Szzkot`EE^Ie7 zpKvBUo7zFgQNAO9T}eCJ%I2=)sI!3R9T(8+cp*mKNw=yy$J6tj;=7w1Q)t8IOr#Y4 zs?^+S>O{XORZR~1t$BMj-6v?dno|o=+^SN;_yY*b)vIC1`&KnE0yAHrzKT#aUZbM? zYQ8s0D+twL7t)(}b(%=0^ooy^kD2K_013R25%8J0?NIaFi4?IygrCo(%Lhlp6Q|i5 zqBCbto;7Jo&4tmK(`Gk>qqQ|Nqdw1+Xk=1N^{h*$M|D%h^r;t`@N+o+<4{v)(HZLb zSu`{)JZLxYjQ_4&G`-7O8X|&q%DOEJD`xG+I z@p+>`f343SaEAkKXHBFk?HaXjFHKgBd&!#Zsdc!0Ubi#gaE2W|zpE-;t=&sQvmLdr z+MwU-isJ1G2fS4o>YKfEX?Be(=<&I0y>)(fjmzcqRJB|FwHTZo@z!{JzA%~`&S+hv zw#upokvKzSR;wi>d}>0vD4{I1G(((}<954zE??9a4S2j!kHc5h{sIH3QHXw`Lyc-| zFZ^XrcQjB}=MMOME{EF_3|9@9Jo(}&1H7()$KmyRJpr%N6Y#mbevhq4&AmeOP_}O= zjXLVTyFuCd&u&nT{;M1Cl^uI%LYX5Pt#deoUVlxEGg1?*jgxe`!AQ^-bh;c)uP@*Y zy8NC_>io}Xa9M2>w?};ekFzdZ7YTa&f6$||+V(jW<^+O4udmkY^@Zzd!Va&ms>>bf z(C2hzso(9csqwoaVW%^UVd9j6evi}X_XHsmkK5;TxSfu?la=#J>aViCq_iH9Xc$H@ z+3BB%Hg~`g#l*o$Fc7WkdWwOU_XTC=c!M5C)a7?NJZ_IG;&xYci??3Ltu-|+cb%us zT?27C!j8JC?q_k|67}#G)IG-)bcJiZo?3q-7!KA%Tvhq49CXZfyCW`#&+P@(K)~&< ztLmW~Us5Os27=M*YHL0IIm< zM@m!n&nY$U_%n+zD_D&u9EJf#y|u7GUzJ^(A5%Zl{0eSXSN@AGE`u=#yir#$;0Ob~ zHenXWkYnK-75f)OJNYNNgHcDVCtMS8x}0IR-(A)FGHyCsee)INbPPva5r?lX5D3F4 z9iBjyI^kQ2mDl>czB;F~CJ=@7IsL(e>U~~^Bj|Vg9iE`a<#zfUfk2=06dj=4;~i&2 z8z$9Eo^)BX0ZN`Wd2%#7t9oX12IdKas0oIh;UH|o=c{%70kytr;&=3y(mME4kex5! zk2vaToqy0n9o|p=Y?li*R2!{vdmK)8ozGQOyy5`)a$Rn>&*2WR)zvtnPFGdQv-mqQ z2jCiG_S5{bx+o%$qc-gJ_ydkepv5PSG0py<=kHis>v#G*?m)0M8UX34QXVBch=34w zIlTcVd@CHNt18prNHkL8b3#y2Z!qGH)>V~jaMWD~ORuYS`=epM--)>?`*)xn4g?(T zXsw4u9P#_BDpZH>>6+|7H~<56A&g-fzPc)hQnv4@hkE{ds?QGBBCG`iE@#By_6FUq zDyItlK-;?3IlVzIj4D`%Na3mpRJpFKKJU~(pjs9DiY49qBXTT`0B%>8bJ8xqM)*Cg85}s}J&pJv$l*_ySJ16C@xoh$X;5 zH0P)w8cd3wVqkKV98|43{YG|m#vvMz?TmOlbelCz0Af#8cyr)&y%@RYR2bCmPT(0z*TOu*>hQje5dWL)EOG zXjC@hEG8EXdcqMz+<>R5QtkYS7P&l#q<)t-;_$nJeg}Nqh|q`?L6^hfL6CFfpUW9^ zh9VHX-%+jFv*>U8nS41lUWeZi4SNxJ;ru~g)v$?b-CinfjS#`#i4ea#%J5Z$VS*6a zzMlwd!Y?$k%;WXexx)_luhZ!Yws`R|cpMQ^uholQ3Rhp#RM%grqSPJm`XY6{+FGa6 z?eIIjzdye!HS<^MmmLlxenw#&PAr+>sIO|I+W0G-o$UqqG z8)na(Rjn2urm~EP@u#FlmFZS>-(j-R$!g1CI#HYwQdUpkfl{uGT~K2-!d zj5*?uP@T5?Vd1CKVTq?8-Qd-m&Qv#$I9HsNY=&ninIWB{?h;~L+PM=Tc%NTw7FdS+ znZ=N_vF9_y%@D6ToFzJD*MuYPpfebS9Xi~#uDYu8)HbsiL*rB@3+|boud4WUftqa* zLutHv7&TEnUOkfnous4yHbGUT0Ha3DP7xHm(oN>n#dCslai-apFFjT)R*aEgqUokBdRV#^rtDD zz>K4*>gxt5d6pOgq0f9SM9*-Ox4{gX3;FQ#47rW*(C-Xc3Q)%OG*uticHZ#%xSHpXT<+f*rfvughiPmTU=J9?43j>b+D7@ zmE(&#Yn_f@O>Ipi>c&D|^_PIwx;mT*0bH(nWQkIBLl$zeE1?d$QZ306Bhz9hLQfu# z8q!S^vYrY>Bk?3hsVVJ+4V#@S+l!OvD)mx(F_7jdO9$Z)^NDBIQ=QpCbQD*|D_5g} z%vg00$CXwTvQmPTT;%r)`R_8(q%hm2& zaU88sJv*U0GWeQ8m=(v^Va4|ir(t3;8^qjh)x0sW@jV{9=LAPq(TZ~j& zibNN6UXjR4f1a*567EWWfnrIZ%09xA_M(BMvU-a?YJDGZX8LyQJ(VB}?0K0s8eLFFPVx5ka!(iy3%^#mhPAC`+($j^JJF6y{`2%E2}@%==={2B$- zgnkHhkMtAy=GU=@)+l=cqBm5#Vlkd}s7b}*c-pBRC>BFvZ&E6fc>JNiwgJMJCOhK5b^SIy`aq)QkR3 z?NOODr+P{hG$&m+^?m9%2^++QsWp>ZU>{IQVRhA(=Yv! zQW-q`(tmMbL_=iu^sgu-GJDFDOZQSnb@k-%tcIGYGry*EphX)RrZs#+?GnZ6hJCvG z-H8BzgVM9t0&3;geO2t6&BXNXxtAnL5f`6-a zW#TV%P%S9K;&Mp6Q6`G%Cv~t))X>lBymILB7jSfr^ALJZ5tpu zrZtp1kuwfF#0^TrUEPMe1$*l*G*X*_={s#i=zTf zwF4a&s2L78?lo$ILkvS4{ig$SS)_V6#bCOY$@EimomkVaQ+GKEaQjv*4`A)MRlOE~f^JhHDC!WGCj}AbZdXqRG0+|A=OBcCC%*=(s|SLG wyNvQ%T)tcF87KzPJ*r?3DBP>g9)tkDQ7s!J&O`j&Hwck(bMv^t;?&{)2a(P3kN^Mx delta 18281 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 BVr~Ec diff --git a/tests/fixtures/wasm/src/echo-tool/Cargo.lock b/tests/fixtures/wasm/src/echo-tool/Cargo.lock new file mode 100644 index 0000000..64bd33a --- /dev/null +++ b/tests/fixtures/wasm/src/echo-tool/Cargo.lock @@ -0,0 +1,861 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "amplifier-guest" +version = "0.1.0" +dependencies = [ + "prost", + "serde", + "serde_json", + "wit-bindgen", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "auditable-serde" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7bf8143dfc3c0258df908843e169b5cc5fcf76c7718bd66135ef4a9cd558c5" +dependencies = [ + "semver", + "serde", + "serde_json", + "topological-sort", +] + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[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 = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "echo-tool" +version = "0.1.0" +dependencies = [ + "amplifier-guest", + "serde_json", + "wit-bindgen-rt", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[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" +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-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[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 = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +dependencies = [ + "serde", + "serde_core", +] + +[[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.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "spdx" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e17e880bafaeb362a7b751ec46bdc5b61445a188f80e0606e68167cd540fa3" +dependencies = [ + "smallvec", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[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 = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "topological-sort" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" + +[[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 = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "wasm-encoder" +version = "0.227.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80bb72f02e7fbf07183443b27b0f3d4144abf8c114189f2e088ed95b696a7822" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.227.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1ef0faabbbba6674e97a56bee857ccddf942785a336c8b47b42373c922a91d" +dependencies = [ + "anyhow", + "auditable-serde", + "flate2", + "indexmap", + "serde", + "serde_derive", + "serde_json", + "spdx", + "url", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.227.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f51cad774fb3c9461ab9bccc9c62dfb7388397b5deda31bf40e8108ccd678b2" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10fb6648689b3929d56bbc7eb1acf70c9a42a29eb5358c67c10f54dbd5d695de" +dependencies = [ + "wit-bindgen-rt", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92fa781d4f2ff6d3f27f3cc9b74a73327b31ca0dc4a3ef25a0ce2983e0e5af9b" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db52a11d4dfb0a59f194c064055794ee6564eb1ced88c25da2cf76e50c5621" +dependencies = [ + "bitflags", + "futures", + "once_cell", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0809dc5ba19e2e98661bf32fc0addc5a3ca5bf3a6a7083aa6ba484085ff3ce" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad19eec017904e04c60719592a803ee5da76cb51c81e3f6fbf9457f59db49799" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.227.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "635c3adc595422cbf2341a17fb73a319669cc8d33deed3a48368a841df86b676" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.227.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddf445ed5157046e4baf56f9138c124a0824d4d1657e7204d71886ad8ce2fc11" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/tests/fixtures/wasm/src/echo-tool/src/lib.rs b/tests/fixtures/wasm/src/echo-tool/src/lib.rs index 317c617..ad8cf88 100644 --- a/tests/fixtures/wasm/src/echo-tool/src/lib.rs +++ b/tests/fixtures/wasm/src/echo-tool/src/lib.rs @@ -31,6 +31,17 @@ impl Tool for EchoTool { success: true, output: Some(input), error: None, + content: Some(vec![ + serde_json::json!({"type": "text", "text": "echo result"}), + serde_json::json!({ + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "AA==" + } + }), + ]), }) } } diff --git a/tests/test_generated_equivalence.py b/tests/test_generated_equivalence.py index 18e643e..2b2f958 100644 --- a/tests/test_generated_equivalence.py +++ b/tests/test_generated_equivalence.py @@ -24,11 +24,15 @@ def test_proto_tool_result_has_error_json(self): tr = pb2.ToolResult() assert hasattr(tr, "error_json") + def test_proto_tool_result_has_content_blocks(self): + tr = pb2.ToolResult() + assert hasattr(tr, "content_blocks") + def test_proto_tool_result_field_count(self): - """ToolResult must have exactly 3 fields (success, output_json, error_json).""" + """ToolResult must have four fields, including canonical rich content.""" tr = pb2.ToolResult() fields = [f.name for f in tr.DESCRIPTOR.fields] - assert len(fields) == 3 + assert fields == ["success", "output_json", "error_json", "content_blocks"] class TestHookResultEquivalence: diff --git a/tests/test_pyo3_proto_bridge.py b/tests/test_pyo3_proto_bridge.py index a8938c5..d960043 100644 --- a/tests/test_pyo3_proto_bridge.py +++ b/tests/test_pyo3_proto_bridge.py @@ -11,6 +11,8 @@ import json +import pytest + class TestProtoChatRequestToJson: """Tests for the proto_chat_request_to_json PyO3 bridge function.""" @@ -189,3 +191,48 @@ def test_roundtrip_request_response(self): assert isinstance(response_proto_bytes, bytes), "Must return bytes" assert len(response_proto_bytes) > 0, "Proto bytes must be non-empty" + + +class TestToolResultContentBridge: + """The private ToolResult bridge normalizes content without input echoing.""" + + def test_normalizes_canonical_content(self): + from amplifier_core._engine import _normalize_tool_result_content + + result = _normalize_tool_result_content( + json.dumps( + [ + {"type": "text", "text": "details", "ignored": True}, + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "AA==", + "ignored": True, + }, + }, + ] + ) + ) + assert json.loads(result) == [ + {"type": "text", "text": "details"}, + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "AA==", + }, + }, + ] + + def test_rejects_bad_content_without_input_echo(self): + from amplifier_core._engine import _normalize_tool_result_content + + with pytest.raises(ValueError) as exc_info: + _normalize_tool_result_content( + '[{"type":"image","source":{"type":"base64","media_type":"image/png","data":"private-bytes"}}]' + ) + assert str(exc_info.value) == "invalid tool result image data" + assert "private-bytes" not in str(exc_info.value) diff --git a/tests/test_tool_result_autopop.py b/tests/test_tool_result_autopop.py index 434bfa7..1b63707 100644 --- a/tests/test_tool_result_autopop.py +++ b/tests/test_tool_result_autopop.py @@ -1,5 +1,7 @@ """Tests for ToolResult auto-populate output from error message.""" +import pytest + from amplifier_core.models import ToolResult @@ -27,3 +29,101 @@ def test_toolresult_no_autopopulate_without_message_key(self) -> None: """When error dict has no 'message' key, output stays None.""" result = ToolResult(success=False, error={"detail": "no message key"}) assert result.output is None + + +class TestToolResultContent: + """ToolResult rich content is normalized by the Rust ingress boundary.""" + + def test_content_normalizes_and_preserves_legacy_dump_shape(self) -> None: + legacy = ToolResult(output="plain") + assert legacy.model_dump() == { + "success": True, + "output": "plain", + "error": None, + } + + result = ToolResult( + content=[ + {"type": "text", "text": "details", "visibility": "user", "extra": True}, + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "AA==", + "extra": True, + }, + "extra": True, + }, + ] + ) + assert result.model_dump() == { + "success": True, + "output": None, + "error": None, + "content": [ + {"type": "text", "text": "details"}, + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "AA==", + }, + }, + ], + } + assert ToolResult(content=[]).content is None + + @pytest.mark.parametrize( + "content, expected", + [ + ([{"type": "thinking", "thinking": "no"}], "invalid tool result content"), + ( + [{"type": "image", "source": {"type": "url", "media_type": "image/png", "data": "AA=="}}], + "invalid tool result image source", + ), + ( + [{"type": "image", "source": {"type": "base64", "media_type": "text/plain", "data": "AA=="}}], + "invalid tool result image media type", + ), + ( + [{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": "not-base64"}}], + "invalid tool result image data", + ), + ], + ) + def test_content_rejects_malformed_input_without_echoing_it(self, content, expected) -> None: + secret = "not-base64" + with pytest.raises(ValueError) as exc_info: + ToolResult(content=content) + assert expected in str(exc_info.value) + assert secret not in str(exc_info.value) + + def test_safe_hook_presentation_omits_image_data(self) -> None: + result = ToolResult( + output={"status": "ok"}, + content=[ + {"type": "text", "text": "details"}, + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "AA==", + }, + }, + ], + ) + assert result.safe_hook_presentation() == { + "success": True, + "output": {"status": "ok"}, + "error": None, + "content": [ + {"type": "text", "text": "details"}, + { + "type": "text", + "text": "[Image omitted from tool event; original retained for model request if unmodified.]", + }, + ], + } diff --git a/tests/test_wasm_integration.py b/tests/test_wasm_integration.py index 808552e..656b569 100644 --- a/tests/test_wasm_integration.py +++ b/tests/test_wasm_integration.py @@ -83,6 +83,18 @@ async def test_load_echo_tool_wasm(): assert hasattr(tool, "get_spec") assert hasattr(tool, "execute") assert tool.name == "echo-tool" + execution = await tool.execute({"message": "hello"}) + assert execution["content"] == [ + {"type": "text", "text": "echo result"}, + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "AA==", + }, + }, + ] finally: shutil.rmtree(tmpdir, ignore_errors=True) diff --git a/uv.lock b/uv.lock index f883d87..f323797 100644 --- a/uv.lock +++ b/uv.lock @@ -4,7 +4,7 @@ requires-python = ">=3.11" [[package]] name = "amplifier-core" -version = "1.5.1" +version = "1.6.1" source = { editable = "." } dependencies = [ { name = "click" }, From 5ee65ec80cbe5e5113f4aa89d769b489c3b4d8e9 Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Fri, 11 Sep 2026 23:23:01 -0700 Subject: [PATCH 09/27] fix(core): enforce canonical ToolResult content boundaries Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- CONTRACTS.md | 2 +- Cargo.lock | 1 + .../amplifier-core/src/bridges/grpc_tool.rs | 6 +- .../amplifier-core/src/bridges/wasm_tool.rs | 23 ++- .../src/generated/conversions.rs | 144 ++++++++++----- crates/amplifier-core/src/grpc_server.rs | 84 ++++++++- crates/amplifier-core/src/lib.rs | 3 +- crates/amplifier-core/src/models.rs | 28 ++- crates/amplifier-core/tests/grpc_tool_e2e.rs | 50 ++++++ crates/amplifier-guest/Cargo.toml | 1 + crates/amplifier-guest/src/types.rs | 166 +++++++++++++++++- python/amplifier_core/loader_grpc.py | 16 +- python/amplifier_core/models.py | 57 ++++-- tests/fixtures/wasm/echo-tool.wasm | Bin 167110 -> 194944 bytes tests/fixtures/wasm/src/echo-tool/Cargo.lock | 7 + tests/fixtures/wasm/src/echo-tool/src/lib.rs | 7 +- tests/test_tool_result_autopop.py | 40 +++++ 17 files changed, 550 insertions(+), 85 deletions(-) diff --git a/CONTRACTS.md b/CONTRACTS.md index 6826827..2201318 100644 --- a/CONTRACTS.md +++ b/CONTRACTS.md @@ -46,7 +46,7 @@ field name differs from the JSON key. |-----------------|-------------|---------------|-------| | `HookResult` | `HookResult` (BaseModel) | JSON round-trip at PyO3 boundary | Field-for-field match. Rust `HookAction` enum ↔ Python `Literal` strings. | | `HookAction` | `Literal["continue","deny","modify","inject_context","ask_user"]` | `serde(rename_all = "snake_case")` | Enum variants map 1:1 to string literals. | -| `ToolResult` | `ToolResult` (BaseModel) | JSON round-trip | 1:1. Python adds `__str__()` and `get_serialized_output()` convenience methods. | +| `ToolResult` | `ToolResult` (BaseModel) | JSON round-trip | 1:1. `content`, when present, is a canonical non-empty ordered list of text/base64-image blocks; `None` and `[]` omit it during serialization. Rust enforces this with a private `ToolResultContent` wrapper; Python validates through PyO3. Python also adds `__str__()` and `get_serialized_output()` convenience methods. | | `ModelInfo` | `ModelInfo` (BaseModel) | JSON round-trip | 1:1. | | `ConfigField` | `ConfigField` (BaseModel) | JSON round-trip | Rust `default_value` ↔ Python `default`. | | `ConfigFieldType` | `Literal["text","secret","choice","boolean"]` | snake_case | | diff --git a/Cargo.lock b/Cargo.lock index 88ed22b..4711ffd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,6 +104,7 @@ dependencies = [ name = "amplifier-guest" version = "0.1.0" dependencies = [ + "base64", "prost", "serde", "serde_json", diff --git a/crates/amplifier-core/src/bridges/grpc_tool.rs b/crates/amplifier-core/src/bridges/grpc_tool.rs index 6eb1a47..76839f5 100644 --- a/crates/amplifier-core/src/bridges/grpc_tool.rs +++ b/crates/amplifier-core/src/bridges/grpc_tool.rs @@ -159,8 +159,12 @@ impl Tool for GrpcToolBridge { }; let content = proto_tool_result_content_to_native(resp.content_blocks).map_err(|error| { + log::warn!( + "gRPC tool '{}' returned invalid ToolResult content: {error}", + self.name + ); ToolError::Other { - message: error.to_string(), + message: "invalid tool result content from gRPC tool".to_string(), } })?; diff --git a/crates/amplifier-core/src/bridges/wasm_tool.rs b/crates/amplifier-core/src/bridges/wasm_tool.rs index c10bf6b..4db6eca 100644 --- a/crates/amplifier-core/src/bridges/wasm_tool.rs +++ b/crates/amplifier-core/src/bridges/wasm_tool.rs @@ -168,6 +168,12 @@ impl WasmToolBridge { } } +fn deserialize_guest_tool_result(result_bytes: &[u8]) -> Result { + serde_json::from_slice(result_bytes).map_err(|_| ToolError::Other { + message: "invalid ToolResult returned by WASM tool".to_string(), + }) +} + impl Tool for WasmToolBridge { fn name(&self) -> &str { &self.name @@ -206,10 +212,7 @@ impl Tool for WasmToolBridge { message: format!("WASM execute failed: {e}"), })?; - let tool_result: ToolResult = - serde_json::from_slice(&result_bytes).map_err(|e| ToolError::Other { - message: format!("failed to deserialize ToolResult: {e}"), - })?; + let tool_result = deserialize_guest_tool_result(&result_bytes)?; Ok(tool_result) }) @@ -232,6 +235,18 @@ mod tests { let _: Arc = Arc::new(bridge); } + #[test] + fn wasm_host_rejects_invalid_guest_rich_content_without_echoing_it() { + let error = deserialize_guest_tool_result( + br#"{"content":[{"type":"thinking","thinking":"WASM-GUEST-SECRET"}]}"#, + ) + .unwrap_err() + .to_string(); + + assert_eq!(error, "invalid ToolResult returned by WASM tool"); + assert!(!error.contains("WASM-GUEST-SECRET")); + } + /// Helper: read the echo-tool.wasm fixture bytes. /// /// The fixture lives at the workspace root under `tests/fixtures/wasm/`. diff --git a/crates/amplifier-core/src/generated/conversions.rs b/crates/amplifier-core/src/generated/conversions.rs index bc1bcdd..de7f7b7 100644 --- a/crates/amplifier-core/src/generated/conversions.rs +++ b/crates/amplifier-core/src/generated/conversions.rs @@ -1,4 +1,4 @@ -//! Bidirectional `From` conversions between hand-written Rust types and +//! Bidirectional conversions between hand-written Rust types and //! proto-generated types, enabling zero-copy-style mapping across the gRPC //! boundary. @@ -26,9 +26,11 @@ fn from_json_or_default(json: &str, la // ToolResult conversions // --------------------------------------------------------------------------- -impl From for super::amplifier_module::ToolResult { - fn from(native: crate::models::ToolResult) -> Self { - Self { +impl TryFrom for super::amplifier_module::ToolResult { + type Error = crate::models::ToolResultContentError; + + fn try_from(native: crate::models::ToolResult) -> Result { + Ok(Self { success: native.success, output_json: native .output @@ -38,14 +40,16 @@ impl From for super::amplifier_module::ToolResult { .error .map(|e| to_json_or_warn(&e, "ToolResult error")) .unwrap_or_default(), - content_blocks: native_tool_result_content_to_proto(native.content), - } + content_blocks: native_tool_result_content_to_proto(native.content)?, + }) } } -impl From for crate::models::ToolResult { - fn from(proto: super::amplifier_module::ToolResult) -> Self { - Self { +impl TryFrom for crate::models::ToolResult { + type Error = crate::models::ToolResultContentError; + + fn try_from(proto: super::amplifier_module::ToolResult) -> Result { + Ok(Self { success: proto.success, output: if proto.output_json.is_empty() { None @@ -67,13 +71,8 @@ impl From for crate::models::ToolResult { }) .ok() }, - content: proto_tool_result_content_to_native(proto.content_blocks).unwrap_or_else( - |error| { - log::warn!("Invalid tool result content from proto: {error}"); - None - }, - ), - } + content: proto_tool_result_content_to_native(proto.content_blocks)?, + }) } } @@ -267,38 +266,44 @@ fn proto_visibility_to_native(vis: i32) -> Option { /// Convert canonical ToolResult blocks without using the generic Message image /// representation, which redundantly stores base64 data in `source_json`. pub(crate) fn native_tool_result_content_to_proto( - content: Option>, -) -> Vec { + content: Option, +) -> Result, crate::models::ToolResultContentError> { use super::amplifier_module::content_block::Block; use crate::messages::ContentBlock; use base64::Engine; content - .unwrap_or_default() - .into_iter() - .filter_map(|block| match block { - ContentBlock::Text { text, .. } => Some(super::amplifier_module::ContentBlock { + .iter() + .flat_map(crate::models::ToolResultContent::iter) + .map(|block| match block { + ContentBlock::Text { text, .. } => Ok(super::amplifier_module::ContentBlock { block: Some(Block::TextBlock(super::amplifier_module::TextBlock { - text, + text: text.clone(), })), visibility: 0, }), ContentBlock::Image { source, .. } => { - let media_type = source.get("media_type")?.as_str()?.to_string(); - let data = source.get("data")?.as_str()?; + let media_type = source + .get("media_type") + .and_then(serde_json::Value::as_str) + .ok_or(crate::models::ToolResultContentError::InvalidImageMediaType)?; + let data = source + .get("data") + .and_then(serde_json::Value::as_str) + .ok_or(crate::models::ToolResultContentError::InvalidImageData)?; let data = base64::engine::general_purpose::STANDARD .decode(data) - .ok()?; - Some(super::amplifier_module::ContentBlock { + .map_err(|_| crate::models::ToolResultContentError::InvalidImageData)?; + Ok(super::amplifier_module::ContentBlock { block: Some(Block::ImageBlock(super::amplifier_module::ImageBlock { - media_type, + media_type: media_type.to_string(), data, source_json: String::new(), })), visibility: 0, }) } - _ => None, + _ => Err(crate::models::ToolResultContentError::InvalidContent), }) .collect() } @@ -307,26 +312,33 @@ pub(crate) fn native_tool_result_content_to_proto( /// canonical ToolResult ingress path. pub(crate) fn proto_tool_result_content_to_native( content: Vec, -) -> Result>, crate::models::ToolResultContentError> { +) -> Result, crate::models::ToolResultContentError> { use super::amplifier_module::content_block::Block; use base64::Engine; let raw_content = content .into_iter() - .map(|block| match block.block { - Some(Block::TextBlock(text)) => Ok(serde_json::json!({ + .map(|block| { + if block.visibility != 0 { + return Err(crate::models::ToolResultContentError::InvalidContent); + } + match block.block { + Some(Block::TextBlock(text)) => Ok(serde_json::json!({ "type": "text", "text": text.text, - })), - Some(Block::ImageBlock(image)) => Ok(serde_json::json!({ - "type": "image", - "source": { - "type": "base64", - "media_type": image.media_type, - "data": base64::engine::general_purpose::STANDARD.encode(image.data), + })), + Some(Block::ImageBlock(image)) if image.source_json.is_empty() => { + Ok(serde_json::json!({ + "type": "image", + "source": { + "type": "base64", + "media_type": image.media_type, + "data": base64::engine::general_purpose::STANDARD.encode(image.data), + } + })) } - })), - _ => Err(crate::models::ToolResultContentError::InvalidContent), + _ => Err(crate::models::ToolResultContentError::InvalidContent), + } }) .collect::, _>>()?; @@ -1088,14 +1100,15 @@ mod tests { ])) .unwrap(), }; - let proto: super::super::amplifier_module::ToolResult = original.clone().into(); + let proto: super::super::amplifier_module::ToolResult = + original.clone().try_into().unwrap(); let image = match proto.content_blocks[1].block.as_ref().unwrap() { super::super::amplifier_module::content_block::Block::ImageBlock(image) => image, _ => panic!("expected image block"), }; assert_eq!(image.data, vec![0]); assert!(image.source_json.is_empty()); - let restored: crate::models::ToolResult = proto.into(); + let restored: crate::models::ToolResult = proto.try_into().unwrap(); assert_eq!(original, restored); } @@ -1110,11 +1123,52 @@ mod tests { )])), content: None, }; - let proto: super::super::amplifier_module::ToolResult = original.clone().into(); - let restored: crate::models::ToolResult = proto.into(); + let proto: super::super::amplifier_module::ToolResult = + original.clone().try_into().unwrap(); + let restored: crate::models::ToolResult = proto.try_into().unwrap(); assert_eq!(original, restored); } + #[test] + fn tool_result_proto_rejects_malformed_rich_content_without_dropping_it() { + let proto = super::super::amplifier_module::ToolResult { + success: true, + output_json: String::new(), + error_json: String::new(), + content_blocks: vec![super::super::amplifier_module::ContentBlock { + block: None, + visibility: 0, + }], + }; + + assert_eq!( + crate::models::ToolResult::try_from(proto), + Err(crate::models::ToolResultContentError::InvalidContent) + ); + + let noncanonical = super::super::amplifier_module::ToolResult { + success: true, + output_json: String::new(), + error_json: String::new(), + content_blocks: vec![super::super::amplifier_module::ContentBlock { + block: Some( + super::super::amplifier_module::content_block::Block::ImageBlock( + super::super::amplifier_module::ImageBlock { + media_type: "image/png".to_string(), + data: vec![0], + source_json: "unexpected".to_string(), + }, + ), + ), + visibility: 1, + }], + }; + assert_eq!( + crate::models::ToolResult::try_from(noncanonical), + Err(crate::models::ToolResultContentError::InvalidContent) + ); + } + #[test] fn model_info_roundtrip() { let original = crate::models::ModelInfo { diff --git a/crates/amplifier-core/src/grpc_server.rs b/crates/amplifier-core/src/grpc_server.rs index 615365f..37d779e 100644 --- a/crates/amplifier-core/src/grpc_server.rs +++ b/crates/amplifier-core/src/grpc_server.rs @@ -226,7 +226,13 @@ impl KernelService for KernelServiceImpl { // Execute the tool match tool.execute(input).await { - Ok(result) => Ok(Response::new(result.into())), + Ok(result) => { + let response = result.try_into().map_err(|error| { + log::warn!("Invalid canonical ToolResult content for {tool_name}: {error}"); + Status::internal("Invalid tool result content") + })?; + Ok(Response::new(response)) + } Err(e) => { log::error!("Tool execution failed for {tool_name}: {e}"); Err(Status::internal("Tool execution failed")) @@ -1502,6 +1508,82 @@ mod tests { // H-07: JSON payload size limits // ----------------------------------------------------------------------- + #[tokio::test] + async fn execute_tool_transports_canonical_rich_content() { + struct RichTool; + + impl crate::traits::Tool for RichTool { + fn name(&self) -> &str { + "rich-tool" + } + + fn description(&self) -> &str { + "Returns canonical rich content" + } + + fn get_spec(&self) -> crate::messages::ToolSpec { + crate::messages::ToolSpec { + name: self.name().to_string(), + parameters: std::collections::HashMap::new(), + description: Some(self.description().to_string()), + extensions: std::collections::HashMap::new(), + } + } + + fn execute( + &self, + _input: serde_json::Value, + ) -> std::pin::Pin< + Box< + dyn std::future::Future< + Output = Result, + > + Send + + '_, + >, + > { + Box::pin(async { + Ok(crate::models::ToolResult { + success: true, + output: None, + error: None, + content: crate::models::ToolResult::normalize_content(Some(vec![ + serde_json::json!({"type": "text", "text": "details"}), + serde_json::json!({ + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "AA==" + } + }), + ])) + .unwrap(), + }) + }) + } + } + + let coord = Arc::new(Coordinator::new(Default::default())); + coord.mount_tool("rich-tool", Arc::new(RichTool)); + let service = KernelServiceImpl::new(coord); + let response = service + .execute_tool(Request::new(amplifier_module::ExecuteToolRequest { + tool_name: "rich-tool".to_string(), + input_json: "{}".to_string(), + })) + .await + .unwrap() + .into_inner(); + + assert_eq!(response.content_blocks.len(), 2); + let image = match response.content_blocks[1].block.as_ref().unwrap() { + amplifier_module::content_block::Block::ImageBlock(image) => image, + _ => panic!("expected image block"), + }; + assert_eq!(image.data, vec![0]); + assert!(image.source_json.is_empty()); + } + #[tokio::test] async fn execute_tool_rejects_oversized_input_json() { use crate::testing::FakeTool; diff --git a/crates/amplifier-core/src/lib.rs b/crates/amplifier-core/src/lib.rs index fd693cb..a514999 100644 --- a/crates/amplifier-core/src/lib.rs +++ b/crates/amplifier-core/src/lib.rs @@ -52,7 +52,8 @@ pub use errors::{AmplifierError, ContextError, HookError, ProviderError, Session pub use models::{ ApprovalDefault, ApprovalRequest, ApprovalResponse, ConfigField, ConfigFieldType, ContextInjectionRole, HookAction, HookResult, ModelInfo, ModuleInfo, ModuleType, ProviderInfo, - SessionState, SessionStatus, ToolResult, ToolResultContentError, UserMessageLevel, + SessionState, SessionStatus, ToolResult, ToolResultContent, ToolResultContentError, + UserMessageLevel, }; // Chat protocol models diff --git a/crates/amplifier-core/src/models.rs b/crates/amplifier-core/src/models.rs index eabd725..99be751 100644 --- a/crates/amplifier-core/src/models.rs +++ b/crates/amplifier-core/src/models.rs @@ -244,7 +244,7 @@ pub struct ToolResult { deserialize_with = "deserialize_tool_result_content", skip_serializing_if = "Option::is_none" )] - pub content: Option>, + pub content: Option, } fn default_true() -> bool { @@ -275,13 +275,29 @@ pub enum ToolResultContentError { InvalidImageData, } +/// A non-empty, ordered collection of canonical ToolResult content blocks. +/// +/// The wrapped vector is deliberately private: only [`ToolResult::normalize_content`] +/// may create this type, so generic message blocks, URL images, extensions, and +/// empty collections cannot cross the ToolResult boundary. +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(transparent)] +pub struct ToolResultContent(Vec); + +impl ToolResultContent { + /// Iterate over the canonical blocks without exposing mutable or owned storage. + pub fn iter(&self) -> impl Iterator { + self.0.iter() + } +} + /// Deserialize ToolResult content through its strict canonicalization boundary. /// /// Decode as a JSON value first so type errors cannot include untrusted image data /// in the error returned to callers. fn deserialize_tool_result_content<'de, D>( deserializer: D, -) -> Result>, D::Error> +) -> Result, D::Error> where D: serde::Deserializer<'de>, { @@ -319,7 +335,7 @@ impl ToolResult { /// fields are deliberately dropped at this boundary. pub fn normalize_content( content: Option>, - ) -> Result>, ToolResultContentError> { + ) -> Result, ToolResultContentError> { let Some(content) = content else { return Ok(None); }; @@ -394,7 +410,7 @@ impl ToolResult { } } - Ok(Some(normalized)) + Ok(Some(ToolResultContent(normalized))) } /// Produce an image-safe representation for hook event payloads. @@ -947,6 +963,10 @@ mod tests { let empty: ToolResult = serde_json::from_value(json!({"content": []})).unwrap(); assert!(empty.content.is_none()); + assert_eq!( + serde_json::to_value(empty).unwrap(), + json!({"success": true, "output": null, "error": null}) + ); for content in [ json!([{"type": "thinking", "thinking": "private-bytes"}]), diff --git a/crates/amplifier-core/tests/grpc_tool_e2e.rs b/crates/amplifier-core/tests/grpc_tool_e2e.rs index 5a2e411..ccc7611 100644 --- a/crates/amplifier-core/tests/grpc_tool_e2e.rs +++ b/crates/amplifier-core/tests/grpc_tool_e2e.rs @@ -63,6 +63,41 @@ impl ToolService for EchoToolService { } } +/// A ToolService response with an invalid rich-content oneof. +struct InvalidContentToolService; + +#[tonic::async_trait] +impl ToolService for InvalidContentToolService { + async fn get_spec( + &self, + _request: tonic::Request, + ) -> Result, tonic::Status> { + Ok(tonic::Response::new(amplifier_module::ToolSpec { + name: "invalid-content".to_string(), + description: "Returns malformed rich content".to_string(), + parameters_json: "{}".to_string(), + })) + } + + async fn execute( + &self, + _request: tonic::Request, + ) -> Result, tonic::Status> { + Ok(tonic::Response::new( + amplifier_module::ToolExecuteResponse { + success: true, + output: b"null".to_vec(), + content_type: "application/json".to_string(), + error: String::new(), + content_blocks: vec![amplifier_module::ContentBlock { + block: None, + visibility: 0, + }], + }, + )) + } +} + /// Helper: bind to random port, spawn gRPC server, return address string. async fn spawn_tool_server(svc: ToolServiceServer) -> String { let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); @@ -221,6 +256,21 @@ async fn grpc_tool_round_trip() -> Result<(), Box Result<(), Box> { + let endpoint = spawn_tool_server(ToolServiceServer::new(InvalidContentToolService)).await; + let bridge = GrpcToolBridge::connect(&endpoint).await?; + + let error = bridge + .execute(serde_json::json!({})) + .await + .unwrap_err() + .to_string(); + assert_eq!(error, "invalid tool result content from gRPC tool"); + Ok(()) +} + /// P1-13: Connect succeeds with invalid (non-empty) parameters_json — parameters /// fall back to empty map and a warning is logged (logging verified by code review). #[tokio::test] diff --git a/crates/amplifier-guest/Cargo.toml b/crates/amplifier-guest/Cargo.toml index 185ffd9..c4f2792 100644 --- a/crates/amplifier-guest/Cargo.toml +++ b/crates/amplifier-guest/Cargo.toml @@ -8,6 +8,7 @@ default = ["kernel-stub"] kernel-stub = [] [dependencies] +base64 = "0.22" prost = "0.13" serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/amplifier-guest/src/types.rs b/crates/amplifier-guest/src/types.rs index 4e7105e..a4cf859 100644 --- a/crates/amplifier-guest/src/types.rs +++ b/crates/amplifier-guest/src/types.rs @@ -1,3 +1,4 @@ +use base64::Engine; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; @@ -20,14 +21,124 @@ pub struct ToolResult { pub output: Option, #[serde(skip_serializing_if = "Option::is_none")] pub error: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub content: Option>, + #[serde( + default, + deserialize_with = "deserialize_tool_result_content", + skip_serializing_if = "Option::is_none" + )] + pub content: Option, } fn default_true() -> bool { true } +/// A non-empty collection of canonical ToolResult text/base64-image blocks. +/// +/// The storage is private so a WASM guest cannot create an empty collection or +/// bypass the ToolResult-only validation rules with arbitrary JSON values. +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(transparent)] +pub struct ToolResultContent(Vec); + +impl ToolResultContent { + /// Strictly validate and canonicalize guest content. + /// + /// `None` and an empty array retain the legacy no-content representation. + pub fn normalize(content: Option>) -> Result, &'static str> { + let Some(content) = content else { + return Ok(None); + }; + if content.is_empty() { + return Ok(None); + } + + let mut normalized = Vec::with_capacity(content.len()); + for block in content { + let object = block.as_object().ok_or("invalid tool result content")?; + match object.get("type").and_then(Value::as_str) { + Some("text") => { + let text = object + .get("text") + .and_then(Value::as_str) + .ok_or("invalid tool result content")?; + normalized.push(serde_json::json!({"type": "text", "text": text})); + } + Some("image") => { + let source = object + .get("source") + .and_then(Value::as_object) + .ok_or("invalid tool result image source")?; + if source.get("type").and_then(Value::as_str) != Some("base64") { + return Err("invalid tool result image source"); + } + let media_type = source + .get("media_type") + .and_then(Value::as_str) + .ok_or("invalid tool result image media type")?; + if !valid_image_media_type(media_type) { + return Err("invalid tool result image media type"); + } + let data = source + .get("data") + .and_then(Value::as_str) + .filter(|data| !data.is_empty()) + .ok_or("invalid tool result image data")?; + let decoded = base64::engine::general_purpose::STANDARD + .decode(data) + .map_err(|_| "invalid tool result image data")?; + if base64::engine::general_purpose::STANDARD.encode(decoded) != data { + return Err("invalid tool result image data"); + } + normalized.push(serde_json::json!({ + "type": "image", + "source": { + "type": "base64", + "media_type": media_type, + "data": data, + } + })); + } + _ => return Err("invalid tool result content"), + } + } + Ok(Some(Self(normalized))) + } +} + +fn deserialize_tool_result_content<'de, D>( + deserializer: D, +) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + let raw_content = Option::::deserialize(deserializer)?; + let raw_content = match raw_content { + None => None, + Some(Value::Array(content)) => Some(content), + Some(_) => return Err(serde::de::Error::custom("invalid tool result content")), + }; + ToolResultContent::normalize(raw_content).map_err(serde::de::Error::custom) +} + +fn valid_image_media_type(media_type: &str) -> bool { + let Some(subtype) = media_type.strip_prefix("image/") else { + return false; + }; + let mut chars = subtype.chars(); + let Some(first) = chars.next() else { + return false; + }; + first.is_ascii_alphanumeric() + && chars.all(|character| { + character.is_ascii_alphanumeric() + || matches!( + character, + '!' | '#' | '$' | '&' | '^' | '_' | '.' | '+' | '-' + ) + }) +} + impl Default for ToolResult { fn default() -> Self { Self { @@ -269,7 +380,11 @@ mod tests { m.insert("code".to_string(), json!(404)); m }), - content: Some(vec![json!({"type": "text", "text": "details"})]), + content: ToolResultContent::normalize(Some(vec![json!({ + "type": "text", + "text": "details" + })])) + .unwrap(), }; let json_str = serde_json::to_string(&result).unwrap(); let deserialized: ToolResult = serde_json::from_str(&json_str).unwrap(); @@ -278,11 +393,46 @@ mod tests { let err = deserialized.error.as_ref().unwrap(); assert_eq!(err.get("code"), Some(&json!(404))); assert_eq!( - deserialized.content, - Some(vec![json!({"type": "text", "text": "details"})]) + serde_json::to_value(deserialized.content).unwrap(), + json!([{"type": "text", "text": "details"}]) ); } + #[test] + fn test_tool_result_empty_content_omits_key() { + let result: ToolResult = serde_json::from_value(json!({"content": []})).unwrap(); + assert!(result.content.is_none()); + assert_eq!( + serde_json::to_value(result).unwrap(), + json!({"success": true}) + ); + } + + #[test] + fn test_tool_result_media_type_matches_core_contract() { + assert!(ToolResultContent::normalize(Some(vec![json!({ + "type": "image", + "source": { + "type": "base64", + "media_type": "image/a_b", + "data": "AA==" + } + })])) + .is_ok()); + } + + #[test] + fn test_tool_result_rejects_invalid_content_without_echoing_it() { + let error = serde_json::from_value::(json!({ + "content": [{"type": "thinking", "thinking": "WASM-GUEST-SECRET"}] + })) + .unwrap_err() + .to_string(); + + assert!(error.contains("invalid tool result content")); + assert!(!error.contains("WASM-GUEST-SECRET")); + } + // --- HookAction tests --- #[test] @@ -560,7 +710,11 @@ mod tests { m.insert("code".to_string(), json!(404)); m }), - content: Some(vec![json!({"type": "text", "text": "details"})]), + content: ToolResultContent::normalize(Some(vec![json!({ + "type": "text", + "text": "details" + })])) + .unwrap(), }; let json_str = serde_json::to_string(&original).unwrap(); let deserialized: ToolResult = serde_json::from_str(&json_str).unwrap(); diff --git a/python/amplifier_core/loader_grpc.py b/python/amplifier_core/loader_grpc.py index 3690652..8700cdd 100644 --- a/python/amplifier_core/loader_grpc.py +++ b/python/amplifier_core/loader_grpc.py @@ -113,10 +113,14 @@ def _deserialize_content_blocks(self, content_blocks: Any) -> list[dict[str, Any raw_content: list[dict[str, Any]] = [] for block in content_blocks: + if block.visibility != 0: + raise ValueError("invalid tool result content") block_type = block.WhichOneof("block") if block_type == "text_block": raw_content.append({"type": "text", "text": block.text_block.text}) elif block_type == "image_block": + if block.image_block.source_json: + raise ValueError("invalid tool result content") raw_content.append( { "type": "image", @@ -165,7 +169,17 @@ async def execute(self, **kwargs: Any) -> dict[str, Any]: content_type=content_type, ) response = await self._stub.Execute(request) - content = self._deserialize_content_blocks(response.content_blocks) + try: + content = self._deserialize_content_blocks(response.content_blocks) + except ValueError: + logger.warning( + "gRPC tool '%s' returned invalid tool result content", self._name + ) + return { + "success": False, + "output": None, + "error": {"message": "invalid tool result content from gRPC tool"}, + } if response.success: output = self._deserialize_output( diff --git a/python/amplifier_core/models.py b/python/amplifier_core/models.py index 7a655f0..724fcbb 100644 --- a/python/amplifier_core/models.py +++ b/python/amplifier_core/models.py @@ -15,6 +15,7 @@ from pydantic import Field from pydantic import field_serializer from pydantic import field_validator +from pydantic import model_serializer from .message_models import ImageBlock from .message_models import TextBlock @@ -26,8 +27,9 @@ def _json_default(obj: Any) -> Any: Handles types that flow through tool result payloads but aren't JSON-native. Currently: Decimal -> string (preserves precision; never lossy). - Used by ToolResult.get_serialized_output(). Pydantic models defend their - own boundary via @field_serializer; this is the safety net for the + Used by ToolResult.get_serialized_output() and as the fallback for + ToolResult rich-content serialization. Pydantic models defend their own + boundary via @field_serializer; this is the safety net for the non-Pydantic path where tool outputs may contain Decimal values (e.g., a tool that returns {"price": Decimal("9.99")}). """ @@ -57,10 +59,26 @@ def _sanitize_for_llm(text: str) -> str: return sanitized +def _validated_tool_result_content(content: Any) -> list[dict[str, Any]] | None: + """Canonicalize rich content through the Rust validation boundary.""" + from amplifier_core._engine import _normalize_tool_result_content + + serialized = json.dumps( + content, + default=lambda value: ( + value.model_dump(exclude_none=True) + if isinstance(value, BaseModel) + else _json_default(value) + ), + ) + normalized = _normalize_tool_result_content(serialized) + return json.loads(normalized) if normalized is not None else None + + class ToolResult(BaseModel): """Result from tool execution.""" - model_config = ConfigDict(hide_input_in_errors=True) + model_config = ConfigDict(hide_input_in_errors=True, validate_assignment=True) success: bool = Field(default=True, description="Whether execution succeeded") output: Any | None = Field(default=None, description="Tool output data") @@ -78,27 +96,30 @@ def normalize_content(cls, content: Any) -> Any: if content is None: return None - from amplifier_core._engine import _normalize_tool_result_content - - serialized = json.dumps( - content, - default=lambda value: value.model_dump(exclude_none=True), - ) - normalized = _normalize_tool_result_content(serialized) - return json.loads(normalized) if normalized is not None else None + return _validated_tool_result_content(content) @field_serializer("content", when_used="unless-none") def serialize_content(self, content: list[TextBlock | ImageBlock]) -> list[dict[str, Any]]: - """Emit only the canonical block fields retained by Rust normalization.""" - return [block.model_dump(exclude_none=True) for block in content] - - def model_dump(self, **kwargs: Any) -> dict[str, Any]: - """Preserve the legacy no-content key set while exposing rich content.""" - result = super().model_dump(**kwargs) - if self.content is None: + """Revalidate content so Pydantic escape hatches cannot serialize a bypass.""" + return _validated_tool_result_content(content) or [] + + @model_serializer(mode="wrap") + def serialize_model(self, handler: Any) -> Any: + """Preserve the legacy no-content key set for every Pydantic dump mode.""" + result = handler(self) + if not self.content: result.pop("content", None) return result + def model_copy( + self, *, update: dict[str, Any] | None = None, deep: bool = False + ) -> "ToolResult": + """Keep copy updates on the same strict ToolResult content boundary.""" + data = self.model_dump() + if update: + data.update(update) + return type(self).model_validate(data) + def model_post_init(self, __context: Any) -> None: """Auto-populate output from error when tools forget to set it. diff --git a/tests/fixtures/wasm/echo-tool.wasm b/tests/fixtures/wasm/echo-tool.wasm index a4024ec445314632f85cbba77aee1d0a4d8ceba3..aa43e367c5d0f85e66f0d9f43f572c1dca83ec1c 100644 GIT binary patch delta 47224 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 lFca*S6P^(xEPKRw+2kqFPcFO*gJpjf None: ], } assert ToolResult(content=[]).content is None + assert "content" not in json.loads(legacy.model_dump_json()) + assert "content" not in json.loads(ToolResult(content=[]).model_dump_json()) + + def test_legacy_model_dump_json_preserves_pydantic_serialization_options(self) -> None: + serialized = ToolResult(output={"status": "ok"}).model_dump_json(indent=2) + + assert serialized.startswith("{\n") + assert '"content"' not in serialized @pytest.mark.parametrize( "content, expected", @@ -127,3 +136,34 @@ def test_safe_hook_presentation_omits_image_data(self) -> None: }, ], } + + def test_pydantic_escape_hatch_cannot_serialize_noncanonical_content(self) -> None: + result = ToolResult.model_construct( + content=[{"type": "thinking", "thinking": "PYDANTIC-SECRET"}] + ) + + for serialize in (result.model_dump, result.model_dump_json): + with pytest.raises(ValueError) as exc_info: + serialize() + assert "invalid tool result content" in str(exc_info.value) + assert "PYDANTIC-SECRET" not in str(exc_info.value) + + def test_assignment_and_copy_updates_stay_inside_content_boundary(self) -> None: + result = ToolResult(content=[{"type": "text", "text": "details"}]) + result.content = [] + assert result.content is None + + with pytest.raises(ValueError, match="invalid tool result image source"): + result.content = [ + { + "type": "image", + "source": { + "type": "url", + "media_type": "image/png", + "data": "AA==", + }, + } + ] + + with pytest.raises(ValueError, match="invalid tool result content"): + result.model_copy(update={"content": [{"type": "thinking", "thinking": "no"}]}) From fec390665cde46d989205ef8379a1b910e2134e5 Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Sat, 12 Sep 2026 06:47:56 -0700 Subject: [PATCH 10/27] fix(loader): preserve canonical source module identity Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- python/amplifier_core/validation/base.py | 32 +++++++------- tests/test_validation_canonical_module.py | 53 +++++++++++++++++++++++ 2 files changed, 70 insertions(+), 15 deletions(-) diff --git a/python/amplifier_core/validation/base.py b/python/amplifier_core/validation/base.py index 2e72853..bf125fe 100644 --- a/python/amplifier_core/validation/base.py +++ b/python/amplifier_core/validation/base.py @@ -83,6 +83,21 @@ def import_module_from_path(module_path: str | Path) -> ModuleType: parent = str(import_root) _imp.acquire_lock() try: + # Audit children even when the matching root package is already loaded: + # a canonical root must not hide a child cached from a different source. + package_dir = source_path.parent.resolve() + for cached_name, cached_module in list(sys.modules.items()): + if not cached_name.startswith(f"{module_name}."): + continue + cached_file = getattr(cached_module, "__file__", None) + if cached_file is None or not Path(cached_file).resolve().is_relative_to( + package_dir + ): + raise ImportError( + f"Refusing to import '{module_name}' from {source_path}: " + f"cached submodule '{cached_name}' is from {cached_file}" + ) + existing = sys.modules.get(module_name) if existing is not None: existing_file = getattr(existing, "__file__", None) @@ -96,19 +111,6 @@ def import_module_from_path(module_path: str | Path) -> ModuleType: f"it is already loaded from {existing_file}" ) - package_dir = source_path.parent.resolve() - for cached_name, cached_module in sys.modules.items(): - if not cached_name.startswith(f"{module_name}."): - continue - cached_file = getattr(cached_module, "__file__", None) - if cached_file is None or not Path(cached_file).resolve().is_relative_to( - package_dir - ): - raise ImportError( - f"Refusing to import '{module_name}' from {source_path}: " - f"cached submodule '{cached_name}' is from {cached_file}" - ) - try: original_path_index = sys.path.index(parent) except ValueError: @@ -128,7 +130,7 @@ def import_module_from_path(module_path: str | Path) -> ModuleType: module = importlib.import_module(module_name) finally: current_path_index = next( - (index for index, value in enumerate(sys.path) if value is parent), + (index for index, value in enumerate(sys.path) if value == parent), None, ) if original_path_index is None: @@ -137,7 +139,7 @@ def import_module_from_path(module_path: str | Path) -> ModuleType: elif original_path_index != 0 and current_path_index is not None: sys.path.pop(current_path_index) next_path_index = next( - (index for index, value in enumerate(sys.path) if value is next_path), + (index for index, value in enumerate(sys.path) if value == next_path), None, ) if next_path_index is None: diff --git a/tests/test_validation_canonical_module.py b/tests/test_validation_canonical_module.py index 9f6604f..e4aa8d4 100644 --- a/tests/test_validation_canonical_module.py +++ b/tests/test_validation_canonical_module.py @@ -242,6 +242,37 @@ async def test_source_resolved_cache_rejects_a_different_source( _clear_package(package_name) +@pytest.mark.asyncio +async def test_matching_root_with_foreign_cached_child_cannot_mount( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """A cached canonical root does not exempt its children from source checks.""" + module_id = "tool-mixed-cache" + package_name = "amplifier_module_tool_mixed_cache" + source_root = tmp_path / "source" + _write_tool_package(source_root, package_name, "canonical") + foreign_package = _write_tool_package(tmp_path / "foreign", package_name, "foreign") + monkeypatch.syspath_prepend(str(source_root)) + loader = ModuleLoader(coordinator=_ResolverCoordinator(source_root)) + + try: + canonical = importlib.import_module(package_name) + foreign_child = types.ModuleType(f"{package_name}.marker") + foreign_child.__file__ = str(foreign_package / "marker.py") + sys.modules[f"{package_name}.marker"] = foreign_child + + with pytest.raises(ModuleValidationError, match="cached submodule"): + await loader.load(module_id) + + assert sys.modules[package_name] is canonical + assert sys.modules[f"{package_name}.marker"] is foreign_child + assert module_id not in loader._loaded_modules + assert canonical.IMPORT_COUNT == 1 + finally: + loader.cleanup() + _clear_package(package_name) + + @pytest.mark.asyncio async def test_direct_cache_does_not_override_a_later_source_resolution( tmp_path: Path, monkeypatch: pytest.MonkeyPatch @@ -350,6 +381,28 @@ def test_standalone_path_validation_prioritizes_requested_source( sys.path.remove(str(requested_root)) +def test_standalone_path_validation_removes_reconstructed_import_path( + tmp_path: Path, +) -> None: + """Cleanup works when the imported package recreates sys.path strings.""" + package_name = "amplifier_module_tool_path_reconstruction" + import_root = tmp_path / "requested" + package = import_root / package_name + package.mkdir(parents=True) + (package / "__init__.py").write_text( + "import sys\n" + "sys.path[:] = [entry.encode().decode() for entry in sys.path]\n" + ) + + try: + module = import_module_from_path(package) + + assert module.__name__ == package_name + assert str(import_root) not in sys.path + finally: + _clear_package(package_name) + + def test_standalone_path_validation_does_not_expose_temporary_import_path( tmp_path: Path, ) -> None: From 98429cd08f3b6d56dd4e04a06f310d759ac03f69 Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Sat, 12 Sep 2026 06:47:58 -0700 Subject: [PATCH 11/27] chore: bump core version to 1.6.2 Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- Cargo.lock | 4 ++-- bindings/python/Cargo.toml | 2 +- crates/amplifier-core/Cargo.toml | 2 +- pyproject.toml | 2 +- python/amplifier_core/__init__.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8ae9b1..7e88905 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,7 +40,7 @@ checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" [[package]] name = "amplifier-core" -version = "1.6.1" +version = "1.6.2" dependencies = [ "chrono", "log", @@ -76,7 +76,7 @@ dependencies = [ [[package]] name = "amplifier-core-py" -version = "1.6.1" +version = "1.6.2" dependencies = [ "amplifier-core", "log", diff --git a/bindings/python/Cargo.toml b/bindings/python/Cargo.toml index d43c48b..d4084a3 100644 --- a/bindings/python/Cargo.toml +++ b/bindings/python/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "amplifier-core-py" -version = "1.6.1" +version = "1.6.2" edition = "2021" description = "PyO3 bridge for amplifier-core Rust kernel" license = "MIT" diff --git a/crates/amplifier-core/Cargo.toml b/crates/amplifier-core/Cargo.toml index fadbe06..fcfdde8 100644 --- a/crates/amplifier-core/Cargo.toml +++ b/crates/amplifier-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "amplifier-core" -version = "1.6.1" +version = "1.6.2" edition = "2021" description = "Pure Rust kernel for the Amplifier modular AI agent system" license = "MIT" diff --git a/pyproject.toml b/pyproject.toml index 194e602..a7c12bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "amplifier-core" -version = "1.6.1" +version = "1.6.2" description = "Rust kernel with Python bindings for the Amplifier modular AI agent framework" license = "MIT" readme = "README.md" diff --git a/python/amplifier_core/__init__.py b/python/amplifier_core/__init__.py index b4e014c..fb4be33 100644 --- a/python/amplifier_core/__init__.py +++ b/python/amplifier_core/__init__.py @@ -6,7 +6,7 @@ AmplifierSession`) still give the pure-Python implementations. """ -__version__ = "1.6.1" +__version__ = "1.6.2" # --- Rust-backed primary types (THE SWITCHOVER) --- # These four were previously imported from their Python submodules. From 6a8bd95b02873695c60d78a63fc2dbc422e95288 Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Sat, 12 Sep 2026 07:43:59 -0700 Subject: [PATCH 12/27] chore: synchronize Python lockfile with core 1.6.2 Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- uv.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uv.lock b/uv.lock index f883d87..e3d45b7 100644 --- a/uv.lock +++ b/uv.lock @@ -4,7 +4,7 @@ requires-python = ">=3.11" [[package]] name = "amplifier-core" -version = "1.5.1" +version = "1.6.2" source = { editable = "." } dependencies = [ { name = "click" }, From 89878d32d0534ec4b299fe513f1e462c3e3e2abc Mon Sep 17 00:00:00 2001 From: Amplifier Resolve Date: Sat, 12 Sep 2026 07:58:39 -0700 Subject: [PATCH 13/27] feat(core): preserve usage cost across transports Related to #90. Preserve optional exact decimal cost text through Rust, protobuf, Python, and WASM boundaries. Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- CONTRACTS.md | 2 +- .../src/generated/amplifier.module.rs | 4 +- .../src/generated/conversions.rs | 15 ++ .../src/generated/equivalence_tests.rs | 2 + crates/amplifier-core/src/grpc_server.rs | 194 +++++++++++++++ crates/amplifier-core/src/messages.rs | 6 + crates/amplifier-core/tests/wasm_e2e.rs | 12 + crates/amplifier-guest/src/lib.rs | 4 + crates/amplifier-guest/src/types.rs | 60 +++++ proto/amplifier_module.proto | 1 + proto/amplifier_module_pb2.py | 222 +++++++++--------- .../_grpc_gen/amplifier_module_pb2.py | 222 +++++++++--------- tests/fixtures/wasm/echo-provider.wasm | Bin 231426 -> 248198 bytes .../wasm/src/echo-provider/Cargo.lock | 7 + .../wasm/src/echo-provider/src/lib.rs | 11 +- tests/test_pyo3_proto_bridge.py | 49 ++++ 16 files changed, 586 insertions(+), 225 deletions(-) diff --git a/CONTRACTS.md b/CONTRACTS.md index 2201318..aa65b54 100644 --- a/CONTRACTS.md +++ b/CONTRACTS.md @@ -70,7 +70,7 @@ field name differs from the JSON key. | `ToolSpec` | `ToolSpec` (BaseModel) | JSON round-trip | 1:1. | | `ChatRequest` | `ChatRequest` (BaseModel) | JSON round-trip | 1:1. | | `ToolCall` | `ToolCall` (BaseModel) | JSON round-trip | 1:1. | -| `Usage` | `Usage` (BaseModel) | JSON round-trip | 1:1. | +| `Usage` | `Usage` (BaseModel) | JSON round-trip | 1:1. `cost_usd` is optional quoted decimal text at the Rust/protobuf boundary (`Option`); Python exposes it as `Decimal \| None`. None is omitted and `"0"` remains present. | | `Degradation` | `Degradation` (BaseModel) | JSON round-trip | 1:1. | | `ChatResponse` | `ChatResponse` (BaseModel) | JSON round-trip | 1:1. | | `ResponseFormat` (tagged enum) | `ResponseFormat` (Union) | `serde(tag = "type")` | Text/Json/JsonSchema variants match. | diff --git a/crates/amplifier-core/src/generated/amplifier.module.rs b/crates/amplifier-core/src/generated/amplifier.module.rs index cc0f4e7..d024cfd 100644 --- a/crates/amplifier-core/src/generated/amplifier.module.rs +++ b/crates/amplifier-core/src/generated/amplifier.module.rs @@ -321,7 +321,7 @@ pub mod response_format { JsonSchema(super::JsonSchemaFormat), } } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct Usage { #[prost(int32, tag = "1")] pub prompt_tokens: i32, @@ -335,6 +335,8 @@ pub struct Usage { pub cache_read_tokens: ::core::option::Option, #[prost(int32, optional, tag = "6")] pub cache_creation_tokens: ::core::option::Option, + #[prost(string, optional, tag = "7")] + pub cost_usd: ::core::option::Option<::prost::alloc::string::String>, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct Degradation { diff --git a/crates/amplifier-core/src/generated/conversions.rs b/crates/amplifier-core/src/generated/conversions.rs index f41c585..58b797b 100644 --- a/crates/amplifier-core/src/generated/conversions.rs +++ b/crates/amplifier-core/src/generated/conversions.rs @@ -192,6 +192,7 @@ impl From for super::amplifier_module::Usage { i32::MAX }) }), + cost_usd: native.cost_usd, } } } @@ -205,6 +206,7 @@ impl From for crate::messages::Usage { reasoning_tokens: proto.reasoning_tokens.map(i64::from), cache_read_tokens: proto.cache_read_tokens.map(i64::from), cache_write_tokens: proto.cache_creation_tokens.map(i64::from), + cost_usd: proto.cost_usd, extensions: HashMap::new(), } } @@ -1255,9 +1257,11 @@ mod tests { reasoning_tokens: Some(20), cache_read_tokens: Some(10), cache_write_tokens: None, // 0 in proto, None when restored + cost_usd: Some("0.000000000123456789".into()), extensions: HashMap::new(), }; let proto: super::super::amplifier_module::Usage = original.clone().into(); + assert_eq!(proto.cost_usd.as_deref(), Some("0.000000000123456789")); let restored: crate::messages::Usage = proto.into(); assert_eq!(original.input_tokens, restored.input_tokens); assert_eq!(original.output_tokens, restored.output_tokens); @@ -1266,6 +1270,7 @@ mod tests { assert_eq!(original.cache_read_tokens, restored.cache_read_tokens); // cache_write_tokens: None → None (optional proto preserves None) assert_eq!(restored.cache_write_tokens, None); + assert_eq!(restored.cost_usd.as_deref(), Some("0.000000000123456789")); // extensions are lost in proto roundtrip (proto has no extensions field) assert!(restored.extensions.is_empty()); } @@ -1279,9 +1284,11 @@ mod tests { reasoning_tokens: Some(50), cache_read_tokens: Some(30), cache_write_tokens: Some(20), + cost_usd: None, extensions: HashMap::new(), }; let proto: super::super::amplifier_module::Usage = original.clone().into(); + assert!(proto.cost_usd.is_none()); let restored: crate::messages::Usage = proto.into(); assert_eq!(original.input_tokens, restored.input_tokens); assert_eq!(original.output_tokens, restored.output_tokens); @@ -1289,6 +1296,7 @@ mod tests { assert_eq!(original.reasoning_tokens, restored.reasoning_tokens); assert_eq!(original.cache_read_tokens, restored.cache_read_tokens); assert_eq!(original.cache_write_tokens, restored.cache_write_tokens); + assert!(restored.cost_usd.is_none()); } /// Verify that `Some(0)` survives roundtrip now that proto uses `optional` fields. @@ -1301,9 +1309,11 @@ mod tests { reasoning_tokens: Some(0), cache_read_tokens: Some(0), cache_write_tokens: Some(0), + cost_usd: Some("0".into()), extensions: HashMap::new(), }; let proto: super::super::amplifier_module::Usage = original.clone().into(); + assert_eq!(proto.cost_usd.as_deref(), Some("0")); let restored: crate::messages::Usage = proto.into(); assert_eq!( restored.reasoning_tokens, @@ -1320,6 +1330,7 @@ mod tests { Some(0), "Some(0) cache_write_tokens must survive roundtrip" ); + assert_eq!(restored.cost_usd.as_deref(), Some("0")); } // -- E-3: ModelInfo i64→i32 overflow clamps to i32::MAX -- @@ -1365,6 +1376,7 @@ mod tests { reasoning_tokens: None, cache_read_tokens: None, cache_write_tokens: None, + cost_usd: None, extensions: HashMap::new(), }; let proto: super::super::amplifier_module::Usage = original.into(); @@ -1380,6 +1392,7 @@ mod tests { reasoning_tokens: None, cache_read_tokens: None, cache_write_tokens: None, + cost_usd: None, extensions: HashMap::new(), }; let proto: super::super::amplifier_module::Usage = original.into(); @@ -1395,6 +1408,7 @@ mod tests { reasoning_tokens: None, cache_read_tokens: None, cache_write_tokens: None, + cost_usd: None, extensions: HashMap::new(), }; let proto: super::super::amplifier_module::Usage = original.into(); @@ -2081,6 +2095,7 @@ mod tests { reasoning_tokens: Some(50), cache_read_tokens: Some(20), cache_write_tokens: None, + cost_usd: None, extensions: HashMap::new(), }), degradation: Some(Degradation { diff --git a/crates/amplifier-core/src/generated/equivalence_tests.rs b/crates/amplifier-core/src/generated/equivalence_tests.rs index 45c0b7b..ce693c3 100644 --- a/crates/amplifier-core/src/generated/equivalence_tests.rs +++ b/crates/amplifier-core/src/generated/equivalence_tests.rs @@ -178,6 +178,7 @@ mod tests { reasoning_tokens: Some(20), cache_read_tokens: Some(30), cache_creation_tokens: Some(10), + cost_usd: Some("0.000000000123456789".into()), }; assert_eq!(usage.prompt_tokens, 100); assert_eq!(usage.completion_tokens, 50); @@ -185,6 +186,7 @@ mod tests { assert_eq!(usage.reasoning_tokens, Some(20)); assert_eq!(usage.cache_read_tokens, Some(30)); assert_eq!(usage.cache_creation_tokens, Some(10)); + assert_eq!(usage.cost_usd.as_deref(), Some("0.000000000123456789")); } #[test] diff --git a/crates/amplifier-core/src/grpc_server.rs b/crates/amplifier-core/src/grpc_server.rs index 37d779e..f835155 100644 --- a/crates/amplifier-core/src/grpc_server.rs +++ b/crates/amplifier-core/src/grpc_server.rs @@ -526,6 +526,102 @@ impl KernelService for KernelServiceImpl { #[cfg(test)] mod tests { use super::*; + use std::collections::HashMap; + use std::future::Future; + use std::pin::Pin; + + struct CostProvider { + cost_usd: Option, + } + + impl CostProvider { + fn new(cost_usd: Option<&str>) -> Self { + Self { + cost_usd: cost_usd.map(String::from), + } + } + } + + impl crate::traits::Provider for CostProvider { + fn name(&self) -> &str { + "cost-provider" + } + + fn get_info(&self) -> crate::models::ProviderInfo { + crate::models::ProviderInfo { + id: self.name().into(), + display_name: "Cost Provider".into(), + credential_env_vars: Vec::new(), + capabilities: Vec::new(), + defaults: HashMap::new(), + config_fields: Vec::new(), + } + } + + fn list_models( + &self, + ) -> Pin< + Box< + dyn Future< + Output = Result< + Vec, + crate::errors::ProviderError, + >, + > + Send + + '_, + >, + > { + Box::pin(async { Ok(Vec::new()) }) + } + + fn complete( + &self, + _request: crate::messages::ChatRequest, + ) -> Pin< + Box< + dyn Future< + Output = Result< + crate::messages::ChatResponse, + crate::errors::ProviderError, + >, + > + Send + + '_, + >, + > { + let cost_usd = self.cost_usd.clone(); + Box::pin(async move { + Ok(crate::messages::ChatResponse { + content: vec![crate::messages::ContentBlock::Text { + text: "response with provider-reported cost".into(), + visibility: None, + extensions: HashMap::new(), + }], + tool_calls: None, + usage: Some(crate::messages::Usage { + input_tokens: 10, + output_tokens: 5, + total_tokens: 15, + reasoning_tokens: None, + cache_read_tokens: None, + cache_write_tokens: None, + cost_usd, + extensions: HashMap::new(), + }), + degradation: None, + finish_reason: Some("stop".into()), + metadata: None, + extensions: HashMap::new(), + }) + }) + } + + fn parse_tool_calls( + &self, + response: &crate::messages::ChatResponse, + ) -> Vec { + response.tool_calls.clone().unwrap_or_default() + } + } #[test] fn kernel_service_impl_compiles() { @@ -1357,6 +1453,104 @@ mod tests { ); } + #[tokio::test] + async fn generated_grpc_client_preserves_provider_reported_cost_for_unary_and_streaming() { + use crate::generated::amplifier_module::kernel_service_client::KernelServiceClient; + use crate::generated::amplifier_module::kernel_service_server::KernelServiceServer; + use tokio::sync::oneshot; + use tokio_stream::wrappers::TcpListenerStream; + use tonic::transport::Server; + + let coord = Arc::new(Coordinator::new(Default::default())); + coord.mount_provider( + "with-cost", + Arc::new(CostProvider::new(Some("0.000000000123456789"))), + ); + coord.mount_provider("without-cost", Arc::new(CostProvider::new(None))); + + let listener = tokio::net::TcpListener::bind("127.0.0.1:0") + .await + .expect("test server must bind"); + let address = listener + .local_addr() + .expect("test server must have an address"); + let (shutdown_tx, shutdown_rx) = oneshot::channel(); + let server = tokio::spawn(async move { + Server::builder() + .add_service(KernelServiceServer::new(KernelServiceImpl::new(coord))) + .serve_with_incoming_shutdown(TcpListenerStream::new(listener), async { + let _ = shutdown_rx.await; + }) + .await + }); + + let mut client = KernelServiceClient::connect(format!("http://{address}")) + .await + .expect("generated client must connect to test server"); + let request = amplifier_module::CompleteWithProviderRequest { + provider_name: "with-cost".into(), + request: Some(make_chat_request("ping")), + }; + + let unary = client + .complete_with_provider(request.clone()) + .await + .expect("unary RPC must succeed") + .into_inner(); + assert_eq!( + unary.usage.and_then(|usage| usage.cost_usd), + Some("0.000000000123456789".into()) + ); + + let mut stream = client + .complete_with_provider_streaming(request) + .await + .expect("one-shot streaming RPC must succeed") + .into_inner(); + let streamed = stream + .message() + .await + .expect("stream must not fail") + .expect("stream must contain one response"); + assert_eq!( + streamed.usage.and_then(|usage| usage.cost_usd), + Some("0.000000000123456789".into()) + ); + assert!( + stream + .message() + .await + .expect("stream must not fail") + .is_none(), + "one-shot streaming RPC must end after one response" + ); + + let absent_cost = client + .complete_with_provider(amplifier_module::CompleteWithProviderRequest { + provider_name: "without-cost".into(), + request: Some(make_chat_request("ping")), + }) + .await + .expect("unary RPC without cost must succeed") + .into_inner(); + assert!( + absent_cost + .usage + .expect("provider returned usage") + .cost_usd + .is_none(), + "None cost must remain absent in protobuf" + ); + + shutdown_tx + .send(()) + .expect("test server must still accept shutdown"); + server + .await + .expect("test server task must not panic") + .expect("test server must shut down cleanly"); + } + #[tokio::test] async fn complete_with_provider_not_found_returns_not_found_status() { let coord = Arc::new(Coordinator::new(Default::default())); diff --git a/crates/amplifier-core/src/messages.rs b/crates/amplifier-core/src/messages.rs index bb0e746..4a5b2a3 100644 --- a/crates/amplifier-core/src/messages.rs +++ b/crates/amplifier-core/src/messages.rs @@ -275,6 +275,8 @@ pub struct Usage { pub cache_read_tokens: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub cache_write_tokens: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cost_usd: Option, #[serde(flatten)] pub extensions: HashMap, } @@ -936,6 +938,7 @@ mod tests { reasoning_tokens: Some(20), cache_read_tokens: None, cache_write_tokens: None, + cost_usd: Some("0.000000000123456789".into()), extensions: HashMap::new(), }; let json = serde_json::to_value(&usage).unwrap(); @@ -943,6 +946,7 @@ mod tests { assert_eq!(json["output_tokens"], 50); assert_eq!(json["total_tokens"], 150); assert_eq!(json["reasoning_tokens"], 20); + assert_eq!(json["cost_usd"], "0.000000000123456789"); assert!(json.get("cache_read_tokens").is_none()); let deserialized: Usage = serde_json::from_value(json).unwrap(); assert_eq!(deserialized, usage); @@ -1003,6 +1007,7 @@ mod tests { reasoning_tokens: None, cache_read_tokens: None, cache_write_tokens: None, + cost_usd: Some("0".into()), extensions: HashMap::new(), }), degradation: None, @@ -1015,6 +1020,7 @@ mod tests { assert_eq!(json["content"][0]["type"], "text"); assert_eq!(json["content"][0]["text"], "Hello!"); assert_eq!(json["usage"]["input_tokens"], 10); + assert_eq!(json["usage"]["cost_usd"], "0"); assert_eq!(json["finish_reason"], "stop"); assert!(json.get("tool_calls").is_none()); assert!(json.get("degradation").is_none()); diff --git a/crates/amplifier-core/tests/wasm_e2e.rs b/crates/amplifier-core/tests/wasm_e2e.rs index 587f216..f22035f 100644 --- a/crates/amplifier-core/tests/wasm_e2e.rs +++ b/crates/amplifier-core/tests/wasm_e2e.rs @@ -305,6 +305,18 @@ async fn provider_complete() { !response.content.is_empty(), "provider.complete() should return non-empty content" ); + assert_eq!( + response + .usage + .as_ref() + .and_then(|usage| usage.cost_usd.as_deref()), + Some("0.000000000123456789"), + "provider-reported cost must survive the WASM response boundary" + ); + assert!( + provider.parse_tool_calls(&response).is_empty(), + "parse_tool_calls must accept a response carrying usage" + ); } // --------------------------------------------------------------------------- diff --git a/crates/amplifier-guest/src/lib.rs b/crates/amplifier-guest/src/lib.rs index 525cdd3..5c9522c 100644 --- a/crates/amplifier-guest/src/lib.rs +++ b/crates/amplifier-guest/src/lib.rs @@ -1299,6 +1299,7 @@ mod provider_tests { Ok(ChatResponse { content: vec![json!({"type": "text", "text": "hello"})], tool_calls: None, + usage: None, finish_reason: Some("stop".to_string()), extra: HashMap::new(), }) @@ -1347,6 +1348,7 @@ mod provider_tests { let resp = ChatResponse { content: vec![], tool_calls: None, + usage: None, finish_reason: None, extra: HashMap::new(), }; @@ -1360,6 +1362,7 @@ mod provider_tests { let resp = ChatResponse { content: vec![], tool_calls: Some(vec![json!({"name": "echo", "args": {}})]), + usage: None, finish_reason: None, extra: HashMap::new(), }; @@ -1433,6 +1436,7 @@ mod provider_macro_tests { Ok(ChatResponse { content: vec![], tool_calls: None, + usage: None, finish_reason: None, extra: HashMap::new(), }) diff --git a/crates/amplifier-guest/src/types.rs b/crates/amplifier-guest/src/types.rs index 449ee11..11ec0e4 100644 --- a/crates/amplifier-guest/src/types.rs +++ b/crates/amplifier-guest/src/types.rs @@ -335,12 +335,30 @@ pub struct ChatRequest { pub extra: HashMap, } +/// Token usage information reported by an LLM provider. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Usage { + pub input_tokens: i64, + pub output_tokens: i64, + pub total_tokens: i64, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub reasoning_tokens: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cache_read_tokens: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cache_write_tokens: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cost_usd: Option, +} + /// Response from an LLM chat completion. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ChatResponse { pub content: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub tool_calls: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub usage: Option, #[serde(skip_serializing_if = "Option::is_none")] pub finish_reason: Option, #[serde(flatten)] @@ -705,6 +723,15 @@ mod tests { let resp = ChatResponse { content: vec![json!({"type": "text", "text": "Hello!"})], tool_calls: None, + usage: Some(Usage { + input_tokens: 10, + output_tokens: 5, + total_tokens: 15, + reasoning_tokens: None, + cache_read_tokens: None, + cache_write_tokens: None, + cost_usd: Some("0.000000000123456789".to_string()), + }), finish_reason: Some("stop".to_string()), extra: HashMap::new(), }; @@ -713,10 +740,43 @@ mod tests { assert_eq!(deserialized.content.len(), 1); assert_eq!(deserialized.content[0]["text"], json!("Hello!")); assert!(deserialized.tool_calls.is_none()); + assert_eq!( + deserialized + .usage + .as_ref() + .and_then(|usage| usage.cost_usd.as_deref()), + Some("0.000000000123456789") + ); assert_eq!(deserialized.finish_reason, Some("stop".to_string())); assert!(deserialized.extra.is_empty()); } + #[test] + fn test_usage_cost_usd_none_is_omitted_and_zero_is_preserved() { + let absent = Usage { + input_tokens: 10, + output_tokens: 5, + total_tokens: 15, + reasoning_tokens: None, + cache_read_tokens: None, + cache_write_tokens: None, + cost_usd: None, + }; + assert!(serde_json::to_value(&absent) + .unwrap() + .get("cost_usd") + .is_none()); + + let present_zero = Usage { + cost_usd: Some("0".to_string()), + ..absent + }; + let serialized = serde_json::to_value(&present_zero).unwrap(); + assert_eq!(serialized["cost_usd"], "0"); + let restored: Usage = serde_json::from_value(serialized).unwrap(); + assert_eq!(restored.cost_usd.as_deref(), Some("0")); + } + // --- PartialEq roundtrip tests --- #[test] diff --git a/proto/amplifier_module.proto b/proto/amplifier_module.proto index c283efa..8637888 100644 --- a/proto/amplifier_module.proto +++ b/proto/amplifier_module.proto @@ -307,6 +307,7 @@ message Usage { optional int32 reasoning_tokens = 4; optional int32 cache_read_tokens = 5; optional int32 cache_creation_tokens = 6; + optional string cost_usd = 7; } message Degradation { diff --git a/proto/amplifier_module_pb2.py b/proto/amplifier_module_pb2.py index d6c0e3e..4559c62 100644 --- a/proto/amplifier_module_pb2.py +++ b/proto/amplifier_module_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: amplifier_module.proto -# Protobuf Python Version: 6.31.1 +# Protobuf Python Version: 7.35.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -11,8 +11,8 @@ from google.protobuf.internal import builder as _builder _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, - 6, - 31, + 7, + 35, 1, '', 'amplifier_module.proto' @@ -24,7 +24,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x61mplifier_module.proto\x12\x10\x61mplifier.module\"\x07\n\x05\x45mpty\"F\n\x08ToolSpec\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"9\n\x12ToolExecuteRequest\x12\r\n\x05input\x18\x01 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x02 \x01(\t\"\x93\x01\n\x13ToolExecuteResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0e\n\x06output\x18\x02 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x03 \x01(\t\x12\r\n\x05\x65rror\x18\x04 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x05 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xd6\x01\n\nModuleInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x31\n\x0bmodule_type\x18\x04 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\x12\x13\n\x0bmount_point\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x06 \x01(\t\x12\x1a\n\x12\x63onfig_schema_json\x18\x07 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x08 \x03(\t\x12\x0e\n\x06\x61uthor\x18\t \x01(\t\"\x8c\x01\n\x0cMountRequest\x12:\n\x06\x63onfig\x18\x01 \x03(\x0b\x32*.amplifier.module.MountRequest.ConfigEntry\x12\x11\n\tmodule_id\x18\x02 \x01(\t\x1a-\n\x0b\x43onfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"_\n\rMountResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12.\n\x06status\x18\x03 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\"V\n\x13HealthCheckResponse\x12.\n\x06status\x18\x01 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xca\x02\n\x0b\x43onfigField\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x35\n\nfield_type\x18\x03 \x01(\x0e\x32!.amplifier.module.ConfigFieldType\x12\x0e\n\x06prompt\x18\x04 \x01(\t\x12\x0f\n\x07\x65nv_var\x18\x05 \x01(\t\x12\x0f\n\x07\x63hoices\x18\x06 \x03(\t\x12\x10\n\x08required\x18\x07 \x01(\x08\x12\x15\n\rdefault_value\x18\x08 \x01(\t\x12>\n\tshow_when\x18\t \x03(\x0b\x32+.amplifier.module.ConfigField.ShowWhenEntry\x12\x16\n\x0erequires_model\x18\n \x01(\x08\x1a/\n\rShowWhenEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xbc\x01\n\rProviderError\x12\x37\n\nerror_type\x18\x01 \x01(\x0e\x32#.amplifier.module.ProviderErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x15\n\rprovider_name\x18\x03 \x01(\t\x12\r\n\x05model\x18\x04 \x01(\t\x12\x13\n\x0bstatus_code\x18\x05 \x01(\x05\x12\x11\n\tretryable\x18\x06 \x01(\x08\x12\x13\n\x0bretry_after\x18\x07 \x01(\x01\"\x97\x01\n\tToolError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.ToolErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\ttool_name\x18\x03 \x01(\t\x12\x0e\n\x06stdout\x18\x04 \x01(\t\x12\x0e\n\x06stderr\x18\x05 \x01(\t\x12\x11\n\texit_code\x18\x06 \x01(\x05\"d\n\tHookError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.HookErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\thook_name\x18\x03 \x01(\t\"\xef\x01\n\x0e\x41mplifierError\x12\x39\n\x0eprovider_error\x18\x01 \x01(\x0b\x32\x1f.amplifier.module.ProviderErrorH\x00\x12\x31\n\ntool_error\x18\x02 \x01(\x0b\x32\x1b.amplifier.module.ToolErrorH\x00\x12\x31\n\nhook_error\x18\x03 \x01(\x0b\x32\x1b.amplifier.module.HookErrorH\x00\x12\x17\n\rgeneric_error\x18\x04 \x01(\tH\x00\x12\x1a\n\x10validation_error\x18\x05 \x01(\tH\x00\x42\x07\n\x05\x65rror\"\x19\n\tTextBlock\x12\x0c\n\x04text\x18\x01 \x01(\t\"E\n\rThinkingBlock\x12\x10\n\x08thinking\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\t\"%\n\x15RedactedThinkingBlock\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\t\"=\n\rToolCallBlock\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x12\n\ninput_json\x18\x03 \x01(\t\"<\n\x0fToolResultBlock\x12\x14\n\x0ctool_call_id\x18\x01 \x01(\t\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\"C\n\nImageBlock\x12\x12\n\nmedia_type\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x13\n\x0bsource_json\x18\x03 \x01(\t\"2\n\x0eReasoningBlock\x12\x0f\n\x07\x63ontent\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x03(\t\"\xf1\x03\n\x0c\x43ontentBlock\x12\x31\n\ntext_block\x18\x01 \x01(\x0b\x32\x1b.amplifier.module.TextBlockH\x00\x12\x39\n\x0ethinking_block\x18\x02 \x01(\x0b\x32\x1f.amplifier.module.ThinkingBlockH\x00\x12J\n\x17redacted_thinking_block\x18\x03 \x01(\x0b\x32\'.amplifier.module.RedactedThinkingBlockH\x00\x12:\n\x0ftool_call_block\x18\x04 \x01(\x0b\x32\x1f.amplifier.module.ToolCallBlockH\x00\x12>\n\x11tool_result_block\x18\x05 \x01(\x0b\x32!.amplifier.module.ToolResultBlockH\x00\x12\x33\n\x0bimage_block\x18\x06 \x01(\x0b\x32\x1c.amplifier.module.ImageBlockH\x00\x12;\n\x0freasoning_block\x18\x07 \x01(\x0b\x32 .amplifier.module.ReasoningBlockH\x00\x12\x30\n\nvisibility\x18\x08 \x01(\x0e\x32\x1c.amplifier.module.VisibilityB\x07\n\x05\x62lock\"B\n\x10\x43ontentBlockList\x12.\n\x06\x62locks\x18\x01 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xca\x01\n\x07Message\x12$\n\x04role\x18\x01 \x01(\x0e\x32\x16.amplifier.module.Role\x12\x16\n\x0ctext_content\x18\x02 \x01(\tH\x00\x12;\n\rblock_content\x18\x03 \x01(\x0b\x32\".amplifier.module.ContentBlockListH\x00\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x14\n\x0ctool_call_id\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\tB\t\n\x07\x63ontent\"C\n\x0fToolCallMessage\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x16\n\x0e\x61rguments_json\x18\x03 \x01(\t\"K\n\rToolSpecProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"7\n\x10JsonSchemaFormat\x12\x13\n\x0bschema_json\x18\x01 \x01(\t\x12\x0e\n\x06strict\x18\x02 \x01(\x08\"u\n\x0eResponseFormat\x12\x0e\n\x04text\x18\x01 \x01(\x08H\x00\x12\x0e\n\x04json\x18\x02 \x01(\x08H\x00\x12\x39\n\x0bjson_schema\x18\x03 \x01(\x0b\x32\".amplifier.module.JsonSchemaFormatH\x00\x42\x08\n\x06\x66ormat\"\xf7\x01\n\x05Usage\x12\x15\n\rprompt_tokens\x18\x01 \x01(\x05\x12\x19\n\x11\x63ompletion_tokens\x18\x02 \x01(\x05\x12\x14\n\x0ctotal_tokens\x18\x03 \x01(\x05\x12\x1d\n\x10reasoning_tokens\x18\x04 \x01(\x05H\x00\x88\x01\x01\x12\x1e\n\x11\x63\x61\x63he_read_tokens\x18\x05 \x01(\x05H\x01\x88\x01\x01\x12\"\n\x15\x63\x61\x63he_creation_tokens\x18\x06 \x01(\x05H\x02\x88\x01\x01\x42\x13\n\x11_reasoning_tokensB\x14\n\x12_cache_read_tokensB\x18\n\x16_cache_creation_tokens\"@\n\x0b\x44\x65gradation\x12\x11\n\trequested\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tual\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x81\x03\n\x0b\x43hatRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\x12.\n\x05tools\x18\x02 \x03(\x0b\x32\x1f.amplifier.module.ToolSpecProto\x12\x39\n\x0fresponse_format\x18\x03 \x01(\x0b\x32 .amplifier.module.ResponseFormat\x12\x13\n\x0btemperature\x18\x04 \x01(\x01\x12\r\n\x05top_p\x18\x05 \x01(\x01\x12\x19\n\x11max_output_tokens\x18\x06 \x01(\x05\x12\x17\n\x0f\x63onversation_id\x18\x07 \x01(\t\x12\x0e\n\x06stream\x18\x08 \x01(\x08\x12\x15\n\rmetadata_json\x18\t \x01(\t\x12\r\n\x05model\x18\n \x01(\t\x12\x13\n\x0btool_choice\x18\x0b \x01(\t\x12\x0c\n\x04stop\x18\x0c \x03(\t\x12\x18\n\x10reasoning_effort\x18\r \x01(\t\x12\x0f\n\x07timeout\x18\x0e \x01(\x01\"\x98\x02\n\x0c\x43hatResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x35\n\ntool_calls\x18\x02 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\x12&\n\x05usage\x18\x03 \x01(\x0b\x32\x17.amplifier.module.Usage\x12\x32\n\x0b\x64\x65gradation\x18\x04 \x01(\x0b\x32\x1d.amplifier.module.Degradation\x12\x15\n\rfinish_reason\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x07 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"~\n\nToolResult\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\x12\x12\n\nerror_json\x18\x03 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x04 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xa7\x04\n\nHookResult\x12,\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x1c.amplifier.module.HookAction\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x19\n\x11\x63ontext_injection\x18\x04 \x01(\t\x12\x46\n\x16\x63ontext_injection_role\x18\x05 \x01(\x0e\x32&.amplifier.module.ContextInjectionRole\x12\x11\n\tephemeral\x18\x06 \x01(\x08\x12\x17\n\x0f\x61pproval_prompt\x18\x07 \x01(\t\x12\x18\n\x10\x61pproval_options\x18\x08 \x03(\t\x12\x1d\n\x10\x61pproval_timeout\x18\t \x01(\x01H\x00\x88\x01\x01\x12;\n\x10\x61pproval_default\x18\n \x01(\x0e\x32!.amplifier.module.ApprovalDefault\x12\x17\n\x0fsuppress_output\x18\x0b \x01(\x08\x12\x14\n\x0cuser_message\x18\x0c \x01(\t\x12>\n\x12user_message_level\x18\r \x01(\x0e\x32\".amplifier.module.UserMessageLevel\x12\x1b\n\x13user_message_source\x18\x0e \x01(\t\x12\"\n\x1a\x61ppend_to_last_tool_result\x18\x0f \x01(\x08\x42\x13\n\x11_approval_timeout\"\x8d\x01\n\tModelInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x16\n\x0e\x63ontext_window\x18\x03 \x01(\x05\x12\x19\n\x11max_output_tokens\x18\x04 \x01(\x05\x12\x14\n\x0c\x63\x61pabilities\x18\x05 \x03(\t\x12\x15\n\rdefaults_json\x18\x06 \x01(\t\"\xb0\x01\n\x0cProviderInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x1b\n\x13\x63redential_env_vars\x18\x03 \x03(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x04 \x03(\t\x12\x15\n\rdefaults_json\x18\x05 \x01(\t\x12\x34\n\rconfig_fields\x18\x06 \x03(\x0b\x32\x1d.amplifier.module.ConfigField\"\x80\x01\n\x0f\x41pprovalRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x14\n\x0c\x64\x65tails_json\x18\x03 \x01(\t\x12\x12\n\nrisk_level\x18\x04 \x01(\t\x12\x14\n\x07timeout\x18\x05 \x01(\x01H\x00\x88\x01\x01\x42\n\n\x08_timeout\"F\n\x10\x41pprovalResponse\x12\x10\n\x08\x61pproved\x18\x01 \x01(\x08\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x10\n\x08remember\x18\x03 \x01(\x08\"A\n\x12ListModelsResponse\x12+\n\x06models\x18\x01 \x03(\x0b\x32\x1b.amplifier.module.ModelInfo\"O\n\x16ParseToolCallsResponse\x12\x35\n\ntool_calls\x18\x01 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\"@\n\x1aOrchestratorExecuteRequest\x12\x0e\n\x06prompt\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\">\n\x1bOrchestratorExecuteResponse\x12\x10\n\x08response\x18\x01 \x01(\t\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"?\n\x11\x41\x64\x64MessageRequest\x12*\n\x07message\x18\x01 \x01(\x0b\x32\x19.amplifier.module.Message\"B\n\x13GetMessagesResponse\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"J\n\x1bGetMessagesForRequestParams\x12\x14\n\x0ctoken_budget\x18\x01 \x01(\x05\x12\x15\n\rprovider_name\x18\x02 \x01(\t\"A\n\x12SetMessagesRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"5\n\x11HookHandleRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\".\n\x17GetSubscriptionsRequest\x12\x13\n\x0b\x63onfig_json\x18\x01 \x01(\t\"V\n\x18GetSubscriptionsResponse\x12:\n\rsubscriptions\x18\x01 \x03(\x0b\x32#.amplifier.module.EventSubscription\"B\n\x11\x45ventSubscription\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x10\n\x08priority\x18\x02 \x01(\x05\x12\x0c\n\x04name\x18\x03 \x01(\t\"d\n\x1b\x43ompleteWithProviderRequest\x12\x15\n\rprovider_name\x18\x01 \x01(\t\x12.\n\x07request\x18\x02 \x01(\x0b\x32\x1d.amplifier.module.ChatRequest\";\n\x12\x45xecuteToolRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x12\n\ninput_json\x18\x02 \x01(\t\"3\n\x0f\x45mitHookRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\"V\n\x19\x45mitHookAndCollectRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x17\n\x0ftimeout_seconds\x18\x03 \x01(\x01\"4\n\x1a\x45mitHookAndCollectResponse\x12\x16\n\x0eresponses_json\x18\x01 \x03(\t\"(\n\x12GetMessagesRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\"Y\n\x17KernelAddMessageRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12*\n\x07message\x18\x02 \x01(\x0b\x32\x19.amplifier.module.Message\"a\n\x17GetMountedModuleRequest\x12\x13\n\x0bmodule_name\x18\x01 \x01(\t\x12\x31\n\x0bmodule_type\x18\x02 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\"U\n\x18GetMountedModuleResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12*\n\x04info\x18\x02 \x01(\x0b\x32\x1c.amplifier.module.ModuleInfo\"=\n\x19RegisterCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\nvalue_json\x18\x02 \x01(\t\"$\n\x14GetCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\":\n\x15GetCapabilityResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12\x12\n\nvalue_json\x18\x02 \x01(\t*\xbc\x01\n\nModuleType\x12\x1b\n\x17MODULE_TYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14MODULE_TYPE_PROVIDER\x10\x01\x12\x14\n\x10MODULE_TYPE_TOOL\x10\x02\x12\x14\n\x10MODULE_TYPE_HOOK\x10\x03\x12\x16\n\x12MODULE_TYPE_MEMORY\x10\x04\x12\x19\n\x15MODULE_TYPE_GUARDRAIL\x10\x05\x12\x18\n\x14MODULE_TYPE_APPROVAL\x10\x06*\x82\x01\n\x0cHealthStatus\x12\x1d\n\x19HEALTH_STATUS_UNSPECIFIED\x10\x00\x12\x19\n\x15HEALTH_STATUS_SERVING\x10\x01\x12\x1d\n\x19HEALTH_STATUS_NOT_SERVING\x10\x02\x12\x19\n\x15HEALTH_STATUS_UNKNOWN\x10\x03*\xad\x01\n\x0f\x43onfigFieldType\x12!\n\x1d\x43ONFIG_FIELD_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_STRING\x10\x01\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_NUMBER\x10\x02\x12\x1d\n\x19\x43ONFIG_FIELD_TYPE_BOOLEAN\x10\x03\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_SECRET\x10\x04*\xd8\x02\n\x11ProviderErrorType\x12#\n\x1fPROVIDER_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18PROVIDER_ERROR_TYPE_AUTH\x10\x01\x12\"\n\x1ePROVIDER_ERROR_TYPE_RATE_LIMIT\x10\x02\x12&\n\"PROVIDER_ERROR_TYPE_CONTEXT_LENGTH\x10\x03\x12\'\n#PROVIDER_ERROR_TYPE_INVALID_REQUEST\x10\x04\x12&\n\"PROVIDER_ERROR_TYPE_CONTENT_FILTER\x10\x05\x12#\n\x1fPROVIDER_ERROR_TYPE_UNAVAILABLE\x10\x06\x12\x1f\n\x1bPROVIDER_ERROR_TYPE_TIMEOUT\x10\x07\x12\x1d\n\x19PROVIDER_ERROR_TYPE_OTHER\x10\x08*\x8c\x01\n\rToolErrorType\x12\x1f\n\x1bTOOL_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19TOOL_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aTOOL_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17TOOL_ERROR_TYPE_TIMEOUT\x10\x03*\x8c\x01\n\rHookErrorType\x12\x1f\n\x1bHOOK_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19HOOK_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aHOOK_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17HOOK_ERROR_TYPE_TIMEOUT\x10\x03*\x86\x01\n\x04Role\x12\x14\n\x10ROLE_UNSPECIFIED\x10\x00\x12\x0f\n\x0bROLE_SYSTEM\x10\x01\x12\r\n\tROLE_USER\x10\x02\x12\x12\n\x0eROLE_ASSISTANT\x10\x03\x12\r\n\tROLE_TOOL\x10\x04\x12\x11\n\rROLE_FUNCTION\x10\x05\x12\x12\n\x0eROLE_DEVELOPER\x10\x06*o\n\nVisibility\x12\x1a\n\x16VISIBILITY_UNSPECIFIED\x10\x00\x12\x12\n\x0eVISIBILITY_ALL\x10\x01\x12\x17\n\x13VISIBILITY_LLM_ONLY\x10\x02\x12\x18\n\x14VISIBILITY_USER_ONLY\x10\x03*\xab\x01\n\nHookAction\x12\x1b\n\x17HOOK_ACTION_UNSPECIFIED\x10\x00\x12\x18\n\x14HOOK_ACTION_CONTINUE\x10\x01\x12\x16\n\x12HOOK_ACTION_MODIFY\x10\x02\x12\x14\n\x10HOOK_ACTION_DENY\x10\x03\x12\x1e\n\x1aHOOK_ACTION_INJECT_CONTEXT\x10\x04\x12\x18\n\x14HOOK_ACTION_ASK_USER\x10\x05*\xa8\x01\n\x14\x43ontextInjectionRole\x12&\n\"CONTEXT_INJECTION_ROLE_UNSPECIFIED\x10\x00\x12!\n\x1d\x43ONTEXT_INJECTION_ROLE_SYSTEM\x10\x01\x12\x1f\n\x1b\x43ONTEXT_INJECTION_ROLE_USER\x10\x02\x12$\n CONTEXT_INJECTION_ROLE_ASSISTANT\x10\x03*l\n\x0f\x41pprovalDefault\x12 \n\x1c\x41PPROVAL_DEFAULT_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x41PPROVAL_DEFAULT_APPROVE\x10\x01\x12\x19\n\x15\x41PPROVAL_DEFAULT_DENY\x10\x02*\x91\x01\n\x10UserMessageLevel\x12\"\n\x1eUSER_MESSAGE_LEVEL_UNSPECIFIED\x10\x00\x12\x1b\n\x17USER_MESSAGE_LEVEL_INFO\x10\x01\x12\x1e\n\x1aUSER_MESSAGE_LEVEL_WARNING\x10\x02\x12\x1c\n\x18USER_MESSAGE_LEVEL_ERROR\x10\x03\x32\xa5\x01\n\x0bToolService\x12>\n\x07GetSpec\x12\x17.amplifier.module.Empty\x1a\x1a.amplifier.module.ToolSpec\x12V\n\x07\x45xecute\x12$.amplifier.module.ToolExecuteRequest\x1a%.amplifier.module.ToolExecuteResponse2\x9f\x03\n\x0fProviderService\x12\x42\n\x07GetInfo\x12\x17.amplifier.module.Empty\x1a\x1e.amplifier.module.ProviderInfo\x12K\n\nListModels\x12\x17.amplifier.module.Empty\x1a$.amplifier.module.ListModelsResponse\x12I\n\x08\x43omplete\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse\x12T\n\x11\x43ompleteStreaming\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Z\n\x0eParseToolCalls\x12\x1e.amplifier.module.ChatResponse\x1a(.amplifier.module.ParseToolCallsResponse2}\n\x13OrchestratorService\x12\x66\n\x07\x45xecute\x12,.amplifier.module.OrchestratorExecuteRequest\x1a-.amplifier.module.OrchestratorExecuteResponse2\xa3\x03\n\x0e\x43ontextService\x12J\n\nAddMessage\x12#.amplifier.module.AddMessageRequest\x1a\x17.amplifier.module.Empty\x12M\n\x0bGetMessages\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.GetMessagesResponse\x12m\n\x15GetMessagesForRequest\x12-.amplifier.module.GetMessagesForRequestParams\x1a%.amplifier.module.GetMessagesResponse\x12L\n\x0bSetMessages\x12$.amplifier.module.SetMessagesRequest\x1a\x17.amplifier.module.Empty\x12\x39\n\x05\x43lear\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty2\xc5\x01\n\x0bHookService\x12K\n\x06Handle\x12#.amplifier.module.HookHandleRequest\x1a\x1c.amplifier.module.HookResult\x12i\n\x10GetSubscriptions\x12).amplifier.module.GetSubscriptionsRequest\x1a*.amplifier.module.GetSubscriptionsResponse2k\n\x0f\x41pprovalService\x12X\n\x0fRequestApproval\x12!.amplifier.module.ApprovalRequest\x1a\".amplifier.module.ApprovalResponse2\xd0\x07\n\rKernelService\x12\x65\n\x14\x43ompleteWithProvider\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse\x12p\n\x1d\x43ompleteWithProviderStreaming\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Q\n\x0b\x45xecuteTool\x12$.amplifier.module.ExecuteToolRequest\x1a\x1c.amplifier.module.ToolResult\x12K\n\x08\x45mitHook\x12!.amplifier.module.EmitHookRequest\x1a\x1c.amplifier.module.HookResult\x12o\n\x12\x45mitHookAndCollect\x12+.amplifier.module.EmitHookAndCollectRequest\x1a,.amplifier.module.EmitHookAndCollectResponse\x12Z\n\x0bGetMessages\x12$.amplifier.module.GetMessagesRequest\x1a%.amplifier.module.GetMessagesResponse\x12P\n\nAddMessage\x12).amplifier.module.KernelAddMessageRequest\x1a\x17.amplifier.module.Empty\x12i\n\x10GetMountedModule\x12).amplifier.module.GetMountedModuleRequest\x1a*.amplifier.module.GetMountedModuleResponse\x12Z\n\x12RegisterCapability\x12+.amplifier.module.RegisterCapabilityRequest\x1a\x17.amplifier.module.Empty\x12`\n\rGetCapability\x12&.amplifier.module.GetCapabilityRequest\x1a\'.amplifier.module.GetCapabilityResponse2\xaf\x02\n\x0fModuleLifecycle\x12H\n\x05Mount\x12\x1e.amplifier.module.MountRequest\x1a\x1f.amplifier.module.MountResponse\x12;\n\x07\x43leanup\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty\x12M\n\x0bHealthCheck\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.HealthCheckResponse\x12\x46\n\rGetModuleInfo\x12\x17.amplifier.module.Empty\x1a\x1c.amplifier.module.ModuleInfob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x61mplifier_module.proto\x12\x10\x61mplifier.module\"\x07\n\x05\x45mpty\"F\n\x08ToolSpec\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"9\n\x12ToolExecuteRequest\x12\r\n\x05input\x18\x01 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x02 \x01(\t\"\x93\x01\n\x13ToolExecuteResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0e\n\x06output\x18\x02 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x03 \x01(\t\x12\r\n\x05\x65rror\x18\x04 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x05 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xd6\x01\n\nModuleInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x31\n\x0bmodule_type\x18\x04 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\x12\x13\n\x0bmount_point\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x06 \x01(\t\x12\x1a\n\x12\x63onfig_schema_json\x18\x07 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x08 \x03(\t\x12\x0e\n\x06\x61uthor\x18\t \x01(\t\"\x8c\x01\n\x0cMountRequest\x12:\n\x06\x63onfig\x18\x01 \x03(\x0b\x32*.amplifier.module.MountRequest.ConfigEntry\x12\x11\n\tmodule_id\x18\x02 \x01(\t\x1a-\n\x0b\x43onfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"_\n\rMountResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12.\n\x06status\x18\x03 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\"V\n\x13HealthCheckResponse\x12.\n\x06status\x18\x01 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xca\x02\n\x0b\x43onfigField\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x35\n\nfield_type\x18\x03 \x01(\x0e\x32!.amplifier.module.ConfigFieldType\x12\x0e\n\x06prompt\x18\x04 \x01(\t\x12\x0f\n\x07\x65nv_var\x18\x05 \x01(\t\x12\x0f\n\x07\x63hoices\x18\x06 \x03(\t\x12\x10\n\x08required\x18\x07 \x01(\x08\x12\x15\n\rdefault_value\x18\x08 \x01(\t\x12>\n\tshow_when\x18\t \x03(\x0b\x32+.amplifier.module.ConfigField.ShowWhenEntry\x12\x16\n\x0erequires_model\x18\n \x01(\x08\x1a/\n\rShowWhenEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xbc\x01\n\rProviderError\x12\x37\n\nerror_type\x18\x01 \x01(\x0e\x32#.amplifier.module.ProviderErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x15\n\rprovider_name\x18\x03 \x01(\t\x12\r\n\x05model\x18\x04 \x01(\t\x12\x13\n\x0bstatus_code\x18\x05 \x01(\x05\x12\x11\n\tretryable\x18\x06 \x01(\x08\x12\x13\n\x0bretry_after\x18\x07 \x01(\x01\"\x97\x01\n\tToolError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.ToolErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\ttool_name\x18\x03 \x01(\t\x12\x0e\n\x06stdout\x18\x04 \x01(\t\x12\x0e\n\x06stderr\x18\x05 \x01(\t\x12\x11\n\texit_code\x18\x06 \x01(\x05\"d\n\tHookError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.HookErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\thook_name\x18\x03 \x01(\t\"\xef\x01\n\x0e\x41mplifierError\x12\x39\n\x0eprovider_error\x18\x01 \x01(\x0b\x32\x1f.amplifier.module.ProviderErrorH\x00\x12\x31\n\ntool_error\x18\x02 \x01(\x0b\x32\x1b.amplifier.module.ToolErrorH\x00\x12\x31\n\nhook_error\x18\x03 \x01(\x0b\x32\x1b.amplifier.module.HookErrorH\x00\x12\x17\n\rgeneric_error\x18\x04 \x01(\tH\x00\x12\x1a\n\x10validation_error\x18\x05 \x01(\tH\x00\x42\x07\n\x05\x65rror\"\x19\n\tTextBlock\x12\x0c\n\x04text\x18\x01 \x01(\t\"E\n\rThinkingBlock\x12\x10\n\x08thinking\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\t\"%\n\x15RedactedThinkingBlock\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\t\"=\n\rToolCallBlock\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x12\n\ninput_json\x18\x03 \x01(\t\"<\n\x0fToolResultBlock\x12\x14\n\x0ctool_call_id\x18\x01 \x01(\t\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\"C\n\nImageBlock\x12\x12\n\nmedia_type\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x13\n\x0bsource_json\x18\x03 \x01(\t\"2\n\x0eReasoningBlock\x12\x0f\n\x07\x63ontent\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x03(\t\"\xf1\x03\n\x0c\x43ontentBlock\x12\x31\n\ntext_block\x18\x01 \x01(\x0b\x32\x1b.amplifier.module.TextBlockH\x00\x12\x39\n\x0ethinking_block\x18\x02 \x01(\x0b\x32\x1f.amplifier.module.ThinkingBlockH\x00\x12J\n\x17redacted_thinking_block\x18\x03 \x01(\x0b\x32\'.amplifier.module.RedactedThinkingBlockH\x00\x12:\n\x0ftool_call_block\x18\x04 \x01(\x0b\x32\x1f.amplifier.module.ToolCallBlockH\x00\x12>\n\x11tool_result_block\x18\x05 \x01(\x0b\x32!.amplifier.module.ToolResultBlockH\x00\x12\x33\n\x0bimage_block\x18\x06 \x01(\x0b\x32\x1c.amplifier.module.ImageBlockH\x00\x12;\n\x0freasoning_block\x18\x07 \x01(\x0b\x32 .amplifier.module.ReasoningBlockH\x00\x12\x30\n\nvisibility\x18\x08 \x01(\x0e\x32\x1c.amplifier.module.VisibilityB\x07\n\x05\x62lock\"B\n\x10\x43ontentBlockList\x12.\n\x06\x62locks\x18\x01 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xca\x01\n\x07Message\x12$\n\x04role\x18\x01 \x01(\x0e\x32\x16.amplifier.module.Role\x12\x16\n\x0ctext_content\x18\x02 \x01(\tH\x00\x12;\n\rblock_content\x18\x03 \x01(\x0b\x32\".amplifier.module.ContentBlockListH\x00\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x14\n\x0ctool_call_id\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\tB\t\n\x07\x63ontent\"C\n\x0fToolCallMessage\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x16\n\x0e\x61rguments_json\x18\x03 \x01(\t\"K\n\rToolSpecProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"7\n\x10JsonSchemaFormat\x12\x13\n\x0bschema_json\x18\x01 \x01(\t\x12\x0e\n\x06strict\x18\x02 \x01(\x08\"u\n\x0eResponseFormat\x12\x0e\n\x04text\x18\x01 \x01(\x08H\x00\x12\x0e\n\x04json\x18\x02 \x01(\x08H\x00\x12\x39\n\x0bjson_schema\x18\x03 \x01(\x0b\x32\".amplifier.module.JsonSchemaFormatH\x00\x42\x08\n\x06\x66ormat\"\x9b\x02\n\x05Usage\x12\x15\n\rprompt_tokens\x18\x01 \x01(\x05\x12\x19\n\x11\x63ompletion_tokens\x18\x02 \x01(\x05\x12\x14\n\x0ctotal_tokens\x18\x03 \x01(\x05\x12\x1d\n\x10reasoning_tokens\x18\x04 \x01(\x05H\x00\x88\x01\x01\x12\x1e\n\x11\x63\x61\x63he_read_tokens\x18\x05 \x01(\x05H\x01\x88\x01\x01\x12\"\n\x15\x63\x61\x63he_creation_tokens\x18\x06 \x01(\x05H\x02\x88\x01\x01\x12\x15\n\x08\x63ost_usd\x18\x07 \x01(\tH\x03\x88\x01\x01\x42\x13\n\x11_reasoning_tokensB\x14\n\x12_cache_read_tokensB\x18\n\x16_cache_creation_tokensB\x0b\n\t_cost_usd\"@\n\x0b\x44\x65gradation\x12\x11\n\trequested\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tual\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x81\x03\n\x0b\x43hatRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\x12.\n\x05tools\x18\x02 \x03(\x0b\x32\x1f.amplifier.module.ToolSpecProto\x12\x39\n\x0fresponse_format\x18\x03 \x01(\x0b\x32 .amplifier.module.ResponseFormat\x12\x13\n\x0btemperature\x18\x04 \x01(\x01\x12\r\n\x05top_p\x18\x05 \x01(\x01\x12\x19\n\x11max_output_tokens\x18\x06 \x01(\x05\x12\x17\n\x0f\x63onversation_id\x18\x07 \x01(\t\x12\x0e\n\x06stream\x18\x08 \x01(\x08\x12\x15\n\rmetadata_json\x18\t \x01(\t\x12\r\n\x05model\x18\n \x01(\t\x12\x13\n\x0btool_choice\x18\x0b \x01(\t\x12\x0c\n\x04stop\x18\x0c \x03(\t\x12\x18\n\x10reasoning_effort\x18\r \x01(\t\x12\x0f\n\x07timeout\x18\x0e \x01(\x01\"\x98\x02\n\x0c\x43hatResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x35\n\ntool_calls\x18\x02 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\x12&\n\x05usage\x18\x03 \x01(\x0b\x32\x17.amplifier.module.Usage\x12\x32\n\x0b\x64\x65gradation\x18\x04 \x01(\x0b\x32\x1d.amplifier.module.Degradation\x12\x15\n\rfinish_reason\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x07 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"~\n\nToolResult\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\x12\x12\n\nerror_json\x18\x03 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x04 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xa7\x04\n\nHookResult\x12,\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x1c.amplifier.module.HookAction\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x19\n\x11\x63ontext_injection\x18\x04 \x01(\t\x12\x46\n\x16\x63ontext_injection_role\x18\x05 \x01(\x0e\x32&.amplifier.module.ContextInjectionRole\x12\x11\n\tephemeral\x18\x06 \x01(\x08\x12\x17\n\x0f\x61pproval_prompt\x18\x07 \x01(\t\x12\x18\n\x10\x61pproval_options\x18\x08 \x03(\t\x12\x1d\n\x10\x61pproval_timeout\x18\t \x01(\x01H\x00\x88\x01\x01\x12;\n\x10\x61pproval_default\x18\n \x01(\x0e\x32!.amplifier.module.ApprovalDefault\x12\x17\n\x0fsuppress_output\x18\x0b \x01(\x08\x12\x14\n\x0cuser_message\x18\x0c \x01(\t\x12>\n\x12user_message_level\x18\r \x01(\x0e\x32\".amplifier.module.UserMessageLevel\x12\x1b\n\x13user_message_source\x18\x0e \x01(\t\x12\"\n\x1a\x61ppend_to_last_tool_result\x18\x0f \x01(\x08\x42\x13\n\x11_approval_timeout\"\xa3\x01\n\tModelInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x16\n\x0e\x63ontext_window\x18\x03 \x01(\x05\x12\x19\n\x11max_output_tokens\x18\x04 \x01(\x05\x12\x14\n\x0c\x63\x61pabilities\x18\x05 \x03(\t\x12\x15\n\rdefaults_json\x18\x06 \x01(\t\x12\x14\n\x0cpricing_json\x18\x07 \x01(\t\"\xb0\x01\n\x0cProviderInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x1b\n\x13\x63redential_env_vars\x18\x03 \x03(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x04 \x03(\t\x12\x15\n\rdefaults_json\x18\x05 \x01(\t\x12\x34\n\rconfig_fields\x18\x06 \x03(\x0b\x32\x1d.amplifier.module.ConfigField\"\x80\x01\n\x0f\x41pprovalRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x14\n\x0c\x64\x65tails_json\x18\x03 \x01(\t\x12\x12\n\nrisk_level\x18\x04 \x01(\t\x12\x14\n\x07timeout\x18\x05 \x01(\x01H\x00\x88\x01\x01\x42\n\n\x08_timeout\"F\n\x10\x41pprovalResponse\x12\x10\n\x08\x61pproved\x18\x01 \x01(\x08\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x10\n\x08remember\x18\x03 \x01(\x08\"A\n\x12ListModelsResponse\x12+\n\x06models\x18\x01 \x03(\x0b\x32\x1b.amplifier.module.ModelInfo\"O\n\x16ParseToolCallsResponse\x12\x35\n\ntool_calls\x18\x01 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\"@\n\x1aOrchestratorExecuteRequest\x12\x0e\n\x06prompt\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\">\n\x1bOrchestratorExecuteResponse\x12\x10\n\x08response\x18\x01 \x01(\t\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"?\n\x11\x41\x64\x64MessageRequest\x12*\n\x07message\x18\x01 \x01(\x0b\x32\x19.amplifier.module.Message\"B\n\x13GetMessagesResponse\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"J\n\x1bGetMessagesForRequestParams\x12\x14\n\x0ctoken_budget\x18\x01 \x01(\x05\x12\x15\n\rprovider_name\x18\x02 \x01(\t\"A\n\x12SetMessagesRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"5\n\x11HookHandleRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\".\n\x17GetSubscriptionsRequest\x12\x13\n\x0b\x63onfig_json\x18\x01 \x01(\t\"V\n\x18GetSubscriptionsResponse\x12:\n\rsubscriptions\x18\x01 \x03(\x0b\x32#.amplifier.module.EventSubscription\"B\n\x11\x45ventSubscription\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x10\n\x08priority\x18\x02 \x01(\x05\x12\x0c\n\x04name\x18\x03 \x01(\t\"d\n\x1b\x43ompleteWithProviderRequest\x12\x15\n\rprovider_name\x18\x01 \x01(\t\x12.\n\x07request\x18\x02 \x01(\x0b\x32\x1d.amplifier.module.ChatRequest\";\n\x12\x45xecuteToolRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x12\n\ninput_json\x18\x02 \x01(\t\"3\n\x0f\x45mitHookRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\"V\n\x19\x45mitHookAndCollectRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x17\n\x0ftimeout_seconds\x18\x03 \x01(\x01\"4\n\x1a\x45mitHookAndCollectResponse\x12\x16\n\x0eresponses_json\x18\x01 \x03(\t\"(\n\x12GetMessagesRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\"Y\n\x17KernelAddMessageRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12*\n\x07message\x18\x02 \x01(\x0b\x32\x19.amplifier.module.Message\"a\n\x17GetMountedModuleRequest\x12\x13\n\x0bmodule_name\x18\x01 \x01(\t\x12\x31\n\x0bmodule_type\x18\x02 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\"U\n\x18GetMountedModuleResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12*\n\x04info\x18\x02 \x01(\x0b\x32\x1c.amplifier.module.ModuleInfo\"=\n\x19RegisterCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\nvalue_json\x18\x02 \x01(\t\"$\n\x14GetCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\":\n\x15GetCapabilityResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12\x12\n\nvalue_json\x18\x02 \x01(\t*\xbc\x01\n\nModuleType\x12\x1b\n\x17MODULE_TYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14MODULE_TYPE_PROVIDER\x10\x01\x12\x14\n\x10MODULE_TYPE_TOOL\x10\x02\x12\x14\n\x10MODULE_TYPE_HOOK\x10\x03\x12\x16\n\x12MODULE_TYPE_MEMORY\x10\x04\x12\x19\n\x15MODULE_TYPE_GUARDRAIL\x10\x05\x12\x18\n\x14MODULE_TYPE_APPROVAL\x10\x06*\x82\x01\n\x0cHealthStatus\x12\x1d\n\x19HEALTH_STATUS_UNSPECIFIED\x10\x00\x12\x19\n\x15HEALTH_STATUS_SERVING\x10\x01\x12\x1d\n\x19HEALTH_STATUS_NOT_SERVING\x10\x02\x12\x19\n\x15HEALTH_STATUS_UNKNOWN\x10\x03*\xad\x01\n\x0f\x43onfigFieldType\x12!\n\x1d\x43ONFIG_FIELD_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_STRING\x10\x01\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_NUMBER\x10\x02\x12\x1d\n\x19\x43ONFIG_FIELD_TYPE_BOOLEAN\x10\x03\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_SECRET\x10\x04*\xd8\x02\n\x11ProviderErrorType\x12#\n\x1fPROVIDER_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18PROVIDER_ERROR_TYPE_AUTH\x10\x01\x12\"\n\x1ePROVIDER_ERROR_TYPE_RATE_LIMIT\x10\x02\x12&\n\"PROVIDER_ERROR_TYPE_CONTEXT_LENGTH\x10\x03\x12\'\n#PROVIDER_ERROR_TYPE_INVALID_REQUEST\x10\x04\x12&\n\"PROVIDER_ERROR_TYPE_CONTENT_FILTER\x10\x05\x12#\n\x1fPROVIDER_ERROR_TYPE_UNAVAILABLE\x10\x06\x12\x1f\n\x1bPROVIDER_ERROR_TYPE_TIMEOUT\x10\x07\x12\x1d\n\x19PROVIDER_ERROR_TYPE_OTHER\x10\x08*\x8c\x01\n\rToolErrorType\x12\x1f\n\x1bTOOL_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19TOOL_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aTOOL_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17TOOL_ERROR_TYPE_TIMEOUT\x10\x03*\x8c\x01\n\rHookErrorType\x12\x1f\n\x1bHOOK_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19HOOK_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aHOOK_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17HOOK_ERROR_TYPE_TIMEOUT\x10\x03*\x86\x01\n\x04Role\x12\x14\n\x10ROLE_UNSPECIFIED\x10\x00\x12\x0f\n\x0bROLE_SYSTEM\x10\x01\x12\r\n\tROLE_USER\x10\x02\x12\x12\n\x0eROLE_ASSISTANT\x10\x03\x12\r\n\tROLE_TOOL\x10\x04\x12\x11\n\rROLE_FUNCTION\x10\x05\x12\x12\n\x0eROLE_DEVELOPER\x10\x06*o\n\nVisibility\x12\x1a\n\x16VISIBILITY_UNSPECIFIED\x10\x00\x12\x12\n\x0eVISIBILITY_ALL\x10\x01\x12\x17\n\x13VISIBILITY_LLM_ONLY\x10\x02\x12\x18\n\x14VISIBILITY_USER_ONLY\x10\x03*\xab\x01\n\nHookAction\x12\x1b\n\x17HOOK_ACTION_UNSPECIFIED\x10\x00\x12\x18\n\x14HOOK_ACTION_CONTINUE\x10\x01\x12\x16\n\x12HOOK_ACTION_MODIFY\x10\x02\x12\x14\n\x10HOOK_ACTION_DENY\x10\x03\x12\x1e\n\x1aHOOK_ACTION_INJECT_CONTEXT\x10\x04\x12\x18\n\x14HOOK_ACTION_ASK_USER\x10\x05*\xa8\x01\n\x14\x43ontextInjectionRole\x12&\n\"CONTEXT_INJECTION_ROLE_UNSPECIFIED\x10\x00\x12!\n\x1d\x43ONTEXT_INJECTION_ROLE_SYSTEM\x10\x01\x12\x1f\n\x1b\x43ONTEXT_INJECTION_ROLE_USER\x10\x02\x12$\n CONTEXT_INJECTION_ROLE_ASSISTANT\x10\x03*l\n\x0f\x41pprovalDefault\x12 \n\x1c\x41PPROVAL_DEFAULT_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x41PPROVAL_DEFAULT_APPROVE\x10\x01\x12\x19\n\x15\x41PPROVAL_DEFAULT_DENY\x10\x02*\x91\x01\n\x10UserMessageLevel\x12\"\n\x1eUSER_MESSAGE_LEVEL_UNSPECIFIED\x10\x00\x12\x1b\n\x17USER_MESSAGE_LEVEL_INFO\x10\x01\x12\x1e\n\x1aUSER_MESSAGE_LEVEL_WARNING\x10\x02\x12\x1c\n\x18USER_MESSAGE_LEVEL_ERROR\x10\x03\x32\xa5\x01\n\x0bToolService\x12>\n\x07GetSpec\x12\x17.amplifier.module.Empty\x1a\x1a.amplifier.module.ToolSpec\x12V\n\x07\x45xecute\x12$.amplifier.module.ToolExecuteRequest\x1a%.amplifier.module.ToolExecuteResponse2\x9f\x03\n\x0fProviderService\x12\x42\n\x07GetInfo\x12\x17.amplifier.module.Empty\x1a\x1e.amplifier.module.ProviderInfo\x12K\n\nListModels\x12\x17.amplifier.module.Empty\x1a$.amplifier.module.ListModelsResponse\x12I\n\x08\x43omplete\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse\x12T\n\x11\x43ompleteStreaming\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Z\n\x0eParseToolCalls\x12\x1e.amplifier.module.ChatResponse\x1a(.amplifier.module.ParseToolCallsResponse2}\n\x13OrchestratorService\x12\x66\n\x07\x45xecute\x12,.amplifier.module.OrchestratorExecuteRequest\x1a-.amplifier.module.OrchestratorExecuteResponse2\xa3\x03\n\x0e\x43ontextService\x12J\n\nAddMessage\x12#.amplifier.module.AddMessageRequest\x1a\x17.amplifier.module.Empty\x12M\n\x0bGetMessages\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.GetMessagesResponse\x12m\n\x15GetMessagesForRequest\x12-.amplifier.module.GetMessagesForRequestParams\x1a%.amplifier.module.GetMessagesResponse\x12L\n\x0bSetMessages\x12$.amplifier.module.SetMessagesRequest\x1a\x17.amplifier.module.Empty\x12\x39\n\x05\x43lear\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty2\xc5\x01\n\x0bHookService\x12K\n\x06Handle\x12#.amplifier.module.HookHandleRequest\x1a\x1c.amplifier.module.HookResult\x12i\n\x10GetSubscriptions\x12).amplifier.module.GetSubscriptionsRequest\x1a*.amplifier.module.GetSubscriptionsResponse2k\n\x0f\x41pprovalService\x12X\n\x0fRequestApproval\x12!.amplifier.module.ApprovalRequest\x1a\".amplifier.module.ApprovalResponse2\xd0\x07\n\rKernelService\x12\x65\n\x14\x43ompleteWithProvider\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse\x12p\n\x1d\x43ompleteWithProviderStreaming\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Q\n\x0b\x45xecuteTool\x12$.amplifier.module.ExecuteToolRequest\x1a\x1c.amplifier.module.ToolResult\x12K\n\x08\x45mitHook\x12!.amplifier.module.EmitHookRequest\x1a\x1c.amplifier.module.HookResult\x12o\n\x12\x45mitHookAndCollect\x12+.amplifier.module.EmitHookAndCollectRequest\x1a,.amplifier.module.EmitHookAndCollectResponse\x12Z\n\x0bGetMessages\x12$.amplifier.module.GetMessagesRequest\x1a%.amplifier.module.GetMessagesResponse\x12P\n\nAddMessage\x12).amplifier.module.KernelAddMessageRequest\x1a\x17.amplifier.module.Empty\x12i\n\x10GetMountedModule\x12).amplifier.module.GetMountedModuleRequest\x1a*.amplifier.module.GetMountedModuleResponse\x12Z\n\x12RegisterCapability\x12+.amplifier.module.RegisterCapabilityRequest\x1a\x17.amplifier.module.Empty\x12`\n\rGetCapability\x12&.amplifier.module.GetCapabilityRequest\x1a\'.amplifier.module.GetCapabilityResponse2\xaf\x02\n\x0fModuleLifecycle\x12H\n\x05Mount\x12\x1e.amplifier.module.MountRequest\x1a\x1f.amplifier.module.MountResponse\x12;\n\x07\x43leanup\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty\x12M\n\x0bHealthCheck\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.HealthCheckResponse\x12\x46\n\rGetModuleInfo\x12\x17.amplifier.module.Empty\x1a\x1c.amplifier.module.ModuleInfob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -35,30 +35,30 @@ _globals['_MOUNTREQUEST_CONFIGENTRY']._serialized_options = b'8\001' _globals['_CONFIGFIELD_SHOWWHENENTRY']._loaded_options = None _globals['_CONFIGFIELD_SHOWWHENENTRY']._serialized_options = b'8\001' - _globals['_MODULETYPE']._serialized_start=7226 - _globals['_MODULETYPE']._serialized_end=7414 - _globals['_HEALTHSTATUS']._serialized_start=7417 - _globals['_HEALTHSTATUS']._serialized_end=7547 - _globals['_CONFIGFIELDTYPE']._serialized_start=7550 - _globals['_CONFIGFIELDTYPE']._serialized_end=7723 - _globals['_PROVIDERERRORTYPE']._serialized_start=7726 - _globals['_PROVIDERERRORTYPE']._serialized_end=8070 - _globals['_TOOLERRORTYPE']._serialized_start=8073 - _globals['_TOOLERRORTYPE']._serialized_end=8213 - _globals['_HOOKERRORTYPE']._serialized_start=8216 - _globals['_HOOKERRORTYPE']._serialized_end=8356 - _globals['_ROLE']._serialized_start=8359 - _globals['_ROLE']._serialized_end=8493 - _globals['_VISIBILITY']._serialized_start=8495 - _globals['_VISIBILITY']._serialized_end=8606 - _globals['_HOOKACTION']._serialized_start=8609 - _globals['_HOOKACTION']._serialized_end=8780 - _globals['_CONTEXTINJECTIONROLE']._serialized_start=8783 - _globals['_CONTEXTINJECTIONROLE']._serialized_end=8951 - _globals['_APPROVALDEFAULT']._serialized_start=8953 - _globals['_APPROVALDEFAULT']._serialized_end=9061 - _globals['_USERMESSAGELEVEL']._serialized_start=9064 - _globals['_USERMESSAGELEVEL']._serialized_end=9209 + _globals['_MODULETYPE']._serialized_start=7284 + _globals['_MODULETYPE']._serialized_end=7472 + _globals['_HEALTHSTATUS']._serialized_start=7475 + _globals['_HEALTHSTATUS']._serialized_end=7605 + _globals['_CONFIGFIELDTYPE']._serialized_start=7608 + _globals['_CONFIGFIELDTYPE']._serialized_end=7781 + _globals['_PROVIDERERRORTYPE']._serialized_start=7784 + _globals['_PROVIDERERRORTYPE']._serialized_end=8128 + _globals['_TOOLERRORTYPE']._serialized_start=8131 + _globals['_TOOLERRORTYPE']._serialized_end=8271 + _globals['_HOOKERRORTYPE']._serialized_start=8274 + _globals['_HOOKERRORTYPE']._serialized_end=8414 + _globals['_ROLE']._serialized_start=8417 + _globals['_ROLE']._serialized_end=8551 + _globals['_VISIBILITY']._serialized_start=8553 + _globals['_VISIBILITY']._serialized_end=8664 + _globals['_HOOKACTION']._serialized_start=8667 + _globals['_HOOKACTION']._serialized_end=8838 + _globals['_CONTEXTINJECTIONROLE']._serialized_start=8841 + _globals['_CONTEXTINJECTIONROLE']._serialized_end=9009 + _globals['_APPROVALDEFAULT']._serialized_start=9011 + _globals['_APPROVALDEFAULT']._serialized_end=9119 + _globals['_USERMESSAGELEVEL']._serialized_start=9122 + _globals['_USERMESSAGELEVEL']._serialized_end=9267 _globals['_EMPTY']._serialized_start=44 _globals['_EMPTY']._serialized_end=51 _globals['_TOOLSPEC']._serialized_start=53 @@ -118,87 +118,87 @@ _globals['_RESPONSEFORMAT']._serialized_start=3260 _globals['_RESPONSEFORMAT']._serialized_end=3377 _globals['_USAGE']._serialized_start=3380 - _globals['_USAGE']._serialized_end=3627 - _globals['_DEGRADATION']._serialized_start=3629 - _globals['_DEGRADATION']._serialized_end=3693 - _globals['_CHATREQUEST']._serialized_start=3696 - _globals['_CHATREQUEST']._serialized_end=4081 - _globals['_CHATRESPONSE']._serialized_start=4084 - _globals['_CHATRESPONSE']._serialized_end=4364 - _globals['_TOOLRESULT']._serialized_start=4366 - _globals['_TOOLRESULT']._serialized_end=4492 - _globals['_HOOKRESULT']._serialized_start=4495 - _globals['_HOOKRESULT']._serialized_end=5046 - _globals['_MODELINFO']._serialized_start=5049 - _globals['_MODELINFO']._serialized_end=5190 - _globals['_PROVIDERINFO']._serialized_start=5193 - _globals['_PROVIDERINFO']._serialized_end=5369 - _globals['_APPROVALREQUEST']._serialized_start=5372 - _globals['_APPROVALREQUEST']._serialized_end=5500 - _globals['_APPROVALRESPONSE']._serialized_start=5502 - _globals['_APPROVALRESPONSE']._serialized_end=5572 - _globals['_LISTMODELSRESPONSE']._serialized_start=5574 - _globals['_LISTMODELSRESPONSE']._serialized_end=5639 - _globals['_PARSETOOLCALLSRESPONSE']._serialized_start=5641 - _globals['_PARSETOOLCALLSRESPONSE']._serialized_end=5720 - _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_start=5722 - _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_end=5786 - _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_start=5788 - _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_end=5850 - _globals['_ADDMESSAGEREQUEST']._serialized_start=5852 - _globals['_ADDMESSAGEREQUEST']._serialized_end=5915 - _globals['_GETMESSAGESRESPONSE']._serialized_start=5917 - _globals['_GETMESSAGESRESPONSE']._serialized_end=5983 - _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_start=5985 - _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_end=6059 - _globals['_SETMESSAGESREQUEST']._serialized_start=6061 - _globals['_SETMESSAGESREQUEST']._serialized_end=6126 - _globals['_HOOKHANDLEREQUEST']._serialized_start=6128 - _globals['_HOOKHANDLEREQUEST']._serialized_end=6181 - _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_start=6183 - _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_end=6229 - _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_start=6231 - _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_end=6317 - _globals['_EVENTSUBSCRIPTION']._serialized_start=6319 - _globals['_EVENTSUBSCRIPTION']._serialized_end=6385 - _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_start=6387 - _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_end=6487 - _globals['_EXECUTETOOLREQUEST']._serialized_start=6489 - _globals['_EXECUTETOOLREQUEST']._serialized_end=6548 - _globals['_EMITHOOKREQUEST']._serialized_start=6550 - _globals['_EMITHOOKREQUEST']._serialized_end=6601 - _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_start=6603 - _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_end=6689 - _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_start=6691 - _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_end=6743 - _globals['_GETMESSAGESREQUEST']._serialized_start=6745 - _globals['_GETMESSAGESREQUEST']._serialized_end=6785 - _globals['_KERNELADDMESSAGEREQUEST']._serialized_start=6787 - _globals['_KERNELADDMESSAGEREQUEST']._serialized_end=6876 - _globals['_GETMOUNTEDMODULEREQUEST']._serialized_start=6878 - _globals['_GETMOUNTEDMODULEREQUEST']._serialized_end=6975 - _globals['_GETMOUNTEDMODULERESPONSE']._serialized_start=6977 - _globals['_GETMOUNTEDMODULERESPONSE']._serialized_end=7062 - _globals['_REGISTERCAPABILITYREQUEST']._serialized_start=7064 - _globals['_REGISTERCAPABILITYREQUEST']._serialized_end=7125 - _globals['_GETCAPABILITYREQUEST']._serialized_start=7127 - _globals['_GETCAPABILITYREQUEST']._serialized_end=7163 - _globals['_GETCAPABILITYRESPONSE']._serialized_start=7165 - _globals['_GETCAPABILITYRESPONSE']._serialized_end=7223 - _globals['_TOOLSERVICE']._serialized_start=9212 - _globals['_TOOLSERVICE']._serialized_end=9377 - _globals['_PROVIDERSERVICE']._serialized_start=9380 - _globals['_PROVIDERSERVICE']._serialized_end=9795 - _globals['_ORCHESTRATORSERVICE']._serialized_start=9797 - _globals['_ORCHESTRATORSERVICE']._serialized_end=9922 - _globals['_CONTEXTSERVICE']._serialized_start=9925 - _globals['_CONTEXTSERVICE']._serialized_end=10344 - _globals['_HOOKSERVICE']._serialized_start=10347 - _globals['_HOOKSERVICE']._serialized_end=10544 - _globals['_APPROVALSERVICE']._serialized_start=10546 - _globals['_APPROVALSERVICE']._serialized_end=10653 - _globals['_KERNELSERVICE']._serialized_start=10656 - _globals['_KERNELSERVICE']._serialized_end=11632 - _globals['_MODULELIFECYCLE']._serialized_start=11635 - _globals['_MODULELIFECYCLE']._serialized_end=11938 + _globals['_USAGE']._serialized_end=3663 + _globals['_DEGRADATION']._serialized_start=3665 + _globals['_DEGRADATION']._serialized_end=3729 + _globals['_CHATREQUEST']._serialized_start=3732 + _globals['_CHATREQUEST']._serialized_end=4117 + _globals['_CHATRESPONSE']._serialized_start=4120 + _globals['_CHATRESPONSE']._serialized_end=4400 + _globals['_TOOLRESULT']._serialized_start=4402 + _globals['_TOOLRESULT']._serialized_end=4528 + _globals['_HOOKRESULT']._serialized_start=4531 + _globals['_HOOKRESULT']._serialized_end=5082 + _globals['_MODELINFO']._serialized_start=5085 + _globals['_MODELINFO']._serialized_end=5248 + _globals['_PROVIDERINFO']._serialized_start=5251 + _globals['_PROVIDERINFO']._serialized_end=5427 + _globals['_APPROVALREQUEST']._serialized_start=5430 + _globals['_APPROVALREQUEST']._serialized_end=5558 + _globals['_APPROVALRESPONSE']._serialized_start=5560 + _globals['_APPROVALRESPONSE']._serialized_end=5630 + _globals['_LISTMODELSRESPONSE']._serialized_start=5632 + _globals['_LISTMODELSRESPONSE']._serialized_end=5697 + _globals['_PARSETOOLCALLSRESPONSE']._serialized_start=5699 + _globals['_PARSETOOLCALLSRESPONSE']._serialized_end=5778 + _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_start=5780 + _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_end=5844 + _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_start=5846 + _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_end=5908 + _globals['_ADDMESSAGEREQUEST']._serialized_start=5910 + _globals['_ADDMESSAGEREQUEST']._serialized_end=5973 + _globals['_GETMESSAGESRESPONSE']._serialized_start=5975 + _globals['_GETMESSAGESRESPONSE']._serialized_end=6041 + _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_start=6043 + _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_end=6117 + _globals['_SETMESSAGESREQUEST']._serialized_start=6119 + _globals['_SETMESSAGESREQUEST']._serialized_end=6184 + _globals['_HOOKHANDLEREQUEST']._serialized_start=6186 + _globals['_HOOKHANDLEREQUEST']._serialized_end=6239 + _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_start=6241 + _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_end=6287 + _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_start=6289 + _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_end=6375 + _globals['_EVENTSUBSCRIPTION']._serialized_start=6377 + _globals['_EVENTSUBSCRIPTION']._serialized_end=6443 + _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_start=6445 + _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_end=6545 + _globals['_EXECUTETOOLREQUEST']._serialized_start=6547 + _globals['_EXECUTETOOLREQUEST']._serialized_end=6606 + _globals['_EMITHOOKREQUEST']._serialized_start=6608 + _globals['_EMITHOOKREQUEST']._serialized_end=6659 + _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_start=6661 + _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_end=6747 + _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_start=6749 + _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_end=6801 + _globals['_GETMESSAGESREQUEST']._serialized_start=6803 + _globals['_GETMESSAGESREQUEST']._serialized_end=6843 + _globals['_KERNELADDMESSAGEREQUEST']._serialized_start=6845 + _globals['_KERNELADDMESSAGEREQUEST']._serialized_end=6934 + _globals['_GETMOUNTEDMODULEREQUEST']._serialized_start=6936 + _globals['_GETMOUNTEDMODULEREQUEST']._serialized_end=7033 + _globals['_GETMOUNTEDMODULERESPONSE']._serialized_start=7035 + _globals['_GETMOUNTEDMODULERESPONSE']._serialized_end=7120 + _globals['_REGISTERCAPABILITYREQUEST']._serialized_start=7122 + _globals['_REGISTERCAPABILITYREQUEST']._serialized_end=7183 + _globals['_GETCAPABILITYREQUEST']._serialized_start=7185 + _globals['_GETCAPABILITYREQUEST']._serialized_end=7221 + _globals['_GETCAPABILITYRESPONSE']._serialized_start=7223 + _globals['_GETCAPABILITYRESPONSE']._serialized_end=7281 + _globals['_TOOLSERVICE']._serialized_start=9270 + _globals['_TOOLSERVICE']._serialized_end=9435 + _globals['_PROVIDERSERVICE']._serialized_start=9438 + _globals['_PROVIDERSERVICE']._serialized_end=9853 + _globals['_ORCHESTRATORSERVICE']._serialized_start=9855 + _globals['_ORCHESTRATORSERVICE']._serialized_end=9980 + _globals['_CONTEXTSERVICE']._serialized_start=9983 + _globals['_CONTEXTSERVICE']._serialized_end=10402 + _globals['_HOOKSERVICE']._serialized_start=10405 + _globals['_HOOKSERVICE']._serialized_end=10602 + _globals['_APPROVALSERVICE']._serialized_start=10604 + _globals['_APPROVALSERVICE']._serialized_end=10711 + _globals['_KERNELSERVICE']._serialized_start=10714 + _globals['_KERNELSERVICE']._serialized_end=11690 + _globals['_MODULELIFECYCLE']._serialized_start=11693 + _globals['_MODULELIFECYCLE']._serialized_end=11996 # @@protoc_insertion_point(module_scope) diff --git a/python/amplifier_core/_grpc_gen/amplifier_module_pb2.py b/python/amplifier_core/_grpc_gen/amplifier_module_pb2.py index d6c0e3e..4559c62 100644 --- a/python/amplifier_core/_grpc_gen/amplifier_module_pb2.py +++ b/python/amplifier_core/_grpc_gen/amplifier_module_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: amplifier_module.proto -# Protobuf Python Version: 6.31.1 +# Protobuf Python Version: 7.35.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -11,8 +11,8 @@ from google.protobuf.internal import builder as _builder _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, - 6, - 31, + 7, + 35, 1, '', 'amplifier_module.proto' @@ -24,7 +24,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x61mplifier_module.proto\x12\x10\x61mplifier.module\"\x07\n\x05\x45mpty\"F\n\x08ToolSpec\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"9\n\x12ToolExecuteRequest\x12\r\n\x05input\x18\x01 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x02 \x01(\t\"\x93\x01\n\x13ToolExecuteResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0e\n\x06output\x18\x02 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x03 \x01(\t\x12\r\n\x05\x65rror\x18\x04 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x05 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xd6\x01\n\nModuleInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x31\n\x0bmodule_type\x18\x04 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\x12\x13\n\x0bmount_point\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x06 \x01(\t\x12\x1a\n\x12\x63onfig_schema_json\x18\x07 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x08 \x03(\t\x12\x0e\n\x06\x61uthor\x18\t \x01(\t\"\x8c\x01\n\x0cMountRequest\x12:\n\x06\x63onfig\x18\x01 \x03(\x0b\x32*.amplifier.module.MountRequest.ConfigEntry\x12\x11\n\tmodule_id\x18\x02 \x01(\t\x1a-\n\x0b\x43onfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"_\n\rMountResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12.\n\x06status\x18\x03 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\"V\n\x13HealthCheckResponse\x12.\n\x06status\x18\x01 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xca\x02\n\x0b\x43onfigField\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x35\n\nfield_type\x18\x03 \x01(\x0e\x32!.amplifier.module.ConfigFieldType\x12\x0e\n\x06prompt\x18\x04 \x01(\t\x12\x0f\n\x07\x65nv_var\x18\x05 \x01(\t\x12\x0f\n\x07\x63hoices\x18\x06 \x03(\t\x12\x10\n\x08required\x18\x07 \x01(\x08\x12\x15\n\rdefault_value\x18\x08 \x01(\t\x12>\n\tshow_when\x18\t \x03(\x0b\x32+.amplifier.module.ConfigField.ShowWhenEntry\x12\x16\n\x0erequires_model\x18\n \x01(\x08\x1a/\n\rShowWhenEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xbc\x01\n\rProviderError\x12\x37\n\nerror_type\x18\x01 \x01(\x0e\x32#.amplifier.module.ProviderErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x15\n\rprovider_name\x18\x03 \x01(\t\x12\r\n\x05model\x18\x04 \x01(\t\x12\x13\n\x0bstatus_code\x18\x05 \x01(\x05\x12\x11\n\tretryable\x18\x06 \x01(\x08\x12\x13\n\x0bretry_after\x18\x07 \x01(\x01\"\x97\x01\n\tToolError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.ToolErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\ttool_name\x18\x03 \x01(\t\x12\x0e\n\x06stdout\x18\x04 \x01(\t\x12\x0e\n\x06stderr\x18\x05 \x01(\t\x12\x11\n\texit_code\x18\x06 \x01(\x05\"d\n\tHookError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.HookErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\thook_name\x18\x03 \x01(\t\"\xef\x01\n\x0e\x41mplifierError\x12\x39\n\x0eprovider_error\x18\x01 \x01(\x0b\x32\x1f.amplifier.module.ProviderErrorH\x00\x12\x31\n\ntool_error\x18\x02 \x01(\x0b\x32\x1b.amplifier.module.ToolErrorH\x00\x12\x31\n\nhook_error\x18\x03 \x01(\x0b\x32\x1b.amplifier.module.HookErrorH\x00\x12\x17\n\rgeneric_error\x18\x04 \x01(\tH\x00\x12\x1a\n\x10validation_error\x18\x05 \x01(\tH\x00\x42\x07\n\x05\x65rror\"\x19\n\tTextBlock\x12\x0c\n\x04text\x18\x01 \x01(\t\"E\n\rThinkingBlock\x12\x10\n\x08thinking\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\t\"%\n\x15RedactedThinkingBlock\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\t\"=\n\rToolCallBlock\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x12\n\ninput_json\x18\x03 \x01(\t\"<\n\x0fToolResultBlock\x12\x14\n\x0ctool_call_id\x18\x01 \x01(\t\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\"C\n\nImageBlock\x12\x12\n\nmedia_type\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x13\n\x0bsource_json\x18\x03 \x01(\t\"2\n\x0eReasoningBlock\x12\x0f\n\x07\x63ontent\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x03(\t\"\xf1\x03\n\x0c\x43ontentBlock\x12\x31\n\ntext_block\x18\x01 \x01(\x0b\x32\x1b.amplifier.module.TextBlockH\x00\x12\x39\n\x0ethinking_block\x18\x02 \x01(\x0b\x32\x1f.amplifier.module.ThinkingBlockH\x00\x12J\n\x17redacted_thinking_block\x18\x03 \x01(\x0b\x32\'.amplifier.module.RedactedThinkingBlockH\x00\x12:\n\x0ftool_call_block\x18\x04 \x01(\x0b\x32\x1f.amplifier.module.ToolCallBlockH\x00\x12>\n\x11tool_result_block\x18\x05 \x01(\x0b\x32!.amplifier.module.ToolResultBlockH\x00\x12\x33\n\x0bimage_block\x18\x06 \x01(\x0b\x32\x1c.amplifier.module.ImageBlockH\x00\x12;\n\x0freasoning_block\x18\x07 \x01(\x0b\x32 .amplifier.module.ReasoningBlockH\x00\x12\x30\n\nvisibility\x18\x08 \x01(\x0e\x32\x1c.amplifier.module.VisibilityB\x07\n\x05\x62lock\"B\n\x10\x43ontentBlockList\x12.\n\x06\x62locks\x18\x01 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xca\x01\n\x07Message\x12$\n\x04role\x18\x01 \x01(\x0e\x32\x16.amplifier.module.Role\x12\x16\n\x0ctext_content\x18\x02 \x01(\tH\x00\x12;\n\rblock_content\x18\x03 \x01(\x0b\x32\".amplifier.module.ContentBlockListH\x00\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x14\n\x0ctool_call_id\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\tB\t\n\x07\x63ontent\"C\n\x0fToolCallMessage\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x16\n\x0e\x61rguments_json\x18\x03 \x01(\t\"K\n\rToolSpecProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"7\n\x10JsonSchemaFormat\x12\x13\n\x0bschema_json\x18\x01 \x01(\t\x12\x0e\n\x06strict\x18\x02 \x01(\x08\"u\n\x0eResponseFormat\x12\x0e\n\x04text\x18\x01 \x01(\x08H\x00\x12\x0e\n\x04json\x18\x02 \x01(\x08H\x00\x12\x39\n\x0bjson_schema\x18\x03 \x01(\x0b\x32\".amplifier.module.JsonSchemaFormatH\x00\x42\x08\n\x06\x66ormat\"\xf7\x01\n\x05Usage\x12\x15\n\rprompt_tokens\x18\x01 \x01(\x05\x12\x19\n\x11\x63ompletion_tokens\x18\x02 \x01(\x05\x12\x14\n\x0ctotal_tokens\x18\x03 \x01(\x05\x12\x1d\n\x10reasoning_tokens\x18\x04 \x01(\x05H\x00\x88\x01\x01\x12\x1e\n\x11\x63\x61\x63he_read_tokens\x18\x05 \x01(\x05H\x01\x88\x01\x01\x12\"\n\x15\x63\x61\x63he_creation_tokens\x18\x06 \x01(\x05H\x02\x88\x01\x01\x42\x13\n\x11_reasoning_tokensB\x14\n\x12_cache_read_tokensB\x18\n\x16_cache_creation_tokens\"@\n\x0b\x44\x65gradation\x12\x11\n\trequested\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tual\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x81\x03\n\x0b\x43hatRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\x12.\n\x05tools\x18\x02 \x03(\x0b\x32\x1f.amplifier.module.ToolSpecProto\x12\x39\n\x0fresponse_format\x18\x03 \x01(\x0b\x32 .amplifier.module.ResponseFormat\x12\x13\n\x0btemperature\x18\x04 \x01(\x01\x12\r\n\x05top_p\x18\x05 \x01(\x01\x12\x19\n\x11max_output_tokens\x18\x06 \x01(\x05\x12\x17\n\x0f\x63onversation_id\x18\x07 \x01(\t\x12\x0e\n\x06stream\x18\x08 \x01(\x08\x12\x15\n\rmetadata_json\x18\t \x01(\t\x12\r\n\x05model\x18\n \x01(\t\x12\x13\n\x0btool_choice\x18\x0b \x01(\t\x12\x0c\n\x04stop\x18\x0c \x03(\t\x12\x18\n\x10reasoning_effort\x18\r \x01(\t\x12\x0f\n\x07timeout\x18\x0e \x01(\x01\"\x98\x02\n\x0c\x43hatResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x35\n\ntool_calls\x18\x02 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\x12&\n\x05usage\x18\x03 \x01(\x0b\x32\x17.amplifier.module.Usage\x12\x32\n\x0b\x64\x65gradation\x18\x04 \x01(\x0b\x32\x1d.amplifier.module.Degradation\x12\x15\n\rfinish_reason\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x07 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"~\n\nToolResult\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\x12\x12\n\nerror_json\x18\x03 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x04 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xa7\x04\n\nHookResult\x12,\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x1c.amplifier.module.HookAction\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x19\n\x11\x63ontext_injection\x18\x04 \x01(\t\x12\x46\n\x16\x63ontext_injection_role\x18\x05 \x01(\x0e\x32&.amplifier.module.ContextInjectionRole\x12\x11\n\tephemeral\x18\x06 \x01(\x08\x12\x17\n\x0f\x61pproval_prompt\x18\x07 \x01(\t\x12\x18\n\x10\x61pproval_options\x18\x08 \x03(\t\x12\x1d\n\x10\x61pproval_timeout\x18\t \x01(\x01H\x00\x88\x01\x01\x12;\n\x10\x61pproval_default\x18\n \x01(\x0e\x32!.amplifier.module.ApprovalDefault\x12\x17\n\x0fsuppress_output\x18\x0b \x01(\x08\x12\x14\n\x0cuser_message\x18\x0c \x01(\t\x12>\n\x12user_message_level\x18\r \x01(\x0e\x32\".amplifier.module.UserMessageLevel\x12\x1b\n\x13user_message_source\x18\x0e \x01(\t\x12\"\n\x1a\x61ppend_to_last_tool_result\x18\x0f \x01(\x08\x42\x13\n\x11_approval_timeout\"\x8d\x01\n\tModelInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x16\n\x0e\x63ontext_window\x18\x03 \x01(\x05\x12\x19\n\x11max_output_tokens\x18\x04 \x01(\x05\x12\x14\n\x0c\x63\x61pabilities\x18\x05 \x03(\t\x12\x15\n\rdefaults_json\x18\x06 \x01(\t\"\xb0\x01\n\x0cProviderInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x1b\n\x13\x63redential_env_vars\x18\x03 \x03(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x04 \x03(\t\x12\x15\n\rdefaults_json\x18\x05 \x01(\t\x12\x34\n\rconfig_fields\x18\x06 \x03(\x0b\x32\x1d.amplifier.module.ConfigField\"\x80\x01\n\x0f\x41pprovalRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x14\n\x0c\x64\x65tails_json\x18\x03 \x01(\t\x12\x12\n\nrisk_level\x18\x04 \x01(\t\x12\x14\n\x07timeout\x18\x05 \x01(\x01H\x00\x88\x01\x01\x42\n\n\x08_timeout\"F\n\x10\x41pprovalResponse\x12\x10\n\x08\x61pproved\x18\x01 \x01(\x08\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x10\n\x08remember\x18\x03 \x01(\x08\"A\n\x12ListModelsResponse\x12+\n\x06models\x18\x01 \x03(\x0b\x32\x1b.amplifier.module.ModelInfo\"O\n\x16ParseToolCallsResponse\x12\x35\n\ntool_calls\x18\x01 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\"@\n\x1aOrchestratorExecuteRequest\x12\x0e\n\x06prompt\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\">\n\x1bOrchestratorExecuteResponse\x12\x10\n\x08response\x18\x01 \x01(\t\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"?\n\x11\x41\x64\x64MessageRequest\x12*\n\x07message\x18\x01 \x01(\x0b\x32\x19.amplifier.module.Message\"B\n\x13GetMessagesResponse\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"J\n\x1bGetMessagesForRequestParams\x12\x14\n\x0ctoken_budget\x18\x01 \x01(\x05\x12\x15\n\rprovider_name\x18\x02 \x01(\t\"A\n\x12SetMessagesRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"5\n\x11HookHandleRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\".\n\x17GetSubscriptionsRequest\x12\x13\n\x0b\x63onfig_json\x18\x01 \x01(\t\"V\n\x18GetSubscriptionsResponse\x12:\n\rsubscriptions\x18\x01 \x03(\x0b\x32#.amplifier.module.EventSubscription\"B\n\x11\x45ventSubscription\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x10\n\x08priority\x18\x02 \x01(\x05\x12\x0c\n\x04name\x18\x03 \x01(\t\"d\n\x1b\x43ompleteWithProviderRequest\x12\x15\n\rprovider_name\x18\x01 \x01(\t\x12.\n\x07request\x18\x02 \x01(\x0b\x32\x1d.amplifier.module.ChatRequest\";\n\x12\x45xecuteToolRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x12\n\ninput_json\x18\x02 \x01(\t\"3\n\x0f\x45mitHookRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\"V\n\x19\x45mitHookAndCollectRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x17\n\x0ftimeout_seconds\x18\x03 \x01(\x01\"4\n\x1a\x45mitHookAndCollectResponse\x12\x16\n\x0eresponses_json\x18\x01 \x03(\t\"(\n\x12GetMessagesRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\"Y\n\x17KernelAddMessageRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12*\n\x07message\x18\x02 \x01(\x0b\x32\x19.amplifier.module.Message\"a\n\x17GetMountedModuleRequest\x12\x13\n\x0bmodule_name\x18\x01 \x01(\t\x12\x31\n\x0bmodule_type\x18\x02 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\"U\n\x18GetMountedModuleResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12*\n\x04info\x18\x02 \x01(\x0b\x32\x1c.amplifier.module.ModuleInfo\"=\n\x19RegisterCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\nvalue_json\x18\x02 \x01(\t\"$\n\x14GetCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\":\n\x15GetCapabilityResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12\x12\n\nvalue_json\x18\x02 \x01(\t*\xbc\x01\n\nModuleType\x12\x1b\n\x17MODULE_TYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14MODULE_TYPE_PROVIDER\x10\x01\x12\x14\n\x10MODULE_TYPE_TOOL\x10\x02\x12\x14\n\x10MODULE_TYPE_HOOK\x10\x03\x12\x16\n\x12MODULE_TYPE_MEMORY\x10\x04\x12\x19\n\x15MODULE_TYPE_GUARDRAIL\x10\x05\x12\x18\n\x14MODULE_TYPE_APPROVAL\x10\x06*\x82\x01\n\x0cHealthStatus\x12\x1d\n\x19HEALTH_STATUS_UNSPECIFIED\x10\x00\x12\x19\n\x15HEALTH_STATUS_SERVING\x10\x01\x12\x1d\n\x19HEALTH_STATUS_NOT_SERVING\x10\x02\x12\x19\n\x15HEALTH_STATUS_UNKNOWN\x10\x03*\xad\x01\n\x0f\x43onfigFieldType\x12!\n\x1d\x43ONFIG_FIELD_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_STRING\x10\x01\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_NUMBER\x10\x02\x12\x1d\n\x19\x43ONFIG_FIELD_TYPE_BOOLEAN\x10\x03\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_SECRET\x10\x04*\xd8\x02\n\x11ProviderErrorType\x12#\n\x1fPROVIDER_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18PROVIDER_ERROR_TYPE_AUTH\x10\x01\x12\"\n\x1ePROVIDER_ERROR_TYPE_RATE_LIMIT\x10\x02\x12&\n\"PROVIDER_ERROR_TYPE_CONTEXT_LENGTH\x10\x03\x12\'\n#PROVIDER_ERROR_TYPE_INVALID_REQUEST\x10\x04\x12&\n\"PROVIDER_ERROR_TYPE_CONTENT_FILTER\x10\x05\x12#\n\x1fPROVIDER_ERROR_TYPE_UNAVAILABLE\x10\x06\x12\x1f\n\x1bPROVIDER_ERROR_TYPE_TIMEOUT\x10\x07\x12\x1d\n\x19PROVIDER_ERROR_TYPE_OTHER\x10\x08*\x8c\x01\n\rToolErrorType\x12\x1f\n\x1bTOOL_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19TOOL_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aTOOL_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17TOOL_ERROR_TYPE_TIMEOUT\x10\x03*\x8c\x01\n\rHookErrorType\x12\x1f\n\x1bHOOK_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19HOOK_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aHOOK_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17HOOK_ERROR_TYPE_TIMEOUT\x10\x03*\x86\x01\n\x04Role\x12\x14\n\x10ROLE_UNSPECIFIED\x10\x00\x12\x0f\n\x0bROLE_SYSTEM\x10\x01\x12\r\n\tROLE_USER\x10\x02\x12\x12\n\x0eROLE_ASSISTANT\x10\x03\x12\r\n\tROLE_TOOL\x10\x04\x12\x11\n\rROLE_FUNCTION\x10\x05\x12\x12\n\x0eROLE_DEVELOPER\x10\x06*o\n\nVisibility\x12\x1a\n\x16VISIBILITY_UNSPECIFIED\x10\x00\x12\x12\n\x0eVISIBILITY_ALL\x10\x01\x12\x17\n\x13VISIBILITY_LLM_ONLY\x10\x02\x12\x18\n\x14VISIBILITY_USER_ONLY\x10\x03*\xab\x01\n\nHookAction\x12\x1b\n\x17HOOK_ACTION_UNSPECIFIED\x10\x00\x12\x18\n\x14HOOK_ACTION_CONTINUE\x10\x01\x12\x16\n\x12HOOK_ACTION_MODIFY\x10\x02\x12\x14\n\x10HOOK_ACTION_DENY\x10\x03\x12\x1e\n\x1aHOOK_ACTION_INJECT_CONTEXT\x10\x04\x12\x18\n\x14HOOK_ACTION_ASK_USER\x10\x05*\xa8\x01\n\x14\x43ontextInjectionRole\x12&\n\"CONTEXT_INJECTION_ROLE_UNSPECIFIED\x10\x00\x12!\n\x1d\x43ONTEXT_INJECTION_ROLE_SYSTEM\x10\x01\x12\x1f\n\x1b\x43ONTEXT_INJECTION_ROLE_USER\x10\x02\x12$\n CONTEXT_INJECTION_ROLE_ASSISTANT\x10\x03*l\n\x0f\x41pprovalDefault\x12 \n\x1c\x41PPROVAL_DEFAULT_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x41PPROVAL_DEFAULT_APPROVE\x10\x01\x12\x19\n\x15\x41PPROVAL_DEFAULT_DENY\x10\x02*\x91\x01\n\x10UserMessageLevel\x12\"\n\x1eUSER_MESSAGE_LEVEL_UNSPECIFIED\x10\x00\x12\x1b\n\x17USER_MESSAGE_LEVEL_INFO\x10\x01\x12\x1e\n\x1aUSER_MESSAGE_LEVEL_WARNING\x10\x02\x12\x1c\n\x18USER_MESSAGE_LEVEL_ERROR\x10\x03\x32\xa5\x01\n\x0bToolService\x12>\n\x07GetSpec\x12\x17.amplifier.module.Empty\x1a\x1a.amplifier.module.ToolSpec\x12V\n\x07\x45xecute\x12$.amplifier.module.ToolExecuteRequest\x1a%.amplifier.module.ToolExecuteResponse2\x9f\x03\n\x0fProviderService\x12\x42\n\x07GetInfo\x12\x17.amplifier.module.Empty\x1a\x1e.amplifier.module.ProviderInfo\x12K\n\nListModels\x12\x17.amplifier.module.Empty\x1a$.amplifier.module.ListModelsResponse\x12I\n\x08\x43omplete\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse\x12T\n\x11\x43ompleteStreaming\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Z\n\x0eParseToolCalls\x12\x1e.amplifier.module.ChatResponse\x1a(.amplifier.module.ParseToolCallsResponse2}\n\x13OrchestratorService\x12\x66\n\x07\x45xecute\x12,.amplifier.module.OrchestratorExecuteRequest\x1a-.amplifier.module.OrchestratorExecuteResponse2\xa3\x03\n\x0e\x43ontextService\x12J\n\nAddMessage\x12#.amplifier.module.AddMessageRequest\x1a\x17.amplifier.module.Empty\x12M\n\x0bGetMessages\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.GetMessagesResponse\x12m\n\x15GetMessagesForRequest\x12-.amplifier.module.GetMessagesForRequestParams\x1a%.amplifier.module.GetMessagesResponse\x12L\n\x0bSetMessages\x12$.amplifier.module.SetMessagesRequest\x1a\x17.amplifier.module.Empty\x12\x39\n\x05\x43lear\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty2\xc5\x01\n\x0bHookService\x12K\n\x06Handle\x12#.amplifier.module.HookHandleRequest\x1a\x1c.amplifier.module.HookResult\x12i\n\x10GetSubscriptions\x12).amplifier.module.GetSubscriptionsRequest\x1a*.amplifier.module.GetSubscriptionsResponse2k\n\x0f\x41pprovalService\x12X\n\x0fRequestApproval\x12!.amplifier.module.ApprovalRequest\x1a\".amplifier.module.ApprovalResponse2\xd0\x07\n\rKernelService\x12\x65\n\x14\x43ompleteWithProvider\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse\x12p\n\x1d\x43ompleteWithProviderStreaming\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Q\n\x0b\x45xecuteTool\x12$.amplifier.module.ExecuteToolRequest\x1a\x1c.amplifier.module.ToolResult\x12K\n\x08\x45mitHook\x12!.amplifier.module.EmitHookRequest\x1a\x1c.amplifier.module.HookResult\x12o\n\x12\x45mitHookAndCollect\x12+.amplifier.module.EmitHookAndCollectRequest\x1a,.amplifier.module.EmitHookAndCollectResponse\x12Z\n\x0bGetMessages\x12$.amplifier.module.GetMessagesRequest\x1a%.amplifier.module.GetMessagesResponse\x12P\n\nAddMessage\x12).amplifier.module.KernelAddMessageRequest\x1a\x17.amplifier.module.Empty\x12i\n\x10GetMountedModule\x12).amplifier.module.GetMountedModuleRequest\x1a*.amplifier.module.GetMountedModuleResponse\x12Z\n\x12RegisterCapability\x12+.amplifier.module.RegisterCapabilityRequest\x1a\x17.amplifier.module.Empty\x12`\n\rGetCapability\x12&.amplifier.module.GetCapabilityRequest\x1a\'.amplifier.module.GetCapabilityResponse2\xaf\x02\n\x0fModuleLifecycle\x12H\n\x05Mount\x12\x1e.amplifier.module.MountRequest\x1a\x1f.amplifier.module.MountResponse\x12;\n\x07\x43leanup\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty\x12M\n\x0bHealthCheck\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.HealthCheckResponse\x12\x46\n\rGetModuleInfo\x12\x17.amplifier.module.Empty\x1a\x1c.amplifier.module.ModuleInfob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x61mplifier_module.proto\x12\x10\x61mplifier.module\"\x07\n\x05\x45mpty\"F\n\x08ToolSpec\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"9\n\x12ToolExecuteRequest\x12\r\n\x05input\x18\x01 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x02 \x01(\t\"\x93\x01\n\x13ToolExecuteResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0e\n\x06output\x18\x02 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x03 \x01(\t\x12\r\n\x05\x65rror\x18\x04 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x05 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xd6\x01\n\nModuleInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x31\n\x0bmodule_type\x18\x04 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\x12\x13\n\x0bmount_point\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x06 \x01(\t\x12\x1a\n\x12\x63onfig_schema_json\x18\x07 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x08 \x03(\t\x12\x0e\n\x06\x61uthor\x18\t \x01(\t\"\x8c\x01\n\x0cMountRequest\x12:\n\x06\x63onfig\x18\x01 \x03(\x0b\x32*.amplifier.module.MountRequest.ConfigEntry\x12\x11\n\tmodule_id\x18\x02 \x01(\t\x1a-\n\x0b\x43onfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"_\n\rMountResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12.\n\x06status\x18\x03 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\"V\n\x13HealthCheckResponse\x12.\n\x06status\x18\x01 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xca\x02\n\x0b\x43onfigField\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x35\n\nfield_type\x18\x03 \x01(\x0e\x32!.amplifier.module.ConfigFieldType\x12\x0e\n\x06prompt\x18\x04 \x01(\t\x12\x0f\n\x07\x65nv_var\x18\x05 \x01(\t\x12\x0f\n\x07\x63hoices\x18\x06 \x03(\t\x12\x10\n\x08required\x18\x07 \x01(\x08\x12\x15\n\rdefault_value\x18\x08 \x01(\t\x12>\n\tshow_when\x18\t \x03(\x0b\x32+.amplifier.module.ConfigField.ShowWhenEntry\x12\x16\n\x0erequires_model\x18\n \x01(\x08\x1a/\n\rShowWhenEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xbc\x01\n\rProviderError\x12\x37\n\nerror_type\x18\x01 \x01(\x0e\x32#.amplifier.module.ProviderErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x15\n\rprovider_name\x18\x03 \x01(\t\x12\r\n\x05model\x18\x04 \x01(\t\x12\x13\n\x0bstatus_code\x18\x05 \x01(\x05\x12\x11\n\tretryable\x18\x06 \x01(\x08\x12\x13\n\x0bretry_after\x18\x07 \x01(\x01\"\x97\x01\n\tToolError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.ToolErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\ttool_name\x18\x03 \x01(\t\x12\x0e\n\x06stdout\x18\x04 \x01(\t\x12\x0e\n\x06stderr\x18\x05 \x01(\t\x12\x11\n\texit_code\x18\x06 \x01(\x05\"d\n\tHookError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.HookErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\thook_name\x18\x03 \x01(\t\"\xef\x01\n\x0e\x41mplifierError\x12\x39\n\x0eprovider_error\x18\x01 \x01(\x0b\x32\x1f.amplifier.module.ProviderErrorH\x00\x12\x31\n\ntool_error\x18\x02 \x01(\x0b\x32\x1b.amplifier.module.ToolErrorH\x00\x12\x31\n\nhook_error\x18\x03 \x01(\x0b\x32\x1b.amplifier.module.HookErrorH\x00\x12\x17\n\rgeneric_error\x18\x04 \x01(\tH\x00\x12\x1a\n\x10validation_error\x18\x05 \x01(\tH\x00\x42\x07\n\x05\x65rror\"\x19\n\tTextBlock\x12\x0c\n\x04text\x18\x01 \x01(\t\"E\n\rThinkingBlock\x12\x10\n\x08thinking\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\t\"%\n\x15RedactedThinkingBlock\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\t\"=\n\rToolCallBlock\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x12\n\ninput_json\x18\x03 \x01(\t\"<\n\x0fToolResultBlock\x12\x14\n\x0ctool_call_id\x18\x01 \x01(\t\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\"C\n\nImageBlock\x12\x12\n\nmedia_type\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x13\n\x0bsource_json\x18\x03 \x01(\t\"2\n\x0eReasoningBlock\x12\x0f\n\x07\x63ontent\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x03(\t\"\xf1\x03\n\x0c\x43ontentBlock\x12\x31\n\ntext_block\x18\x01 \x01(\x0b\x32\x1b.amplifier.module.TextBlockH\x00\x12\x39\n\x0ethinking_block\x18\x02 \x01(\x0b\x32\x1f.amplifier.module.ThinkingBlockH\x00\x12J\n\x17redacted_thinking_block\x18\x03 \x01(\x0b\x32\'.amplifier.module.RedactedThinkingBlockH\x00\x12:\n\x0ftool_call_block\x18\x04 \x01(\x0b\x32\x1f.amplifier.module.ToolCallBlockH\x00\x12>\n\x11tool_result_block\x18\x05 \x01(\x0b\x32!.amplifier.module.ToolResultBlockH\x00\x12\x33\n\x0bimage_block\x18\x06 \x01(\x0b\x32\x1c.amplifier.module.ImageBlockH\x00\x12;\n\x0freasoning_block\x18\x07 \x01(\x0b\x32 .amplifier.module.ReasoningBlockH\x00\x12\x30\n\nvisibility\x18\x08 \x01(\x0e\x32\x1c.amplifier.module.VisibilityB\x07\n\x05\x62lock\"B\n\x10\x43ontentBlockList\x12.\n\x06\x62locks\x18\x01 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xca\x01\n\x07Message\x12$\n\x04role\x18\x01 \x01(\x0e\x32\x16.amplifier.module.Role\x12\x16\n\x0ctext_content\x18\x02 \x01(\tH\x00\x12;\n\rblock_content\x18\x03 \x01(\x0b\x32\".amplifier.module.ContentBlockListH\x00\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x14\n\x0ctool_call_id\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\tB\t\n\x07\x63ontent\"C\n\x0fToolCallMessage\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x16\n\x0e\x61rguments_json\x18\x03 \x01(\t\"K\n\rToolSpecProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"7\n\x10JsonSchemaFormat\x12\x13\n\x0bschema_json\x18\x01 \x01(\t\x12\x0e\n\x06strict\x18\x02 \x01(\x08\"u\n\x0eResponseFormat\x12\x0e\n\x04text\x18\x01 \x01(\x08H\x00\x12\x0e\n\x04json\x18\x02 \x01(\x08H\x00\x12\x39\n\x0bjson_schema\x18\x03 \x01(\x0b\x32\".amplifier.module.JsonSchemaFormatH\x00\x42\x08\n\x06\x66ormat\"\x9b\x02\n\x05Usage\x12\x15\n\rprompt_tokens\x18\x01 \x01(\x05\x12\x19\n\x11\x63ompletion_tokens\x18\x02 \x01(\x05\x12\x14\n\x0ctotal_tokens\x18\x03 \x01(\x05\x12\x1d\n\x10reasoning_tokens\x18\x04 \x01(\x05H\x00\x88\x01\x01\x12\x1e\n\x11\x63\x61\x63he_read_tokens\x18\x05 \x01(\x05H\x01\x88\x01\x01\x12\"\n\x15\x63\x61\x63he_creation_tokens\x18\x06 \x01(\x05H\x02\x88\x01\x01\x12\x15\n\x08\x63ost_usd\x18\x07 \x01(\tH\x03\x88\x01\x01\x42\x13\n\x11_reasoning_tokensB\x14\n\x12_cache_read_tokensB\x18\n\x16_cache_creation_tokensB\x0b\n\t_cost_usd\"@\n\x0b\x44\x65gradation\x12\x11\n\trequested\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tual\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x81\x03\n\x0b\x43hatRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\x12.\n\x05tools\x18\x02 \x03(\x0b\x32\x1f.amplifier.module.ToolSpecProto\x12\x39\n\x0fresponse_format\x18\x03 \x01(\x0b\x32 .amplifier.module.ResponseFormat\x12\x13\n\x0btemperature\x18\x04 \x01(\x01\x12\r\n\x05top_p\x18\x05 \x01(\x01\x12\x19\n\x11max_output_tokens\x18\x06 \x01(\x05\x12\x17\n\x0f\x63onversation_id\x18\x07 \x01(\t\x12\x0e\n\x06stream\x18\x08 \x01(\x08\x12\x15\n\rmetadata_json\x18\t \x01(\t\x12\r\n\x05model\x18\n \x01(\t\x12\x13\n\x0btool_choice\x18\x0b \x01(\t\x12\x0c\n\x04stop\x18\x0c \x03(\t\x12\x18\n\x10reasoning_effort\x18\r \x01(\t\x12\x0f\n\x07timeout\x18\x0e \x01(\x01\"\x98\x02\n\x0c\x43hatResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x35\n\ntool_calls\x18\x02 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\x12&\n\x05usage\x18\x03 \x01(\x0b\x32\x17.amplifier.module.Usage\x12\x32\n\x0b\x64\x65gradation\x18\x04 \x01(\x0b\x32\x1d.amplifier.module.Degradation\x12\x15\n\rfinish_reason\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x07 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"~\n\nToolResult\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\x12\x12\n\nerror_json\x18\x03 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x04 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xa7\x04\n\nHookResult\x12,\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x1c.amplifier.module.HookAction\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x19\n\x11\x63ontext_injection\x18\x04 \x01(\t\x12\x46\n\x16\x63ontext_injection_role\x18\x05 \x01(\x0e\x32&.amplifier.module.ContextInjectionRole\x12\x11\n\tephemeral\x18\x06 \x01(\x08\x12\x17\n\x0f\x61pproval_prompt\x18\x07 \x01(\t\x12\x18\n\x10\x61pproval_options\x18\x08 \x03(\t\x12\x1d\n\x10\x61pproval_timeout\x18\t \x01(\x01H\x00\x88\x01\x01\x12;\n\x10\x61pproval_default\x18\n \x01(\x0e\x32!.amplifier.module.ApprovalDefault\x12\x17\n\x0fsuppress_output\x18\x0b \x01(\x08\x12\x14\n\x0cuser_message\x18\x0c \x01(\t\x12>\n\x12user_message_level\x18\r \x01(\x0e\x32\".amplifier.module.UserMessageLevel\x12\x1b\n\x13user_message_source\x18\x0e \x01(\t\x12\"\n\x1a\x61ppend_to_last_tool_result\x18\x0f \x01(\x08\x42\x13\n\x11_approval_timeout\"\xa3\x01\n\tModelInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x16\n\x0e\x63ontext_window\x18\x03 \x01(\x05\x12\x19\n\x11max_output_tokens\x18\x04 \x01(\x05\x12\x14\n\x0c\x63\x61pabilities\x18\x05 \x03(\t\x12\x15\n\rdefaults_json\x18\x06 \x01(\t\x12\x14\n\x0cpricing_json\x18\x07 \x01(\t\"\xb0\x01\n\x0cProviderInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x1b\n\x13\x63redential_env_vars\x18\x03 \x03(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x04 \x03(\t\x12\x15\n\rdefaults_json\x18\x05 \x01(\t\x12\x34\n\rconfig_fields\x18\x06 \x03(\x0b\x32\x1d.amplifier.module.ConfigField\"\x80\x01\n\x0f\x41pprovalRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x14\n\x0c\x64\x65tails_json\x18\x03 \x01(\t\x12\x12\n\nrisk_level\x18\x04 \x01(\t\x12\x14\n\x07timeout\x18\x05 \x01(\x01H\x00\x88\x01\x01\x42\n\n\x08_timeout\"F\n\x10\x41pprovalResponse\x12\x10\n\x08\x61pproved\x18\x01 \x01(\x08\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x10\n\x08remember\x18\x03 \x01(\x08\"A\n\x12ListModelsResponse\x12+\n\x06models\x18\x01 \x03(\x0b\x32\x1b.amplifier.module.ModelInfo\"O\n\x16ParseToolCallsResponse\x12\x35\n\ntool_calls\x18\x01 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\"@\n\x1aOrchestratorExecuteRequest\x12\x0e\n\x06prompt\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\">\n\x1bOrchestratorExecuteResponse\x12\x10\n\x08response\x18\x01 \x01(\t\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"?\n\x11\x41\x64\x64MessageRequest\x12*\n\x07message\x18\x01 \x01(\x0b\x32\x19.amplifier.module.Message\"B\n\x13GetMessagesResponse\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"J\n\x1bGetMessagesForRequestParams\x12\x14\n\x0ctoken_budget\x18\x01 \x01(\x05\x12\x15\n\rprovider_name\x18\x02 \x01(\t\"A\n\x12SetMessagesRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"5\n\x11HookHandleRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\".\n\x17GetSubscriptionsRequest\x12\x13\n\x0b\x63onfig_json\x18\x01 \x01(\t\"V\n\x18GetSubscriptionsResponse\x12:\n\rsubscriptions\x18\x01 \x03(\x0b\x32#.amplifier.module.EventSubscription\"B\n\x11\x45ventSubscription\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x10\n\x08priority\x18\x02 \x01(\x05\x12\x0c\n\x04name\x18\x03 \x01(\t\"d\n\x1b\x43ompleteWithProviderRequest\x12\x15\n\rprovider_name\x18\x01 \x01(\t\x12.\n\x07request\x18\x02 \x01(\x0b\x32\x1d.amplifier.module.ChatRequest\";\n\x12\x45xecuteToolRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x12\n\ninput_json\x18\x02 \x01(\t\"3\n\x0f\x45mitHookRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\"V\n\x19\x45mitHookAndCollectRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x17\n\x0ftimeout_seconds\x18\x03 \x01(\x01\"4\n\x1a\x45mitHookAndCollectResponse\x12\x16\n\x0eresponses_json\x18\x01 \x03(\t\"(\n\x12GetMessagesRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\"Y\n\x17KernelAddMessageRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12*\n\x07message\x18\x02 \x01(\x0b\x32\x19.amplifier.module.Message\"a\n\x17GetMountedModuleRequest\x12\x13\n\x0bmodule_name\x18\x01 \x01(\t\x12\x31\n\x0bmodule_type\x18\x02 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\"U\n\x18GetMountedModuleResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12*\n\x04info\x18\x02 \x01(\x0b\x32\x1c.amplifier.module.ModuleInfo\"=\n\x19RegisterCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\nvalue_json\x18\x02 \x01(\t\"$\n\x14GetCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\":\n\x15GetCapabilityResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12\x12\n\nvalue_json\x18\x02 \x01(\t*\xbc\x01\n\nModuleType\x12\x1b\n\x17MODULE_TYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14MODULE_TYPE_PROVIDER\x10\x01\x12\x14\n\x10MODULE_TYPE_TOOL\x10\x02\x12\x14\n\x10MODULE_TYPE_HOOK\x10\x03\x12\x16\n\x12MODULE_TYPE_MEMORY\x10\x04\x12\x19\n\x15MODULE_TYPE_GUARDRAIL\x10\x05\x12\x18\n\x14MODULE_TYPE_APPROVAL\x10\x06*\x82\x01\n\x0cHealthStatus\x12\x1d\n\x19HEALTH_STATUS_UNSPECIFIED\x10\x00\x12\x19\n\x15HEALTH_STATUS_SERVING\x10\x01\x12\x1d\n\x19HEALTH_STATUS_NOT_SERVING\x10\x02\x12\x19\n\x15HEALTH_STATUS_UNKNOWN\x10\x03*\xad\x01\n\x0f\x43onfigFieldType\x12!\n\x1d\x43ONFIG_FIELD_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_STRING\x10\x01\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_NUMBER\x10\x02\x12\x1d\n\x19\x43ONFIG_FIELD_TYPE_BOOLEAN\x10\x03\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_SECRET\x10\x04*\xd8\x02\n\x11ProviderErrorType\x12#\n\x1fPROVIDER_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18PROVIDER_ERROR_TYPE_AUTH\x10\x01\x12\"\n\x1ePROVIDER_ERROR_TYPE_RATE_LIMIT\x10\x02\x12&\n\"PROVIDER_ERROR_TYPE_CONTEXT_LENGTH\x10\x03\x12\'\n#PROVIDER_ERROR_TYPE_INVALID_REQUEST\x10\x04\x12&\n\"PROVIDER_ERROR_TYPE_CONTENT_FILTER\x10\x05\x12#\n\x1fPROVIDER_ERROR_TYPE_UNAVAILABLE\x10\x06\x12\x1f\n\x1bPROVIDER_ERROR_TYPE_TIMEOUT\x10\x07\x12\x1d\n\x19PROVIDER_ERROR_TYPE_OTHER\x10\x08*\x8c\x01\n\rToolErrorType\x12\x1f\n\x1bTOOL_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19TOOL_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aTOOL_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17TOOL_ERROR_TYPE_TIMEOUT\x10\x03*\x8c\x01\n\rHookErrorType\x12\x1f\n\x1bHOOK_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19HOOK_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aHOOK_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17HOOK_ERROR_TYPE_TIMEOUT\x10\x03*\x86\x01\n\x04Role\x12\x14\n\x10ROLE_UNSPECIFIED\x10\x00\x12\x0f\n\x0bROLE_SYSTEM\x10\x01\x12\r\n\tROLE_USER\x10\x02\x12\x12\n\x0eROLE_ASSISTANT\x10\x03\x12\r\n\tROLE_TOOL\x10\x04\x12\x11\n\rROLE_FUNCTION\x10\x05\x12\x12\n\x0eROLE_DEVELOPER\x10\x06*o\n\nVisibility\x12\x1a\n\x16VISIBILITY_UNSPECIFIED\x10\x00\x12\x12\n\x0eVISIBILITY_ALL\x10\x01\x12\x17\n\x13VISIBILITY_LLM_ONLY\x10\x02\x12\x18\n\x14VISIBILITY_USER_ONLY\x10\x03*\xab\x01\n\nHookAction\x12\x1b\n\x17HOOK_ACTION_UNSPECIFIED\x10\x00\x12\x18\n\x14HOOK_ACTION_CONTINUE\x10\x01\x12\x16\n\x12HOOK_ACTION_MODIFY\x10\x02\x12\x14\n\x10HOOK_ACTION_DENY\x10\x03\x12\x1e\n\x1aHOOK_ACTION_INJECT_CONTEXT\x10\x04\x12\x18\n\x14HOOK_ACTION_ASK_USER\x10\x05*\xa8\x01\n\x14\x43ontextInjectionRole\x12&\n\"CONTEXT_INJECTION_ROLE_UNSPECIFIED\x10\x00\x12!\n\x1d\x43ONTEXT_INJECTION_ROLE_SYSTEM\x10\x01\x12\x1f\n\x1b\x43ONTEXT_INJECTION_ROLE_USER\x10\x02\x12$\n CONTEXT_INJECTION_ROLE_ASSISTANT\x10\x03*l\n\x0f\x41pprovalDefault\x12 \n\x1c\x41PPROVAL_DEFAULT_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x41PPROVAL_DEFAULT_APPROVE\x10\x01\x12\x19\n\x15\x41PPROVAL_DEFAULT_DENY\x10\x02*\x91\x01\n\x10UserMessageLevel\x12\"\n\x1eUSER_MESSAGE_LEVEL_UNSPECIFIED\x10\x00\x12\x1b\n\x17USER_MESSAGE_LEVEL_INFO\x10\x01\x12\x1e\n\x1aUSER_MESSAGE_LEVEL_WARNING\x10\x02\x12\x1c\n\x18USER_MESSAGE_LEVEL_ERROR\x10\x03\x32\xa5\x01\n\x0bToolService\x12>\n\x07GetSpec\x12\x17.amplifier.module.Empty\x1a\x1a.amplifier.module.ToolSpec\x12V\n\x07\x45xecute\x12$.amplifier.module.ToolExecuteRequest\x1a%.amplifier.module.ToolExecuteResponse2\x9f\x03\n\x0fProviderService\x12\x42\n\x07GetInfo\x12\x17.amplifier.module.Empty\x1a\x1e.amplifier.module.ProviderInfo\x12K\n\nListModels\x12\x17.amplifier.module.Empty\x1a$.amplifier.module.ListModelsResponse\x12I\n\x08\x43omplete\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse\x12T\n\x11\x43ompleteStreaming\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Z\n\x0eParseToolCalls\x12\x1e.amplifier.module.ChatResponse\x1a(.amplifier.module.ParseToolCallsResponse2}\n\x13OrchestratorService\x12\x66\n\x07\x45xecute\x12,.amplifier.module.OrchestratorExecuteRequest\x1a-.amplifier.module.OrchestratorExecuteResponse2\xa3\x03\n\x0e\x43ontextService\x12J\n\nAddMessage\x12#.amplifier.module.AddMessageRequest\x1a\x17.amplifier.module.Empty\x12M\n\x0bGetMessages\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.GetMessagesResponse\x12m\n\x15GetMessagesForRequest\x12-.amplifier.module.GetMessagesForRequestParams\x1a%.amplifier.module.GetMessagesResponse\x12L\n\x0bSetMessages\x12$.amplifier.module.SetMessagesRequest\x1a\x17.amplifier.module.Empty\x12\x39\n\x05\x43lear\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty2\xc5\x01\n\x0bHookService\x12K\n\x06Handle\x12#.amplifier.module.HookHandleRequest\x1a\x1c.amplifier.module.HookResult\x12i\n\x10GetSubscriptions\x12).amplifier.module.GetSubscriptionsRequest\x1a*.amplifier.module.GetSubscriptionsResponse2k\n\x0f\x41pprovalService\x12X\n\x0fRequestApproval\x12!.amplifier.module.ApprovalRequest\x1a\".amplifier.module.ApprovalResponse2\xd0\x07\n\rKernelService\x12\x65\n\x14\x43ompleteWithProvider\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse\x12p\n\x1d\x43ompleteWithProviderStreaming\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Q\n\x0b\x45xecuteTool\x12$.amplifier.module.ExecuteToolRequest\x1a\x1c.amplifier.module.ToolResult\x12K\n\x08\x45mitHook\x12!.amplifier.module.EmitHookRequest\x1a\x1c.amplifier.module.HookResult\x12o\n\x12\x45mitHookAndCollect\x12+.amplifier.module.EmitHookAndCollectRequest\x1a,.amplifier.module.EmitHookAndCollectResponse\x12Z\n\x0bGetMessages\x12$.amplifier.module.GetMessagesRequest\x1a%.amplifier.module.GetMessagesResponse\x12P\n\nAddMessage\x12).amplifier.module.KernelAddMessageRequest\x1a\x17.amplifier.module.Empty\x12i\n\x10GetMountedModule\x12).amplifier.module.GetMountedModuleRequest\x1a*.amplifier.module.GetMountedModuleResponse\x12Z\n\x12RegisterCapability\x12+.amplifier.module.RegisterCapabilityRequest\x1a\x17.amplifier.module.Empty\x12`\n\rGetCapability\x12&.amplifier.module.GetCapabilityRequest\x1a\'.amplifier.module.GetCapabilityResponse2\xaf\x02\n\x0fModuleLifecycle\x12H\n\x05Mount\x12\x1e.amplifier.module.MountRequest\x1a\x1f.amplifier.module.MountResponse\x12;\n\x07\x43leanup\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty\x12M\n\x0bHealthCheck\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.HealthCheckResponse\x12\x46\n\rGetModuleInfo\x12\x17.amplifier.module.Empty\x1a\x1c.amplifier.module.ModuleInfob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -35,30 +35,30 @@ _globals['_MOUNTREQUEST_CONFIGENTRY']._serialized_options = b'8\001' _globals['_CONFIGFIELD_SHOWWHENENTRY']._loaded_options = None _globals['_CONFIGFIELD_SHOWWHENENTRY']._serialized_options = b'8\001' - _globals['_MODULETYPE']._serialized_start=7226 - _globals['_MODULETYPE']._serialized_end=7414 - _globals['_HEALTHSTATUS']._serialized_start=7417 - _globals['_HEALTHSTATUS']._serialized_end=7547 - _globals['_CONFIGFIELDTYPE']._serialized_start=7550 - _globals['_CONFIGFIELDTYPE']._serialized_end=7723 - _globals['_PROVIDERERRORTYPE']._serialized_start=7726 - _globals['_PROVIDERERRORTYPE']._serialized_end=8070 - _globals['_TOOLERRORTYPE']._serialized_start=8073 - _globals['_TOOLERRORTYPE']._serialized_end=8213 - _globals['_HOOKERRORTYPE']._serialized_start=8216 - _globals['_HOOKERRORTYPE']._serialized_end=8356 - _globals['_ROLE']._serialized_start=8359 - _globals['_ROLE']._serialized_end=8493 - _globals['_VISIBILITY']._serialized_start=8495 - _globals['_VISIBILITY']._serialized_end=8606 - _globals['_HOOKACTION']._serialized_start=8609 - _globals['_HOOKACTION']._serialized_end=8780 - _globals['_CONTEXTINJECTIONROLE']._serialized_start=8783 - _globals['_CONTEXTINJECTIONROLE']._serialized_end=8951 - _globals['_APPROVALDEFAULT']._serialized_start=8953 - _globals['_APPROVALDEFAULT']._serialized_end=9061 - _globals['_USERMESSAGELEVEL']._serialized_start=9064 - _globals['_USERMESSAGELEVEL']._serialized_end=9209 + _globals['_MODULETYPE']._serialized_start=7284 + _globals['_MODULETYPE']._serialized_end=7472 + _globals['_HEALTHSTATUS']._serialized_start=7475 + _globals['_HEALTHSTATUS']._serialized_end=7605 + _globals['_CONFIGFIELDTYPE']._serialized_start=7608 + _globals['_CONFIGFIELDTYPE']._serialized_end=7781 + _globals['_PROVIDERERRORTYPE']._serialized_start=7784 + _globals['_PROVIDERERRORTYPE']._serialized_end=8128 + _globals['_TOOLERRORTYPE']._serialized_start=8131 + _globals['_TOOLERRORTYPE']._serialized_end=8271 + _globals['_HOOKERRORTYPE']._serialized_start=8274 + _globals['_HOOKERRORTYPE']._serialized_end=8414 + _globals['_ROLE']._serialized_start=8417 + _globals['_ROLE']._serialized_end=8551 + _globals['_VISIBILITY']._serialized_start=8553 + _globals['_VISIBILITY']._serialized_end=8664 + _globals['_HOOKACTION']._serialized_start=8667 + _globals['_HOOKACTION']._serialized_end=8838 + _globals['_CONTEXTINJECTIONROLE']._serialized_start=8841 + _globals['_CONTEXTINJECTIONROLE']._serialized_end=9009 + _globals['_APPROVALDEFAULT']._serialized_start=9011 + _globals['_APPROVALDEFAULT']._serialized_end=9119 + _globals['_USERMESSAGELEVEL']._serialized_start=9122 + _globals['_USERMESSAGELEVEL']._serialized_end=9267 _globals['_EMPTY']._serialized_start=44 _globals['_EMPTY']._serialized_end=51 _globals['_TOOLSPEC']._serialized_start=53 @@ -118,87 +118,87 @@ _globals['_RESPONSEFORMAT']._serialized_start=3260 _globals['_RESPONSEFORMAT']._serialized_end=3377 _globals['_USAGE']._serialized_start=3380 - _globals['_USAGE']._serialized_end=3627 - _globals['_DEGRADATION']._serialized_start=3629 - _globals['_DEGRADATION']._serialized_end=3693 - _globals['_CHATREQUEST']._serialized_start=3696 - _globals['_CHATREQUEST']._serialized_end=4081 - _globals['_CHATRESPONSE']._serialized_start=4084 - _globals['_CHATRESPONSE']._serialized_end=4364 - _globals['_TOOLRESULT']._serialized_start=4366 - _globals['_TOOLRESULT']._serialized_end=4492 - _globals['_HOOKRESULT']._serialized_start=4495 - _globals['_HOOKRESULT']._serialized_end=5046 - _globals['_MODELINFO']._serialized_start=5049 - _globals['_MODELINFO']._serialized_end=5190 - _globals['_PROVIDERINFO']._serialized_start=5193 - _globals['_PROVIDERINFO']._serialized_end=5369 - _globals['_APPROVALREQUEST']._serialized_start=5372 - _globals['_APPROVALREQUEST']._serialized_end=5500 - _globals['_APPROVALRESPONSE']._serialized_start=5502 - _globals['_APPROVALRESPONSE']._serialized_end=5572 - _globals['_LISTMODELSRESPONSE']._serialized_start=5574 - _globals['_LISTMODELSRESPONSE']._serialized_end=5639 - _globals['_PARSETOOLCALLSRESPONSE']._serialized_start=5641 - _globals['_PARSETOOLCALLSRESPONSE']._serialized_end=5720 - _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_start=5722 - _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_end=5786 - _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_start=5788 - _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_end=5850 - _globals['_ADDMESSAGEREQUEST']._serialized_start=5852 - _globals['_ADDMESSAGEREQUEST']._serialized_end=5915 - _globals['_GETMESSAGESRESPONSE']._serialized_start=5917 - _globals['_GETMESSAGESRESPONSE']._serialized_end=5983 - _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_start=5985 - _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_end=6059 - _globals['_SETMESSAGESREQUEST']._serialized_start=6061 - _globals['_SETMESSAGESREQUEST']._serialized_end=6126 - _globals['_HOOKHANDLEREQUEST']._serialized_start=6128 - _globals['_HOOKHANDLEREQUEST']._serialized_end=6181 - _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_start=6183 - _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_end=6229 - _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_start=6231 - _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_end=6317 - _globals['_EVENTSUBSCRIPTION']._serialized_start=6319 - _globals['_EVENTSUBSCRIPTION']._serialized_end=6385 - _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_start=6387 - _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_end=6487 - _globals['_EXECUTETOOLREQUEST']._serialized_start=6489 - _globals['_EXECUTETOOLREQUEST']._serialized_end=6548 - _globals['_EMITHOOKREQUEST']._serialized_start=6550 - _globals['_EMITHOOKREQUEST']._serialized_end=6601 - _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_start=6603 - _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_end=6689 - _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_start=6691 - _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_end=6743 - _globals['_GETMESSAGESREQUEST']._serialized_start=6745 - _globals['_GETMESSAGESREQUEST']._serialized_end=6785 - _globals['_KERNELADDMESSAGEREQUEST']._serialized_start=6787 - _globals['_KERNELADDMESSAGEREQUEST']._serialized_end=6876 - _globals['_GETMOUNTEDMODULEREQUEST']._serialized_start=6878 - _globals['_GETMOUNTEDMODULEREQUEST']._serialized_end=6975 - _globals['_GETMOUNTEDMODULERESPONSE']._serialized_start=6977 - _globals['_GETMOUNTEDMODULERESPONSE']._serialized_end=7062 - _globals['_REGISTERCAPABILITYREQUEST']._serialized_start=7064 - _globals['_REGISTERCAPABILITYREQUEST']._serialized_end=7125 - _globals['_GETCAPABILITYREQUEST']._serialized_start=7127 - _globals['_GETCAPABILITYREQUEST']._serialized_end=7163 - _globals['_GETCAPABILITYRESPONSE']._serialized_start=7165 - _globals['_GETCAPABILITYRESPONSE']._serialized_end=7223 - _globals['_TOOLSERVICE']._serialized_start=9212 - _globals['_TOOLSERVICE']._serialized_end=9377 - _globals['_PROVIDERSERVICE']._serialized_start=9380 - _globals['_PROVIDERSERVICE']._serialized_end=9795 - _globals['_ORCHESTRATORSERVICE']._serialized_start=9797 - _globals['_ORCHESTRATORSERVICE']._serialized_end=9922 - _globals['_CONTEXTSERVICE']._serialized_start=9925 - _globals['_CONTEXTSERVICE']._serialized_end=10344 - _globals['_HOOKSERVICE']._serialized_start=10347 - _globals['_HOOKSERVICE']._serialized_end=10544 - _globals['_APPROVALSERVICE']._serialized_start=10546 - _globals['_APPROVALSERVICE']._serialized_end=10653 - _globals['_KERNELSERVICE']._serialized_start=10656 - _globals['_KERNELSERVICE']._serialized_end=11632 - _globals['_MODULELIFECYCLE']._serialized_start=11635 - _globals['_MODULELIFECYCLE']._serialized_end=11938 + _globals['_USAGE']._serialized_end=3663 + _globals['_DEGRADATION']._serialized_start=3665 + _globals['_DEGRADATION']._serialized_end=3729 + _globals['_CHATREQUEST']._serialized_start=3732 + _globals['_CHATREQUEST']._serialized_end=4117 + _globals['_CHATRESPONSE']._serialized_start=4120 + _globals['_CHATRESPONSE']._serialized_end=4400 + _globals['_TOOLRESULT']._serialized_start=4402 + _globals['_TOOLRESULT']._serialized_end=4528 + _globals['_HOOKRESULT']._serialized_start=4531 + _globals['_HOOKRESULT']._serialized_end=5082 + _globals['_MODELINFO']._serialized_start=5085 + _globals['_MODELINFO']._serialized_end=5248 + _globals['_PROVIDERINFO']._serialized_start=5251 + _globals['_PROVIDERINFO']._serialized_end=5427 + _globals['_APPROVALREQUEST']._serialized_start=5430 + _globals['_APPROVALREQUEST']._serialized_end=5558 + _globals['_APPROVALRESPONSE']._serialized_start=5560 + _globals['_APPROVALRESPONSE']._serialized_end=5630 + _globals['_LISTMODELSRESPONSE']._serialized_start=5632 + _globals['_LISTMODELSRESPONSE']._serialized_end=5697 + _globals['_PARSETOOLCALLSRESPONSE']._serialized_start=5699 + _globals['_PARSETOOLCALLSRESPONSE']._serialized_end=5778 + _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_start=5780 + _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_end=5844 + _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_start=5846 + _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_end=5908 + _globals['_ADDMESSAGEREQUEST']._serialized_start=5910 + _globals['_ADDMESSAGEREQUEST']._serialized_end=5973 + _globals['_GETMESSAGESRESPONSE']._serialized_start=5975 + _globals['_GETMESSAGESRESPONSE']._serialized_end=6041 + _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_start=6043 + _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_end=6117 + _globals['_SETMESSAGESREQUEST']._serialized_start=6119 + _globals['_SETMESSAGESREQUEST']._serialized_end=6184 + _globals['_HOOKHANDLEREQUEST']._serialized_start=6186 + _globals['_HOOKHANDLEREQUEST']._serialized_end=6239 + _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_start=6241 + _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_end=6287 + _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_start=6289 + _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_end=6375 + _globals['_EVENTSUBSCRIPTION']._serialized_start=6377 + _globals['_EVENTSUBSCRIPTION']._serialized_end=6443 + _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_start=6445 + _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_end=6545 + _globals['_EXECUTETOOLREQUEST']._serialized_start=6547 + _globals['_EXECUTETOOLREQUEST']._serialized_end=6606 + _globals['_EMITHOOKREQUEST']._serialized_start=6608 + _globals['_EMITHOOKREQUEST']._serialized_end=6659 + _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_start=6661 + _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_end=6747 + _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_start=6749 + _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_end=6801 + _globals['_GETMESSAGESREQUEST']._serialized_start=6803 + _globals['_GETMESSAGESREQUEST']._serialized_end=6843 + _globals['_KERNELADDMESSAGEREQUEST']._serialized_start=6845 + _globals['_KERNELADDMESSAGEREQUEST']._serialized_end=6934 + _globals['_GETMOUNTEDMODULEREQUEST']._serialized_start=6936 + _globals['_GETMOUNTEDMODULEREQUEST']._serialized_end=7033 + _globals['_GETMOUNTEDMODULERESPONSE']._serialized_start=7035 + _globals['_GETMOUNTEDMODULERESPONSE']._serialized_end=7120 + _globals['_REGISTERCAPABILITYREQUEST']._serialized_start=7122 + _globals['_REGISTERCAPABILITYREQUEST']._serialized_end=7183 + _globals['_GETCAPABILITYREQUEST']._serialized_start=7185 + _globals['_GETCAPABILITYREQUEST']._serialized_end=7221 + _globals['_GETCAPABILITYRESPONSE']._serialized_start=7223 + _globals['_GETCAPABILITYRESPONSE']._serialized_end=7281 + _globals['_TOOLSERVICE']._serialized_start=9270 + _globals['_TOOLSERVICE']._serialized_end=9435 + _globals['_PROVIDERSERVICE']._serialized_start=9438 + _globals['_PROVIDERSERVICE']._serialized_end=9853 + _globals['_ORCHESTRATORSERVICE']._serialized_start=9855 + _globals['_ORCHESTRATORSERVICE']._serialized_end=9980 + _globals['_CONTEXTSERVICE']._serialized_start=9983 + _globals['_CONTEXTSERVICE']._serialized_end=10402 + _globals['_HOOKSERVICE']._serialized_start=10405 + _globals['_HOOKSERVICE']._serialized_end=10602 + _globals['_APPROVALSERVICE']._serialized_start=10604 + _globals['_APPROVALSERVICE']._serialized_end=10711 + _globals['_KERNELSERVICE']._serialized_start=10714 + _globals['_KERNELSERVICE']._serialized_end=11690 + _globals['_MODULELIFECYCLE']._serialized_start=11693 + _globals['_MODULELIFECYCLE']._serialized_end=11996 # @@protoc_insertion_point(module_scope) diff --git a/tests/fixtures/wasm/echo-provider.wasm b/tests/fixtures/wasm/echo-provider.wasm index d047493f9459ef925b91e0dcb659353776ab0387..87b0b213a7bb9c175baaa9f21d683a2815c9fd3e 100644 GIT binary patch delta 64174 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 delta 51810 zcmbq+34ByV^8c%TGnq+dLIwyS7v#-wB|tb75^nNP5C!G%7DZ8TF(8Pb;;uSFKo=Di zc}Pb7@VD*O-KX)A~I0Q5Um0f@MmZEGyD|H zDK`P8ssn{-ngKK;0%*zS3!sSc8sQK{S~HO8q8aHJvMmzi9T9Rol!=I*;G`na0!#S%pdp z_`JdDT?3ydTODayapRBS^QN48!Pys2n=*6e?DLE(GT#P8HihO~JZ}62YpJwH_ih)> z`Ky?5>NPZJ@>yq2zhWW%nd;~|x|0^r)wGCKQ4Ou8+vzsiDc%$Bh==J_@gRL89-#Ya z9W~Hi@iXnE4`?HOL|@QBS|GlmAE-urP2bVCar!$QphL7&B*ZWDEB!`W=>_^H?WFCr zgI=Lm>1EnR|Dd1f-}F8Gix!D%#bWUzEfjwiSBq=Je)=1IMPJgh^c-C;t`nQYX7RY# zC>|5L#BZWOyduu%F@mnv#5tZcZCU z7iwErnwwTw+5)ap#?=#K0@(SaP3!hex-o#uhp6&0%5?g-?c8oxqJi7xf~nB}KhBBz zomp*5;wv=^O2wJcAm^VJGi@;{J5$31M3tzrz=k;^vkfr9v;wv4w=lpcAPHnsI19|t#BwUgtitF;uH_~*q>DNfvI3Fo(zC-^PV3xW z^a#-AIltt#foewDMRPPINxV)~yIho<+%8s9+gLmlf5LhUEsE@LTD3259%)x~a*Nz$ zu?gW5HN=!=k<1ZmdRt0#!Q#fTqo%Erel{(CKyPpK!ph70TB)2 zVY=w--)N4N=}c(<1ub<3=3BJPIU~P!p6-IlI%Srk0)Bx_&K>y`d0I5Rq6in|s-pIe zD7coEJ4*_VX#$kbK*b8Eo%3tKQ5~1^AabUa?PGEkHnM~TY9q@wo?SSJwSCv7@g3q-Bz66q$t8gOf6aMoPEWU!}n`K>_~Ow zerLnhF5;gFXHJJge%#QZV8B1QI0xKAdofdsB#4dJ!N~6xIUjV0(*4Uic4tIAJNDtn zvb{aiv^|nd=INMDq zP=cI}{YLg=l{7>Z_hf+0C9Hm;%HD3UE(;r0$v{Hsv$Lk(AL8>c8or1p$_KSM1*GD< zvU!9W3R8s`0Er@&C}Hn|>fYrx2Hncm^zIrbTRLt1Cn<_(Fq>r7YoK(-J&w zqe5)La|Ab?ph~fhGa0wNgsLOAGC`IoHwKK%CL8RAea&)P@w@2|G{Mv}A3PSVrP zc8X>2`~nI}#V*XQjL!lC=CFY2|Cj_WuWl7EOgK-bfUOIFa1d$-BKOsR!ffFhdS#G@ zL<2B!fcdk6$%@1^5ZT>&R5tM0JV5-4DrII>H0<>h?F8+we*5jWK(%e+ zbveV0DQe{-0Y8#y30IoCNE5S@pP+3!p_&V@xrQ@9U7X2dDhGhoYVJHH z8w`T=tPpD%N2?04hM!p#qK=IfC1nO0W+ov$S-L%J%;!lr3WP#@oXf4p~ZWL$IcN^GB#d^X(`{z~1 zKw=r{GAfhD8S@^?TrM6N6#`QwnzC9a8w)Dqu!+JM|6wfS%?DE`%}S18EJI0+?ijkY zT=fuBr83GO3*)c4<&97*ixrjyf@uxsIZ;b#!x(Mv|rSt4GG8!V|;|RKrF+q zF|yo2!tIt__E&F;f6q zobOU7O?Q%kMw#ie3>pVd^n}FCZ3c#s5LCQA-54~iB*UtPD}YM7jHFD!2a0PR4d6m= z^zf>4H4D>NZB|v9R!Wv4x?Q$fWoVR51?NDdGF+n9!^<`?p6n30&K@(sjCkHjoZS;G zZtN++9nP@h)jZ}*bSdTz?o~FwLKaQPqBqU5QXWQaFtRK}E!mwlX~V=w;{#&u{BcaS zvSI&|-BHG30Z`75hLn3>DypJwuvqZHfFF*8d(uhJ_SY5NmPycths~cdIzSRq8YBvu<815x|huF@H3_a@vO+ zS7-HXiggjDS5_2%69-D!Q+MeVO*&9YD6K3s8plvV+m+lL$7@xffst}m+0%E-dX4m5 zGn{=8D}yOtp0pUpxHw=Lo8Z6<*$4*)2+LOI$Ug1jAOz#2O#)0qYNB8Rfih(-k`@83 zFY=UKNSXt7IUs^2i`Pv)@NLJNr?8e6P%*K2WRsX>( zGlOBs;i+Nl(KeDt$9znHAFP6v@;mf-+j!XWM}sDl9v)##%r?-)pxtm~H7ueD-bKS` z7PD~%DuhQQ7St?SytJ+%5nmWfPfdWWYgBwS>Y)n97trB9-e;Sa&ag{oR@3Ah1z_9zV2i>RI6%51f$?4K+Rlz_x zmbPr$lj&kk=cm%SoW}IS4KO@A<1CO~dMku40~xvjUhqS7cwN>8vd0weORjUCb6WSC zNH;lC`#sd&Ti|hZj2{5?(z>*NU$H0QOzK}aRV`YzI+t=UQ?qH$xmZ}K ziDN!HOrnaHu5RFtHBN=@41Q68yG%@|$-o_@1@1C=p@IxAVuhFx7bEV+ZkSs+7h~M4 zwzI2kai$4X4RDMT(YAc2_t0$TuED(!kk~zV7{n+tq+Pu(*NBqQFcrEy z6KLNbY6Td^hj-tw&S&po3}FOlz^!>PHbeY2U-evzZ7ZH?F-(o*R)H!}XV5f5$sVn# zRbI8Rc33K8=#F*zyrqI{e;YQI0#3#7ll+Ro&P~Jfnu_gcrV0^2=jq|ynwE{{vXfBu z)9?=cFzhP!sqC_yp(52jOy;h$d|E71O@xS+VhVQYc4k#|i6flH0h{0;12SxdD$V&U zEqZLN5G2j=gEyfO7NjnKST*D3EajHC{86(ej3idSimw_5!w%OasFj|xfK<)CnQWij zc#Z{ChKW>ROuyzIJ6?vJG01?ivP|a$yQ|++{JPwZ=3rI~BO8Hx6^nH4w|A7@ti}?$ zVL(y^FPS3CR2C*fcli9%ky=VGX;+~Z5US_{TTo? z4J$@oUCzsKgFBnvTBf@X(X6%6qb{PGohL?J-d54hp+%4M$TDZ-F;^k}y7idy_RULG z$kfRh6Zgp6c<-1g!1x`>7%XH8fn@~UwM@M}p=)<7Zp7k-T#RxmaLTqAQ@x%7 zE#dsdT9U89d7g$N0E>{Ea{d$@*sL(Z5V@dXXY^(m*vT=L+Dl@cngM#o%2^*PcdUu& z&Gb{vBxkOz69-f6vJ)9m`xBisL>eyH;`1kVZ&sLe8hj`9ZC02h;rNp}3{3S>qxE{p zPT9%5dSDv!!?(jUW-4O`;{_CE z`aH(vynW@&J$Xh342UoCg!AdieH5A!r{5`Kfoj1i{X3?%yEr)PZg=sl$wuo5XU{3) ze5-u0^Y2;3&T$plPVSlc&R3_~Nox*w4P)YUc7fAtQob|kj4FB+TT{g+!#pca7bA!W zkyVjHwxT}tve;XXz!_E3zSYQiXJLhk*}bdn^@#?=c9Wr9r+iX(R{|Lex1SuQ3nxX3 zla|_@(O|{dU)EN*d{RLKCK7Be6P9*agU*|iYQc_~XHKA6x!$B)-fQKKe0^qCPD8WC z3!IK;bxb-80jQK>^w_hG@~e%pjgMY5ld?5qYvHT%DD=4a<7PPp8;_hD_Pg2JHQ>gv z^Ply_AFY907}#>dY!|Y(-^$(p#LYuA){LY8vYG+xXK=f}UAK~}eU_}YJ-r$=tHy1G z?Y&&NAlcYbg{#!wtVsB*#<}atA&m)kzPfUJ$A(&NvX6a$pUEcItJJSG&goZOnWQ+O zFd_5vt4e)eo5ClTUPigj54R7^-lYh>TbVX;7M$7E8GlDvlFz0m(BIzC*Lm`eE-e+D zn7#E5ra&3isK#+_T`b_{I@j0rXy@%+0-xsIU?g{%5a36hx34s98Bq5~RxeVI#3geH;!*m7~$7&aMFW@|5rTlV z4l6NmS2U(F+8nk^igHHWH71Z9USekRdR#f3aIq~@ZBk+YU{%?R#EzP~qm6~P4LW#5 z;I$Y`Yti>REjDiOjQ_bstTU)g1fy)x!2_)LEFUlY-8f&k+TNs9(h1z#x$t+2N8LN5 zEsAw`Fs<5(d%Iv8h6fD0i6eLt;ax+u?_{YRP_ljVc$bJn5s1EED&jY~IGSlzX?$kn zPS!@Xtr*L)Gk7OUATVKz>KMoLM0L4g=&4r)yJBIEi6kT5Ry?z}VGO2z=vz<)y)2${ zhD5Uk8h}F*Qz5so+B)PN)4O*g~>B41ZC!-RukTG{tHDF&<&D&)va{mHH2<)(P*4p;*a3y3HkY&bz zN;G2d0zlZqtIG_=5JtpG(ak090tGU}BgaH*7O|{?t*S@7VeGARV9c;Yfhc1xO{x)0 z5swN>Vi=FOj=^K&m(7I2`kBINphL`d?2hr+pjQTdDONI^8aPF*mt`614f24wo!O>T zNN=yh&nvIcAhGVF%F+71Ymz?;gz z^4hceY42L|Rr_!fq7USX0oBeE_jD>|GIn`xAhep;8`D$ygU*wW6*-Y@Vbd_HoFxww z$88@+zhZ1qaIOhIKAw2Mn+nkxxS=|hPUY$p##M1!RbmfKf#xGG%!s1wk1FpAaoV1` zuvkA}z*tt^;!)KLT{L|Lx6L64mN$0!%_KW{{sVP5LZZbBO9%yvIV}mBk}w9&ua2@v zAZ#fh;7;PfZq`Feh9Gs`f(sC=Tkbl-s9!3mgy>e~y0!IUTRa?U5fZsgKIf}w z4?~GY)WSp#&Y-UKm;0#W?_gN{A2E#VXNW5PZ?UXn*P9H1jlq5FP1xTVcCC`k-FgJk zO}5sdP%iUfGZ(4OZIRI|D#=g*oRITj@!9|5F&(v4#;oxIx~UU9jf;}UbONfaoRI8O zMw7)03Awb4zUAkF?$n1S%Covt%uSXDLrs%A!kUk3=C^hHeT-!0&1)?e_Mx>G&=wmX3{|mHs}wz z0vlq1#TE~ak-_M)*n~Wj=sd8!XDbY#ilHL{jT1Od){c%o=@S{w{tca+%?)`@mq*)l zVX-hVk0T0>Ur?WwEYMn9KpAwRPmG+YkM=8p9c2+hPB@m~9*ck)pcZU_mEqJs+9^wo zBx}koPRHlFx)LIw4milwZez3~D(6G;)3!o+?So}E6fPEUCTuL3uJ#{BM>BO(#hAWO zDVL`&6B^@f@GC?aYJVT+(<*t5$Dwsh7w(ZK-o?NY3TrP89tYTBv+}#sRB_mFzS~j) zC(z`xQCe9;*W(ln3rq2)66Q}aq7aHDwT)R)ZdlodbLFO3+{d(8*o9QbKX~RwtEEvK z+vI}c7}DvqKhJp3q-KH-XA{WEVA>HoW!kap!Mg$+Z{!0?#b~_|36_bbG2ED1funP@ zH6kpH&LR4ovgeC3wK{?8PKS-{BQT#{ds++@JT|~-6_3&jPM`Rx_`Ntj8ozhPtHj4! zowkX-NQ_LJ82NY`G#@MZfbF}q-1vB#vo=wJqLKxL&c}%%>g`D16N3ECDGT}_`R4`w z;v2bx*a0Wz`b>LzH9RlgFNyktsyXn8JEAFL3z$2HRULI zzvguOu3Xr;?Jl-!6+p)`fDN;_XO%N+VHrwm7akFRg9(9sQj{7TLBOwYWCLdx^3+1A zUgS|1j!UD6cBl$5ANLQxf-%AUV;3g9e_Rp;4wVmM#EvrZkgT?UW=~IafcU8M7YdVK zBk_AkoZ%}=oU0dg9K^1mVeW z-d{8-j-3YZPN*t)1%BQTL^whP_7@AbT_zk*Vz(8g7)ACFmzt<$03m+&re)?hpj0EJ z;^9mM3fWX~gV8LDjgy#Hjle*4R48)&AUE3>$uOCCjWQwJGEaQ5Oby7#z0hPVh|U9* z6`X7QPgCEkO9k!e0!--Hqvh|S1;~x zDggMuETcRQ|NYQ;1BVqrwLOSs(+@{T>ww_kmt``~d5L6Jsh$Tk%Z^pP5TriIa0n0> zF4(no8P4uBGg>hmjK@eS_PY#M-I1`sDR$Cc3S)a~rUVH?Z z+nD>oIqV~;W@DL(+n`~Fy9)*c@oGD8Y&LNX=fKt0v0jx-W_D&ads?tU;666!(LSh# z3k!{$$-;#f&3nyqB}jT0aQ6JQCB<>A@=7I?!qf;o7cwI@1p$rof{x{S5HUwLJh(a9vO5+2v#L)^J0A z+Thr?u<@FG%TT)4dFYmLc>m>=(K&ln({E|7?I+XOu&fd#@$0xo_nX-eE?Cyt>9eF4 z-p*b!47s;2VVuKPc5+(X5I{Qbh9gy3Nyhu!AKN)}>j(v4qj14WZuF6rr9mapp+`jv zEku`30??Jqd$(4m5WD%{34C-Yp7Y+yDwH*)=k`_H<{PVy!SBM=&!eIDSC^{yk}Rb| zmB~6899A%L2$r2 zLvr9Cd`G5F`K=H^K>B!v2Sd;A;beWRRNym3ea>&6wZkSJE4$x2$YH939Ec&p>w!}G z*tyC8=-+ntIRj62dSFdjI-TL?(;5DSZ0T7cc$I_6jt^(RJ5h%-RKN$6z)ULNk|V>~ z33k4E{G-Z$a}Qt!IIhfU5QZAS=SRrHhLwpdV`*iFB!ZPE6rP;}O#C-H=H`FW4;z2l zd>(T z?(MR8(wxe19Cl+d(ic@n?sV3E7G>dl<+FNv###9J;8HD(aJ4bDmzn3VC!C2v(Y5g6 zddHd0=buldEN9FYr;0BV&YCZlj?{~3Uaq1Y7DNQ6O2u@@gcp>+yyNaeCt+auG>j|Y z&YkA6JC5v8kYQqV&A?1cbP!XfK zT1y45O6vF(R5W-C4jTwdT2o1fpSeDW1z+BF+k}b*-8Lt+rpaRu(dDHnIKT z_8d(2@rP;g|I$nJJG9eT-Y^3DqJL=^g5xQh zMgGO6BNPUIF*fTL$nv(dp6-($v?U2GslP0j+ERzx)d*lJ$c14_2EE-4GI?J+x;5x- zYi)GOc8tVCJbOoHOcBd>l#|bSaYqz6U+g#@IsIQQ$GMsFUmjbLww!_Vu`v3SQ-f6n zV&c{I-@n}ONe0#+Xiwn0kJgL(oUdN)rVvN7^u-_4^f~q`MS=HJ7vFNud!;Mwajtu% zU*M3+S>n9-%7|o`p4|?$<=1}aQ7~+F9$nW~X)##hNkQnlVEqSqbQ&95IUt{Uv{Hj@ zuUGp^zt+e(`Lrd@4l%po?7;qO`fMZjLd{_wS&uRG(Xtw21-0w%zA7h8l5g;c!7(`A^U5^P4s?dA?;w!R2EV9xI)U4V3K*i zWrz%zj!qC6$X=frG17KJRPjP`jZ%1%X9V>cxtGD*Z4{4D`U#IOIZ?7-;ErLI8b4Ix zrGpAv2Qh>6Hg@0MkT3=tYzvJtV+W@YIB!-7t88p@z?N{Gzj}4BAzx;^*|-khchl1} z?vR=;B5j7bb*wLamh;}ZWi1F;{SBSBe7{NC|BbDN4v6U1>Rmk z&=(sOc2-{DXxJ(&iu z^9N%aNSjkJJ@N8^ELcN$E}ZfhmEfrh4#9_C4xmj={#PA%s;vBKkl1sZGxw{mwAfjR z-+ytoe04R?_sRzx(WP8ccFu7ama}HCG(E4WnsS zf>azS_*x}4-R?(TA!U-xGkaaHliq%gK{RS~3y?hpGof|U{Xd*WH=Z1}!o9APjX zj<6mAb|xBdc}z{jd`;L8Ro;J|?0%$}&qj7)K8GIEjNbqbF5!(SOlh@vj^JX9^HR>+ z!}KcbBkGWikQTQmc)`u1AnbEtG4R0jwwQOH)DG8Tb+;wPjt}5a2A@(nKYaLMDK=IY z6N*qV@)zLbQ9zgxU~1hLLuV9aWdMu$E6wpvkH2?}GtNHnbpee2;Z<$aa9u7bg&A{8 zaKzWWT%<}Y1RC5D9QI8w$;TSkEy1zh^pZ$*4BnS&$J;I9tw6V1>}@a!sf{|k+Uh*7 z%Ewz;f6WHy2ltozeh52=P#>?mA+*Xp^f&;wEsL{olgZ0MfHRY`;@PP@1P*ySFxD!S zRjT8lv7A&Eg1#9Hg@f&k3?zcVZevKGEw>pY(29#637~f|BLTz;XIxtU;kFgeI5et4G$=kYmn+0# z60T$e8bgEMkRlzoOWyQppyVM{U`b9SJX1j#w&KDRh7Z5ngVCn!~3I^y6 zcaRfDoRViu*m~SF!8`I?8F42k91$#NgS?`8 zL`44Slpo0VW`1GU3DQQC6uwyZ{;F+mEb0d_S4zQQRrx&0zdm4+*+aMmmT>7$96uMyOK-)}bNT z%+k`ys>1gbJ-e+gp1MNH7(F3Xt*D!dZVM1Vw+Uss5y4Mm!RxSa=`4u5JOG8*<{!u> za#&5(7J8WhZ>7C1XUUi%C?J@a zpLD8t!vXE1?`+=j@SK1~P7X(i4}QPpLH5M~1|Gw`VBNv#0Zv6)@{D*V>EO#B-5p3) zg3XR|ttwrMPnlpN6VO!&Gjp$9HeE(`>qKGe(Z@A}YT*gX7-H z8u5T`h8?O9Cus`D10lKv$BXs;XpFx{0dj}1o!~0C-^j-b7zB2eAx7&569;FtGx<1o@NkQVj6aFnlPfQJ^Yd(Jr0(c^4XK#eKaen z8ve#>NwBLkZ()J^N+^;#QR=EhoL3=fY-gQ7*N~aUcEmPu;=Yka0D!rUM+;m>lgf-> zO5}YBy~Gy%h=rI(g1f*iqc#pP{Q6WWKG$0+!Fe>eLRw6tvh!h zsP+w8qa@+$Q_c|Pg{IEXCgRXAUubUP41sm%H@B9LzA(w?O9VNM<`}lmSRJ^NOau-Z z&tib$DNHX7vMk^!6R3^pUi(1#Nf#*T{wN~cuR|z)Z+5*P(2?c@@y;Tm%qe)-U-$p_{K!Kcfv&T*Nz|?YKfs1Q7wI3 zAYN%Ez+m&Ge#{`@<_|ql0 z%zBu0ZE}q}0nSh|fp;ryft;S-!a`WsO`QwnTR43VvD;$n#WHX&d5A_;; zc=L!5036daOOb9by?q0x4`1>TZOBM&uRNT`w5DGa<(aMB))@oPkqvyL!^n;Vc?u6rf&?OL5GjGs&?t}Jmz^nl* zqp0ILn6hf!x@u{G{H5Sn6!`ftvghKgb79n{Q&c;;dyQPse)NC&n@QL)kLg>?w>2nI-?u+|zW8bczFPw#MPbcYo{dT2wlM*LB)~mz`oU+u+=2>g! zsK0dLGYQlGa(uvDb~+!uyNt8O?=9Bmn)_fXS0cH5z{cAm9IoZQ;B_&NN8`?D&B5ro z^`Jziy_%PY)lpK1{Y;xbPiOKb%qk{QRO$bWaGo1q9y-6emGJWyxR5ztf49Lb!yE3C za7GZ_!HX}J>7F-*hoqadG9x!R!{5m(fqSPSI5;chnTj;s@jLV0`BRw|BS;%&Z}xCl zW7PEpO?^^h$jN_q8oS+NF+hP~P#ipPJ9`*bbbNkC=?*XBm98))oTpYtNln65yz}h4 z9oc4n@NSnlUw-5>>+nMro4Gq26IEV0q(H@UOg5wNgiZESA;>Bpf~@lE*o>-!rc2;_ zgHsIjv3CVDyc^XV{(1UUZ+WG9SH{#ymy!XyI zULB+W6*%4o9{+k;Of659M|7kz67r4@sL&~SjnAYGe{E3w zE%v``0khesI`Whv5wkSp7S7aPR9P5hLz0zu@b>8)#u*q&}omtM3Hq2#>(w^29_CG;ZvuM<$}|wZL&j_^VP2Q z@f8Y90F59@^R#LLoCSYzI>6G?j-KLfQ-D}IaW6KzfNEPAkUi5O?^BT3fb3Zvc`^yf z7%`~e77ml}xuFs9t4BEZ?`}p^TEW^=Ald?^UfVR3% zk1GO_1C?Sb_D3R33e$LsTF&!t7RG;%t=|*w19(QqdxG=olZOIlxP%D_#@7oqkKHd7 z@`~pcpvQt+9RRjn&U#-&MDdDbu`}bXf|$!t4FF$1CGqMUH*YoG`&QLh{*0iD6#1Uk z@Er>&Xdh<`IiVbtLgSlty0x3;+duN^j?^`-&#}Plt7}2L+|lX_BO_eB7dYX^5*&&` z4mlWm54*kCyS|#q!CD2>sH{*ZlEVKJ7ix!mryhUzSjMxh1h%-{@V6XV0~{+SY5OL&ogh|-3@k6a#kvA!gR4`>H!@6@RWI?)}}<(IpV!d>6ZD)%#H zCld?$@tq=jt%jAey3n99THJxs52Bv;qR_(t}(_7t@HqmjLncm1-E(|_RX=PT&@LdEeGNuS9 zwl^w9zP-OuPHQ-Rdv%VSUP}Fnl%>YFXv)!hb?-{iEbB{YOn24mkRFL2xh5}-xOrFV zu)yaN5;%*m)=RWT-c&{h@JMv0^TqZ%<^Jx}h5jsC^`LRILQd{M9j0u!1$ZEP`PGpv zjEW_L9YdU7a-AVwY`A_r8$WpA+Ox4BmG%MV9=5&(o9cZku~zlT-AuT}=Y02lfn3^C z6v;?W>J?Y?^6eq`7NB&umS!~Z(A|tFUw4R)@7ph`oT9XxeJZCgE$1hdQ;?S9tYJLi zw46IsPFs}|*M{NIv@D(TqRMHNR_)DZIUlK!``@D6N!o6m1!oD?2lBReB7rK`_O+lz^K2tAaMeF z)c?>aC%sd(ed#8;R+b(?v*{*Tdj#EDxO71RIwttjm|z+V^UZEXmK@)Y+R?Le%8_&= z9=9Ax{qT5Mr+(FsBl@Yb^Yvq;e!SL?rnYq$4cjy}0QiMdlyYQ$Ixbfipd~KAFFsx= zxBN(L<>US73|b~zS5S}sbx$$J_yCwq^LkU=b4Xsi#SYHnJb}DkZXTyioqkHrser4u zOx{&Nm(Ws~UrG3Ol{~eQPKU|aP)XM#bLar-fXqn)=xAit4WNm1qx@k2n7>R897v^Z z%YhbJMqZY|G{wl6$q%=`!?Mis8cxKl9!C2Rpt~p%&pI*TvowKxgs5BD=b}3LSQ;6sQ_9TM8(m0McSO-dYPRY-SjGP~Gd$+(^hpkWiT1a& zYt_WxVkgWw^Wh=B{K(x4oYfB({)cnrmGY^v)WS*fySZNiaVWMY>J!E4@F9MMu`0kx z@*G9r+loM*5#;jY={T|ICAsf-%H!vc$J3F8i(Wzp1{)lZ^4Y87Gx-J!D;Ua0o+|KZEy1i4!(GX7Kipf!dgWJ$<wo5J= zPucfgMKuDtPUQG)gnm8iS(yJ7lfN^f}(!oMCe{fQy5U~zkebX z4AbP$6^*FIh6qrIMOMZ&vFs6ib}-c=?5S9sr+O*-oka87d4%obh=x~GzvU!qZHk6_ z<(^aN%ji!Iy4`GABYW}MEfBy60?-paqjqw}=~S3?9WyWV16C+<{p*x3?>ikgyswel zPN#bYD?!C|Dl@f46it@|FC;xSd&&JkkFkY5)X3M)py_nC+%Sm>JALlXU^sN=&0zS( z2o}id3o1-0EGz@=Z4Riu9g+gP=dgg7`GFCiH5tf3MEhP1=uMt!P4gH3l-5-U2E_b~ zro-FQnwAsJq&zuy5*5Y2T-c12_`Vt0wThMQiEi9mRCiPEYLbaRFhce^mFc=!fP}>& zd$Y=P-56xz4{(sZO=SkPeLyA-O5iUCy{2-pCeZ$@`nrU@TV?XxkI@4tf-z*jr7}}3 z!H&U8P? zjVoM;`f(7JIedQtL@M{jVBdx#X^5$^u-HuzsE7h0(mpH~$zG#iu+vc^r%a|HZSGZR zmqDRSBT_FPnM^U-Am5lwrLjj>Xlk&Lypata$B11oVH2k#kICY*sWPsc>2&HK3(ld*`EE)2U+G|K-jX++L&pwZ zrN#uIapj;`RHVc3M#Rwt%%Eh5IZ?p!vc@PAo4LOnc{Dh+ z-c4Z{orKul2%^D*XkmIXLJv7-KawxkpGy&adCBhZJhzBZKJ0YktbFK#!`+Rllvz{h zR(M%!rqTeh_Cfi*!xuW>xRAfQ9W`uO{3mLwJPC$2+YDA zQUt)=Jo(Nvngrzi&!<6*e9rmU<6QqBSN^0%b~u|VLc9Jy#N+WlP~XrlX4n5oz9!Kl z^6TkT7+BBa9@!(aFQD%3og^Lalt*1a!*e!mL<@EodpkcNuQy*nZM*%*y|#Nmx@s}Z z`&DX<0&rKaHeq4&gnZ=!`i!>74b^aPx6~h~MhFwh!kL_ur_ZFhc=WoEy2>AB(jfgl ziG!WjUr62EZ*It46GYFxo1Wl);z~2VU8G(SuCx4+#XO@Jp?t%>rk*MK*03A+1$5@m zBKxRyPa>hW`(Ri0Jf%{+Nua)8;x2=HY8ix$WDZHGWsqkopxs;K>RD7oFU!rqH}WNx zHdI$9~;DwtC#aS@j z2|4y6I;CsUn0Pip?NTSw&LjZ%3Az0unq2rMw++R@l4vEXR|J3a6XesIKGE}KIY@Q`=Rp#`3e!ycRZ9*8{sV(J7fzLFo$$vZEm@?Q6H zo9g_Ds6x~JNILcm^vI3Nx!4a~OcR-pPDfvedFx~$;4dz9WXbL1q*n$ zymKy9!a;jyE)^Hvxe3+mj+g;DR@-}(tT%8LR^>VKXk@|q#Sg z=!Hjl(06jI5hsi{NV*iuk;i1aOX=vM^_xLvYR-%yC<)Tajm*f$a^9sBE!z1z0P8QM zlAMPaFK?`J^ztFZDK4d=zF$Aa40%HR-eTDA@k``&O_-Iox34OPrs7iYl!x2Op_fsY zyq2rV`Ik}ejz2OV`44>Y&QQ6rT{R7X;-=5Wl^*tsdN*-$? zAIe_y;W()a%J^elf6>}mZr}tKj#%qTyk*Rs#;=lUTZ7VJK$Sz`f!03GjJOT=l=^#@m6}TeZdLv1MoFG z{sgkE2Dr?0Vfp+Pg`i1Z4OZ#>a>bvhL>#J@(LYh!llLpM`dYQpcr{i&vkGg6_=Id> zb5sc{3mis-IZ(hj32FHz#HgrJXIQvzt-b_@_W26Lekrg06OHeYG`lhXBL1}Cc5nz* zU0tl5j9{Z^;%A>EKX`UVNJR-UI3QSj@%Ac;F%e=Cr zAC@ z1)3~&_2t`F(HQzf#^N-geG1x-9XYNr8o}y~^13*6=zg2(j9*fR&$fbV{)O_NJtL^5bj0jT;qVTYF>v%)V0u&BBk36dR?>VQF`IA;kZW;d}C1Ei6XhFS;DhwZXTzSfY5|*PeP{4M@ zQY|w%in4<;i{H88L0Gp1R1rSNpf-)c&drC?s=;RxKU*Nj)KLHUIxfM~fvPY`D#fj@ ze|`?Wg$Q3F6dw>3fTLNyw6!N_6F=d$|+GWOgn#dqDeEY zR68i++tX{M%w96>KMs;Rh1ruN-crkuTmE@;*){uXfw`#0XR>;~$|=~Hi@neYHr&~FQGQT3Id z-$*$Dy^lHt+-+Z>Z(Z{a)Thi)JJbF~J=qR!Rc}A)e9iSeoQV8WHvEWa=~*{nWN(u3 zoA4Dm^{A4=mQpF2RBhERrNzvu*v%>adIu-v9XI0u4HnTa+zf{Y&;3Yet4NGy&ypu) z`(@NCxn&nrn~mTpj(LG*98P{zp0kYF3jkiejLyo_tb-V1Q;&rRzkD#dUoXRoZS|9~ zt3&vkn~J^7VAeQvVX`3}|9`NtbQ{9$RN0e{M*+zR%c<1I(It8IHB?&gZkjwjpZ2~_ zKDC^VjO>36yy6SCkoHosdbj*_IknH<^=5kRqPJDb2%ROjoDY`Tx6mKrk8(xqJbuSG z!FOEk*sijG%HGUxk#D&u3aZ#+F(Nl;NIvj}qu~}t^F6nM!0wN|vq>GC$uS}~rs@PM zad;nxQdONmF0ht1suVd;BL}ab4##ZX%ajeY_wdtO@q#FH(+G7kM3w%mE5|Y1?*X9m zIOhh!`QWD;PgpMRSV05hYk1D!C4s173;O9QPU5hGjnfsC;s7V{nn*0dXf(iO->-8( zDAtk}&9V1B@YVzVxqQ4%RfVMl89!+p;EEJdSsg=9# zqW1FiTFO1*-n81zPOSOiKVsjg;g|$ z(~;Hm96xujrZbq3^4mzZQCMP#3#f5d4ZeLFogY*A7-k%BRc%@(Bn`*3!XZ(`grU=u zZ>P@LE^Cjq`t>ULXdQJb zQP>$b;86`H9)0+u)3^u$zi|fY9IT^$WlAM++SpT01i^mgzdJexgP0jnD55wB0`LiU zQr}UE3Z?-Pr{2*eXen+Rb4QP7t>rUMuGG3}11Vht!M~dZv$R&I)9Zc5CSBcoT$atz$z|4F*OU>sf?$_b{_K(2G;bWtzqK z#wQxdgtArW;=_&vC{@e`aA#{hwaR;}SJevZL(^zTRT_=EX8l2Q}E7GKp3Os=~Y+c`t1lqIkw; zL zQy-vhP3>=w2SFWJbi#wwKijjvU;-@Y(g*3}ZfZzDDd@uV48oCHPa@=*4{HW}y9v9EH$F^fMKr59TBPyFpC5)h4HU(XPy-(q zcmz|)>RQ=-J!)cZI%YkUCCT)q$gEjUYneekH_*rwxiAGv!j${doLs+4Zrui2t{o!H zD84J8^|ujMJx#R;GNNP)j6&~S7UL>3J3j6_#;>X%2euJ&nFpu7D~pjs8;u4d3{BwC zP#ENMkJ9Ltx>>N1-ua)q7u`gQM=7!tEgazEMX6!WpW`|`(X>GV9jrPiX0*|yq0!^J_e*CsdS(_R5+p`@PPu?nqrKE zJ4R{M-H}o<3t{QKnXXML7zPAcFK(vsNyf9~a@C;Q6X|N;_vA`#XsW9(Or56&uoOxS z+B``+bJF`jk>&E6C#ed8-Rmjz69OH7s%3$G@+mFQQ=e{8pxGu`_``OM_0JRNy-#CW zNHY@57>fw*JMDDwLmM@{1*Z(nhCTg@0z0JwMw9MBjD@)XId8M5zn?L(7)fk zf|@j9M9ljp3);GZ*9<0`(y)chj1?3LdVc*`N|fW z#WTd<=P8EV$e22_ z22eZM5R$N3+9p=ZpI)Hiv(y%FOkHN->!`I12>?iML~Zqk>}^KmHr)Wrbfk{o+3j`# z5X*4^UrGXM^RiURxm#(>#N{eTq%<5+F<9-$gQ^uo$S_t)>6lCsMI3q74l0eWW0=rHzDB9WZ8a|_ z6(bk%o7x^xZ&)sJ2D_JR;dUBhiqwgu#AjO)hW#n8oDo~&eTRn>$_o`rrZe&|XW$Pm z=G1>?sK40BcjiG73uvVeR_E%7bZ_H-*XGmsb-|U zhW|*wPTBcYY_L8dPk5D%?C=z~iPkw@;%?t1PdGjyZ+{gFI8xiDAXx!_m50I)I!1_6_ z(`5HJy-m0R23mMy@WWlysaIM@85?``+g118h5@p3s_o4xRrdwDjQzMiyXlAmx83&m z@F-{7o$Rfuc(?r1ZW<<@s!1JP>n4BMP36g(IPlzb+k@VqeK>Yh^d@#OpO6)AVjJ&C zdE=WnYPz-l*EgxVDZc(zcK-|YEco(UZh~*>BW#K*28iTh=x0P;S8=lXg@2(MqOCIH z9lAEK=NqQ-#rhTR&^!u!s#CJ{Um=bQSx^l=GSrYH%e>v*foAApD@^<`1ch`0`sh{zKejdR_kELu7-Y zO|sRguunh4Da;)*XCF<-d0U|~tLX*>{s;U$L#+s$A=&tL-@ zjaNDt<5u@B?c^79%@F=j1P*6GeqWIjKf)2fA8P8Ceni)a=s_Ml=-4Y<4MvFUf2$my z&Ivuhe*rL0PWY65qgVKRTg|9n7(jhM#J&<+jR;6V()rEa&ZC~Z>)^AmXr75LAL0)T z*}r}x3qGel#c8|uphTk#P>Dw z$-mKQf$y1p*+O3RB|=nvz657c^7NNDfBcI4=1UsY?n!O*+8Xwz6+BNI2~)Yde)Lx~ zQ;5CS)Ia_WY;vo0H4U(aCdWesc1({tKz|g!d?TMbK>0lz6P(o&Z((m--kRT~^EL}R zR+_hP^;5j9Y{FIz=5a>e^&P@ZX#LmksN{&IEX+zVjC&3~@s>t9jk0bWRwq-xr~EW# zW`UV$Rpl+;(|B&2DRSrg@t%|e23VlHy9M!x?qx{X!+iXW** znyR-_HW%l9Q#N-rtNMQi%a!N;oBA=z|H6#RKmJ7h(+$_SpOoQJ3{+)l)+@ct{F!2- z7wQv8up=BCU*x7prLNW~KFMY;J^+1#@_Bnj?T`t>Li6AqE5W|-XSzPJ{|7*gK)lKd zT%*qN1`knj!2!;#o`Ea(!{chE3{C-~S+o9-?kj5?4cc_+v3v=y-f2))%rj z|B4EBvAu_%$-n*hsmkqaFa9kp7c}fP>>q#Othcn-+CP5J)uZ-y7qI@tLv$YQoT=m+ zviGm(#!oeJ!mo5??oV8Yzq$ZfwAU(#x8%paQfIe>H}FuhS(nro{zfAx|KD7xkY&KH zKCT5$%-9n7cg-#HgjH~;aUsm!Z#Xs*IaKr5Lt+?-MOW8G z6*|O2Y!|uN?kzkBZxc{JmL@BEEN=u=|3^PA^oxJjNGpJ?T0D;nh?DTFuM3D<1A#q? zmbYY8jyNi=iAdqeN#g#52}5g!Rnja^GV|=hX0%5CYe*MooX5az=x|7zll0$1He;35 z(SoqqpDG@FQd7SmB4&u9HJT;K9y7>R4I%?=#Ti9S*_1XY@`|=%Xke2I-OyItE*}40 zo|7wvioM^;J90&opU>xt^M`nvv6?k9DP^0LzLeN|`I*f9NU=C(?{{=pfdAFE4K| z`ihO;%XRHV|K5*tONceFm%S7_99hsX7ZGVB;i9OQ^Uk+2k}rmd&uirPd@)gcQ6tyn zi=)K;8u_<;F^C)KQXtBi6(<#lf%Ja;jRgV+oxWlo{~p$^MPhW`;mPkT*A|IRjQ-Uk z(OG<5BY!ADAJA;qV$qTNG^!ZQqED9;i{9M)y~W~4H2-d~z|}KlA!FbOCJ${R*~p^9 z@DDgQGkK)}{yx%KUU~#ioXTz<_(6{AD2@=Yp9?#R9ZA!%SJ@~}Z#~non9V|RP(9PJ zsZp+HIu13;^-RZBHX%t23ze{x+k3p+c1lS<<)mG1FL!N{+*cyn2e)6tRe!EgsX{NM z)Vh)3uJ2=sF%(#%D!)-*6BTDt!6ub*ErZ2Yc&pB-|5r>b7ZutHgLP}*B4Lfj16zpX zc9BZ5jrQ4J!`Fa?QZ`y{=^{!jZKRWRlSy6oP|b?2qMbNYBinZsbCQ<&rmkX*pUqkS zp4zOP-BX*j$9jrj*i}*2n1J5k3CfFR%IfzH5JM?`IJ05>*H)|5e+}6J&iG-BW|}QY z8ldJTx0xktX=+f@=$=J9>YhnY)r&XJI($x}8AX$2Vf|-++6cL!@(!vzm-FC|H?=jv z&bHpmGOS1v?C=#0tGVrOfv^^u^wealB26o0JY;)1wJ9w-WPAFzrcth^tIHeZdV0O6 zQLg7c98|M_YXim$5vMCSrh~s}h7EJMbcpDZ|0Oe%SA}rzP1`tzU9nb?ztz;gIz)U- z;+qw6!!Yq!@Q^a#bqD0E;bM%q`+$6ExEK|B4YRuTK>cuA zyx|iM9*}KEiAsKs9VOlle9cJVEtMT3P9TvuATJ*ctGnQUTsKljVVautPqP1 z$cv5x?u(8UnPQE==IJJ=rOoVmi1<^jD` z|HrYotmFC}UU_Cxlv3j5OXhjv#a7;v%unk7db}79ozVj#Y;ql_#z*;wP(*%(hSnMX&O%UVrck#*<>uH=^)35Hu1erTg zOlN2Hs)?dXd94j6ib@);3HG|wT-6wtM@kD&^%W{=sk1EV+TqxYaL#nW?Dm;S!&=2(3n=_yeFlH$pwm`T% z=~S^_>|HE}ohAm+-{j?|iGi&aFK7Vs{5X|_dk4E^!)fA=C>nga7}e^%M5CgcP8atH z{EdnMXNU;^x$X?ng}#*Q&k*hCE4l3qQ8sErBV5cEBZSDp`fHg}J*?yed>q2E&?5w` zRS&kgY6m4Z?5|eH{*y$Ew#m~bL1*?YR(4qSKU1tuCib2wX5hO#N1r7Yn8WKByXXI6 z70;99(nfh<q{^4SRm^Op2Y5u*y+gj<|eL7q27^bqe|E0<0YCH&klMHKS$ z)wE~3_f!u<_?YULqL`@;%m5_+Tv6Og6{)|*RLa#xoGZ>2d%lo&pDSL@T6{A|j|%&` zo8@KaiBrU$o8{~0iGlssus|ofgo8VXfBNk`3_Yp{U%9gQXty6XHQh{XMubB0j%lJm z9B|}mQ$;uNog)`a743q{6)KF{?NddUtbNz%20y+|zB(22{?pfUgS|T8?gK1ohP?-u z@LFsefb*t_qs7u2Xc@-BDCu zrDJ)1XSz79cS3?%{{Eo49(a&@h;M5{`qVdbaqfcu;wk>p7JouEXn!pyULay4zDZ)n z=lxWBr6^3b7lgaZOI(cb1;&O3Ri3e(;h_Nish!}sy^E6=XV;FMud=Zrs{wM~-x1qgx zDETXXa`D6eP)Yr8BN_M!yJ@s*fa?n5`BP@jord>6W=4!N@O&0OJVIVed1$FO(m2MY z{$=BRiZyrIoJ*$7I&YdiZ{D;EFPb-PsyyQg(Z4F&7cs5@zzqB_;Ng&NLs>Wcc>g?+ zkWXA824ubrQXm{g1uk=lqSoAR?;iuUm0hnCgZ(C0F;|{{rRbLB(RNObTy>@BCEveN z6i3smcnsk@M!}4^Qf~N@uv<5iYMFC|h{oFiV(z?)FF9|Xb=34J^CnK4d(rG!bEjET z=2j-H+en12BsH|3>&Mw;2|rJ+EL%0ozZPb<$sx@THC7io03t5JkZZ@h}} z-T^-y@#FpDZj>laj8vRJ^4Jjm{1l!)g0{=>9E>sNo~NgoRxjNt_)8@9r>_*X-DKwTB9X}q?t!14 za@q5ucf0<0_0Zs3&iws+V}R`Sk(e~J23Z?ePOF#%iQ%>hD64Nq?2kZxfZRy;jZu!&xnAWY$Y!aJ2OFh>`^!Bd&Q8 z()^EAlO=SxJpU#9$>FEuikCzuJf3_>6j7CY`z0}n-jYXb7prj5i_~)f3>tr&0wtVAd zu=^Zjr;{;77QP}Lj5lClcKyG;&I8Vh>iXl%J1A2EL+>3$-cE&u zZS1m3XQkLHQ8>{UYmCt-Y8q%_iAk^<6Fcwu+lNT>&*$@H?zwI5 z+(3tk7G0$NzuA`Q+2HgLf_umY?BP2gH^C)fw}gO|Z^U@aFc=I678nV}g7IK-rm(5S3?$}&h2UJ^gO#8GYyvyLmEan19k>zP2JQrV zz^}jq;9>9tcozH>ybN9i?|{$1F(AqdTeJr4K^pWb*YRpSBnE&Xzyf1|3#NdXU@lk$ zG9UsK<$E7_NtjYoJ%l^Kqu{ULb?`ap>=m|{0DRC0wt?%xJ>XIB0(cvI4!Zb-EmYuv zm7o!913SU(U>|r6ya|qgb^-bag9A*Rg~SrD9$XBPPOiuCZty#B0DJ^GgwRZ2fLWjt zBz2o`yc*mK_Jf1qGf))a0>C0r1vY>^;IH5VaC!`56JzSeBQX=42iAaVz)!$~;BkmYCKy2_$PkEw_4G%#vbW__mlN z-OM+>EpF@n5Sk_t(mv1<8_-tXWQD!T*(~n^AWCp4#zkY<-5NJ4YI@Ghd&Zy zn)2EA=ChlxxgE}W@+b=+C(%^C{@7-@wq;>9#%td^%Dmj+6gg?1oHk$HF()^j$mn!R zg*wOsPHM0Na_S^`E#wg=$uZ=_8Rbtf9dhwWawF0w4uJ`c|o42-*ii?g5g>q*rV1bVQhwS0g=YRR2*`-kM-VR9{lk5C4!?eiD0!;vK@6u56%wV>`Iohrg2eiX)m$dcE2zb+-Cv3kdk-GLgOftj2`yWR z&A|7RSn)I-b5x8jlnp~uVk0tx&@mk^rP-Df z*tQ|7au9@p>9*r7ooE=B9~0@69h<6Z>Z%ud4*Ig)_I%Y*F``?QSF2bn3_q}B!#AQh zmi-_$+zx#BYf+q1LrXP6Q}GN$;(swrwi1 zQOM(t3bl6_1r=3ZB?jnQRv0R>X9kh!ICkh3&EV&ci?eo3rc^qRWh2tH$hSgU*236L zH5W1pO<7lbT@BSp)_f(_-Sh&!`luMP>n-Y;mQ7iXHAfG%P>xJbcRSC?cFUtAIz6rF zvaAQ86-A*HMsetNS(t6gC4zdTO(hDfe8cuU z+3k5!X(~WLB%>qU9&yf{KDa@zuE1GU=ma6-v@Al=p3h0uwqZ(KSEpjX@b!4h;zf1Z4 zA=F)14v#x7Os+beTJv?osT23_Pw7;I;i`6MnOIgwHWjyj($Dr{uX;UBZ>pwg8HVX- za)=EUxy51ToSBxK88Cf5%d-jJVd+k0I#wMO8W0+>W`+*5|BQ9~d?%VY)H8hD^lZ)c zR9W}*uz5aBS#c~&(-hNCWX+N_M;^fU4WaERJ5-H8Q+3M;ZO2ngcVI5x!9F%4T&tlt zHg=ACCa*81(jpl%8p%dzS&HvDsym1eolE^vvFZn2jGjWv*Wyrf2lK}UjY`>$5ks|g z%?dPMx7{HPJgtzX4f`K+k=u>mG0w9xtPG|x+-WereK?fm!L&>o#>cv07O;$_Z(%|m zSRBKO-QnjYv(SsHQ&iIDJNhd5uE|U}wxwcsnyO*>5mw(F!S|5=5Sf$8|G$V{WSNSQIw4u#IF)PW4BFBpy+my}bC2$l=lWkd1brZ^E zD~=+o{AgQ}6O#q|tH*{OB>UNNwcEHyTiP(xi@g8~t^0-#Q>WP9-;(f^JL;dd zbwAY1*o%|%Yntxb&81E4(bO>v)wT@TgdGl`3(m<+eMb&G_z>*SC=5;6E&Wz$Aj1&* zrssIRYzB@y^5oJ$cbwQjHnx@)MygxZTzXRnoE8Jo0^2nFNOoeya7SItccm#a6PFO{ zb`)y5rDF%F&C{hQj%quysu?Owo1+=JGP)|+R8F#~2CVSvSNL@`YpY92>b$ijC3C&C z^F6;Z;$_{bH0>LnVMacjM;t_$Q+G_nW@j2X)byYnzN2Dd{76;*pOcN{`-(_Qn}KS} zc5E221s|&U?l_-2t)f9(TS-MjV-lnA~SkCxtcPZP{ zqu8-bC4dRjZO5I!9aquR6y`K=qR3JsQ&V8S-LrY!8MG-K#g>vxoUS>c8NyAmpN*Bm+tUwWb4=!x-s9fn?U8sA?ixnk;zCyngO9_2<3cuKe zhNlB1a4_%~R>X?5z;~xs@O>q)0NYE@DzYpiR83pAy;z3=xYPLF5?Y$p)!2yxKk{`L zGf$V@>8o?CmW`m|?r`mT;%z-uK@?)RGY&}50L4~0Gm+*bCb}-=AV>*t$~g3N)iGmt zCig0)X(?Dy)rzdp((S-P%ALhO?n)z5PGmR%o=@AJ4|R=ncXl>E9b*VSCN#uWj-ie2 zoP}K28dhnNOhZzMx*%YYV?ER{THBq=cXh}3wd`eEfd?(o;J4j*jofDnE#T@NRG79s z$IzU>GCU3DF0|bFTe+=5L-}5rCZ<#vd(%=K!;_(^n&mFwHVV}Z*JCx*<1mKO8L@3R zd9C`6bz67?e=(3Y6)Q?*Mg5vg6&^KI*25pER%|;ulrxD1oKSZceG}HD|0k?ld`q?$ zt{+HUQjzW@MNY2hycQVT3RXM5M4^Y+T7em=;={eDil5f0u&+}e)m3_o2U~;92az^mQ0YJCNv4>k&{A5h;Ye(lSHhIg>_fw@sM-bTb6A`psVU2*Gq z$1oa{(qr6T7`7jUvTA79R%`g7VYDzU+lVVvOp>i4~=c z=GSs|!{)78Zqk;f;ORBo8z5S?4OIz3cMF#qgojI9XV3&Z?)Z&CHh%4K$e@|{HO6R@ z=F@g*Q4-adJhqk!`5z_~(GKY(Wfm7mIc>g>@3m+t{>DHDn|$eFuCWpRT*A-Uv`V^^ zOC4HHmr38)>f+9b*Kk{=V-5}3fKh-$xt#l#Ql)eSUs+0L)0NUJezBCwPInC3vTa31 zBx$I!0TZrYC6(}1Wl$A&Bt~>~UTTCS?c|F`(pbVEBgzw{&jOOJ6=xMnk@<0;6%Z!s1ahSo$F!8b!6zbzDB0R?_uS1s@)T z`-yI2s1KfjXX8;`JO)F)Av@>v<8RIEjk%c>H%ZBk-p5~0qz*iG9ID^UJ4a9{U!J6I z$)*2}w76ADT&~W2$HQdY#`1WY-NlAC)lEgQbuGE+l}+3DiInxg{OWkLznu?{rv|(Z zarp#t@do~TCScuu#vRV4M!dTA%d=?@-G#)lv*~iK9ZUs$_C%cN=Mqax$m02vAl%)2 zDHlP&tNcHlMCH;Ro;jIfyrc8i$>BckHH8}Je*9P~tMT_uq0aORX;4<`AlEg(lGl3G z6~Qn0&=ivKTBd$eF{oej?x{4Cej^RZHG6F;70_Pkn{CsP_94>FcIix`PV_)tTAoM` zVqLRkyQWbJpF{j?8cmXZ#~)9lQhErzj9x&e^MvWt8SjX{c{+6b%IUa(N0Rg;>Cx=G zrV+hZh(z>QmsLD`P~6`)PhQgu)P0P1&Y-E%ANZ9SREl3xVes8qo=IJ$KPBnDynH5V z?a!ruh%`MejpY00;k=K{q=EDVHeK~BTE>%S(HZz9QROVUke-ruw(>LESLRdZ+)UbFT792zVBEb+J|8i>0D+0;EmF*5GSO;2^7=U^_4 zmj1%G&ZTql%EzzgQi=2e51&Uv@N&yV^JpPHmf16p&X)ehpU$IE(o3w&r?JxCxo$pf zm0nIJ&g8Qe;8G57*#cbfKlq{rG#@YYdSwAt>z`b(5RDJ=+=XcTDqp=2X5=+~av_~3 zy`G)M6ds(Ii4*p9In)qYxc}n%MX2=#-@OQJ-sJZeks;oaxc_1@>1{kW{9qBLZ~bCY z#XAz;vzX?Jce6S-S-h9iy|gVmq2b%Gun1E1D0JU%R?26VKq>z%@vBQHBR-IL>^Y=~ z53|Q&=5tW^QM2$&A{;`^bErXl+^oip=OXiPvv6l3e9|nuk_ew>1x5Tv;^L)rgZND1 zN0-tJ@n4BMokw%T=Mwwp(Ms`!#Lt~aHXZrarv5UwsoNZtmcSjGGOu1m3Kz~meA_ug zh2ogRXJ%*_eI>2pr^*r6K9`{Z^tDu-lgi3zkT@>!ljSsp_moq2D9{t-6u@d9JcnY0 zAC3cOj@v{8$WG*LOR^(w<74f%a}OVzY6oBK)4Dbnhz%Hkqj7(qCi0Sqy7HkA_y21G zDr$3~*nDc?`vLWAbCGEJcK#XM7*bK|i*xn4pHBn%x5XuBoB1ytx|YWnhe5a=EbZ)UPYgj^W>~z?+9Lk5>vOoA-y9is>q$XNBW2 z^~PUr=)D|9YbS47PLt>dqKvmJr#}29{%g8Mw8En?IK*p3o3JvM+$9QX>!RAII?QB? zc8k;Ex+r4NDy&QrKNPK!?}^j|*NN86?(}+55LV!$^!g|htZ1lS^&`;{LdCjFRfIk( zmRH{(iYo9mle%iJ@Y zQW?IPvL+YT3|2EOm^K0tX z{6w@ujbLqfyEv`huUqxgtc-!&fmw>Gf~wk|iT0TczCRGGsH<+Myi>GEq}rf%{avC} zl3lz0=h;NqPib1&WLM)NZd&z#Fv)6_Q4ipkg& zy2gL5!sX9iL`D4QYP5NdU#-Wz_L0>H@czvG>M;1{@n`c8pYpCcl>deAs3R3t{6HO| zv=?|tJ&lJIudK&ByvVoKQyHu{x|l&P36Ec|$J3JuYiI_H`OY;I!kFVQk;@uzg$I(f z%QrVrUs&{PdQS~?^GYsbWh3>ae~K}=3%IorF5@5{T!NYEoy=72TErwh*P{Nb zx!mmhA-6Xi048)oZc`sjYP_Cgc+$U4OtemKh*I{p(wY3qI(XqX6ID>fTY?9!r{VOr zn8LTLM~p{J@T!-sr?hxS@XhP7$KMqrlcLW2^?KOD_XH2xKoiCLf@?NlzW*(z=NkTf z1C5{$a@`E%5gTcM_)zeIjpT`s1n=8OE5sqe#hYl9_*k&FiAIaVf^XSG4t*jf<}TT; z!}CzFnfi-QF|N&IiT?;*wwcD$XTnXYbma#(<96o1xdvTXY{5xC7kaMw)Gdhhz7V`} z3yq>9!r@h05b^KdLcQrrvE;<{m5HMw@!lpK6XTQI0eoW4RUhKHw zq32TstH1Mn8VRfa?D;eov3kF)aPZqXvlT17BRQ1uds{K*7v$2`Hl#1)DcjKIBEDc7 rH2GqFa2q`DCH%!UItLN@yzNv@mr(^juo151_;wiH%lG!%L9zRP4{?B& diff --git a/tests/fixtures/wasm/src/echo-provider/Cargo.lock b/tests/fixtures/wasm/src/echo-provider/Cargo.lock index ac5e60f..ecda1db 100644 --- a/tests/fixtures/wasm/src/echo-provider/Cargo.lock +++ b/tests/fixtures/wasm/src/echo-provider/Cargo.lock @@ -12,6 +12,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" name = "amplifier-guest" version = "0.1.0" dependencies = [ + "base64", "prost", "serde", "serde_json", @@ -36,6 +37,12 @@ dependencies = [ "topological-sort", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "2.11.0" diff --git a/tests/fixtures/wasm/src/echo-provider/src/lib.rs b/tests/fixtures/wasm/src/echo-provider/src/lib.rs index f4c46cf..933d426 100644 --- a/tests/fixtures/wasm/src/echo-provider/src/lib.rs +++ b/tests/fixtures/wasm/src/echo-provider/src/lib.rs @@ -1,7 +1,7 @@ #[allow(warnings)] mod bindings; -use amplifier_guest::{ChatResponse, ModelInfo, Provider, ProviderInfo}; +use amplifier_guest::{ChatResponse, ModelInfo, Provider, ProviderInfo, Usage}; use serde_json::Value; use std::collections::HashMap; @@ -42,6 +42,15 @@ impl Provider for EchoProvider { "text": "Echo response from WASM provider" })], tool_calls: None, + usage: Some(Usage { + input_tokens: 10, + output_tokens: 5, + total_tokens: 15, + reasoning_tokens: None, + cache_read_tokens: None, + cache_write_tokens: None, + cost_usd: Some("0.000000000123456789".to_string()), + }), finish_reason: Some("stop".to_string()), extra: HashMap::new(), }) diff --git a/tests/test_pyo3_proto_bridge.py b/tests/test_pyo3_proto_bridge.py index d960043..9eee82b 100644 --- a/tests/test_pyo3_proto_bridge.py +++ b/tests/test_pyo3_proto_bridge.py @@ -10,6 +10,7 @@ """ import json +from decimal import Decimal import pytest @@ -192,6 +193,54 @@ def test_roundtrip_request_response(self): assert isinstance(response_proto_bytes, bytes), "Must return bytes" assert len(response_proto_bytes) > 0, "Proto bytes must be non-empty" + @pytest.mark.parametrize( + ("cost_usd", "expected_cost_usd", "is_present"), + [ + (Decimal("0.000000000123456789"), "1.23456789E-10", True), + (Decimal("0.123456789123456789"), "0.123456789123456789", True), + (None, None, False), + (Decimal("0"), "0", True), + ], + ) + def test_usage_cost_usd_preserves_decimal_text_and_presence( + self, cost_usd, expected_cost_usd, is_present + ): + """Python Decimal cost crosses PyO3/protobuf as exact optional quoted text.""" + from amplifier_core._engine import json_to_proto_chat_response + from amplifier_core._grpc_gen import amplifier_module_pb2 as pb2 + from amplifier_core.message_models import ChatResponse, Usage + + response = ChatResponse( + content=[{"type": "text", "text": "cost response"}], + usage=Usage( + input_tokens=10, + output_tokens=5, + total_tokens=15, + cost_usd=cost_usd, + ), + ) + serialized = response.model_dump() + assert serialized["usage"]["cost_usd"] == expected_cost_usd + proto_bytes = json_to_proto_chat_response(json.dumps(serialized)) + proto = pb2.ChatResponse() + proto.ParseFromString(proto_bytes) + + assert proto.usage.HasField("cost_usd") is is_present + if is_present: + assert proto.usage.cost_usd == expected_cost_usd + + restored = Usage.model_validate( + { + "input_tokens": proto.usage.prompt_tokens, + "output_tokens": proto.usage.completion_tokens, + "total_tokens": proto.usage.total_tokens, + "cost_usd": proto.usage.cost_usd if is_present else None, + } + ) + assert restored.cost_usd == cost_usd + if is_present: + assert isinstance(proto.usage.cost_usd, str) + class TestToolResultContentBridge: """The private ToolResult bridge normalizes content without input echoing.""" From af1ab64a4d56bcc1f2114aa6983cfbf996180691 Mon Sep 17 00:00:00 2001 From: dluc Date: Sat, 12 Sep 2026 07:58:44 -0700 Subject: [PATCH 14/27] fix(node): await async hook handler results Related to #61. Exercise Promise-returning hook handlers in a child process and preserve generated Node binding declarations. Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- bindings/node/__tests__/hooks.test.ts | 104 ++++++++++++++++++++++++++ bindings/node/index.d.ts | 88 +++++++++++----------- bindings/node/index.js | 14 ++-- bindings/node/src/hooks.rs | 26 +++++-- 4 files changed, 174 insertions(+), 58 deletions(-) diff --git a/bindings/node/__tests__/hooks.test.ts b/bindings/node/__tests__/hooks.test.ts index 8ebd9eb..be3d03f 100644 --- a/bindings/node/__tests__/hooks.test.ts +++ b/bindings/node/__tests__/hooks.test.ts @@ -1,6 +1,81 @@ import { describe, it, expect } from 'vitest' +import { spawn } from 'node:child_process' +import { fileURLToPath } from 'node:url' import { JsHookRegistry, HookAction } from '../index.js' +const compiledIndexPath = fileURLToPath(new URL('../index.js', import.meta.url)) + +function runAsyncContinueChild(): Promise<{ + code: number | null + signal: string | null + stdout: string + stderr: string +}> { + const childSource = ` + const { pathToFileURL } = await import('node:url') + const { default: binding } = await import(pathToFileURL(process.argv[1]).href) + const { JsHookRegistry, HookAction } = binding + const registry = new JsHookRegistry() + let handlerCalled = false + let receivedEvent = '' + + registry.register('tool:pre', async (event, _data) => { + handlerCalled = true + receivedEvent = event + await new Promise(resolve => setImmediate(resolve)) + return JSON.stringify({ action: 'continue' }) + }, 10, 'async-hook') + + const result = await registry.emit('tool:pre', '{"tool":"grep"}') + if (!handlerCalled || receivedEvent !== 'tool:pre') { + throw new Error('async handler did not receive the expected event') + } + console.log('afterEmit') + if (result.action !== HookAction.Continue) { + throw new Error('async handler did not continue') + } + console.log('resultContinue') + process.exit(0) + ` + + return new Promise((resolve, reject) => { + const child = spawn( + process.execPath, + ['--input-type=module', '--eval', childSource, compiledIndexPath], + { stdio: ['ignore', 'pipe', 'pipe'] } + ) + let stdout = '' + let stderr = '' + let settled = false + const timeout = setTimeout(() => { + settled = true + child.kill('SIGKILL') + reject(new Error(`async hook child timed out: ${stderr}`)) + }, 10_000) + + child.stdout.on('data', chunk => { + stdout += chunk + }) + child.stderr.on('data', chunk => { + stderr += chunk + }) + child.once('error', error => { + if (!settled) { + settled = true + clearTimeout(timeout) + reject(error) + } + }) + child.once('close', (code, signal) => { + if (!settled) { + settled = true + clearTimeout(timeout) + resolve({ code, signal, stdout, stderr }) + } + }) + }) +} + describe('JsHookRegistry', () => { it('creates empty registry (listHandlers returns empty object)', () => { const registry = new JsHookRegistry() @@ -85,4 +160,33 @@ describe('JsHookRegistry', () => { expect(parsed).toHaveProperty('custom', 'value') expect(parsed).toHaveProperty('tool', 'grep') }) + + it('supports async handlers returning Promise', async () => { + const result = await runAsyncContinueChild() + + expect(result.signal).toBeNull() + expect(result.code).toBe(0) + expect(result.stdout).toContain('afterEmit') + expect(result.stdout).toContain('resultContinue') + }) + + it('async handler returning deny short-circuits pipeline', async () => { + const registry = new JsHookRegistry() + let secondRan = false + + registry.register('tool:pre', async (_event: string, _data: string) => { + await new Promise(resolve => setImmediate(() => resolve())) + return JSON.stringify({ action: 'deny', reason: 'async blocked' }) + }, 10, 'async-deny') + registry.register('tool:pre', (_event: string, _data: string) => { + secondRan = true + return JSON.stringify({ action: 'continue' }) + }, 20, 'after') + + const result = await registry.emit('tool:pre', '{"tool":"rm"}') + + expect(result.action).toBe(HookAction.Deny) + expect(result.reason).toBe('async blocked') + expect(secondRan).toBe(false) + }) }) diff --git a/bindings/node/index.d.ts b/bindings/node/index.d.ts index b06fa5d..40ddbc5 100644 --- a/bindings/node/index.d.ts +++ b/bindings/node/index.d.ts @@ -3,7 +3,6 @@ /* auto-generated by NAPI-RS */ -export declare function hello(): string export const enum HookAction { Continue = 'Continue', Deny = 'Deny', @@ -31,6 +30,17 @@ export const enum UserMessageLevel { Warning = 'Warning', Error = 'Error' } +/** Structured error object returned to JS with a typed `code` property. */ +export interface JsAmplifierError { + code: string + message: string +} +/** + * Converts an error variant name and message into a typed `JsAmplifierError`. + * + * See [`error_code_for_variant`] for the variant → code mapping. + */ +export declare function amplifierErrorToJs(variant: string, message: string): JsAmplifierError export interface JsHookResult { action: HookAction reason?: string @@ -45,24 +55,13 @@ export interface JsHookResult { approvalTimeout?: number approvalDefault?: ApprovalDefault } -/** Structured error object returned to JS with a typed `code` property. */ -export interface JsAmplifierError { - code: string - message: string -} -/** - * Converts an error variant name and message into a typed `JsAmplifierError`. - * - * See [`error_code_for_variant`] for the variant → code mapping. - */ -export declare function amplifierErrorToJs(variant: string, message: string): JsAmplifierError /** Result from resolving a module path. */ export interface JsModuleManifest { /** * How the module is loaded and invoked. * * Valid values (string literal union): - * `"python"` | `"wasm"` | `"grpc"` | `"native"` + * `"python"` | `"wasm"` | `"grpc"` | `"rust"` */ transport: string /** @@ -76,7 +75,7 @@ export interface JsModuleManifest { * Artifact format used to locate or load the module. * * Valid values (string literal union): - * `"wasm"` | `"grpc"` | `"python"` + * `"wasm"` | `"grpc"` | `"python"` | `"rust"` * * - `"wasm"` — `artifactPath` contains the `.wasm` component file path * - `"grpc"` — `endpoint` contains the gRPC service URL @@ -89,6 +88,8 @@ export interface JsModuleManifest { endpoint?: string /** Python package name for import (present when `artifactType` is `"python"`). */ packageName?: string + /** Rust crate name for native linking (present when `artifactType` is `"rust"`). */ + crateName?: string } /** * Resolve a module from a filesystem path. @@ -103,6 +104,7 @@ export declare function resolveModule(path: string): JsModuleManifest * For Python modules: returns an error (TS host can't load Python). */ export declare function loadWasmFromPath(path: string): string +export declare function hello(): string /** * Wraps `amplifier_core::CancellationToken` for Node.js. * @@ -117,35 +119,6 @@ export declare class JsCancellationToken { requestImmediate(): void reset(): void } -/** - * Wraps `amplifier_core::HookRegistry` for Node.js. - * - * Provides register/emit/listHandlers/setDefaultFields — the event backbone - * of the kernel. - */ -export declare class JsHookRegistry { - constructor() - /** - * Register a hook handler for the given event name. - * - * ## Handler signature - * - * The `handler` callback receives two string arguments and must return a - * JSON-serialized `HookResult` (or a `Promise` that resolves to one): - * - * ```ts - * (event: string, dataJson: string) => string | Promise - * ``` - * - * Where the return value is a JSON string matching the `JsHookResult` - * shape, e.g. `'{"action":"Continue"}'`. If the handler returns an - * invalid JSON string, the kernel fails closed and treats it as `Deny`. - */ - register(event: string, handler: (...args: any[]) => any, priority: number, name: string): void - emit(event: string, dataJson: string): Promise - listHandlers(): Record> - setDefaultFields(defaultsJson: string): void -} /** * Wraps `amplifier_core::Coordinator` for Node.js — the central hub holding * module mount points, capabilities, hook registry, cancellation token, and config. @@ -175,6 +148,35 @@ export declare class JsCoordinator { toDict(): Record cleanup(): Promise } +/** + * Wraps `amplifier_core::HookRegistry` for Node.js. + * + * Provides register/emit/listHandlers/setDefaultFields — the event backbone + * of the kernel. + */ +export declare class JsHookRegistry { + constructor() + /** + * Register a hook handler for the given event name. + * + * ## Handler signature + * + * The `handler` callback receives two string arguments and must return a + * JSON-serialized `HookResult` (or a `Promise` that resolves to one): + * + * ```ts + * (event: string, dataJson: string) => string | Promise + * ``` + * + * Where the return value is a JSON string matching the `JsHookResult` + * shape, e.g. `'{"action":"Continue"}'`. If the handler returns an + * invalid JSON string, the kernel fails closed and treats it as `Deny`. + */ + register(event: string, handler: (...args: any[]) => any, priority: number, name: string): void + emit(event: string, dataJson: string): Promise + listHandlers(): Record> + setDefaultFields(defaultsJson: string): void +} /** * Wraps `amplifier_core::Session` for Node.js — the top-level entry point. * diff --git a/bindings/node/index.js b/bindings/node/index.js index a87ad59..31243e8 100644 --- a/bindings/node/index.js +++ b/bindings/node/index.js @@ -310,19 +310,19 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { hello, HookAction, SessionState, ContextInjectionRole, ApprovalDefault, UserMessageLevel, JsCancellationToken, JsHookRegistry, JsCoordinator, JsAmplifierSession, JsToolBridge, amplifierErrorToJs, resolveModule, loadWasmFromPath } = nativeBinding +const { JsCancellationToken, JsCoordinator, HookAction, SessionState, ContextInjectionRole, ApprovalDefault, UserMessageLevel, amplifierErrorToJs, JsHookRegistry, resolveModule, loadWasmFromPath, JsAmplifierSession, JsToolBridge, hello } = nativeBinding -module.exports.hello = hello +module.exports.JsCancellationToken = JsCancellationToken +module.exports.JsCoordinator = JsCoordinator module.exports.HookAction = HookAction module.exports.SessionState = SessionState module.exports.ContextInjectionRole = ContextInjectionRole module.exports.ApprovalDefault = ApprovalDefault module.exports.UserMessageLevel = UserMessageLevel -module.exports.JsCancellationToken = JsCancellationToken -module.exports.JsHookRegistry = JsHookRegistry -module.exports.JsCoordinator = JsCoordinator -module.exports.JsAmplifierSession = JsAmplifierSession -module.exports.JsToolBridge = JsToolBridge module.exports.amplifierErrorToJs = amplifierErrorToJs +module.exports.JsHookRegistry = JsHookRegistry module.exports.resolveModule = resolveModule module.exports.loadWasmFromPath = loadWasmFromPath +module.exports.JsAmplifierSession = JsAmplifierSession +module.exports.JsToolBridge = JsToolBridge +module.exports.hello = hello diff --git a/bindings/node/src/hooks.rs b/bindings/node/src/hooks.rs index 91f6adb..f802dc2 100644 --- a/bindings/node/src/hooks.rs +++ b/bindings/node/src/hooks.rs @@ -43,14 +43,24 @@ impl HookHandler for JsHookHandlerBridge { "{}".to_string() }); Box::pin(async move { - let result_str: String = - self.callback - .call_async((event, data_str)) - .await - .map_err(|e| HookError::HandlerFailed { - message: e.to_string(), - handler_name: None, - })?; + // The JS handler may return either a bare string (synchronous) or a + // Promise (async). `call_async` returns whichever value the + // JS function returned, so await a promise result explicitly. + let ret: Either> = self + .callback + .call_async((event, data_str)) + .await + .map_err(|e| HookError::HandlerFailed { + message: e.to_string(), + handler_name: None, + })?; + let result_str = match ret { + Either::A(result) => result, + Either::B(promise) => promise.await.map_err(|e| HookError::HandlerFailed { + message: e.to_string(), + handler_name: None, + })?, + }; let hook_result: HookResult = serde_json::from_str(&result_str).unwrap_or_else(|e| { log::error!( "SECURITY: Hook handler returned unparseable result — failing closed (Deny): {e} — json: {result_str}" From a4f566067bcf15389c17f63fec2865389b3c0e36 Mon Sep 17 00:00:00 2001 From: Dan Fiedler Date: Sat, 12 Sep 2026 07:59:04 -0700 Subject: [PATCH 15/27] chore(maintenance): pin actions and refresh dependencies Related to #101, #95, #100, #104, #107, and #108. Pin workflow actions and record the reviewed dependency updates. Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- .github/dependabot.yml | 11 + .github/workflows/proto-check.yml | 10 +- .github/workflows/rust-core-ci.yml | 29 +- .github/workflows/rust-core-wheels.yml | 44 +- Cargo.lock | 150 +-- bindings/node/package-lock.json | 1635 +++++++++--------------- bindings/node/package.json | 4 +- proto/amplifier_module_pb2_grpc.py | 50 +- uv.lock | 6 +- 9 files changed, 770 insertions(+), 1169 deletions(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5e5931a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + groups: + github-actions: + patterns: ["*"] + schedule: + interval: "weekly" + cooldown: + default-days: 7 \ No newline at end of file diff --git a/.github/workflows/proto-check.yml b/.github/workflows/proto-check.yml index c7e24af..cc2bb44 100644 --- a/.github/workflows/proto-check.yml +++ b/.github/workflows/proto-check.yml @@ -19,7 +19,7 @@ jobs: name: Verify Python stubs match proto runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Install protoc run: | @@ -27,7 +27,7 @@ jobs: sudo apt-get install -y protobuf-compiler - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: '3.12' @@ -63,10 +63,12 @@ jobs: name: Rust build and test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable + with: + toolchain: stable - name: Install protoc run: | diff --git a/.github/workflows/rust-core-ci.yml b/.github/workflows/rust-core-ci.yml index 65f04cc..7a4e572 100644 --- a/.github/workflows/rust-core-ci.yml +++ b/.github/workflows/rust-core-ci.yml @@ -11,11 +11,12 @@ jobs: name: Rust Kernel Tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable with: + toolchain: stable components: clippy, rustfmt - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 - name: Run Rust tests run: cargo test -p amplifier-core --verbose - name: Check workspace @@ -29,12 +30,14 @@ jobs: name: Node.js Binding Tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - - uses: actions/setup-node@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable with: - node-version: '20' + toolchain: stable + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '24' - name: Build native module working-directory: bindings/node run: | @@ -53,10 +56,12 @@ jobs: matrix: python-version: ['3.11', '3.12', '3.13'] steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - - uses: actions/setup-python@v5 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable + with: + toolchain: stable + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ matrix.python-version }} - name: Create venv and build diff --git a/.github/workflows/rust-core-wheels.yml b/.github/workflows/rust-core-wheels.yml index 031bf89..9e028ea 100644 --- a/.github/workflows/rust-core-wheels.yml +++ b/.github/workflows/rust-core-wheels.yml @@ -14,19 +14,19 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: - - uses: actions/checkout@v6 - - uses: Swatinem/rust-cache@v2 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: workspaces: ". -> target" cache-on-failure: true - - uses: actions/setup-python@v6 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.11' - - uses: PyO3/maturin-action@v1 + - uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: args: --profile dist --out dist --interpreter python3.11 manylinux: auto - - uses: actions/upload-artifact@v7 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels-${{ matrix.os }} path: dist/*.whl @@ -36,20 +36,20 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v6 - - uses: Swatinem/rust-cache@v2 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: workspaces: ". -> target" cache-on-failure: true - - uses: actions/setup-python@v6 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.11' - - uses: PyO3/maturin-action@v1 + - uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: target: aarch64-unknown-linux-gnu args: --profile dist --out dist --interpreter python3.11 manylinux: auto - - uses: actions/upload-artifact@v7 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels-linux-aarch64 path: dist/*.whl @@ -59,19 +59,19 @@ jobs: runs-on: macos-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v6 - - uses: Swatinem/rust-cache@v2 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: workspaces: ". -> target" cache-on-failure: true - - uses: actions/setup-python@v6 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.11' - - uses: PyO3/maturin-action@v1 + - uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: target: x86_64-apple-darwin args: --profile dist --out dist - - uses: actions/upload-artifact@v7 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels-macos-x86_64 path: dist/*.whl @@ -81,19 +81,19 @@ jobs: runs-on: windows-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v6 - - uses: Swatinem/rust-cache@v2 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: workspaces: ". -> target" cache-on-failure: true - - uses: actions/setup-python@v6 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.11' - - uses: PyO3/maturin-action@v1 + - uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 with: target: aarch64-pc-windows-msvc args: --profile dist --out dist - - uses: actions/upload-artifact@v7 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels-windows-arm64 path: dist/*.whl @@ -107,9 +107,9 @@ jobs: permissions: id-token: write steps: - - uses: actions/download-artifact@v8 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: 'wheels-*' merge-multiple: true path: dist/ - - uses: pypa/gh-action-pypi-publish@release/v1 + - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1 diff --git a/Cargo.lock b/Cargo.lock index 4711ffd..d35f6b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -435,27 +435,27 @@ dependencies = [ [[package]] name = "cranelift-assembler-x64" -version = "0.131.2" +version = "0.131.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008f1a8d1da5074ad858f398775a6d1989031892e46927df5ed18d3be1ed8717" +checksum = "3867f7a56768640a79fc660d2f60298251dc6d65b5d1c907706cd1afff024957" dependencies = [ "cranelift-assembler-x64-meta", ] [[package]] name = "cranelift-assembler-x64-meta" -version = "0.131.2" +version = "0.131.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fd76237df1f4e26edb5ad7971d20280ed1e193331fd257f1b4e4dfefd88dda2" +checksum = "a0661d63dcf8fc4a6538c1ee4d523917c5b27e9fce7a4114cdf9e2b30b4043cf" dependencies = [ "cranelift-srcgen", ] [[package]] name = "cranelift-bforest" -version = "0.131.2" +version = "0.131.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380f0bc43e535df6855bbee649efb00bde39c3f33434c47c8e10ac836d21bf47" +checksum = "a8d535b489159ea63e3c40dfbe8d0e12bfb71f2a14845ef2407353e06c5a697c" dependencies = [ "cranelift-entity", "wasmtime-internal-core", @@ -463,9 +463,9 @@ dependencies = [ [[package]] name = "cranelift-bitset" -version = "0.131.2" +version = "0.131.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4811e3e4502de04257e90c0a93225b56d9b85e0f9ad10b81446b415511009610" +checksum = "c3af4f7d421b2354deb01d714266022f38fcdbebc9f5f1ec6d310d3c27286d9e" dependencies = [ "serde", "serde_derive", @@ -474,9 +474,9 @@ dependencies = [ [[package]] name = "cranelift-codegen" -version = "0.131.2" +version = "0.131.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82ffadb34d497f3e76fb3b4baf764c24ba8a51512976a1b77f78bdbf8f4aa687" +checksum = "09fe4c289e67e0221d1705734a57f95e25c289ed0ead7728743ea21285fc4cf1" dependencies = [ "bumpalo", "cranelift-assembler-x64", @@ -502,9 +502,9 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.131.2" +version = "0.131.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4f6992eb6faf086ddc7deaaa5f279abfe7f5fd5ae5709bd38253450fc7b945" +checksum = "b3063e5363dc5ee6ee8edd930314582c08eb91c209b9564da1cd667f6424b9b3" dependencies = [ "cranelift-assembler-x64-meta", "cranelift-codegen-shared", @@ -515,24 +515,24 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" -version = "0.131.2" +version = "0.131.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e1b2aad7d055925a4ea9cdbfa9d1d987f9dfc8ad6b708be28f901ac620a298" +checksum = "c34b9c8dbc9edf37744918e56898d4979ef1e764e8e4bbe8b4d50250838ddfe8" [[package]] name = "cranelift-control" -version = "0.131.2" +version = "0.131.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a355348325e0a63b65c00def3871597b9fcc79d25456397010d16d872b3772" +checksum = "4eed9dc54204dc99aad19669bca50142659ed583396d9a99a2aef34d7c136ef4" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.131.2" +version = "0.131.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f4847d93ce2c80d2bff929aa1004dfb3ce2cf5d881f6ced54b8d654d967ba3" +checksum = "9aa2846b239a046217ecf95cfed0e31be4e86843785d07438ad33f456871e888" dependencies = [ "cranelift-bitset", "serde", @@ -542,9 +542,9 @@ dependencies = [ [[package]] name = "cranelift-frontend" -version = "0.131.2" +version = "0.131.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba24e5fe5242cc445e7892ef0a51a4351cf716e3a04ac7a3a05820d056c39818" +checksum = "144f70fa9cd07efb83497c12dc8fb73f360a690cd990c44e8ceebc293d8c13b5" dependencies = [ "cranelift-codegen", "log", @@ -554,15 +554,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.131.2" +version = "0.131.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89bc2035de85c4f04ba7bd57eb5bd3a8b775235bf28852dbf87105115cb8919a" +checksum = "0733ca5b2aaa5f6d5d6a1439e3c44280d34730d4d5c262ca08c6775c8d83f191" [[package]] name = "cranelift-native" -version = "0.131.2" +version = "0.131.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea6630c16921ab087792750f239d0c0173411e80179ca7c0ce0710ce9e7646a" +checksum = "75b1290d6193b171172d5fe9a6e42326edf487a79f211fbf1e76f912a4aed035" dependencies = [ "cranelift-codegen", "libc", @@ -571,9 +571,9 @@ dependencies = [ [[package]] name = "cranelift-srcgen" -version = "0.131.2" +version = "0.131.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa4bbad54fc28cc0da1f9a5d7f7f826ec8cafda3d503b401b2daaaa93c63ef0" +checksum = "ce0d5c2b4d719566816a0f1c9a9712d35d61e27df0ffd6c72a9afec9048db6c0" [[package]] name = "crc32fast" @@ -720,7 +720,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -1122,7 +1122,7 @@ dependencies = [ "hyper", "libc", "pin-project-lite", - "socket2 0.6.2", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -1764,9 +1764,9 @@ dependencies = [ [[package]] name = "pulley-interpreter" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff0ead8b4616f81b3d3efd41ce41bcf9ea364a5d8df8be8a8a1f98b50104349" +checksum = "34dff5fd3d9ac4845939fcb4597cd413cb244bc530448ed4766d11b1725e53d0" dependencies = [ "cranelift-bitset", "log", @@ -1776,9 +1776,9 @@ dependencies = [ [[package]] name = "pulley-macros" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4389e5820b1b39810ac12a27aa665320cab3caa51913a79637c06f284cfe223" +checksum = "6c60fb1c885bdb1efd7c50e8e973714de558b75a65f20c3e9a41398c652aa44b" dependencies = [ "proc-macro2", "quote", @@ -2033,7 +2033,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2256,7 +2256,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2873,9 +2873,9 @@ dependencies = [ [[package]] name = "wasmtime" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4eccc0728f061979efa8ff4c962cff7041fead4baadb74973f01b9c47158a4" +checksum = "d807f646bfecc1dbb4990d8c864beebccbdb3f2cd1b39b2b82957b4e294c6058" dependencies = [ "addr2line", "async-trait", @@ -2926,9 +2926,9 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e84dbe3208c1336a41546beb75927b3b37e2e4fce06653d214b407136fbe295" +checksum = "48b945309908f22473ebcd585ac2993948044228491cd96941d367aa81a49c3f" dependencies = [ "anyhow", "cpp_demangle", @@ -2957,9 +2957,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-cache" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "910b8dcadc0888344b2dea5a087c836b58156d4f455c52b6dac0bdc776a9d029" +checksum = "f14b8b93c2137c88ed84114d9a09cb11cb8bf9394aba4856e48f5304a4f99eec" dependencies = [ "base64", "directories-next", @@ -2977,9 +2977,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-component-macro" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c223bd503db76df8d74d1fcca39e734d25f7a0c1dcaf1509b67f3855d1b0f803" +checksum = "7307dec6251a18ffa9df03120d2945ac2476ce150b5b099f39b199e8f81464dc" dependencies = [ "anyhow", "proc-macro2", @@ -2992,15 +2992,15 @@ dependencies = [ [[package]] name = "wasmtime-internal-component-util" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab123ad511483a1b918399789d0cc7dea7c5c6476743df73949007b5b225fc74" +checksum = "7a3d899b0270bcf04852f141bd9538cb162a436e7f56b2c6e0f4e57ecc70743a" [[package]] name = "wasmtime-internal-core" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4364d345719bba7fc4c435992ea1cb0c118f1e90a88c6e6f22a7a4fc507700c6" +checksum = "aedd3947487d0afdd37accb981466fcd60571e898004c8955111f88686581dfc" dependencies = [ "anyhow", "hashbrown 0.16.1", @@ -3010,9 +3010,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-cranelift" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5a3bc28a172037c7864128bb208017a02bba659a59c27acacc048c09e25c1fc" +checksum = "512fd846630c064bfc42909eeba90a7d26703b6ded09ad4778ff6afcbdc868dd" dependencies = [ "cfg-if", "cranelift-codegen", @@ -3037,9 +3037,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-fiber" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c90a899a47d3da6e384e7b4cad61fdcb27535a395742b32440bdf9980ea83fa" +checksum = "15629ea71394be5812a52cb8fbc6cd039484ab1dd48fce5e1ef58ae89606289a" dependencies = [ "cc", "cfg-if", @@ -3052,9 +3052,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-jit-debug" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f364747aa74c686b18925918e5cfd615a73c9613c7a31fc1cd86f42df12fbe" +checksum = "f5fce5fedc1c952a64cdf3c87e4632af072d2aca0bc2c460d53296fb2654757d" dependencies = [ "cc", "object", @@ -3064,9 +3064,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-jit-icache-coherence" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ba98c1492f530833e0d3cc17dbb0c3c57c9f1bb3b078ae44bb55a233e43eba" +checksum = "10005b038e662775ac002f233e429447a58892e89918580fa67ce8cdd9192d0a" dependencies = [ "cfg-if", "libc", @@ -3076,9 +3076,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-unwinder" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b8f8a89e8f3660646f820c7d8310a67094156bb866e9d56f1b00892e011206" +checksum = "03f3e0f474281b405a3e9d97239f83f643b572324d292e1ef9dc5e3e0ab04c68" dependencies = [ "cfg-if", "cranelift-codegen", @@ -3089,9 +3089,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-versioned-export-macros" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a12754f1ffc4a3300d56d324c418b8b32cf029606618da22c7d076213882a3f" +checksum = "910e5393af4aca456113581a5913b8d499cd2189013e983f8764d06ebc42b2ed" dependencies = [ "proc-macro2", "quote", @@ -3100,9 +3100,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-winch" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b06e4ed07adc579645e5c55c67b3138c49da2e468fad52d3db7b7a098ecc733" +checksum = "55481651bea5b8336fb200fa49914ccf802f5e7617ba9ab0b4691f16d4f97ff8" dependencies = [ "cranelift-codegen", "gimli", @@ -3117,9 +3117,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-wit-bindgen" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f08787948e3c983799d616ef7dd57463253e9ca8bab6607eef8134f12353f70" +checksum = "d890c3804d0e46000fa901c86ac1a9fdedf684e72dfd64e582b56bb4a78a6746" dependencies = [ "anyhow", "bitflags", @@ -3130,9 +3130,9 @@ dependencies = [ [[package]] name = "wasmtime-wasi" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b2f19834bc6edbc31ac95fdcfd5ddcd7643759265a1d545dec36ac6cc788ca8" +checksum = "5aa7f927779d92863a1447c1d88192b2242080608cb91ece69c177321488948b" dependencies = [ "async-trait", "bitflags", @@ -3160,9 +3160,9 @@ dependencies = [ [[package]] name = "wasmtime-wasi-io" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e0c6efdbaf90906016be9ed9ff17b7b58f393876287beebe5bd7fa1de54dbb" +checksum = "d6fd8b702c2aa82bb4907da5ad44120b4385048f6a20731908a6e2485ea7567e" dependencies = [ "async-trait", "bytes", @@ -3204,9 +3204,9 @@ dependencies = [ [[package]] name = "wiggle" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b644ab90da80bbca28973192978ac452cbd876955bb209e6ff2cd1955e43a7" +checksum = "165512f7870210d0fd45911990b832782b5fb82d40f4ce28cf86b40aaecd453c" dependencies = [ "bitflags", "thiserror 2.0.18", @@ -3218,9 +3218,9 @@ dependencies = [ [[package]] name = "wiggle-generate" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521f9d558365357274d960340eb9eb4f4d768fafdc79f381fd2e13a85b925ebc" +checksum = "f7f63d06fdf2e9a133407b318574574f66ea6590ea7a42c4a7554c029824454a" dependencies = [ "heck", "proc-macro2", @@ -3232,9 +3232,9 @@ dependencies = [ [[package]] name = "wiggle-macro" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a386e86021363c9f0abd1e189e8f8a729d9b5aab2bb7172a3e40f2ab647a936" +checksum = "1b976b7285ca32a637e21721021442691a2d7938ab4650cb99472a672e9def6c" dependencies = [ "proc-macro2", "quote", @@ -3264,7 +3264,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3275,9 +3275,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winch-codegen" -version = "44.0.2" +version = "44.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16496e92d2b232f9d195ae74f71a674aabae7b7fa722d39068836723d3b653c" +checksum = "436a7fa4109b13b0e555d01ec615ab8b928b7834639aca7b2fdc1f55d70a1f0c" dependencies = [ "cranelift-assembler-x64", "cranelift-codegen", diff --git a/bindings/node/package-lock.json b/bindings/node/package-lock.json index e85aabf..a254742 100644 --- a/bindings/node/package-lock.json +++ b/bindings/node/package-lock.json @@ -10,66 +10,71 @@ "devDependencies": { "@napi-rs/cli": "^2", "typescript": "^5", - "vitest": "^3" + "vitest": "^5.0.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", - "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", - "cpu": [ - "ppc64" - ], + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "aix" - ], "engines": { - "node": ">=18" + "node": ">=6.0.0" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", - "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", - "cpu": [ - "arm" - ], + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", - "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", - "cpu": [ - "arm64" - ], + "node_modules/@napi-rs/cli": { + "version": "2.18.4", + "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.18.4.tgz", + "integrity": "sha512-SgJeA4df9DE2iAEpr3M2H0OKl/yjtg1BnRI5/JyowS71tUWhrfSu2LT0V3vlHET+g1hBVlrO60PmEXwUEKp8Mg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "bin": { + "napi": "scripts/index.js" + }, "engines": { - "node": ">=18" + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.149.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.149.0.tgz", + "integrity": "sha512-Efcc+iF0j3Bf67YjEqIqWXbX5XddXoK/Mw4K1/JuXwRCZ8N16VR7iT23nlCc9XrveFVh/E5Rqs2StT0V8v9LdA==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/oxc-project" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", - "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.8.tgz", + "integrity": "sha512-tN5aztYkKCte4i5SIrrz5yK/HMjEuCqCSCJa418jOV8tZ1cBY3YF2otxB1ktPxzsLA1BeTqwapK0bfjxNvHJVw==", "cpu": [ - "x64" + "arm" ], "dev": true, "license": "MIT", @@ -77,14 +82,15 @@ "os": [ "android" ], + "peer": true, "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", - "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.8.tgz", + "integrity": "sha512-dIYTWl9XprMUiQFoc55KUyk/oS8SKYH3zFl0LTR7RT0Xj4hgSVyuJcroH8JUu8RcpF8fTB6E0aOwCkZoYPcDSQ==", "cpu": [ "arm64" ], @@ -92,18 +98,19 @@ "license": "MIT", "optional": true, "os": [ - "darwin" + "android" ], + "peer": true, "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", - "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.8.tgz", + "integrity": "sha512-PCSDQGXD2IyTEFrcgPyBM8jJuGmrbCMuoIOXdbEGVemruKACXoLQJrb+A45Z0L5t1RQkdfJprAYPkikbh7dzdA==", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", @@ -111,31 +118,33 @@ "os": [ "darwin" ], + "peer": true, "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", - "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.8.tgz", + "integrity": "sha512-Uk7lRsGhPFHVX/sAUC6D5H9Ol30dFHd6iquokll2th3LpdJ3F5CzQB+7DHn0Ri2mG+U7k2zXiPHDrwZenXhwSA==", "cpu": [ - "arm64" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "freebsd" + "darwin" ], + "peer": true, "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", - "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.8.tgz", + "integrity": "sha512-DjszaTEVogPqA5bYzsEeqDCQxbcp2fexQwKcRspYji2yzR68fCf+e4fx6kBSRDwX5/brZaHw/hWS9+A/+/w9sQ==", "cpu": [ "x64" ], @@ -145,14 +154,15 @@ "os": [ "freebsd" ], + "peer": true, "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", - "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.8.tgz", + "integrity": "sha512-zmwa7FTmdzB6aaEEuuls18H6Ap5JmJPSoPTuXixeJZV6tG40SyLkApQtz1g8ptZtiEKqj9OM0oNLPh1AgvE31Q==", "cpu": [ "arm" ], @@ -162,218 +172,141 @@ "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", - "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.8.tgz", + "integrity": "sha512-KdYQDPHwJVnbFwdTGMgxsI9SqblBlz6STGM+w1We/d5B8OWWidYH0MwkU/uA1wM5fIpO2MkOVxXrNzzuZhw9ew==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" + "libc": [ + "glibc" ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", - "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", - "cpu": [ - "ia32" - ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", - "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.8.tgz", + "integrity": "sha512-jFJTifHnNPY+yzOoNZQfSIysrVyXzEQPhPnOUjmD1bcQGHH6s7c8cViKWar8YplQImE5N9JRqMCLrM2CdxOrZA==", "cpu": [ - "loong64" + "arm64" ], "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", - "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", - "cpu": [ - "mips64el" + "libc": [ + "musl" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", - "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.8.tgz", + "integrity": "sha512-FhiOziBDWPBjbcmRzfLyIJnaP7AVMFXT7YCXPjXxj7wKU3vx24RjrCNN/zjvVa+N2vVoHJwCoUBvsrN/DG3zIA==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" + "libc": [ + "glibc" ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", - "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", - "cpu": [ - "riscv64" - ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", - "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.8.tgz", + "integrity": "sha512-WnHfADMzOV2Y55wlx1hzzQnar/wDt/VdvWSD99r18Mz9ylNieIGOkRx3UV21h7m/eJvjySYJkO26VvGNFkwsIQ==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" + "libc": [ + "glibc" ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", - "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", - "cpu": [ - "x64" - ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", - "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", - "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.8.tgz", + "integrity": "sha512-H9tRr5ibfXFVLxbPOseVewewFpl28zcEdjRDt2FTUZU7odxP0gEv1ki4/kGmcGOh78oRwZuuQllGLZ9zTJp84g==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" + "libc": [ + "glibc" ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", - "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", - "cpu": [ - "arm64" - ], - "dev": true, "license": "MIT", "optional": true, "os": [ - "openbsd" + "linux" ], + "peer": true, "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", - "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.8.tgz", + "integrity": "sha512-UefiqfM3D6IVNlZ8tSGs9+Ejjud2T+oxO0IHADU45Y+lyEjD2dVFyZHbkfX0LUb5Zugo/oIv1eCO/KVYhgYJYA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ - "openbsd" + "linux" ], + "peer": true, "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", - "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.8.tgz", + "integrity": "sha512-637Ke4kWSy6rp9cxQ9gMOXlxPgIw/c1beASV4M//3+9I4uwBVOOl74G+e3zyU3u19U7RkRl/HuewixZ/Z6+Rjg==", "cpu": [ "arm64" ], @@ -383,31 +316,15 @@ "os": [ "openharmony" ], + "peer": true, "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", - "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", - "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.8.tgz", + "integrity": "sha512-xWBkPOF1Q9k/Gv1nQXnVdLxKu74jXppuOM4Z3mnypVUJJJwLsMl7hNJGRAUJoG8A5MgOI1ACKM+wBFxSJzKy4A==", "cpu": [ "arm64" ], @@ -417,31 +334,15 @@ "os": [ "win32" ], + "peer": true, "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", - "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", - "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.8.tgz", + "integrity": "sha512-uz2ZvfgXbxqNwijjjbxrnvALwpyODDcgc1T1N8N3rf/DXKQmaFwmB4LX4yyjggpwN2obdQLb2rgirX5ffCWYng==", "cpu": [ "x64" ], @@ -451,383 +352,18 @@ "os": [ "win32" ], + "peer": true, "engines": { - "node": ">=18" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@napi-rs/cli": { - "version": "2.18.4", - "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.18.4.tgz", - "integrity": "sha512-SgJeA4df9DE2iAEpr3M2H0OKl/yjtg1BnRI5/JyowS71tUWhrfSu2LT0V3vlHET+g1hBVlrO60PmEXwUEKp8Mg==", - "dev": true, - "license": "MIT", - "bin": { - "napi": "scripts/index.js" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", - "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", - "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", - "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", - "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", - "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", - "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", - "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", - "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", - "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", - "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", - "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", - "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", - "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", - "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", - "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", - "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", - "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", - "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", - "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", - "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", - "cpu": [ - "x64" - ], + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", - "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", - "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", - "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", - "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", - "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "peer": true }, "node_modules/@types/chai": { "version": "5.2.3", @@ -848,46 +384,30 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, - "node_modules/@vitest/expect": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", - "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, "node_modules/@vitest/mocker": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", - "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.0.tgz", + "integrity": "sha512-66PGTMIiVJP3t4a5yxU9qPtf7MdTBs8jmToMvy+HVflB3Yy13WJZTtPePdvU+wjRV02SKK5doLbSA6o9pwOmiA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.2.4", + "@jridgewell/trace-mapping": "0.3.31", + "@vitest/spy": "5.0.0", "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" + "magic-string": "^1.2.3" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { @@ -898,201 +418,54 @@ } } }, - "node_modules/@vitest/pretty-format": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", - "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", - "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "3.2.4", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", - "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.4", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, "node_modules/@vitest/spy": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", - "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.0.tgz", + "integrity": "sha512-uy+luWBAPw9XfthoHi5AkfHUnuPYEESjl0p/r+meoBnU8bxg5GDQ3Ey8MjcJ6sqahkL4PFyrvfMJJBw7LbU06g==", "dev": true, "license": "MIT", - "dependencies": { - "tinyspy": "^4.0.3" - }, "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", - "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.4", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=18" + "url": "https://opencollective.com/vitest" } }, - "node_modules/check-error": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", - "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 16" + "node": ">=12" } }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=18" } }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "peer": true, "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", "dev": true, "license": "MIT" }, - "node_modules/esbuild": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", - "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.3", - "@esbuild/android-arm": "0.27.3", - "@esbuild/android-arm64": "0.27.3", - "@esbuild/android-x64": "0.27.3", - "@esbuild/darwin-arm64": "0.27.3", - "@esbuild/darwin-x64": "0.27.3", - "@esbuild/freebsd-arm64": "0.27.3", - "@esbuild/freebsd-x64": "0.27.3", - "@esbuild/linux-arm": "0.27.3", - "@esbuild/linux-arm64": "0.27.3", - "@esbuild/linux-ia32": "0.27.3", - "@esbuild/linux-loong64": "0.27.3", - "@esbuild/linux-mips64el": "0.27.3", - "@esbuild/linux-ppc64": "0.27.3", - "@esbuild/linux-riscv64": "0.27.3", - "@esbuild/linux-s390x": "0.27.3", - "@esbuild/linux-x64": "0.27.3", - "@esbuild/netbsd-arm64": "0.27.3", - "@esbuild/netbsd-x64": "0.27.3", - "@esbuild/openbsd-arm64": "0.27.3", - "@esbuild/openbsd-x64": "0.27.3", - "@esbuild/openharmony-arm64": "0.27.3", - "@esbuild/sunos-x64": "0.27.3", - "@esbuild/win32-arm64": "0.27.3", - "@esbuild/win32-ia32": "0.27.3", - "@esbuild/win32-x64": "0.27.3" - } - }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -1104,9 +477,9 @@ } }, "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1142,45 +515,310 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, - "license": "MIT" + "license": "MPL-2.0", + "peer": true, + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.3.1.tgz", + "integrity": "sha512-rm91zr2Ou+XueDTohjQQjdQEcYM6zVi8KVUCG8Ec3vHwUEKrhSdCNyfuIywkA6hcCAteIn0ZOtAHA6eGpiX+Pg==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "@jridgewell/sourcemap-codec": "^1.6.0" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", "dev": true, "funding": [ { @@ -1189,6 +827,7 @@ } ], "license": "MIT", + "peer": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -1196,21 +835,18 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "node_modules/obug": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz", + "integrity": "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==", "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], "license": "MIT", "engines": { - "node": ">= 14.16" + "node": ">=12.20.0" } }, "node_modules/picocolors": { @@ -1218,12 +854,13 @@ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -1234,9 +871,9 @@ } }, "node_modules/postcss": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", - "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", "dev": true, "funding": [ { @@ -1253,8 +890,9 @@ } ], "license": "MIT", + "peer": true, "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -1262,49 +900,39 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/rollup": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", - "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "node_modules/rolldown": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.8.tgz", + "integrity": "sha512-Z67nTmhZe7anqnM/EjI392w5i/ANUinjip7QYsOyN37oayduxt3ksdX0hf5OOamkAd53BiIHfbfSzfUmzKFQqQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "@types/estree": "1.0.8" + "@oxc-project/types": "=0.149.0", + "@rolldown/pluginutils": "^1.0.0" }, "bin": { - "rollup": "dist/bin/rollup" + "rolldown": "bin/cli.mjs" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.59.0", - "@rollup/rollup-android-arm64": "4.59.0", - "@rollup/rollup-darwin-arm64": "4.59.0", - "@rollup/rollup-darwin-x64": "4.59.0", - "@rollup/rollup-freebsd-arm64": "4.59.0", - "@rollup/rollup-freebsd-x64": "4.59.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", - "@rollup/rollup-linux-arm-musleabihf": "4.59.0", - "@rollup/rollup-linux-arm64-gnu": "4.59.0", - "@rollup/rollup-linux-arm64-musl": "4.59.0", - "@rollup/rollup-linux-loong64-gnu": "4.59.0", - "@rollup/rollup-linux-loong64-musl": "4.59.0", - "@rollup/rollup-linux-ppc64-gnu": "4.59.0", - "@rollup/rollup-linux-ppc64-musl": "4.59.0", - "@rollup/rollup-linux-riscv64-gnu": "4.59.0", - "@rollup/rollup-linux-riscv64-musl": "4.59.0", - "@rollup/rollup-linux-s390x-gnu": "4.59.0", - "@rollup/rollup-linux-x64-gnu": "4.59.0", - "@rollup/rollup-linux-x64-musl": "4.59.0", - "@rollup/rollup-openbsd-x64": "4.59.0", - "@rollup/rollup-openharmony-arm64": "4.59.0", - "@rollup/rollup-win32-arm64-msvc": "4.59.0", - "@rollup/rollup-win32-ia32-msvc": "4.59.0", - "@rollup/rollup-win32-x64-gnu": "4.59.0", - "@rollup/rollup-win32-x64-msvc": "4.59.0", - "fsevents": "~2.3.2" + "@rolldown/binding-android-arm-eabi": "1.2.8", + "@rolldown/binding-android-arm64": "1.2.8", + "@rolldown/binding-darwin-arm64": "1.2.8", + "@rolldown/binding-darwin-x64": "1.2.8", + "@rolldown/binding-freebsd-x64": "1.2.8", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.8", + "@rolldown/binding-linux-arm64-gnu": "1.2.8", + "@rolldown/binding-linux-arm64-musl": "1.2.8", + "@rolldown/binding-linux-ppc64-gnu": "1.2.8", + "@rolldown/binding-linux-s390x-gnu": "1.2.8", + "@rolldown/binding-linux-x64-gnu": "1.2.8", + "@rolldown/binding-linux-x64-musl": "1.2.8", + "@rolldown/binding-openharmony-arm64": "1.2.8", + "@rolldown/binding-win32-arm64-msvc": "1.2.8", + "@rolldown/binding-win32-x64-msvc": "1.2.8" } }, "node_modules/siginfo": { @@ -1320,6 +948,7 @@ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -1332,48 +961,41 @@ "license": "MIT" }, "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", "dev": true, "license": "MIT" }, - "node_modules/strip-literal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", - "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "node_modules/tinybench": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.1.4.tgz", + "integrity": "sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==", "dev": true, "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -1382,36 +1004,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", - "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -1427,18 +1019,18 @@ } }, "node_modules/vite": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", - "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.0.tgz", + "integrity": "sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "esbuild": "^0.27.0 || ^0.28.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.28", + "rolldown": "~1.2.6", + "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" @@ -1454,9 +1046,10 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1", + "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", - "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", @@ -1469,13 +1062,16 @@ "@types/node": { "optional": true }, - "jiti": { + "@vitejs/devtools": { "optional": true }, - "less": { + "esbuild": { + "optional": true + }, + "jiti": { "optional": true }, - "lightningcss": { + "less": { "optional": true }, "sass": { @@ -1501,89 +1097,73 @@ } } }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, "node_modules/vitest": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", - "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.0.tgz", + "integrity": "sha512-gpsMNoRhMjMktVxPtstOH4/PJuPyovVaMDr4oDilXaGH1EcqM2OE96SoHT2VIQ6fTGtTjqmHDrEu2X9RQiXf8Q==", "dev": true, "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.4", - "@vitest/mocker": "3.2.4", - "@vitest/pretty-format": "^3.2.4", - "@vitest/runner": "3.2.4", - "@vitest/snapshot": "3.2.4", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", + "@vitest/mocker": "5.0.0", + "chai": "^6.2.2", + "es-module-lexer": "^2.3.2", + "expect-type": "^1.4.0", + "magic-string": "^1.2.3", + "obug": "^2.1.4", + "picomatch": "^4.0.7", + "std-env": "^4.2.0", + "tinybench": "6.1.4", + "tinyexec": "1.3.0", + "tinyglobby": "^0.2.17", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^22.12.0 || ^24.0.0 || >=26.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.4", - "@vitest/ui": "3.2.4", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "5.0.0", + "@vitest/browser-preview": "5.0.0", + "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", + "@vitest/coverage-istanbul": "5.0.0", + "@vitest/coverage-v8": "5.0.0", + "@vitest/ui": "5.0.0", "happy-dom": "*", - "jsdom": "*" + "jsdom": "*", + "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { "optional": true }, - "@types/debug": { + "@opentelemetry/api": { "optional": true }, "@types/node": { "optional": true }, - "@vitest/browser": { + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { "optional": true }, "@vitest/ui": { @@ -1594,6 +1174,9 @@ }, "jsdom": { "optional": true + }, + "vite": { + "optional": false } } }, diff --git a/bindings/node/package.json b/bindings/node/package.json index 323fed1..61d4832 100644 --- a/bindings/node/package.json +++ b/bindings/node/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@napi-rs/cli": "^2", - "vitest": "^3", - "typescript": "^5" + "typescript": "^5", + "vitest": "^5.0.0" } } diff --git a/proto/amplifier_module_pb2_grpc.py b/proto/amplifier_module_pb2_grpc.py index e6b7668..958322b 100644 --- a/proto/amplifier_module_pb2_grpc.py +++ b/proto/amplifier_module_pb2_grpc.py @@ -5,7 +5,7 @@ import amplifier_module_pb2 as amplifier__module__pb2 -GRPC_GENERATED_VERSION = '1.78.0' +GRPC_GENERATED_VERSION = '1.83.1' GRPC_VERSION = grpc.__version__ _version_not_supported = False @@ -25,7 +25,7 @@ ) -class ToolServiceStub(object): +class ToolServiceStub: """Universal contract for tool modules in any language. Implement this service to create an Amplifier tool in Go, TypeScript, C#, etc. """ @@ -48,7 +48,7 @@ def __init__(self, channel): _registered_method=True) -class ToolServiceServicer(object): +class ToolServiceServicer: """Universal contract for tool modules in any language. Implement this service to create an Amplifier tool in Go, TypeScript, C#, etc. """ @@ -88,7 +88,7 @@ def add_ToolServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. -class ToolService(object): +class ToolService: """Universal contract for tool modules in any language. Implement this service to create an Amplifier tool in Go, TypeScript, C#, etc. """ @@ -148,7 +148,7 @@ def Execute(request, _registered_method=True) -class ProviderServiceStub(object): +class ProviderServiceStub: """--------------------------------------------------------------------------- Module services --------------------------------------------------------------------------- @@ -189,7 +189,7 @@ def __init__(self, channel): _registered_method=True) -class ProviderServiceServicer(object): +class ProviderServiceServicer: """--------------------------------------------------------------------------- Module services --------------------------------------------------------------------------- @@ -263,7 +263,7 @@ def add_ProviderServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. -class ProviderService(object): +class ProviderService: """--------------------------------------------------------------------------- Module services --------------------------------------------------------------------------- @@ -407,7 +407,7 @@ def ParseToolCalls(request, _registered_method=True) -class OrchestratorServiceStub(object): +class OrchestratorServiceStub: """Orchestrator module contract — high-level prompt execution. """ @@ -424,7 +424,7 @@ def __init__(self, channel): _registered_method=True) -class OrchestratorServiceServicer(object): +class OrchestratorServiceServicer: """Orchestrator module contract — high-level prompt execution. """ @@ -450,7 +450,7 @@ def add_OrchestratorServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. -class OrchestratorService(object): +class OrchestratorService: """Orchestrator module contract — high-level prompt execution. """ @@ -482,7 +482,7 @@ def Execute(request, _registered_method=True) -class ContextServiceStub(object): +class ContextServiceStub: """Context module contract — conversation message management. """ @@ -519,7 +519,7 @@ def __init__(self, channel): _registered_method=True) -class ContextServiceServicer(object): +class ContextServiceServicer: """Context module contract — conversation message management. """ @@ -589,7 +589,7 @@ def add_ContextServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. -class ContextService(object): +class ContextService: """Context module contract — conversation message management. """ @@ -729,7 +729,7 @@ def Clear(request, _registered_method=True) -class HookServiceStub(object): +class HookServiceStub: """Hook module contract — event interception. """ @@ -751,7 +751,7 @@ def __init__(self, channel): _registered_method=True) -class HookServiceServicer(object): +class HookServiceServicer: """Hook module contract — event interception. """ @@ -792,7 +792,7 @@ def add_HookServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. -class HookService(object): +class HookService: """Hook module contract — event interception. """ @@ -851,7 +851,7 @@ def GetSubscriptions(request, _registered_method=True) -class ApprovalServiceStub(object): +class ApprovalServiceStub: """Approval module contract — human-in-the-loop approval. """ @@ -868,7 +868,7 @@ def __init__(self, channel): _registered_method=True) -class ApprovalServiceServicer(object): +class ApprovalServiceServicer: """Approval module contract — human-in-the-loop approval. """ @@ -894,7 +894,7 @@ def add_ApprovalServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. -class ApprovalService(object): +class ApprovalService: """Approval module contract — human-in-the-loop approval. """ @@ -926,7 +926,7 @@ def RequestApproval(request, _registered_method=True) -class KernelServiceStub(object): +class KernelServiceStub: """Missing associated documentation comment in .proto file.""" def __init__(self, channel): @@ -987,7 +987,7 @@ def __init__(self, channel): _registered_method=True) -class KernelServiceServicer(object): +class KernelServiceServicer: """Missing associated documentation comment in .proto file.""" def CompleteWithProvider(self, request, context): @@ -1117,7 +1117,7 @@ def add_KernelServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. -class KernelService(object): +class KernelService: """Missing associated documentation comment in .proto file.""" @staticmethod @@ -1391,7 +1391,7 @@ def GetCapability(request, _registered_method=True) -class ModuleLifecycleStub(object): +class ModuleLifecycleStub: """--------------------------------------------------------------------------- ModuleLifecycle — every gRPC module implements this service --------------------------------------------------------------------------- @@ -1428,7 +1428,7 @@ def __init__(self, channel): _registered_method=True) -class ModuleLifecycleServicer(object): +class ModuleLifecycleServicer: """--------------------------------------------------------------------------- ModuleLifecycle — every gRPC module implements this service --------------------------------------------------------------------------- @@ -1492,7 +1492,7 @@ def add_ModuleLifecycleServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. -class ModuleLifecycle(object): +class ModuleLifecycle: """--------------------------------------------------------------------------- ModuleLifecycle — every gRPC module implements this service --------------------------------------------------------------------------- diff --git a/uv.lock b/uv.lock index f323797..90a6a16 100644 --- a/uv.lock +++ b/uv.lock @@ -439,11 +439,11 @@ wheels = [ [[package]] name = "setuptools" -version = "82.0.1" +version = "83.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/26/f5d29e25ffdb535afef2d35cdb55b325298f96debd670da4c325e08d70f4/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef", size = 1154254, upload-time = "2026-07-04T15:31:22.699Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, + { url = "https://files.pythonhosted.org/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3", size = 1008090, upload-time = "2026-07-04T15:31:20.885Z" }, ] [[package]] From a1054abd436bddcd8cbdf697997314f24bfd8d4e Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Sat, 12 Sep 2026 07:59:04 -0700 Subject: [PATCH 16/27] test(core): stabilize hook logging and WASM hash coverage Refresh the fixture hash and make concurrency and test logging regressions deterministic. Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- crates/amplifier-core/src/hooks.rs | 79 ++++++++++++++++++-- crates/amplifier-core/src/module_resolver.rs | 2 +- tests/test_hooks_request_id.py | 7 +- 3 files changed, 76 insertions(+), 12 deletions(-) diff --git a/crates/amplifier-core/src/hooks.rs b/crates/amplifier-core/src/hooks.rs index d10b113..f13c766 100644 --- a/crates/amplifier-core/src/hooks.rs +++ b/crates/amplifier-core/src/hooks.rs @@ -1282,15 +1282,19 @@ mod tests { static LOG_MESSAGES: OnceLock>> = OnceLock::new(); impl log::Log for TestLogger { - fn enabled(&self, _metadata: &log::Metadata) -> bool { - true + fn enabled(&self, metadata: &log::Metadata) -> bool { + metadata.target().starts_with("amplifier_core::hooks") + && metadata.level() <= log::Level::Warn } fn log(&self, record: &log::Record) { + if !self.enabled(record.metadata()) { + return; + } + // Display implementations may themselves log. Never format while + // holding the non-reentrant capture mutex. + let message = format!("{}", record.args()); let messages = LOG_MESSAGES.get_or_init(|| Mutex::new(Vec::new())); - messages - .lock() - .unwrap() - .push((record.level(), format!("{}", record.args()))); + messages.lock().unwrap().push((record.level(), message)); } fn flush(&self) {} } @@ -1299,7 +1303,68 @@ mod tests { fn install_test_logger() { // set_logger is one-shot per process; ignore AlreadySet errors - let _ = log::set_logger(&TEST_LOGGER).map(|()| log::set_max_level(log::LevelFilter::Trace)); + let _ = log::set_logger(&TEST_LOGGER).map(|()| log::set_max_level(log::LevelFilter::Warn)); + } + + #[test] + fn test_logger_formats_reentrant_messages_before_locking() { + use log::Log; + use std::cell::Cell; + use std::fmt; + + struct ReentrantDisplay<'a>(&'a Cell); + + impl fmt::Display for ReentrantDisplay<'_> { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + TEST_LOGGER.log( + &log::Record::builder() + .level(log::Level::Warn) + .target("amplifier_core::hooks") + .args(format_args!("nested formatting regression")) + .build(), + ); + self.0.set(true); + formatter.write_str("outer formatting regression") + } + } + + let nested_returned = Cell::new(false); + TEST_LOGGER.log( + &log::Record::builder() + .level(log::Level::Warn) + .target("amplifier_core::hooks") + .args(format_args!("{}", ReentrantDisplay(&nested_returned))) + .build(), + ); + assert!(nested_returned.get(), "nested logging must return"); + } + + #[test] + fn test_logger_ignores_dependency_records_without_formatting() { + use log::Log; + use std::fmt; + + struct MustNotFormat; + + impl fmt::Display for MustNotFormat { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + panic!("dependency log arguments must not be formatted"); + } + } + + assert!(!TEST_LOGGER.enabled( + &log::Metadata::builder() + .level(log::Level::Warn) + .target("cranelift_codegen") + .build() + )); + TEST_LOGGER.log( + &log::Record::builder() + .level(log::Level::Warn) + .target("cranelift_codegen") + .args(format_args!("{}", MustNotFormat)) + .build(), + ); } fn clear_captured_logs() { diff --git a/crates/amplifier-core/src/module_resolver.rs b/crates/amplifier-core/src/module_resolver.rs index 89d6a1d..4aba0df 100644 --- a/crates/amplifier-core/src/module_resolver.rs +++ b/crates/amplifier-core/src/module_resolver.rs @@ -1625,7 +1625,7 @@ artifact = "module.wasm" std::fs::write(&wasm_file, &wasm_bytes).expect("write wasm"); // This is the actual sha256 of echo-tool.wasm. - let correct_hash = "114d733baedeec912b8da160adbc863ae14519b88f776d0d3c19f8446e73afb7"; + let correct_hash = "9b82c883832bec6b73fe3b4af31f65ed63f744a2081c5483895f6d3d62b3dd73"; let manifest = ModuleManifest { transport: Transport::Wasm, diff --git a/tests/test_hooks_request_id.py b/tests/test_hooks_request_id.py index 89657a2..453d917 100644 --- a/tests/test_hooks_request_id.py +++ b/tests/test_hooks_request_id.py @@ -218,16 +218,17 @@ async def test_concurrent_calls_get_distinct_ids_and_pair_correctly(): for event in ("llm:request", "llm:response"): registry.register(event, _recorder(seen), name=f"cap-{event}") - summarizer_requested = asyncio.Event() + agent_requested = asyncio.Event() summarizer_responded = asyncio.Event() async def agent_call(): await registry.emit("llm:request", {"caller": "agent"}) + agent_requested.set() await summarizer_responded.wait() await registry.emit("llm:response", {"caller": "agent"}) async def summarizer_call(): - await summarizer_requested.wait() + await agent_requested.wait() await registry.emit("llm:request", {"caller": "summarizer"}) await registry.emit("llm:response", {"caller": "summarizer"}) summarizer_responded.set() @@ -235,8 +236,6 @@ async def summarizer_call(): async def run(): task_agent = asyncio.create_task(agent_call()) task_summarizer = asyncio.create_task(summarizer_call()) - await asyncio.sleep(0) - summarizer_requested.set() await asyncio.gather(task_agent, task_summarizer) await run() From 2b2bc7e6f70e975208bc0ef6c561e60f4e79a99a Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Sat, 12 Sep 2026 07:59:04 -0700 Subject: [PATCH 17/27] fix(core): share native hook registry with Python coordinator Ensure Python and native/WASM dispatch use the coordinator registry, and verify mounted deny-hook behavior. Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- bindings/python/src/coordinator/mod.rs | 4 ++-- bindings/python/src/hooks.rs | 15 +++++++++++---- tests/test_wasm_integration.py | 18 +++++++++++++++--- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/bindings/python/src/coordinator/mod.rs b/bindings/python/src/coordinator/mod.rs index ead0986..ebf6188 100644 --- a/bindings/python/src/coordinator/mod.rs +++ b/bindings/python/src/coordinator/mod.rs @@ -138,8 +138,8 @@ impl PyCoordinator { let inner = Arc::new(amplifier_core::Coordinator::new(rust_config)); - // Create the hooks registry - let hooks_instance = Py::new(py, PyHookRegistry::new())?; + // Python and native/WASM dispatch must use the same registry. + let hooks_instance = Py::new(py, PyHookRegistry::from_shared(inner.hooks_shared()))?; let hooks_any: Py = hooks_instance.clone_ref(py).into_any(); // Create the cancellation token diff --git a/bindings/python/src/hooks.rs b/bindings/python/src/hooks.rs index b9dec4c..ebfa887 100644 --- a/bindings/python/src/hooks.rs +++ b/bindings/python/src/hooks.rs @@ -61,15 +61,22 @@ pub(crate) struct PyHookRegistry { unregister_fns: Arc>>>, } +impl PyHookRegistry { + /// Wrap the coordinator's registry so every transport shares dispatch. + pub(crate) fn from_shared(inner: Arc) -> Self { + Self { + inner, + unregister_fns: Arc::new(std::sync::Mutex::new(HashMap::new())), + } + } +} + #[pymethods] impl PyHookRegistry { /// Create a new empty hook registry. #[new] pub(crate) fn new() -> Self { - Self { - inner: Arc::new(amplifier_core::HookRegistry::new()), - unregister_fns: Arc::new(std::sync::Mutex::new(HashMap::new())), - } + Self::from_shared(Arc::new(amplifier_core::HookRegistry::new())) } /// Register a Python callable as a hook handler. diff --git a/tests/test_wasm_integration.py b/tests/test_wasm_integration.py index 656b569..f4ee444 100644 --- a/tests/test_wasm_integration.py +++ b/tests/test_wasm_integration.py @@ -147,7 +147,6 @@ async def test_load_passthrough_orchestrator_wasm(): shutil.rmtree(tmpdir, ignore_errors=True) -@pytest.mark.slow @pytest.mark.asyncio async def test_load_deny_hook_wasm(): """Load deny-hook.wasm via load_and_mount_wasm — hook module.""" @@ -157,8 +156,21 @@ async def test_load_deny_hook_wasm(): result = load_and_mount_wasm(coord, tmpdir) assert result["module_type"] == "hook" - assert result["status"] == "loaded" - assert "wrapper" in result + assert result["status"] == "mounted" + assert result["subscriptions_count"] == 1 + assert coord.hooks.list_handlers("tool:pre") == {"tool:pre": ["deny-all"]} + assert coord.mount_points["hooks"] is coord.hooks + + denial = await coord.hooks.emit("tool:pre", {"tool": "test-tool"}) + assert denial.action == "deny" + assert denial.reason == "Denied by WASM hook" + + # A separately constructed registry must not inherit coordinator hooks. + from amplifier_core._engine import RustHookRegistry + + standalone = RustHookRegistry() + assert standalone.list_handlers("tool:pre") == {"tool:pre": []} + assert (await standalone.emit("tool:pre", {})).action == "continue" finally: shutil.rmtree(tmpdir, ignore_errors=True) From c15f610b24b0ac173d6c503eb5813b025e12ea02 Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Sat, 12 Sep 2026 08:27:23 -0700 Subject: [PATCH 18/27] fix(core): lock protobuf stub regeneration Derive the proto-check generator versions from uv.lock and compare all four committed Python stubs byte-for-byte, including the package import adjustment. Refresh the checked-in schema stub from the locked toolchain and trigger the workflow when its inputs change. Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- .github/workflows/proto-check.yml | 65 ++++++++++++++----- .../_grpc_gen/amplifier_module_pb2.py | 6 +- 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/.github/workflows/proto-check.yml b/.github/workflows/proto-check.yml index cc2bb44..0c6c857 100644 --- a/.github/workflows/proto-check.yml +++ b/.github/workflows/proto-check.yml @@ -7,17 +7,23 @@ on: - 'proto/**' - 'python/amplifier_core/_grpc_gen/**' - 'crates/amplifier-core/src/generated/**' + - 'uv.lock' + - '.github/workflows/proto-check.yml' pull_request: branches: [main] paths: - 'proto/**' - 'python/amplifier_core/_grpc_gen/**' - 'crates/amplifier-core/src/generated/**' + - 'uv.lock' + - '.github/workflows/proto-check.yml' jobs: proto-sync: name: Verify Python stubs match proto runs-on: ubuntu-latest + env: + PROTO_CHECK_DIR: /tmp/proto_check steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 @@ -31,33 +37,56 @@ jobs: with: python-version: '3.12' - - name: Install grpcio-tools - run: pip install grpcio-tools + - name: Install locked protobuf generator and runtimes + run: | + python - <<'PY' + import subprocess + import sys + import tomllib + from pathlib import Path + + packages = tomllib.loads(Path("uv.lock").read_text())["package"] + versions = {package["name"]: package["version"] for package in packages} + names = ("grpcio", "grpcio-tools", "protobuf", "setuptools") + subprocess.check_call([ + sys.executable, "-m", "pip", "install", + *(f"{name}=={versions[name]}" for name in names), + ]) + PY - name: Regenerate Python stubs run: | - mkdir -p /tmp/proto_check + mkdir -p "$PROTO_CHECK_DIR" python -m grpc_tools.protoc \ -Iproto \ - --python_out=/tmp/proto_check \ - --grpc_python_out=/tmp/proto_check \ + --python_out="$PROTO_CHECK_DIR" \ + --grpc_python_out="$PROTO_CHECK_DIR" \ proto/amplifier_module.proto - name: Compare with committed stubs run: | - # Check that key message classes exist in regenerated output - python -c " - import importlib.util, sys - spec = importlib.util.spec_from_file_location('pb2', '/tmp/proto_check/amplifier_module_pb2.py') - mod = importlib.util.module_from_spec(spec) - spec.loader.exec_module(mod) - required = ['ChatRequest', 'ChatResponse', 'ToolResult', 'HookResult', 'ModuleInfo'] - missing = [t for t in required if not hasattr(mod, t)] - if missing: - print(f'FAIL: Missing types in regenerated stubs: {missing}') - sys.exit(1) - print('PASS: Regenerated stubs contain all required types') - " + python - <<'PY' + import os + from pathlib import Path + + generated = Path(os.environ["PROTO_CHECK_DIR"]) + mismatches = [] + for directory in (Path("proto"), Path("python/amplifier_core/_grpc_gen")): + for filename in ("amplifier_module_pb2.py", "amplifier_module_pb2_grpc.py"): + expected = (generated / filename).read_bytes() + if directory.name == "_grpc_gen" and filename.endswith("_grpc.py"): + # The packaged service stub uses its sibling schema module. + expected = expected.replace( + b"import amplifier_module_pb2 as amplifier__module__pb2", + b"from . import amplifier_module_pb2 as amplifier__module__pb2", + ) + committed = directory / filename + if committed.read_bytes() != expected: + mismatches.append(str(committed)) + if mismatches: + raise SystemExit("FAIL: regenerate committed stubs: " + ", ".join(mismatches)) + print("PASS: all four committed Python stubs match the locked generator") + PY rust-build: name: Rust build and test diff --git a/python/amplifier_core/_grpc_gen/amplifier_module_pb2.py b/python/amplifier_core/_grpc_gen/amplifier_module_pb2.py index 4559c62..0e11421 100644 --- a/python/amplifier_core/_grpc_gen/amplifier_module_pb2.py +++ b/python/amplifier_core/_grpc_gen/amplifier_module_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: amplifier_module.proto -# Protobuf Python Version: 7.35.1 +# Protobuf Python Version: 6.31.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -11,8 +11,8 @@ from google.protobuf.internal import builder as _builder _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, - 7, - 35, + 6, + 31, 1, '', 'amplifier_module.proto' From 2f0018dec455d3001ab877b563325e4136b7ccfe Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Sat, 12 Sep 2026 08:27:26 -0700 Subject: [PATCH 19/27] fix(core): preserve async hook loop ownership Prefer current Python TaskLocals for coroutine hooks and retain only the registration event loop as a native-callback fallback. Add loop and context propagation coverage plus a real passthrough orchestrator emit-hook regression fixture and rebuilt fixture binary. Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- bindings/python/src/bridges.rs | 31 ++++-- bindings/python/src/hooks.rs | 12 ++- .../python/tests/test_switchover_hooks.py | 45 +++++++++ .../wasm/passthrough-orchestrator.wasm | Bin 155238 -> 158483 bytes .../src/passthrough-orchestrator/Cargo.lock | 7 ++ .../passthrough-orchestrator/src/bindings.rs | 88 ++++++++++++++++-- .../src/passthrough-orchestrator/src/lib.rs | 13 +++ .../wit/orchestrator.wit | 4 + tests/test_wasm_integration.py | 41 ++++++++ 9 files changed, 225 insertions(+), 16 deletions(-) diff --git a/bindings/python/src/bridges.rs b/bindings/python/src/bridges.rs index 5195d78..9092c6d 100644 --- a/bindings/python/src/bridges.rs +++ b/bindings/python/src/bridges.rs @@ -32,6 +32,12 @@ use crate::helpers::{is_approval_granted, json_dumps_safe, try_model_dump}; /// and return a dict (or None for a default continue result). pub(crate) struct PyHookHandlerBridge { pub(crate) callable: Py, + /// Event-loop fallback captured while registering the handler. + /// + /// Native callbacks from blocking WASM work do not inherit Tokio task + /// locals. This intentionally retains only the loop, not a contextvars + /// snapshot, so the emitting task's current context remains authoritative. + pub(crate) fallback_locals: Option, } // Safety: Py is Send+Sync (PyO3 handles GIL acquisition). @@ -89,14 +95,24 @@ impl HookHandler for PyHookHandlerBridge { handler_name: None, })?; - // Step 2: If it's a coroutine, convert to a Rust Future via - // pyo3_async_runtimes::tokio::into_future() and await OUTSIDE the GIL. - // This is the key fix: the old code used run_coroutine_threadsafe / - // asyncio.run() which either deadlocked or created a throwaway event loop. - // into_future() properly drives the coroutine on the caller's event loop. + // Step 2: If it's a coroutine, convert to a Rust Future and await + // OUTSIDE the GIL. Prefer the current emitting task's locals, then + // fall back to the registration loop when a native callback (such as + // a blocking WASM host import) has lost Tokio task-local state. let py_result = if is_coro { let future = Python::try_attach(|py| { - pyo3_async_runtimes::tokio::into_future(py_result_or_coro.into_bound(py)) + let locals = pyo3_async_runtimes::tokio::get_current_locals(py) + .ok() + .or_else(|| self.fallback_locals.clone()) + .ok_or_else(|| { + pyo3::exceptions::PyRuntimeError::new_err( + "No running Python event loop available for coroutine conversion", + ) + })?; + pyo3_async_runtimes::into_future_with_locals( + &locals, + py_result_or_coro.into_bound(py), + ) }) .ok_or_else(|| HookError::HandlerFailed { message: "Failed to attach to Python runtime for coroutine conversion" @@ -109,7 +125,8 @@ impl HookHandler for PyHookHandlerBridge { })?; // Await OUTSIDE the GIL — drives the Python coroutine on the - // caller's asyncio event loop via pyo3-async-runtimes task locals. + // current task's loop, or its registration loop after native + // re-entry has lost task-local state. future.await.map_err(|e| HookError::HandlerFailed { message: format!("Python async handler error: {e}"), handler_name: None, diff --git a/bindings/python/src/hooks.rs b/bindings/python/src/hooks.rs index ebfa887..054c911 100644 --- a/bindings/python/src/hooks.rs +++ b/bindings/python/src/hooks.rs @@ -103,7 +103,17 @@ impl PyHookRegistry { ) -> PyResult> { let handler_name = name.unwrap_or_else(|| format!("_auto_{event}_{}", uuid::Uuid::new_v4())); - let bridge = Arc::new(PyHookHandlerBridge { callable: handler }); + // A native callback can re-enter the shared registry from a Tokio + // blocking thread, where task locals are unavailable. Keep the + // registration loop as a fallback only; do not snapshot contextvars, + // because the current emitting task's context must take precedence. + let fallback_locals = pyo3_async_runtimes::tokio::get_current_locals(py) + .ok() + .map(|locals| pyo3_async_runtimes::TaskLocals::new(locals.event_loop(py))); + let bridge = Arc::new(PyHookHandlerBridge { + callable: handler, + fallback_locals, + }); let unregister_fn = self.inner .register(event, bridge, priority, Some(handler_name.clone())); diff --git a/bindings/python/tests/test_switchover_hooks.py b/bindings/python/tests/test_switchover_hooks.py index d075cb6..990c7db 100644 --- a/bindings/python/tests/test_switchover_hooks.py +++ b/bindings/python/tests/test_switchover_hooks.py @@ -167,3 +167,48 @@ async def loop_detecting_handler(event, data): "Handler ran on a different event loop — " "this means asyncio.run() was used instead of into_future()" ) + + +def test_async_handler_registered_outside_event_loop_uses_emitting_loop(): + """An async handler registered synchronously can deny from asyncio.run().""" + import asyncio + + registry = RustHookRegistry() + + async def async_handler(event, data): + await asyncio.sleep(0) + return {"action": "deny", "reason": "async denial"} + + registry.register("test:async-hook", async_handler, 0, name="outside-loop") + + async def emit(): + return await registry.emit("test:async-hook", {}) + + result = asyncio.run(emit()) + assert result.action == "deny" + assert result.reason == "async denial" + + +@pytest.mark.asyncio +async def test_async_handler_uses_emitting_contextvars_not_registration_context(): + """The current emitting task's contextvars win over registration context.""" + import asyncio + import contextvars + + context_value = contextvars.ContextVar("hook_context_value") + registry = RustHookRegistry() + context_value.set("A") + + async def async_handler(event, data): + await asyncio.sleep(0) + return { + "action": "deny", + "reason": f"context={context_value.get()}", + } + + registry.register("test:async-hook", async_handler, 0, name="context-aware") + context_value.set("B") + + result = await registry.emit("test:async-hook", {}) + assert result.action == "deny" + assert result.reason == "context=B" diff --git a/tests/fixtures/wasm/passthrough-orchestrator.wasm b/tests/fixtures/wasm/passthrough-orchestrator.wasm index b5bcdcfd7f28f2514ae63adb6bafd771fc14555b..b8aa8a2a1e522e99d751904a552a1bfebbe807a9 100644 GIT binary patch delta 32011 zcmchA2Ygh;_WwPzCA-;_O##vfn-CzBKsMQI5~N);h*$tYKtMvuMhG2(V%?yiSSc4A z;GtqiMZt#{^;tmS$x~E7P*hY<>?o+%QU2dEcXzW1_pgXhr^7!|NPjIZ1Za&3N3Is1otbP&Hdynbb$LHTF7##dnD1 zBE~_s7}7Kn8j;0GHmlGq8db6PijTM1Y{F>-T%)L1 z(z{eDG@o4a5qW}I@3@+(>dBR}165O}&ZyD)IBz4;baD-g&1|2Q(W}T?*yjZ4BM#9) zx@h#sQFDK%jdU9sH0cidgVxZ)^c3Ao>!^wDp$F-Hx{n^9CulSMoi@>vbj7?!={mYL zIG?VkM!M<|+CY!fL$scrp%>_RdW^b0P0!J@^bfj|?xMAHH_d4{PPdCM=v$Ivp;#%d z7qO2$Z(Anx3j#-A{4OCD+{$v|vY-)7?{Y zEyjuG)14oi1oK#o;IQ0sba0%TnJe6jxsMp&vE&P@PG`q!oO19j&K>E35+2Ya{K9ym zG1XYpErb4HG?o)ZkWpcXAC z$Lg_QJ`|b_m}>Ql1^c#wu$YfuEaD_k_JroBH*$f?2gLcGM=Q{@1S{lR5M;sFf_~p= z`Zi9ue+txfc#3fX$$WvSR>YIXf^NuoKt2=yIO3MKaZ4ohGynze!TMa09gp$NF|}Na8j~AL zh4eEYjU(N!qZ^GGJ@Q(r!QDMphvofhOc`B*Lx3-c;}=%^fI?a#;G&8csK8W!ms?Rg zhk%b;=@~kbdo$yeLyQJ75Rb(Vek&l&up-4weJ-|RD9v@H@k;wrT4kJUpWR6b9#0h# zY))0{s9>@0B>mxoky_fn&AR1a?Y_fh@uv+1W%pA#B?IHngD{xUY7mTl^D@rA^>gBP`RYuOZ0dg`t%tBcifa*OOMV1(_u*RyGzV+#I<|A#Kb~0Q_~lf;;?@ zGa1(EODbriaqtp_Udn_KJvKfID&@q$Mp-W_(9E0zAQd$G6y0_%*lqLJbvt^0Ga=0khm8Z3gSln5iF5hqfr&o))Oc?q ztOqy9F*;UtYI8tQ<`+}hR>fMZLAq8LmsAzd!^SODJxLnRRxPAQjRDo8X{E8Ux(mc; zOLhBTM2w(N>nO(tgppcLxk{34tdb&1B}HqM6wN9L>lji=aXOV0VL25nr>rB8)1VSm z9`b%Nt$+>8jA*1%;Iewg0wzjdOGwVnv4@lOjIV_i@`P58dtANy5vC!lqEbh|y*gLu zC~KTopCD`+n}&SRNJ=X`maaig$ArpYcJ)OR0`w>Y@>mMAMWox$W;>HFZbd(jvQ;c{ zVROmq*;`{(R?2G8Q|m)Sft1-Wh-DEw2p$^*&Kv;=7F?8(&G^11pu@0_gb9x6C&uv4 zK+mFnVm#U&)atpDl{*MXIuKH24lZ*<$~;zMWo@=?C8KQIRh?$ByHCqqr)a@lNa}yd zT?ZT7Fn1kc3a}MAEq60d;jRu0vw0CdO5DXVJ&TmbwkJ*O+;!q^AtWq)l|Y@!X{Jp0C2$W9`OTqnU!O zmLCz4614ykbBkm<8pioN%hg*ikb7(?ln~-=4eU}#Ud|ZF;uql|3K@lu=z+PYoH{Vi$ zMN&o^V|(JVcBbDb^iaKr>+?`Q-F;{4xv0{MK;t8BPztsXeu>jO+TK?!II%-EZtmUI9vK z&Pi?zYZ?lmX zxd1)Yl$~+0JY@z;?z@f8+FVEP8k5`3gKB@=wsVJ8oufT+v|psC-=|$XW;mqX6Sg}T zr14!wx{;oCIlXQyO=}0rJ)G7N>DSZRWrc@i^QE`(iz7_sbr{<2etlUww@pj0rH71b z(ywYg;Ux;~+~&tzmmHTPva*qUEs7^Cr++Oo`! zt?Pzy-AL5!%v{;JZW7l`Mcq|d8LjIka$POz)@S)rA7XOp_8f=D29ohA>0W0X&$^OK z4lZb~8xynd>w6D3i?hSUWmAEL(eA!i6+ta*dc0qx!W28)rcl_h?3%t794@v813FxG zrU?Pk9EM=^*r&MfQ$WCV8|xN;U@1KX4scVuJ-|sd;yo)xwawdj3r&$!|Qy! zYo_US@i6Zi(O#rRW7`6wZk&U9)Rf!uqzre`ag8_ zLM1wN8=W08{e*x+WRu^#<2U*h+@5Aq8ak`{)EW@6zlRIXfRcN9vY3?iG)`$4Su8&8 z+3l1{Wdm4#^LlwtsSF!DV_L8Fy<3l;#wrFrv2bD6s8f!UMa9X!snEAXmar#;be~Oa z9876g&4-Ocy>z>|^D!O|c!F;-0EXqru?Vc`{ExF%U8wkka+1uVINoStAS*U}p*=3| zl|l|1tP+~3jRXf6=tL~`{~$KtH_kho7a2{T&uQ#!Ju2p4#n#@b6FYgRFqYTl8Q&H@ z0b;K8ruNu^rf@%$x2fA%aRm&ANd>{$4;&-ZJLv6Fb2oQjdmN5ixW11eM>sO$nUX8= z&;l_E9jM~{3 zAV|p0x>fC$S1#Yuh#(9XX!+W5&hd6yE$xmt2k%9XAKv$<-7G}x#bK@^aQy`~HR`6A z#R?72F3!E2@%D%U&8=I5gW?h3;JvWB)B+)Zqu@68;mj5j0;`96DR%;|KzNJ(yFy5C z@Pc=#3^muLaQ*`|gl?Fh!}LI?x0b~t02jYVGh16Z`l4~a3mrLj5GtgzCco`DG2jzQ zW6;{p4ecu0V`uk}DQm%ySc`JVW6c*UEoyrg@+#qi8h1|dpNaL<^e&YE#&`%}3(HU* z6P^*d7(%{SBp4h#QJr{?h&D?syrBbN+>K)owV7z;S%o%y*nW$Kdc-H|b1-Y*W@Uuz zXu*p&mIV9&u#-`9dK?g_TryjuC-6f{7f;L1`>%7Kn1s&T1-r2#>)VmGl1NJjk0J35Y09;TW9SXFpig;VdB}h|)vq)o_j$AA29;gNN zx=AsgOWE~SWkE1TiH6k!kz)>Oysi?jY|J+I3X}JkV1=o8$VV%lQXaO4U^7%5J;Tv7 zB`x26^~5Jp;>mVJ@QxJNX*|Xm0(aobQ7Q}=R=7l$4s|!R8M?2pTmjK{hvGLoyI5?9 zQp6co4d@=62>F8GkH-(x3?7JnjQx9kzgF+Qi*aXeMgL=J&CEqKh3TS@Zpg)Iek-Aq zsjmZhif%?x=xdeEG5CRNQED$0JRl^TN4k~jgQ1~bLZyaHlh9Wp*5IhmWrYvVjC-hU z_)3D|d7PV~7KT-KVL#67vY8s3rb^5V>*Y$yjVaa6M1TBbf@08VRjY9UZyJMaQ9J2SYe)}d4bE<9Evm0GaCcvMJ2m86ww zq_1KEz|0Hxe#V=X7X*jh(k-yWdQgKihAF@X=-OZq0BJ~v$MF!MzvD2R7!)vxhYF z0WSFQg-~ZDimXx482#IqHb+_CwctMs;~JI&{H~->KOh?JhuMMG7tFNk;jR2 zXr>S7%>$Y1!4Aff*8^C9d~F-&BI|+292@qQeZ#cS$ce2+P7IR|ARIxp$h;5*w5#?M z=af+BPQR!%fldTaR}<`n>u467%y!%;&kv@}l~*M`IVT3CWxatG226$o+YJw{7c9^RW_=}Mv`lqdZw<1_dVsr93@i_3*S{iB5D%wJ&G>5?{cn;e z7}kt`L!oJx-5@}WEaWvOfq%y5B${XzXk&Dm1;^Br0BDH-m0&uAf(<(N_avArf+vv;yomlkRj6#2_#IjEg}T?W1WdRY#UwK)nNS}M_rSMVr$oX%;2is^@6+I@@K69ko) zQ#xnRYI|dFK^`8GxY+8+AtHbXpInVME8`DAQ>U(!9c&WR5-L#(p&C;OIgi;)n7DY3I#|OyB&4G@o16~v<`B^cJlJn; z6tyG$ANQ~S^A7jlw&4AW8on9F|Lq~wh#^DXh+tKC9}B(^+9SRIk&&5dbSAkpmV z2gj5u;Z}a`K`m_hLm?elP8-`7G|d+XO|wz}1y%~IYaFw~<0n-F&G2B40-Zs?Ftj^? z#PdH7v?Y9MElOM1!O9mwCAq}OXl>!ivSLM^ORHuIjg|t=T7~Fe=qLjM>yg0$U?bJ% z!5%f@L>dGBq9+9YWQvWDPBrutx)}y5%~I z>H5nD_>MS43K7@8#aK9^gV%)5n^rFS+C(=|^*$@^}#at&ujp^VlXX=TkzY zLfH5XZHf{84#i~zLOvX$aOHqG95%X*!$#OW-@tuw780RsRpfHHo54S8teieDUCn^m z$wzB&qV$D3cxF!b9^;4Ui*S_e>KQc&EeT_MGb78fhMVCys*yBvvGX%U4XgWkZ$IJj4Q6}c$#`k zq`v9O6+tD4{B#sNhkD$|%N4Su?@(x_nYyM*4yuyOsFL4RNk&x33dtl&k1BawmBdAr zJgZ7lR7ubr?DeQ3v*e&ENr>w9?I|TER7o`GMJsvu=w5f6QgXj4F^AWJYGzHUD0&{d zRY||7;TNxB(5X=+_o)(JRLMK4q(@ZA^{cs8UR23Lsw9k}mr1=@6@@S|iLXksqPiVc zC18i~+tsr(Z@dm*p=UUW`XzHLN9j7-x2?vF*UU6#&bJ$XT(dl5yOK*0$IqZWSOnbv z3>Cy08-s1=Gh^$u#kl`?tq=EZK~7Il_hst-Pj&xI-Fr6HrtIL}SoZLYKJu{tfyN=B z^3WoKl?Dgb4KaT@y<*Io-`%(FaYoiwpur+G^rCftM|SpB-O<2hcqrJ=GgQWTiPhE|G4fVdf!O8{uY$od3}2|%PrUUr}vjFxR72pMl7g6msJb$ z-R6ifkr9pE3sREIaXlyvkMZLI9;3^`k$`&j!X*lJs*!U;txB~u*4$8ykxt$)3uKvf zW4El8O~5?-GJq!rfex!4E;E{L%tqt4Z#<{NX{e;AP96~84B}Jun=V{*7dd`;%7kb9 za?{<8MIqqE-8c6-EujzH942%N25jng%b!F$jlQ=njajDv{$lLAZ7=OG{+f)#A8FIbW3TCd2a z&LSKAmYw(i2y9FUtTA&%2bVIj9taIyTUouLy_r|`c(d`+icWb8o@O?lVC>ir;4W?adHX0XWS_> z>0_DQg}N9A<>Zb{JdAqaEpWb;9SCGZ_&|gO(a6ol)Rh-wWxr%tI_?Ko4grwvs{q9K za99^(`YLbYo6ll6Oh-8hVt_GxO;(P%O7f9Zz~%D zIkV>ez{p+Q%egz!a_nkP^q~OAJ(}FSx>HgF7*B#B7!y}#fRXR79^rgVb!T3e{*C>C zv488B^O7pFnTVR+42~&S^j9Kn@A_LuuZfg}B~LvcCiiYdB=u&1;4{s@fSnk4wrra| zHeOlV{PqCcWK=7U$LO-Ay>nM&8n>@WH*R|{2DnUL)6Tgis+?V!4Qt+_{vY!|yc@yM z2o;G{qrqL)_k^2s3y?J22FC*s1jUni*y;~9ZN2L(iXvRXS{CbGYuoj037CB^p2jJF zBVZwQP_PhK_6 zzB9sBasziZR+2e(kagG_fEoQ^?v;DtpQTR4`V zh!cB?VDeFOQ-VG+zF)_#ZQjFuqbOPbFuSX>?r+!K$?w59AA;9$s^~DXmMCNoJ;W}l&vpuZjFfV z2albHcXGNt-T^cj@_4V#>Lr}sT0B-dyz3BtE2p={TY5WYiEW-8#_jIMb9sS#tXsA@ zQEVF_5_so>Jv6)C4FA5zy#F6Zi`_6f{RGeL49yL>v+8StQ$3wM)gt1{m zJKSH{Fd*C#%YW;Zo{eXPYgsp2!MSGRnSii$Bg1)jV_)3cJ#kjd5gyn5i&6c=eQ-PM zf6p&yd>tjZaRW8?D&FZsgm>Y&sCA&G{|gVEr|6ajoIT+-+`<_5_n|oI`RL#OmK|=* zu^9V6ZQ!2UZrl6d{x7aSVm7SZiuO3XrxxeJR`zvGCv!$*r zcOnYGPP+R`W7ta`_m(xt>yP2k7+1J|G#X#3496Bi+e%~DKd$c3GTgubU3j_KeT(VK z%vyFR&9FSj-ek|`(mHQZyABmaioDrI-25vO`B*Hta0{nCcRo5i`&_T&m$`#Sgu`7H z_dku_pF2DAnAzOL&6k)X!S^8KzBev;zSmi|n`JquXAOdPw1lS<@s$>Za)`l1TC8o+ zV&xiR*Yj=ZsPXypJ(WMy!x;EN8wgsnPxQAJ{!_oI%&2^E6%5I*FP4eMyNu#3H+NZh zKa=+rO<%`}@GGeL)2jGQO+RQB8;7^_$~vafp;wn-6`U{?;WDGw*0F$m^VZ%B`JdPy zExL;VUU!|5_>$kX?*EFb*@o+7uWKC>nJ) zljwkP%eIV|513ExpN;j~x`q5@756m`Z5wdTtq(GVI_YcqH%uVqV~8b=Qp-w*zUu*Q z(H*kA5=`Erawqui4jtms2^RQcqkj9R^pi2{pZ(~kO^g58m5_Pn6=aNGUYUdY>pMCd z%Xaiusd0#(bp02{7%%#l*Sqsk0E2mrtJlpW9AyOI?_leqxzaTKu7??eqnf^-6Ht<- zf9Z+a)+5ck9Knv`5hLc+{I~@RwlFQR;y}I5cr_D(HTqSZ{xDX(S{ApcSwp`xK6o_) zqyO@1w>D}6p*LFj^f@TmsE&jgrLX0s>}IlooLpxafCE}$n=$>hoV=fy(B@f8gk1TN zhj6R52pM8USFerk9MR>la+(3!=4Sxh^kd`Fof9&C zViW}S68T~|_BiU)HT2B#^aI96J2U7loj@$ZB+} z{-sg7E02CNZrU}hC`2&ks~OSuSY65}EoPiGR}st$yeGc#IXba&pW)h_;S5>3Rb*7j zRAb=od|10FyVLOo?84nS*v^{it-CuUuHe{k4vsByRCvWcY2r4?7`}Xv2d-~?aO?!G}Kh5%@pQKsf5Il-I)L<5Kusik{Z|^BBdVcYi zu;4~k56}_2qR{HGln3U6)8;?-a-;uS9Vgw&rD~hOZBxy*>~1otaO+U7t0!#W$LX7f z)(ErdMm6I*sd)jz@lgfVc=fGbS*x1$=M)@`HhT+`58f7@gx(lZ)X{r8i#7X<;NEu5 zB}$1C@D|_QdlzMyECmffkTpz@T&R-AlGz(MnrwsAio($%s~ zaR%oR;mrhx<1j4790+#;Ct*XCxppiJ*5SF7S!%ZS*b#f@7aM?#$5x8r021`)v|F$_ zVXY9xhPPM6Kf1)E&qf6Be={z6ClFT2(9;!T$2$YXqf3mq{g;Jn!Y;s){i_Ftw4g<> z8NQb_q!WoQ_YJ0W0uy38wwC7{j)*PA8wtieds2=NJ)ynaM*4z|U0|!k2GWq>}XY5h4 z{dd6lwE&oXKJl}I$;OOf@H>rL-q+nbxd^T~km15z#)0=Q%w5GZL^p*6H^7nkP;V~m zt!*)e9cUZ8l~)w~{nMbvs;D+6Sh7Cn498Z ztt>1>fwqNPu;6%jT!oCn>$!cgJUVxL4yNRQKkhkINqFradh7k5E3G!hec%r+=VFM5 zX$e%0M_$6kIb4e6O=Ubdh|ZyCY{*FORj)5%MRos;X~A<*73_hzmvIr0#sj4zN&{6J z#v5mhTMv_<#zCD&jbmhgSOWVz^20|(# zA7$Zl56^$pqt-Mm*b>IE@pm`dAZ`nth6Txs*q_$TSOf~R<9j1B?rml~Ocym8X18&j zasI&^`qG$ju)6DSJVk`zA>;0oQRzQ9?cyhgyb_T6JL9*5y}RDYPJ+%W67YVGrNVs^ zyA@_g9*5!0dIat-Idn$Gi70sUxmjqO#_Kly{ZJLL>(}O!>0#Z({CMHowDgk<_&S@` zANCMlH+}un(Qp(yeOAt`Cw?|7+&Zk|AAMFaKI}VU1kHR6+T0d^SmqN0v(WS^TNQ3J z4Oy#}%7;`fjmFO>w;nU?3lA1+^IrZ%0n(R#F%6E`&Z)<6=a;H~{xUJClcqy6YQXVgM`W`gd6y5ZcwXfx%MoG-r`)Y+L|vTi^$6 zG)q7?ILk9<>H8nq_lRvyE@DNhtEtsn%tlJVIqA|H8@B<_G0vQWB~Qx{W>L^Ao1(vq za*UJ%2gfYJLDCQu)yppqGp5jH4E2kd)PsI;Jd}(Y03Ra4sZz5uw_enoT);^S)<1p8 z+ep=Wrdss{D#d*O8#N~wB3@MDyj5RhwhQG}nmMa}tJz5v-i8ZyOW7Pe;YP@6X2OXv_D7;cCo9RsZ>YCaN=j7#45#!^kge35?=ej1CR zC;zlENp;V~UPrN(M@jzq(i~L^KH~h98VY96f%h5W;t>32Tn-wmf9@0)nm?2Fpz+$z zo(W34)M}D1o@5%TcjNwNF@^F*#PqL))HvstBF{Dk8^PmN&haUpkZLS=*ZA8nIeFnm zY}l7@6Wuy1-X;d{Keb2Dm%nQq{KcELht)h6Z)psQ$7VYR-_zjPZG6}0`fH!=&A_=( zi6uNlJ3dX}v7C>%DcE&xJPr-maVTTSuX(h`*!(MW{$1nfuN^xm$PAYS)?Ux==;iR+ zl^`l!`rc!BfAe(>p*9;!EFz~nd11-U5jb*FDA6`(*xbTmK!BzuAVS?wQ<^WFYl;GZrHB38>Tsb+XC|-&$ zt#ICfg-})hC1ZfOqCFbtsTZ0+t{8lxZPcGbl0s(0$}+*&?=_zIGYh8VjX$TQEZEP? z$kCeRWxsL3Ne@iMjFT^&p@_leLCIG{mOM7r0u)clMXUk>%mNcNH6U|j#Z=0rZ7UYz z{?dxSQ(jj!5{C~o-5ivApc}6>cw6pTkK^)Lxv_q+f5l09^;DeYdqhB5{!BCnHcUQ-e{w-Nqd!mUW--Rav5X0 zSAK7!=X*w`Mq#R9L~Q4nY79x)d=M|joZ$y{dL&FcGn&NwmMdcv?Ka2IoJ5oOypdP^ zWvPR}ZaKz5TWOojaMHQi$`HT_0E?St1G0}Xu|&x0ozyLYuQh`23r>77@_?KaOM{xx zhW;xFzMF#Q~pS~&E1I%0>_(?bvj)nBd!of8G`)XMdzGmiWkMvd;jg+E{Ug`@)b@B5e~!fK1S2$ z%?dd(IZ>QSNKl;WoWra(Agh|+R| zoaLZ&`AY(=PBy(Tyxxsh-BlFgxV%4+&Y`XH+e9jDx0RP0g&^qzlrx|gV%sVExM>&` zHF>+6CS?6JCgY=Q7cT|M zdf_;xiZenWSx5KEk?Ax$Y<92+agY6SU%E0oKc&;ZAcI>n=xq9zbY#+WaOCn#>WIR> zWzueXMNZ422|X0=c<1M+Hv_M4vMg$@bj<|#3vb}{Af(B?Q~sPq-Ggs%1NIDgKOU#H zSvz=z#feLy93&vibmDD3S~%S5nU(qBKR&X*o%^F3MAxmJPPz1dYVKFfVLe>>I;Yj4 zb+ZBX{Go=gXzqkiOViT)A~&?BcERg;$ETk>X_@6RPZYa((-taG@#KY^*IX7{HF&(j zv^t?;6Us&)3@<0TnWz*tkzBwYDgNm@ zwR1DoL*|YD;5jbefV(>AlIng>ft;p5s!`wPf?PcLXzJAwUmb4uok>dG#q%Qp<}o<* zm84FT+QketWT^=%2|(3H3;@x%PtD&?K?eR5X_g$*i4d)qS9YRd%}-RYr_>?B?k0Jl z6BWjX&vbFo$1=S$m50t^@d*Lo#~XG)f$W73iu1z}iDSt9+rIi7Pp+eWfPceJz^=kG zgMv6bTnc@i_wanOKFy%7n+A2Ic~<&bzF0sv#TKO%2HIBqeWS=DvN7TmZZwup)zUp!s;HABfa>%^b#3YL>okL8wj89fNDZ>9h^}|u%EZxJTh$kbQsvBIDo^}gi44NKv3$tz2l;d{r2LpX zR7{;k<90dAM{Vf`>G9za+jZOJ_`%dEs(6tPr!|ksZ9eKli<+o}ZV{QczRaAoxsNem zC>H5URf*MoOg>afvj?rXoj#pg9_2a#0=RSLHMZRG844))L()Po6;+akgjBezZ*ztJz63cdpZhX z4GK}HV$tV_j)`Ig4F9c=ENde zQ$eFnk=vxflq!1;!iV%?55>ywDk$SGoAgTjH7xY%WbF@f>L3~nIo>>odMN2WTM4}a znQ%5xgXBLoh`P6|J13+j>5bc2;`vo4KG?-C>H}wmDIlB^R-1p{ELA=}m~sa%H8m85 zpHHVO3+1rVB4?O66_Q=ULwa}xHsiEyc&z}j8sK80@W-aKbLcS9KKaKGddjg(A^7BW z`HyqyT+y^$I?kg(;+gGo*m;Ca_jY;Jc~q3Lkm0j8tv8tyaxauy&ZAyp`Szyo&Z8k# zamRMqcQ|>4+%9Jhr@}aeh56k-O)fr<(&V<`l#TWh?bGB5&aB=p`(H?DE_Hm1PuHzd z1&xU}@NiHd9Fz0Jx^IwgTuA-J^V?JglRf}&X^TU~AvT_Vfq$6?*j-kh{c@r4s2>q@%<%)}Gl=$LJ zsgI+cGIK1sN}gt4DEvSX#9+y@>0fY{L7DYA_;hU!Ry%yrIaCOL(B+cPkE3+4V2`|P zEOikJ_sF})Qk$4{3e;`#A7iPr^Y{*P(4XX2W9cH=B?pY7-tA65+Ag_t994+>Uz7XC z0p0_zNg5voul;zMo3O5>8CyG^E=;)VI%ZbHw5hUpT9h?SyaY~mLZdoEqCU~goc?(N z4N3fhv1MsfC%O*EVU;vBVf*zA(V^q8S_~S#T1f*EZf{wZHj&!Xm(o9xxZn&FyUt84y_6avDSy+Mlju>h*@atMAT9NDMw>BKw-(ei?J1#YPW-C898pij zox)WkV>HbcuEOswtEP1f=jEgIbfzcV)PgF8^0>vVUC&JbwGh8DqlyLu_hYP?$i0s% z6<1x=)HwnC-eh-cqwxCzF2WV=-4?xjA&(y|4i|uHaMIN;Bc%GzoIPXO%m&Sle7Ftp z)f~9EP53{+6`E6?F@??;lLPE#pyC`CKHG=iirKSg%*Jo)aT@}28hS_mwn?C7(u{(J z88fC%o<4JKL*TN&^ajIKfX%HF2~?5@tUEWt)Iv27beN?wk^pcOu^}DsMx+I4YlrJlupx|3?9@Z&dz4 zO_$Q~rta6k$;xj{jo2Qsy*=n3=GFy(hyOJ><0abB zG%83|hkW1~Dr*{hBjMY1&)8(c1{<{{=0{aUA()Y)ywZP_{+t{+EBNakEiR)2ZTX5~f^$xBhxM-ML zbK}a!<;67`*J51jaJ`7@HC%gfeTeH@T(;qEEd^H&uAaCC;usAJjSdJKx|JX}mKzE~S@kZJE$4Tofq#N&>W_87OOefAJp zr&mo2XxZ)MhJBRoyc~bphwgZYR?8)?(d3kBe_>H^O>J$NKTuQdD~1y^M^^5p&hnYp zD7QyBD@G;%(I2n(a-=3tn>k~4Ly@<1Qf;85xT?%wTv%RPROYMpRS4n z<;tsfVsmCKj$|CMb0%LIm{WOapur}e-$^~%+LJ0P8JWr&{1KhXnuZy(=ftcLa_Rx> z3-8%WN0a@f{%UWPucW4~q@=pMtgONzuh~TfDMh6KimS6W`~uPZ963sm%+I`y(?JxdA;%ZiFid?kKwSxISO3FeAFhq0SR zrub^A0);+*ahbQQsw&{Es&LISDe(1f(vwSTii-+st4n>p!jh7z;_8Yx*>4Y(r_`2J z7nW4j)%j|QeZ}6Q0A6^P3x|jfvgbP#Bm0wxlezCxiahu(Ig9ho9+?N4`Td2yLSIe5 z8z?WVDG!wWHEIc!Ob3sAWDoUBt|{?V`-`dz>k3P&YXW6O6^YXOC6#se6=C4=lCqir zaI3B>{%de<`TpxPGC5FKQd3$|R8w6AoNK%_6-n~gmsFTsURYl4t@49tUO#B-t4Nkt zy-QaWRF&1%m6Vm%)|Li*es5h}XaH|XNg;SsT3k|E>MJcRD=qUDrwo$ceF;~&^gYT+ z4pfzt*Ob(j7Z-y%CFR8xsj~h(>YnVa4R~wHi>hjC>WZsseg29zGU+p#kQVS2)z
  • rX< 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 V5ws_{x{;P=-0sSgyDkxT{2%kw3Zno3 delta 28742 zcmchA33yb+^6#mhnPig5GFgB?0+~qyBtT#$>x9i5B<#o{>J`P1WdcO@O;lteq9P)K z2W(JKR9pc?K~XMhR1`#1R6sx!+&5INqM{3N|k6~G>Z**mx{YYwAHHF@y`|$OPUr<8dk$0UD&p}7&4Ko8 zwrGKtwYYd|91x5SjuH3_I5K0=MSQ%?W)rc-V~$HH+c@p;QI6q@a|cS}YO1QoRZjI+ zjUPX$Mk|Y1K&0v97@E???dqJ_#p@~igL)4Agg&O>BZgf(^#rY_1+jT z_tV3)iSDC^=mAT1RVX4LwfJ(LZSmJweaX z3)FTqJx}-0y|ju}(&Q=sqT9un^gS&VbHy^zAY%7CX1hxWV{^Al=vE`X`}!6;8yXrk zm(aIq^)8VskjxcU=ZnUB-MyXHb6K2lFsxG+Rm2mgJTB77xlxr7%JkfN-8$7d-|+NE zr;Wy-9_h_gAs6k|>T`hDpgG=XvvGEhRut}U2dVy?++r&FTi^517H=^oY6Z(&T$Y2f zt1nn}cA1CEicpqWzP0aJ^~$-dxAR4A&25RZF(I}Ru>;iWY4xrs=UMn^jGuwFs`TcD zPftF(uUszgq{?&8F7t6&_h8-U^mMwzcwfJnb{YTZ)vwKSOo2{ryUPNOJ;S-I=y>oG z0Ow)jrCyndXQ3EJd;QijG$lbc%vW4rNa*CR3&)<#z*$VdY?NKsyGN(9YBs2v^H5W< zA|@`DS;5Vc^H$^Y-g9Z6QCBgG4jYFn+GUv#Rx z<6f*9S<2&1=$}P*8Q%UGXOF%g6FCSNwf%GZgop_8Z8Dcl4~?BizJcnU^O+SwiO2@Z zvQ)%tjJAm|MQP)8yK|$_bHF2Kck>U%xsoSfKsLsmIj|YVT|97k!C767WxVmhE^TpY z)XjsEcLWN?qdu)FHyc!xxR!bLMLG^;>pLshJd|RV@tA#;S!{y1kw& zG8mcEdY3I%U|>pwjO`b#6DRI5W(>)tw~YISG)L3V4#`h5^{@#aDbx7;Ipj+qnl$`6 z^yd6KRd1jnvbUI^9-q_Oqr)cC0wZnswJD1B#0b)zw;OAQAEeuiz{M`Q-B^8b7fk2w zi+QZx5iN|97iTs%iL&UfdY2_vIQfI^lXt{wT4cOGqVHJ9TUFGw%OB!?-rRd1;6%5T!^RbQ9(@Rkxa! zm&Mr`w5=|3j3+WqT-JI}*kVIGtfs|A%HCwLnHi9mCLvN-5F!)HQ~~C8qQh{`>VmTvPu+VvNd+OnTl3WaQAj#+r;SqH&M0H^bd}6E~7&jTbt>dK|tHInAMK zy4F<#MoMeWC}6=M;t}*jRN%>$0=^ z53Nhg$r54J$sNO@9Me@}8XLn)>sn*zk7-d>w(8PIx>AqTvFy2s4TOCtH^5`D zyRD*JlylOmi$3w1nU2dMUF)ZEuoURV7H4rdLn{+n3NKzZUc3MZ&ZqI|m8qHJKr8SK zpu8@ti%!O3o?U9D?D(=738S`$*Cm*RbEMj|l2L3#1T5fAgRWwAY<^XH=dDaQOL}mw zpNSt&XHMX(gwM|aY#fu=&K891P;PHm`HyS^+>~RLU zhOgDI*2h_bb&tNrL<)BJg_7$9#v`rfB!KduBLO4W1j!^5iG^VEmne5x^EH?u=o=TYyjxip zV_X4z$7NZeJEpd|pyNR|F}5NeljpXva@i{2HEOO{SB%T1)C*l}`PwdJ6IHV+>0HJX zu)2W3z0I%gK{ak_7`>rk{l{0IT^kiJ8H^cUgQ;R!S1+c(W;p2InAsY=KAP3v-K4B_og+Bm*po>zHbc5~A2I z%c@5yv&8ImSrcp)i-oWmGb{5hBfP*tRtGWbKya=YsyL@sDbt*=81bIOl&T;qCe%a? zt#SddD>zPDGu%VI)=C zU9=NM0L^tXnV737VKN(sF&t{PJqS%A*H17%Y)XKv7-3Lc&LBFPMTO zc|kfG_#64zXcIGnjYEJ<0dnSSn9~C-iJX$r|6xi{`RpmF4Nu9~v!^789i9@P26)xL zoG@-^PD}~r`GZ<3(jamXHiJ!uV7A!0YS8Q82$WF4)Bu30|Jwnu{QcjJ;NK3w6{NpA z8@TH&@UKe5 zSi>FP3f7#r>2WLq`rjyIc@!u#Rf4)|U>iWnT0^23Jtr#~Lx8_vkHY~pdw+!!Z4A4? z9f$_*ta>()D2sN(bhue)(O!bf+J)}23}Mohh2(Z~#eBh?v~)%5RF9T{v)JNJaK)fI zd1c@+0OdU781yfu*JE|BcPFB-0;NoMJW!(DPDa5OdUEmzoUTN*=9uWH zictX5!K05y#o$O1jf>{OZU?cPJRT=wp=;B?#ekZ)@%8#bQ4b0dAlLQIN7d2*(s;zK zIBq%)W*8J;igN{YjBP7Ywct=Fv|>S7zBULJPBoq@`YHPGq1Fw1R&-xx7c+-s1GM+4bo^4<|WueAhd=BM+?t27t4V;+@)CKIlq`N;JBb;lc02m z1vRvNb220vvKNLp%*73oWaBaz@uo6Nbcu*_LAH5H1DY!l8-k#x`{^pp+mxvK&YHU`Lxx6S&B$u!;Wv^y(sRdyUm(?_@5CR@{h&5-r ze}OP-Fo7noO))T+5T4P0GV9}$QOKqo#!N}X8gj=mZ&n9+v=TfLP+R^Vx!k8Tt@Fve z#3#9uL%d+w38Vy9b`)x=FkXqRL8stM!;BHI%r%fJpzJcQqimP4kRpY3 zvqZR%FnQo&8DulgRv^y#SV$84>DE4`9OkjlIT~2ZuB}{d%Fiq|$;ukcW*rU&TG<)| zoyAo2&w+_1Q<2K*0LLuS<57O4Vmf0s@h2`twKO~2*%>wfQpO68VPa-Olg~s3)dFLN zRs@tV2?ak0!IP$ROhnoi zk&bFgV>{f0-w@h$JM_SD)yINhAOABI|1U5I!X6~2@Bd=tm=qO(P<{k&9Kv=^#bMQ{ zfRQ+}lNn)ZqkMMpm7!nrwxJ)-rd3AgSzaUX;J9oRigHK8wt#F4 zWXy_;2)Wsy9({7UNc)?iZ>KtnNZC4RJVhDFx2++gb1%L@| zPY7_oyOZ^uc$_0W@O^V*=Tps$KizFZW$vsIq2x7LxhZ#rwi_I=<6YO?%Zz8TvO_2z zW)-AA&XU*);TotSfi@M!!v`#mk&~U3V%il8BcK?~I3G4H&+Zgz;@0e*!Q{s6*vFa+(}p#p8*(IHIR)(#gZKDGsfDJRuBGhchmXwxx0gukd`E8~)mrA^sF z>qe`^V$8X(y>YZ-I(=%`J7tDS^EwRy;Mz|8LV(vg%?KsC<`>at#-x12RZMxy>*O{z z<+p9NTyf=BGwjr8w^$IVA>+sVe3C{+=gzd&7})t{T4B7``7&B(^w8f$Lo(9(Vt*Fw=I;ccN2U1$ex6^WWip`xttj- z%FX{A)J7;stqsPdp2Pqlqn@{X%;W#B%EF|=; z^q)Qdu>Na0LMg0B*qb2Mi?f2@Y7a|~dMe@qCzmkaW)Dc%S%4U59QF9w7aGB57d^#4 z_^X#|eot{FaIq9|C1FhQrsT7=&Jz$jkj3i|Qlo(b4yr!!u}2M^m)~{9)832$wkEFw z)PuF4T@3`R4b#tjfK@Sv8^S^tY63zFt#NC?74bonW)9H!sh~ej1Q>sxBuoE7#WEKd(@WO}APVR^nzt=kupJ9UmU2Kf-ox2u&Lt!fSF;c+&;W^F|5Q z7?zmrvVdJS-32DYC3r8VnJ|}dlWN{=H7+Wfi;=!pR+ttv(5a9${dv|sh?j7Fb))gr z%r+tG{qGls4rm%RoZm2)6CH^vhx*@{`J`YTC%NAnn`gBfX&z_bM2`5x90!ZR5E@Hg zFoY(s(c&$b!>x&OHt2722oKS}o4HLKj$n_jKn{nqsrk)D>9xIDs&$Or9~FYj)t|c? z1JYxh?;3YqJD(OB>DSdH1SKxGh#Cv8%djtHG(?|`4cFZn^9eHnPRJIc-}P*2uf9Gr zXRm_UoLiU#=AnSuzz0^GgJJY1ug?bh``5RF6#j92R&#S%p*WmJ2pz7jT3HSl-r4Cf zpQ%cOJ}#N<%4tHxjA7#sbWgDLJz>NZvt6`xSh21+9aI3mCmuL+!YfFTl^2Dl}_1N*sOD?BeXqaojqSz-wT zjf-z8hIgU3Kq-R9;x31u(QzIbvDo9ZT z_umsqqL*2{UV(xXnao#^jEHK76a>>@thjY*`u4eKL`gX8WdjsXnFcn8FB$FTPo`H4 zIe$f~SCv$XIE1lG+xl7%5Nixs(2PDeM&DM5&!x8&;qy(E{#$)|7cktfK3AyEw-?l= zyw0_ua9B%mLB_l}(EIiQK{#wT!|{PjDaHf0Po!;zb79A#)lV|1HY`XwP47ahpFwu& zE4qC;!vtWRgD}op%)Bvap%>L3SU7`THCo)!6XUt`j>~DkvEh!}0qMS|H6SAw^#
    Al_<2zU@O^jY2D^(sPj`Tw(H-)k?v~!k-Mw+=2sD_!c(Ljz)o5H?t5VI4ZcD1s z?R`t8fGf_sI%F)~j6sCX3|;V^VD&>V|suKWK;X z>x#>0yD|8l9vIH;_Y9!p#`t?PjGymGp*M`Edo%5gA#(EX?Wqd8pkO5mb~hEYFb>|E zuj>1Z@|CgR`M{N(^0u=`sH-vX_BWVBnEo?(Vgu|*sD;%lZ%h1?v9sU=rn?$BXM=6Ls?Z9!~+}4vem84yfVSh7*(s=W}j3f{K3>Eau5O6#mr&Dvc{Q*)yC5^ zGg2d6oLn;`xXC!M+LibPH^3e<+=@JL$gn@~AbiKQ4=kn+jqVR((=1=~Qv3g6^LzPu z_TL|d_uv0W+`ff*i&ckd7UhQb(Pp{4FgrtQCdYY1xr>w&fR)p#zZI%t-*c9*<;uL;GhALh*7>PSX!jJ8B-Bi(vYicHM`K zC68p4g=(=gjCyd64=`1@i-=M2?wo^fsyPnnTl5{_;pIQt(jMZP5&vk5nCByk*h{+T z(GTc?!`vGCC{Ep~qS}fO_8@kK(9Ky02!NHBAAMZ^OJYD|;Oa41gwCHhW z!%QTP@SKLj%|bAtgTZEvthMdhgoe%$iE!nnL4Z4XArwsXE++TPIDu20w&U^|;sj5x zz3O~&)x@E0>vChl6K{-P2N%n@eO;mZW5otIQp^U^spZMj4~5uc99~z|uWj$U214ugnNW7i3MvOZo8n(u!lYA$sG7>OP}l>)6~s@Cre;^oKJO( zAhY*V9YE%!r}FT*;;G9br1OubSUNK{{BNXl^oH!r5GU0Q*K=g?i1FZtrU;PQTq{}p z$oOzWVaypammiY3pqOIzth|5Ydm{+gv#8qirdfo#whHwP=aU5+q8?O`w>KJ3H&Rd2 z$+f8COpu0U8!j(PQrO8oM?`rKYRb|fW64H#eP7$yBZ8SRo7kOoKGmXQ3h0 zRA)aw2!X6OpU(tY-#*`^or+;~)#9=K#po)G#i(7iI9Q@63jR$N)PAq$z5gG3d;EpV-~!N=7Wizrr3jxFsdU|z@|MSWAl-1yhT|5T()2fRY2s?#GCj|ta2Uh*bL$Yg+ZgcTy;p>=8xb&y z>ZSF1|BgHMJhK0rn~rJ`O?kK_=e#UNczpB1N7w%R+rs+-5eV2PpIg}|f9ZPkvh}4j zx_jf^mvEcTBr;l&aQfwzJxzjW_8D~ZRR?P?G0zosOICa@oRviM(||IDsc{;YHqL4m zq936ymnBrP@$<$c3pE(OzP3L0Th>KQ-6Bm|lVZHQEeek48{2ZT4%`WjflpY)zhoS? z{6Qc5E6zK=Hk_}QIvQ4~JDG4(zA}E=<#INxL|$J{`hHG0e>O7Tt_(#df;&y)r`Km^ zo*A&<1|5zV+;BF1ohi5yn`S)t20NN>zmeANMFzW7h$7})g~+Xth< z<=eX?H*y7+z^$*xr#!Ly zUJN7bP3;Kg5XK2(961AH|Gmbf9nI;eam$X*umgdIIvJntXa>1#I&?8jq$|WUdK1gkpI4}t3Oe| zJbo4BrbEoLrS8=!*VlOEow0Pl=Fkc9q+g7mcK4)THgEj=Gy$jbe2ci&L*Uco(kq|mI*z{1J;aA4NJ?Rjv-}dPAr{UY{ zi~D9#MB(hctAtl1&_0FX zhmEKYuWI!R_b1AEN59mf6W>~~K&dPz));d>Y*o6Oi}@7=9sM`Vato~KOfs*8st9pk z-HiJNuf3sD4CUPqOYsU*^!^(|hDDe@nsN939O&FX_h-`=#^L?>`DOs$Rv=gj2e0Z(ivHO{Mz?tg|*p0C`&2?rGcg%-Y=6~lB3Alv~0_X1%TsrZ8>iM-46}Ch9RY)>YKB)WIzdH_L%EzH!Hb4>ye+ zW251dw2VKR^e?R#K!DH8?8DMAe^z+n${`1-d6pn< zFKsx^9YA)d2A3V%V{AB*DfT>Wymut!l4Gh>8)KryJj*v~imVpI?0W@<;T;b2(hDw# zEnJutg{8zgEQ``hO>9?`8=e`P3Lx7`xr4G#pNzkKi8-+wunGR%IR2?Wq^+StiZP${ z61Oig)_yiKR1$I!x_o|*`Pvh2Et*sbbG6$VRF_1D^CnXw(J8T!JEBZ!aJ5@P!eW$v zoN8o#aYfvb8%;{=Y(#JQVjwLu-v8p}ylqPIZ2US!aNZFn^zn;G-Gk!948>hUqv6XI zt&XW>;uoz66-Q_Mvf)?8(_i-N_KX_se`<_xQ({fvt7P-{V*Xg1x%q`ghp%+!LI%Nw z2Qds>Y+V1<@T|Legs7&dU<4eC1#2^~tJYxr^i}h~F4l4Vqw|o)>c>(G7CFD*3~oIK zO~_S*OTf@8v-Nr0dKQcv+E-~&4sP?SAuzze$=4bfgJs0U{VHMm2u(q0aiKe&PeZ4 zukTRyXF2nN$D#_@J#*g4AP|iMt95Gf6%Nx4Wj(u_1l149T&f@A#czt~M&p-n){4d_ zjMd*((xSV*>(J~3W8wH-fO;1nZ|k_wV_fiE23|tE^1Dv8rY*7LW6OnQ!P#VX7#I2q zqmdIfIcKA>1mtU*ng-m}%($2>sy7Vo*rUeL@7(m2k??(W-tRm@gzb6$PDZ5v;5(@0#~9yB#{ORiOJM3x)F*b~(%tKIsR{as>KMe&4Ot9}(@Hib(%`0y*o1_um6#deEV=O#Q*9BSGxQ?U2lnH0TIF56kXd!@>cVbj?lVuQ2 zEFdf#?7hH7tvulq$JZMBPK^C`7$g3e5blHLIcADrN$dtK{=%fcr(i)2H?JeZ$GI910?2sjJhd-4>0{}X$kuuaQC zxJgwt?Lfl;RZW=$#&_rq4cpMr28Q>?3aGh-2@067tMvnbqbE1KB@A~pq+5glC3)a(z^iR6x^nX~Gdyz1|C6uWdUAxsz=AiOv>2KuH+sgtzaxbO5J&Ky1cGQO6rr;)T$+KHAYsrp&iJE^%vH}YwsF>VEh zCAXId)fsH39d9DU#bNuvU9Fa7g4)IfS-{*~E$aoju2LeV)}UN*ocX8%^Z&D`!k&gj z_1_Ds{6c_VIva5yMIUy`w5q2|D!HBtHo(9I_jzX04UD?G-IjBFvN>Kj& za*>s?Gv8&yq6hl$ii2vn(`9wr-x^8+igs zOBKUhc!OaGTmf^U6&z_+a2H;T3h5_gAf)FJ+Kjf#fKswwDTYh`b%kr7Hfnvjf`I1OPb)%Bto87ro`Lb{dcrv?bP% zU@VnGV<>}`$_X(vA!XZ#JSlFdP^f&l{2_*1v|J{{(yK{|Cu}B^s6njcr?J!~F2pMI zyiPhCltuGoXa0Iw4s%dWo@$nZ5}I!Iz*7N9<@LoCtIu)SubGk+i`V_+eGb}r9vZKW zQ#9TkN7G=qq$3{cwp@DR>7(}lW7{#;OyQOb5@`FGmQ+S1g2bgVFOf>(Lbeowu~J^0 zNSB)HErw@gU_?u}PoD+f$2PH#kq-ro+J=i>pjIyc0>Sj7z9mfrL zr3uZ#|CZ%WDo;SlHpf0YiJs^j zoOv@=1v*sIcrBeUE0&qlvosYua3PD4ER`dYXm>4 zo6)k!$w8Rle3KK>U>=Y;IYE+`qkZF}uwny6O@NtHQZBe8%UKYQ#4FNzOXUw1j zWjux4g1o0W^*_%Lk2j}-|LxdLwxHW`6-|l|j_C0INYiIc33@y3rumcW)09^1OrxpI zf-_|^H8(V0!&;In-V_EqEK-_VNM{PQl(Hq=lWaOE9y@#$vaw6 zX^WS5@lj-uUas5%-Ge=={HPTTWNZd42rDM0V5MB0LBqnVwuhxjw2ssXTS$yh1ygsnPQcc%J@}zD zy`30IKJHz~H``D-1Uoj9hR|bjWafEgcUh*A-3wgjm0h(+SD7zb7bW5b{vcXgu z4j~H}7Q%#t3=7B)%C15Pvgbd^t~f__(Z{*6Yj(0yzUDr!?6%H2uk8NEv9-;n+n~}< zWmEHzt+9kw5!2QnKCTSe^ei(@36sHAbE*6#o2G_r4OS)|!g4u1N7tIXljm@n%Us<91YL|5KZTs z^6vK3G4K{6u)D|s-8dBC=`e4)gZ2^QI<< z3S0B`4#TqGslho8^J=RZabad7q=RL^xmS^f@D5BHE3LW9GnE;|2e}wyQ;v(i4H6Q> zu&WS=aPE;ub4l-LLiv6rg3;3v34Gfe+zKLVz`27n+@;>b`M&%|9_7;W^2R)BPanz0 z^Jp6#+&r-Ztru-RU_@M^cX$vA;|gH{vhd4OvMir+1N#`qF&rZjYW~+S$!bm(nsb7Y zs1#nBWB@y>_^|7k)b zr#JjE2&CA#NnYq5GWj>s8>G>N|1{AD4{veGy5Dj(ezz3x@+_S?-r`e-^uZ#8prTvtr< zLfN?+~+iuL$WLMyWEfCzU1sWC}~Y z9?KUVkIBn=Le4kHDZ{CqXxt{97f^FLCSPXgt7k*oUqIdH2U&9g_8B*CUVQ<6DKLE( zvre};f8ci2Md9}p#OnM(_UT1a`|RZ`zrADCZ-6tXTq~8*S2Ks5>e8fo7iBhz&jQ9m zt6dI#->dReFY<`JuS(B_lqQgOT|}*9?S<4jb~TS zRV;Dzg~H0lLDWM2+?U$X(akOU(Rd+FZ`*w909c2F#d8~3g4OAh^Jkeln66`j7Z0Yi zPG=GqtDlEMScHNc+B29u8DVb6BINH9L-67-yi~kV%sd3oO3z}Ki5B95oOTh7=(2p2K zodgU0a3%Dr&Go~m2$FwpQU9Qpv}|Ot)%iVUzBa}`)&ZIorD&jkP;34(R;nC)F=h2z zWa_62L&iz@oxvQISL7C(ITgq~#67xr+5L(K;%LTe1$Z?b&J=(@Z{B<{eL=KLuDpbv zv%jzCJHAbhyp#rt-?zy}FQq=>k8Sd&O9@-?ZL-y6RFE>4+sEQ+(|zvQ@@hRfgpj9ChxtH+K7YO6b6^>T(QoZsTKD3!$NH>9Yf=?O<08d1fD z9|y%nfS={ON_r)s@l4poqoH04-6gOzSyUJ@ z1j>RM^kH4-&+?!Oak!*P>)qHTPJDpE@I8LSNFix9{jqle?URmoFkO6TYQtNS0Lp*W;$_Zl@5yF)YyrUFCg0m14K9!){nRdI(+;!i=j3gEx`Y7(>QF9PBUKfm^;R&CdY0wl*-o#TI!rOd$41DW^Ms-KU z#+Hxg`|fcxAn`Hw{rHF7)RnQ7@~3e$KH+ACXxDKaE*gmA>Zv^89o1qOwz-~K(^qnD zJ>@|UPS?}Agb(%*@kDNH@|Q-oMC(Q)l;jgt7^u|DU)cTES*S?p8>j3 zmQSMmGeD1=k((qJOs4oVK*Qw^ld1GvkX_E5LKii`2Ti3C89SAFhC!PF`jndG+ML>S zP18QYlZvO0aBA~q!F1}@Y>m~a-2~8cLepaKt@3j2bSi8cDjE^3X|_-izIR(SEjyH# z2dC48gF~nm6mcuRS)AIf`2Gi4_27H*)JYR2g92o=I6Oep;FYUyZaVajkcg6}W>B|) zA5Ba{w!Z_fcH+BY>eNY7@f~WqIoj%iyuW7br2OfVCXF9Aaq^7mbyef1`L!>joZ3i~ zy@ZFGJi87C6eh!tG?UC^#NxR@H8EYToJno-K96;3834Ax!?fe1T6+P^oPy-#Xe9^V zs`1U=&7|KG1LM$fHDDXjYb$(*@?1G3A|Hc%&xpJY`ND{NBJywy!u8jpy&e(yN06_G z$WH`*OQwI~jPc{u=w@G^n6-iO@ z;w?0?`SOd-9Qh{XlcF}iwuRbQL=TJn{6#8BPQq}}tu``BgVoc3AUnQ9U6aa(JGBnT zv#fzKZM>ZM5?z3x|8pmT^s2#F(^tJjnsz@Y(CgXZ&6@B z`uhgYA9x%?oLV}bj(B?F8H#5Vo~!XR;8}=A;#r616+FA~e1hj+cv=i~YD4gh#S_4D zJD#O@?!)sWp4ahwfag;@KjIO?oLcNKXFIJe5`}m!#B({GiFmHXb0?macpk;`%;o{F z(OXfXW7OsrJ1N1Evb&J zcb$J)`;}WHHU4U^*Y7R$RabN#KYr$f&fbzzAMi>_3w>op1%+ka;({`J zZRN;|Nw6PNtM6O>#g;a zRQWu`zKS@R^aWLqk_UTw3d^@)wl(YfHR^g+6ahMRU3PBN~!gP~!Chv#zF~)L-TG zl~%M^P15x>T`Et0MTxThD{|%+DgJppRn^|Ay29eJ|1iaAv%-~=lB)}feTCJ&Vt;X6 zRf!LLZYeXrrut-0QFUQKX-SFSS6b&SE3B<(wRzZn#D$VUZSPVWc(*SwH_%q9H9QmH8n+b)kP(>pxaaDFDR;LBag<3 z?#Vn5NR!W7i#dUqR%FV&Z|P#;s+5Pnr3>VX-@-?8%TKJ|8s->#9)V@z)h&SRu9+dVNJ@5HODiA4P>lMWsdU`^coUaR5xL~xBhKyTprVVKl9Y3%#1lCeUNsk@po4tnFUpXO z-%)}n>||z+en(DG)Rc*d3I3Ub;(m;fF8h8@`JzNOGfTe54!IP66v0J4dEk5UC6=A> zw-LHZ*HQTC-Q>`tG)i)ung`hlt-E5K{P>KZRs|3GD; ze2Dz$2kM?$R_rT5eC7T7n&p=qLj-1koO2AZ ztAX;zV>C_-8YctCkXiFDYD0sS@W{-cLE}Z7olb`=WB{W>N zIu7W?a=>w13K$`0^VcQv$>TH%RwV9M>P?r)VZYLlX1-Fdw+QyeS5)Zpfhi@v;(wUB zDo_22ZZ2ni(P^Yo5Bc_Ql$Kfy`v5M2e`TJslER{rvXU#5R>{2IQR7Mz^|{~Ak2*?z z_B-9y>^zNJ6`@e_*%N50(o~`WtTsIQzi93Fq~?6%O4Pr z8jmT0_#+vqqPD%t#CfL9m^Qs~a@E9fHGUSRG{%qUlcFKHOY2tRQ05()9X8$r5pYtdrYnQmpiU*f+f;;+4vXOK0&%|qFA;)3Avf* z<2vc`;*;2`Op;ShQb(FBSDl1QHHE*5Wy`-o##GtyZyF$`1zFz+n3&#=ZDxD<=ilU} z88YJ(mC;P5zm1%J3U#gtkEI2{#Phh`IR&k_wu@Z-Gc}igpQ4s@oosQMx{K=@mQ{+Y z)5}UkJGx=nRpJ(!BP$7oy-Bo0*b3=uEDdzsveDv7Su8{wx>1f0qMYW+ zWkQtFP4aCadLrHxXAxJ>&2p?obf#ORw1_;qRlZ^om(qNhW);0Hw?QZW^%r74)2%35 zAn&t^8|ilGvWdP3xQ?}n0SJk1w22XxMc4@|%*-gzaHlMb61}leof9Q`&=R>dO8Dq5 z`D>IIB@ALtmB~AzMH0@v?~g{&GPyq*h=K! zaWUN^8)Jk|*2bXny=MAJPOs#2e_ADP$P)RoF-=6t9*ePo08U7j|dnSRc8%=6oOhTPaoSsR~%09_r20bUY zCW~42&2$~F8ijIjvgjjEr-&AEMT$tXKTkKD2`{j}K!FHQAXOAaZ!xjtpk&cjj&3Hp z%k*Zb{7;&z)}k={a?4gKlA}L_k^Z2WXdQTwq9&+6f`cCJapT8T*Hro^PMa~+kFS~IrcRpp zrd-loWYSLgbaRm!c#G0Z%usP<*kRvA&R`Mt)o2CnzD>3&HXZL!6g>KxYii#m>$K{r zSMOGNbnqTV@1IaJVeM7NX4Y zF|8Ds#De0YO8LXj^oe{TO_Vx5q5Di|Nf5fTr6|Mi09ac=w~olJtwbLj5#87d%KMqz z)k<^~pA)Yrg|b^Z1nUbqI9*&%U&=SrMLz^*k~2gueJ#6XKRQcKJX%Q3rEsZx1tahrFUa zZ1W;{PkS*Kb`)RTWe%bTFs7VN_7)Wu`%8Sa1y$Hhm6Z7_mdJUzq8qGhBmPbj8S>X$ zSkk4kFi(txU0sw1qjdM?Kl4Nfk2oDGf3Fl}tu0ATjfAjRZBh8!R<>vx{=$}xCX>?{ zJv>R?FjDjh*mK5Bm<+L>Mq2D!gkL`9D6C89S`tDvp`XY?Xb`Iy-WgM+x=6w=xChe~ zPR9VQTazrolr6~`E{{sGX^wEK(MeI^5<8d3{#S^cf{1?NlA^;H@r)5smcV7IuSB`_ z3Q^+P7j5q2Q{3&r74Vt|M#SCCk=mOuU4$^O{xDu1^Je{{@IE+kLFkQ2wJ42znoW)#Et+?Y)`YhZVBsx797K2-uW71T0%DpIZh0UYM@l}T z?J=>AxcG!b`P3DnKz=(~J#xd zK#Jfx2(sEk{MN`3G|wC=(L_=x7q-TlB_?c!IiM-pRZW;Yew^`Mj9b23g&8cCr>jJB zmkn3wELu!#t$$k0)Nzv$U$I-rl7d&{RmUZ=OEnlcq*}P)Gft@%Eht*P(^<47yPVVo zivHjj(OPJ+@|`gv4N@x;YDCxOY5>BkM!-D75WqBfRgK7@IN2p1w|(N}12rO>5{!RQ zhR_n_d--t4obtxbQ1v9)tyXlRWI480w9iV3vMZi};i4IsmdIjZ%G9K((5t*eHxUDwjx$;Q{5y z(PJPu(XwugD8%6I86!GSjC_3z$cmLm$ABz{Y&{mhIN5J3P~zq6u`m`1a?4oJW>lhT zSQ8@!B{j(e3t3A{XDf(UIwULu0@fPxl?m~3LAu-!t}H6kvW3^9Tj1z$ Result<_rt::Vec, _rt::String> { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 3 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 3 + * ::core::mem::size_of::<*const u8>()], + ); + let vec0 = request; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let ptr1 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link( + wasm_import_module = "amplifier:modules/kernel-service@1.0.0" + )] + unsafe extern "C" { + #[link_name = "emit-hook"] + fn wit_import2(_: *mut u8, _: usize, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2(_: *mut u8, _: usize, _: *mut u8) { + unreachable!() + } + unsafe { wit_import2(ptr0.cast_mut(), len0, ptr1) }; + let l3 = i32::from(*ptr1.add(0).cast::()); + let result10 = match l3 { + 0 => { + let e = { + let l4 = *ptr1 + .add(::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l5 = *ptr1 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len6 = l5; + _rt::Vec::from_raw_parts(l4.cast(), len6, len6) + }; + Ok(e) + } + 1 => { + let e = { + let l7 = *ptr1 + .add(::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l8 = *ptr1 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len9 = l8; + let bytes9 = _rt::Vec::from_raw_parts( + l7.cast(), + len9, + len9, + ); + _rt::string_lift(bytes9) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result10 + } + } } } } @@ -273,14 +344,15 @@ pub(crate) use __export_orchestrator_module_impl as export; )] #[doc(hidden)] #[allow(clippy::octal_escapes)] -pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 357] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xdb\x01\x01A\x02\x01\ -A\x04\x01B\x04\x01p}\x01j\x01\0\x01s\x01@\x01\x07request\0\0\x01\x04\0\x0cexecut\ -e-tool\x01\x02\x03\0&lifier:modules/kernel-service@1.0.0\x05\0\x01B\x04\x01p}\ -\x01j\x01\0\x01s\x01@\x01\x07request\0\0\x01\x04\0\x07execute\x01\x02\x04\0$ampl\ -ifier:modules/orchestrator@1.0.0\x05\x01\x04\0+amplifier:modules/orchestrator-mo\ -dule@1.0.0\x04\0\x0b\x19\x01\0\x13orchestrator-module\x03\0\0\0G\x09producers\x01\ -\x0cprocessed-by\x02\x0dwit-component\x070.227.1\x10wit-bindgen-rust\x060.41.0"; +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 371] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xe9\x01\x01A\x02\x01\ +A\x04\x01B\x05\x01p}\x01j\x01\0\x01s\x01@\x01\x07request\0\0\x01\x04\0\x0cexecut\ +e-tool\x01\x02\x04\0\x09emit-hook\x01\x02\x03\0&lifier:modules/kernel-service\ +@1.0.0\x05\0\x01B\x04\x01p}\x01j\x01\0\x01s\x01@\x01\x07request\0\0\x01\x04\0\x07\ +execute\x01\x02\x04\0$amplifier:modules/orchestrator@1.0.0\x05\x01\x04\0+amplifi\ +er:modules/orchestrator-module@1.0.0\x04\0\x0b\x19\x01\0\x13orchestrator-module\x03\ +\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x070.227.1\x10wit-\ +bindgen-rust\x060.41.0"; #[inline(never)] #[doc(hidden)] pub fn __link_custom_section_describing_imports() { diff --git a/tests/fixtures/wasm/src/passthrough-orchestrator/src/lib.rs b/tests/fixtures/wasm/src/passthrough-orchestrator/src/lib.rs index 018cc5d..5cb3b2e 100644 --- a/tests/fixtures/wasm/src/passthrough-orchestrator/src/lib.rs +++ b/tests/fixtures/wasm/src/passthrough-orchestrator/src/lib.rs @@ -10,6 +10,19 @@ struct PassthroughOrchestrator; impl Orchestrator for PassthroughOrchestrator { fn execute(&self, prompt: String) -> Result { + if prompt == "test:emit-hook" { + let request = serde_json::json!({ + "event": "test:async-hook", + "data": {} + }); + let request_bytes = serde_json::to_vec(&request).map_err(|e| e.to_string())?; + let result_bytes = + bindings::amplifier::modules::kernel_service::emit_hook(&request_bytes)?; + let result: serde_json::Value = + serde_json::from_slice(&result_bytes).map_err(|e| e.to_string())?; + return Ok(result.to_string()); + } + // Build a JSON request for the echo-tool via the kernel service. let input = serde_json::json!({ "name": "echo-tool", diff --git a/tests/fixtures/wasm/src/passthrough-orchestrator/wit/orchestrator.wit b/tests/fixtures/wasm/src/passthrough-orchestrator/wit/orchestrator.wit index e8a29d9..89d9d4f 100644 --- a/tests/fixtures/wasm/src/passthrough-orchestrator/wit/orchestrator.wit +++ b/tests/fixtures/wasm/src/passthrough-orchestrator/wit/orchestrator.wit @@ -9,6 +9,10 @@ interface kernel-service { /// Execute a tool by name (ExecuteToolRequest proto, serialized as bytes). /// Returns serialized result on success. execute-tool: func(request: list) -> result, string>; + + /// Emit a hook event (EmitHookRequest proto, serialized as bytes). + /// Returns serialized HookResult on success. + emit-hook: func(request: list) -> result, string>; } /// Orchestrator interface — high-level agent-loop execution. diff --git a/tests/test_wasm_integration.py b/tests/test_wasm_integration.py index f4ee444..5dad24c 100644 --- a/tests/test_wasm_integration.py +++ b/tests/test_wasm_integration.py @@ -3,6 +3,7 @@ Uses real Rust _engine module (no mocks). May be slow on ARM64 due to WASM compilation. """ +import json import os import shutil import tempfile @@ -147,6 +148,46 @@ async def test_load_passthrough_orchestrator_wasm(): shutil.rmtree(tmpdir, ignore_errors=True) +@pytest.mark.asyncio +async def test_passthrough_orchestrator_emits_async_python_hook(): + """A WASM host callback preserves an async Python hook's owning loop.""" + import asyncio + + tmpdir = _isolated_wasm_dir("passthrough-orchestrator.wasm") + try: + coord = _get_coordinator() + result = load_and_mount_wasm(coord, tmpdir) + assert result["status"] == "mounted" + + callback_calls = [] + + async def async_deny_handler(event, data): + await asyncio.sleep(0) + callback_calls.append((event, data)) + return {"action": "deny", "reason": "async Python denial"} + + coord.hooks.register( + "test:async-hook", + async_deny_handler, + 0, + name="async-python-deny", + ) + mounted = coord.mount_points["orchestrator"] + response = await mounted.execute("test:emit-hook") + hook_result = json.loads(response) + + assert hook_result["action"] == "deny" + assert hook_result["reason"] == "async Python denial" + assert len(callback_calls) == 1 + event, payload = callback_calls[0] + assert event == "test:async-hook" + # The kernel adds its standard event timestamp to the empty input. + assert isinstance(payload["timestamp"], str) + assert {key: value for key, value in payload.items() if key != "timestamp"} == {} + finally: + shutil.rmtree(tmpdir, ignore_errors=True) + + @pytest.mark.asyncio async def test_load_deny_hook_wasm(): """Load deny-hook.wasm via load_and_mount_wasm — hook module.""" From 6763eea1442d25b279fc6bc1ba623e179de69028 Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Sat, 12 Sep 2026 08:45:24 -0700 Subject: [PATCH 20/27] test(core): keep proto compilation tests hermetic Write generated protobuf output to pytest temporary directories, assert the expected files exist, and refresh the two root stubs from the locked generator without modifying tracked sources during the test suites. Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- proto/amplifier_module_pb2.py | 6 ++-- proto/amplifier_module_pb2_grpc.py | 50 ++++++++++++++-------------- tests/test_proto_kernel_service.py | 14 ++++---- tests/test_proto_module_lifecycle.py | 14 ++++---- tests/test_proto_module_services.py | 14 ++++---- 5 files changed, 52 insertions(+), 46 deletions(-) diff --git a/proto/amplifier_module_pb2.py b/proto/amplifier_module_pb2.py index 4559c62..0e11421 100644 --- a/proto/amplifier_module_pb2.py +++ b/proto/amplifier_module_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: amplifier_module.proto -# Protobuf Python Version: 7.35.1 +# Protobuf Python Version: 6.31.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -11,8 +11,8 @@ from google.protobuf.internal import builder as _builder _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, - 7, - 35, + 6, + 31, 1, '', 'amplifier_module.proto' diff --git a/proto/amplifier_module_pb2_grpc.py b/proto/amplifier_module_pb2_grpc.py index 958322b..e6b7668 100644 --- a/proto/amplifier_module_pb2_grpc.py +++ b/proto/amplifier_module_pb2_grpc.py @@ -5,7 +5,7 @@ import amplifier_module_pb2 as amplifier__module__pb2 -GRPC_GENERATED_VERSION = '1.83.1' +GRPC_GENERATED_VERSION = '1.78.0' GRPC_VERSION = grpc.__version__ _version_not_supported = False @@ -25,7 +25,7 @@ ) -class ToolServiceStub: +class ToolServiceStub(object): """Universal contract for tool modules in any language. Implement this service to create an Amplifier tool in Go, TypeScript, C#, etc. """ @@ -48,7 +48,7 @@ def __init__(self, channel): _registered_method=True) -class ToolServiceServicer: +class ToolServiceServicer(object): """Universal contract for tool modules in any language. Implement this service to create an Amplifier tool in Go, TypeScript, C#, etc. """ @@ -88,7 +88,7 @@ def add_ToolServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. -class ToolService: +class ToolService(object): """Universal contract for tool modules in any language. Implement this service to create an Amplifier tool in Go, TypeScript, C#, etc. """ @@ -148,7 +148,7 @@ def Execute(request, _registered_method=True) -class ProviderServiceStub: +class ProviderServiceStub(object): """--------------------------------------------------------------------------- Module services --------------------------------------------------------------------------- @@ -189,7 +189,7 @@ def __init__(self, channel): _registered_method=True) -class ProviderServiceServicer: +class ProviderServiceServicer(object): """--------------------------------------------------------------------------- Module services --------------------------------------------------------------------------- @@ -263,7 +263,7 @@ def add_ProviderServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. -class ProviderService: +class ProviderService(object): """--------------------------------------------------------------------------- Module services --------------------------------------------------------------------------- @@ -407,7 +407,7 @@ def ParseToolCalls(request, _registered_method=True) -class OrchestratorServiceStub: +class OrchestratorServiceStub(object): """Orchestrator module contract — high-level prompt execution. """ @@ -424,7 +424,7 @@ def __init__(self, channel): _registered_method=True) -class OrchestratorServiceServicer: +class OrchestratorServiceServicer(object): """Orchestrator module contract — high-level prompt execution. """ @@ -450,7 +450,7 @@ def add_OrchestratorServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. -class OrchestratorService: +class OrchestratorService(object): """Orchestrator module contract — high-level prompt execution. """ @@ -482,7 +482,7 @@ def Execute(request, _registered_method=True) -class ContextServiceStub: +class ContextServiceStub(object): """Context module contract — conversation message management. """ @@ -519,7 +519,7 @@ def __init__(self, channel): _registered_method=True) -class ContextServiceServicer: +class ContextServiceServicer(object): """Context module contract — conversation message management. """ @@ -589,7 +589,7 @@ def add_ContextServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. -class ContextService: +class ContextService(object): """Context module contract — conversation message management. """ @@ -729,7 +729,7 @@ def Clear(request, _registered_method=True) -class HookServiceStub: +class HookServiceStub(object): """Hook module contract — event interception. """ @@ -751,7 +751,7 @@ def __init__(self, channel): _registered_method=True) -class HookServiceServicer: +class HookServiceServicer(object): """Hook module contract — event interception. """ @@ -792,7 +792,7 @@ def add_HookServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. -class HookService: +class HookService(object): """Hook module contract — event interception. """ @@ -851,7 +851,7 @@ def GetSubscriptions(request, _registered_method=True) -class ApprovalServiceStub: +class ApprovalServiceStub(object): """Approval module contract — human-in-the-loop approval. """ @@ -868,7 +868,7 @@ def __init__(self, channel): _registered_method=True) -class ApprovalServiceServicer: +class ApprovalServiceServicer(object): """Approval module contract — human-in-the-loop approval. """ @@ -894,7 +894,7 @@ def add_ApprovalServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. -class ApprovalService: +class ApprovalService(object): """Approval module contract — human-in-the-loop approval. """ @@ -926,7 +926,7 @@ def RequestApproval(request, _registered_method=True) -class KernelServiceStub: +class KernelServiceStub(object): """Missing associated documentation comment in .proto file.""" def __init__(self, channel): @@ -987,7 +987,7 @@ def __init__(self, channel): _registered_method=True) -class KernelServiceServicer: +class KernelServiceServicer(object): """Missing associated documentation comment in .proto file.""" def CompleteWithProvider(self, request, context): @@ -1117,7 +1117,7 @@ def add_KernelServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. -class KernelService: +class KernelService(object): """Missing associated documentation comment in .proto file.""" @staticmethod @@ -1391,7 +1391,7 @@ def GetCapability(request, _registered_method=True) -class ModuleLifecycleStub: +class ModuleLifecycleStub(object): """--------------------------------------------------------------------------- ModuleLifecycle — every gRPC module implements this service --------------------------------------------------------------------------- @@ -1428,7 +1428,7 @@ def __init__(self, channel): _registered_method=True) -class ModuleLifecycleServicer: +class ModuleLifecycleServicer(object): """--------------------------------------------------------------------------- ModuleLifecycle — every gRPC module implements this service --------------------------------------------------------------------------- @@ -1492,7 +1492,7 @@ def add_ModuleLifecycleServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. -class ModuleLifecycle: +class ModuleLifecycle(object): """--------------------------------------------------------------------------- ModuleLifecycle — every gRPC module implements this service --------------------------------------------------------------------------- diff --git a/tests/test_proto_kernel_service.py b/tests/test_proto_kernel_service.py index 6c0c0aa..68cf714 100644 --- a/tests/test_proto_kernel_service.py +++ b/tests/test_proto_kernel_service.py @@ -30,8 +30,8 @@ def kernel_service_body(proto_text: str) -> str: return match.group(1) -def _compile_proto() -> subprocess.CompletedProcess[str]: - """Compile the proto file using protoc and return the result.""" +def _compile_proto(output_dir: Path) -> subprocess.CompletedProcess[str]: + """Compile into test storage without changing committed generated files.""" proto_dir = PROTO_PATH.parent result = subprocess.run( [ @@ -39,8 +39,8 @@ def _compile_proto() -> subprocess.CompletedProcess[str]: "-m", "grpc_tools.protoc", f"--proto_path={proto_dir}", - f"--python_out={proto_dir}", - f"--grpc_python_out={proto_dir}", + f"--python_out={output_dir}", + f"--grpc_python_out={output_dir}", str(PROTO_PATH.name), ], capture_output=True, @@ -51,12 +51,14 @@ def _compile_proto() -> subprocess.CompletedProcess[str]: class TestProtoStillCompiles: - def test_proto_compiles_with_exit_code_0(self): - result = _compile_proto() + def test_proto_compiles_with_exit_code_0(self, tmp_path: Path): + result = _compile_proto(tmp_path) assert result.returncode == 0, ( f"Proto compilation failed (exit {result.returncode}):\n" f"stdout: {result.stdout}\nstderr: {result.stderr}" ) + assert (tmp_path / "amplifier_module_pb2.py").is_file() + assert (tmp_path / "amplifier_module_pb2_grpc.py").is_file() class TestKernelServiceExists: diff --git a/tests/test_proto_module_lifecycle.py b/tests/test_proto_module_lifecycle.py index 2524bc6..972ef5d 100644 --- a/tests/test_proto_module_lifecycle.py +++ b/tests/test_proto_module_lifecycle.py @@ -29,8 +29,8 @@ def lifecycle_service_body(proto_text: str) -> str: return match.group(1) -def _compile_proto() -> subprocess.CompletedProcess[str]: - """Compile the proto file using protoc and return the result.""" +def _compile_proto(output_dir: Path) -> subprocess.CompletedProcess[str]: + """Compile into test storage without changing committed generated files.""" proto_dir = PROTO_PATH.parent result = subprocess.run( [ @@ -38,8 +38,8 @@ def _compile_proto() -> subprocess.CompletedProcess[str]: "-m", "grpc_tools.protoc", f"--proto_path={proto_dir}", - f"--python_out={proto_dir}", - f"--grpc_python_out={proto_dir}", + f"--python_out={output_dir}", + f"--grpc_python_out={output_dir}", str(PROTO_PATH.name), ], capture_output=True, @@ -50,12 +50,14 @@ def _compile_proto() -> subprocess.CompletedProcess[str]: class TestProtoStillCompiles: - def test_proto_compiles_with_exit_code_0(self): - result = _compile_proto() + def test_proto_compiles_with_exit_code_0(self, tmp_path: Path): + result = _compile_proto(tmp_path) assert result.returncode == 0, ( f"Proto compilation failed (exit {result.returncode}):\n" f"stdout: {result.stdout}\nstderr: {result.stderr}" ) + assert (tmp_path / "amplifier_module_pb2.py").is_file() + assert (tmp_path / "amplifier_module_pb2_grpc.py").is_file() class TestModuleLifecycleExists: diff --git a/tests/test_proto_module_services.py b/tests/test_proto_module_services.py index 3d2a91d..4cd0c71 100644 --- a/tests/test_proto_module_services.py +++ b/tests/test_proto_module_services.py @@ -28,8 +28,8 @@ def _service_body(proto_text: str, service_name: str) -> str: return match.group(1) -def _compile_proto() -> subprocess.CompletedProcess[str]: - """Compile the proto file using protoc and return the result.""" +def _compile_proto(output_dir: Path) -> subprocess.CompletedProcess[str]: + """Compile into test storage without changing committed generated files.""" proto_dir = PROTO_PATH.parent result = subprocess.run( [ @@ -37,8 +37,8 @@ def _compile_proto() -> subprocess.CompletedProcess[str]: "-m", "grpc_tools.protoc", f"--proto_path={proto_dir}", - f"--python_out={proto_dir}", - f"--grpc_python_out={proto_dir}", + f"--python_out={output_dir}", + f"--grpc_python_out={output_dir}", str(PROTO_PATH.name), ], capture_output=True, @@ -49,12 +49,14 @@ def _compile_proto() -> subprocess.CompletedProcess[str]: class TestProtoCompiles: - def test_proto_compiles_with_exit_code_0(self): - result = _compile_proto() + def test_proto_compiles_with_exit_code_0(self, tmp_path: Path): + result = _compile_proto(tmp_path) assert result.returncode == 0, ( f"Proto compilation failed (exit {result.returncode}):\n" f"stdout: {result.stdout}\nstderr: {result.stderr}" ) + assert (tmp_path / "amplifier_module_pb2.py").is_file() + assert (tmp_path / "amplifier_module_pb2_grpc.py").is_file() class TestProviderService: From e7c3f9cf32f251aecbc50743434940906a0bc781 Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Sat, 12 Sep 2026 10:05:31 -0700 Subject: [PATCH 21/27] test(loader): cover independent async source resolvers Add independent async resolver coverage for same-source module reuse and cross-source collision rejection. Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- tests/test_validation_canonical_module.py | 128 ++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/tests/test_validation_canonical_module.py b/tests/test_validation_canonical_module.py index e4aa8d4..f6155a2 100644 --- a/tests/test_validation_canonical_module.py +++ b/tests/test_validation_canonical_module.py @@ -45,6 +45,40 @@ def get(self, mount_point: str) -> _Resolver: return self.resolver +class _AsyncResolver: + def __init__(self, root: Path) -> None: + self.source = _Source(root) + self.async_calls = 0 + self.sync_calls = 0 + + async def async_resolve( + self, + module_id: str, + source_hint: str | dict | None = None, + profile_hint: str | dict | None = None, + ) -> _Source: + self.async_calls += 1 + return self.source + + def resolve( + self, + module_id: str, + source_hint: str | dict | None = None, + profile_hint: str | dict | None = None, + ) -> _Source: + self.sync_calls += 1 + return self.source + + +class _AsyncResolverCoordinator: + def __init__(self, root: Path) -> None: + self.resolver = _AsyncResolver(root) + + def get(self, mount_point: str) -> _AsyncResolver: + assert mount_point == "module-source-resolver" + return self.resolver + + class _RecordingCoordinator: def __init__(self) -> None: self.mounted: dict[str, object] = {} @@ -148,6 +182,100 @@ def capture_validated_module(self, result, module): _clear_package(package_name) +@pytest.mark.asyncio +async def test_independent_loaders_share_canonical_module_via_async_resolution( + tmp_path: Path, +) -> None: + """Independent loaders share one same-source module object and its state.""" + module_id = "tool-cross-session" + package_name = "amplifier_module_tool_cross_session" + source_root = tmp_path / "source" + _write_tool_package(source_root, package_name, "canonical") + parent_coordinator = _AsyncResolverCoordinator(source_root) + child_coordinator = _AsyncResolverCoordinator(source_root) + parent_loader = ModuleLoader(coordinator=parent_coordinator) + child_loader = ModuleLoader(coordinator=child_coordinator) + original_path = list(sys.path) + + try: + parent_mount = await parent_loader.load(module_id) + canonical = sys.modules[package_name] + canonical_marker = sys.modules[f"{package_name}.marker"] + child_mount = await child_loader.load(module_id) + + assert sys.modules[package_name] is canonical + assert sys.modules[f"{package_name}.marker"] is canonical_marker + assert parent_loader._loaded_modules[module_id] is canonical.mount + assert child_loader._loaded_modules[module_id] is canonical.mount + assert canonical.IMPORT_COUNT == 1 + assert parent_coordinator.resolver.async_calls == 1 + assert child_coordinator.resolver.async_calls == 1 + assert parent_coordinator.resolver.sync_calls == 0 + assert child_coordinator.resolver.sync_calls == 0 + + parent_runtime = _RecordingCoordinator() + child_runtime = _RecordingCoordinator() + await parent_mount(parent_runtime) + await child_mount(child_runtime) + + parent_tool = parent_runtime.mounted["stateful"] + child_tool = child_runtime.mounted["stateful"] + assert type(parent_tool) is canonical.StatefulTool + assert type(child_tool) is canonical.StatefulTool + assert parent_tool.module_token is canonical.MODULE_TOKEN + assert child_tool.module_token is canonical.MODULE_TOKEN + assert canonical.IMPORT_COUNT == 1 + finally: + child_loader.cleanup() + parent_loader.cleanup() + _clear_package(package_name) + + assert sys.path == original_path + + +@pytest.mark.asyncio +async def test_independent_loaders_reject_different_async_resolved_sources( + tmp_path: Path, +) -> None: + """A child loader cannot replace another loader's canonical package source.""" + module_id = "tool-cross-session-collision" + package_name = "amplifier_module_tool_cross_session_collision" + first_root = tmp_path / "first" + second_root = tmp_path / "second" + _write_tool_package(first_root, package_name, "first") + _write_tool_package(second_root, package_name, "second") + parent_coordinator = _AsyncResolverCoordinator(first_root) + child_coordinator = _AsyncResolverCoordinator(second_root) + parent_loader = ModuleLoader(coordinator=parent_coordinator) + child_loader = ModuleLoader(coordinator=child_coordinator) + child_runtime = _RecordingCoordinator() + original_path = list(sys.path) + + try: + await parent_loader.load(module_id) + canonical = sys.modules[package_name] + canonical_marker = sys.modules[f"{package_name}.marker"] + + with pytest.raises(ModuleValidationError, match="Refusing to import"): + await child_loader.load(module_id) + + assert sys.modules[package_name] is canonical + assert sys.modules[f"{package_name}.marker"] is canonical_marker + assert canonical.StatefulTool.description == "first" + assert child_loader._loaded_modules == {} + assert child_runtime.mounted == {} + assert parent_coordinator.resolver.async_calls == 1 + assert child_coordinator.resolver.async_calls == 1 + assert parent_coordinator.resolver.sync_calls == 0 + assert child_coordinator.resolver.sync_calls == 0 + finally: + child_loader.cleanup() + parent_loader.cleanup() + _clear_package(package_name) + + assert sys.path == original_path + + @pytest.mark.asyncio async def test_path_validation_rejects_same_named_different_source_without_cache_mutation( tmp_path: Path, monkeypatch: pytest.MonkeyPatch From f21d898f8bb8770482e00525a7574ee5f23865e0 Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Sat, 12 Sep 2026 11:08:21 -0700 Subject: [PATCH 22/27] fix: preserve per-injection lifetime through hook aggregation Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- bindings/node/index.d.ts | 9 + bindings/node/src/hook_result.rs | 31 +- bindings/node/src/lib.rs | 2 +- .../python/src/coordinator/hook_dispatch.rs | 304 ++++++++++++++++++ .../amplifier-core/src/bridges/grpc_hook.rs | 31 ++ .../src/generated/amplifier.module.rs | 17 + .../src/generated/conversions.rs | 34 +- .../src/generated/equivalence_tests.rs | 3 +- crates/amplifier-core/src/hooks.rs | 139 ++++++-- crates/amplifier-core/src/lib.rs | 4 +- crates/amplifier-core/src/models.rs | 59 ++++ proto/amplifier_module.proto | 10 + python/amplifier_core/__init__.py | 2 + .../_grpc_gen/amplifier_module_pb2.py | 200 ++++++------ .../_grpc_gen/amplifier_module_pb2_grpc.py | 2 +- python/amplifier_core/hooks.py | 4 +- python/amplifier_core/models.py | 18 ++ tests/test_injection_items.py | 20 ++ 18 files changed, 746 insertions(+), 143 deletions(-) create mode 100644 tests/test_injection_items.py diff --git a/bindings/node/index.d.ts b/bindings/node/index.d.ts index b06fa5d..12f6e39 100644 --- a/bindings/node/index.d.ts +++ b/bindings/node/index.d.ts @@ -31,6 +31,14 @@ export const enum UserMessageLevel { Warning = 'Warning', Error = 'Error' } +export interface JsContextInjection { + content: string + role: ContextInjectionRole + ephemeral: boolean + appendToLastToolResult: boolean + hookName: string + event: string +} export interface JsHookResult { action: HookAction reason?: string @@ -44,6 +52,7 @@ export interface JsHookResult { approvalPrompt?: string approvalTimeout?: number approvalDefault?: ApprovalDefault + contextInjections?: Array } /** Structured error object returned to JS with a typed `code` property. */ export interface JsAmplifierError { diff --git a/bindings/node/src/hook_result.rs b/bindings/node/src/hook_result.rs index f0a528d..4c94bde 100644 --- a/bindings/node/src/hook_result.rs +++ b/bindings/node/src/hook_result.rs @@ -2,10 +2,20 @@ // Structs — exported as TypeScript interfaces via #[napi(object)] // --------------------------------------------------------------------------- -use amplifier_core::models::HookResult; +use amplifier_core::models::{ContextInjection, HookResult}; use crate::enums::{ApprovalDefault, ContextInjectionRole, HookAction, UserMessageLevel}; +#[napi(object)] +pub struct JsContextInjection { + pub content: String, + pub role: ContextInjectionRole, + pub ephemeral: bool, + pub append_to_last_tool_result: bool, + pub hook_name: String, + pub event: String, +} + #[napi(object)] pub struct JsHookResult { pub action: HookAction, @@ -20,6 +30,7 @@ pub struct JsHookResult { pub approval_prompt: Option, pub approval_timeout: Option, pub approval_default: Option, + pub context_injections: Option>, } // --------------------------------------------------------------------------- @@ -40,5 +51,23 @@ pub(crate) fn hook_result_to_js(result: HookResult) -> JsHookResult { approval_prompt: result.approval_prompt, approval_timeout: Some(result.approval_timeout), approval_default: Some(result.approval_default.into()), + context_injections: Some( + result + .context_injections + .into_iter() + .map(context_injection_to_js) + .collect(), + ), + } +} + +fn context_injection_to_js(injection: ContextInjection) -> JsContextInjection { + JsContextInjection { + content: injection.content, + role: injection.role.into(), + ephemeral: injection.ephemeral, + append_to_last_tool_result: injection.append_to_last_tool_result, + hook_name: injection.hook_name, + event: injection.event, } } diff --git a/bindings/node/src/lib.rs b/bindings/node/src/lib.rs index 3e4f5f2..9f5b196 100644 --- a/bindings/node/src/lib.rs +++ b/bindings/node/src/lib.rs @@ -33,7 +33,7 @@ pub use enums::{ ApprovalDefault, ContextInjectionRole, HookAction, SessionState, UserMessageLevel, }; pub use errors::{amplifier_error_to_js, JsAmplifierError}; -pub use hook_result::JsHookResult; +pub use hook_result::{JsContextInjection, JsHookResult}; pub use hooks::JsHookRegistry; pub use module_resolver::{load_wasm_from_path, resolve_module, JsModuleManifest}; pub use session::JsAmplifierSession; diff --git a/bindings/python/src/coordinator/hook_dispatch.rs b/bindings/python/src/coordinator/hook_dispatch.rs index ffa8b40..e5baba6 100644 --- a/bindings/python/src/coordinator/hook_dispatch.rs +++ b/bindings/python/src/coordinator/hook_dispatch.rs @@ -34,6 +34,16 @@ fn truncate_for_log(s: &str, max_chars: usize) -> String { } } +struct PreparedInjection { + content: String, + role: String, + ephemeral: bool, + append_to_last_tool_result: bool, + hook_name: String, + event: String, + tokens: usize, +} + #[pymethods] impl PyCoordinator { /// Process a HookResult and route actions to appropriate subsystems. @@ -118,6 +128,10 @@ impl PyCoordinator { let approval_timeout: f64 = result.getattr("approval_timeout")?.extract()?; let approval_default: String = result.getattr("approval_default")?.extract()?; + if action == "inject_context" { + return self.process_context_injections(py, result, event, hook_name); + } + // Read coordinator config let size_limit: Option = { let val = self.get_injection_size_limit(py)?; @@ -505,6 +519,296 @@ impl PyCoordinator { } impl PyCoordinator { + fn process_context_injections<'py>( + &mut self, + py: Python<'py>, + result: Bound<'py, PyAny>, + event: String, + hook_name: &str, + ) -> PyResult> { + let scalar_content: Option = { + let value = result.getattr("context_injection")?; + (!value.is_none()).then(|| value.extract()).transpose()? + }; + let scalar_role: String = result.getattr("context_injection_role")?.extract()?; + let scalar_ephemeral: bool = result.getattr("ephemeral")?.extract()?; + let scalar_append: bool = result.getattr("append_to_last_tool_result")?.extract()?; + let suppress_output: bool = result.getattr("suppress_output")?.extract()?; + let user_message: Option = { + let value = result.getattr("user_message")?; + (!value.is_none()).then(|| value.extract()).transpose()? + }; + let user_message_level: String = result.getattr("user_message_level")?.extract()?; + let user_message_source: Option = { + let value = result.getattr("user_message_source")?; + (!value.is_none()).then(|| value.extract()).transpose()? + }; + + let size_limit: Option = { + let value = self.get_injection_size_limit(py)?; + (!value.bind(py).is_none()) + .then(|| value.extract(py)) + .transpose()? + }; + let budget: Option = { + let value = self.get_injection_budget_per_turn(py)?; + (!value.bind(py).is_none()) + .then(|| value.extract(py)) + .transpose()? + }; + + let items = result.getattr("context_injections")?; + let mut raw_items = Vec::new(); + for item in items.try_iter()? { + let item = item?; + raw_items.push(( + item.getattr("content")?.extract::()?, + item.getattr("role")?.extract::()?, + item.getattr("ephemeral")?.extract::()?, + item.getattr("append_to_last_tool_result")? + .extract::()?, + item.getattr("hook_name")?.extract::()?, + item.getattr("event")?.extract::()?, + )); + } + + // Scalar-only results predate the structured carrier. Normalize them + // here for direct callers that have not passed through HookRegistry. + if raw_items.is_empty() { + if let Some(content) = scalar_content { + raw_items.push(( + content, + scalar_role, + scalar_ephemeral, + scalar_append, + hook_name.to_string(), + event.clone(), + )); + } + } + + const CHARS_PER_TOKEN_ESTIMATE: usize = 4; + let sanitize = py + .import("amplifier_core.models")? + .getattr("_sanitize_for_llm")?; + let mut prepared = Vec::new(); + for (content, role, ephemeral, append, item_hook_name, item_event) in raw_items { + let sanitized_content: String = sanitize + .call1((content,))? + .extract() + .map_err(|error| { + log::error!( + "SECURITY: Sanitization unavailable for hook '{}' — rejecting injection: {error}", + item_hook_name + ); + PyValueError::new_err( + "Context injection rejected: content sanitization function unavailable", + ) + })?; + if sanitized_content.is_empty() { + continue; + } + let char_count = sanitized_content.chars().count(); + if let Some(limit) = size_limit { + if char_count > limit { + log::error!( + "Hook injection too large: {} (chars={}, limit={})", + item_hook_name, + char_count, + limit + ); + return Err(PyValueError::new_err(format!( + "Context injection exceeds {} characters", + limit + ))); + } + } + prepared.push(PreparedInjection { + content: sanitized_content, + role, + ephemeral, + append_to_last_tool_result: append, + hook_name: item_hook_name, + event: item_event, + tokens: char_count / CHARS_PER_TOKEN_ESTIMATE, + }); + } + + // Every item is validated before the budget state is charged or any + // context write begins. Budget is still advisory, as it was for the + // legacy scalar path, but applies to the total item sequence. + let mut running_total = self.current_turn_injections; + for item in &prepared { + if let Some(limit) = budget { + if running_total + item.tokens > limit { + log::warn!( + "Warning: Hook injection budget exceeded \ + (hook={}, current={}, attempted={}, budget={})", + item.hook_name, + running_total, + item.tokens, + limit + ); + } + } + running_total += item.tokens; + } + self.current_turn_injections = running_total; + + let context_obj: Py = { + let mount_points = self.mount_points.bind(py); + match mount_points.get_item("context")? { + Some(context) if !context.is_none() => context.unbind(), + _ => py.None(), + } + }; + let has_context = { + let context = context_obj.bind(py); + !context.is_none() && context.hasattr("add_message")? + }; + + let now = if has_context && prepared.iter().any(|item| !item.ephemeral) { + let datetime = py.import("datetime")?; + let utc = datetime.getattr("timezone")?.getattr("utc")?; + Some( + datetime + .getattr("datetime")? + .call_method1("now", (utc,))? + .call_method0("isoformat")? + .unbind(), + ) + } else { + None + }; + + let mut messages = Vec::new(); + let mut residual = Vec::new(); + for item in prepared { + log::info!( + "Hook context injection \ + (hook={}, event={}, chars={}, role={}, tokens={}, ephemeral={})", + item.hook_name, + item.event, + item.content.chars().count(), + item.role, + item.tokens, + item.ephemeral + ); + if item.ephemeral { + residual.push(item); + continue; + } + if has_context { + let metadata = PyDict::new(py); + metadata.set_item("source", "hook")?; + metadata.set_item("hook_name", &item.hook_name)?; + metadata.set_item("event", &item.event)?; + metadata.set_item("timestamp", now.as_ref().unwrap().bind(py))?; + metadata.set_item("requested_role", &item.role)?; + metadata.set_item("ephemeral", false)?; + metadata.set_item("persisted", true)?; + + let message = PyDict::new(py); + message.set_item("role", &item.role)?; + message.set_item("content", &item.content)?; + message.set_item("metadata", metadata)?; + messages.push(message.into_any().unbind()); + } + } + + if let Some(message) = user_message.filter(|message| !message.is_empty()) { + let source = user_message_source.as_deref().unwrap_or(hook_name); + let display = self.display_system_obj.bind(py); + if display.is_none() { + log::debug!( + "Hook message ({}): {} (hook={})", + user_message_level, + truncate_for_log(&message, 200), + source + ); + } else if let Err(error) = display.call_method( + "show_message", + (&message, &user_message_level, format!("hook:{source}")), + None, + ) { + log::error!("Error calling display_system: {error}"); + } + } + if suppress_output { + log::debug!("Hook '{}' requested output suppression", hook_name); + } + + let result_py = result.unbind(); + let hook_result_cls = py + .import("amplifier_core.models")? + .getattr("HookResult")? + .unbind(); + wrap_future_as_coroutine( + py, + pyo3_async_runtimes::tokio::future_into_py(py, async move { + let bridge = PyContextManagerBridge { + py_obj: context_obj, + }; + for message in messages { + bridge.add_message(message).await?; + } + Self::make_residual_hook_result(&hook_result_cls, &result_py, &residual) + }), + ) + } + + fn make_residual_hook_result( + hook_result_cls: &Py, + original: &Py, + residual: &[PreparedInjection], + ) -> PyResult> { + Python::try_attach(|py| -> PyResult> { + let values = original.call_method0(py, "model_dump")?; + let updates = PyDict::new(py); + let injections = pyo3::types::PyList::empty(py); + for item in residual { + let injection = PyDict::new(py); + injection.set_item("content", &item.content)?; + injection.set_item("role", &item.role)?; + injection.set_item("ephemeral", item.ephemeral)?; + injection.set_item( + "append_to_last_tool_result", + item.append_to_last_tool_result, + )?; + injection.set_item("hook_name", &item.hook_name)?; + injection.set_item("event", &item.event)?; + injections.append(injection)?; + } + updates.set_item("context_injections", injections)?; + if residual.is_empty() { + updates.set_item("action", "continue")?; + updates.set_item("context_injection", py.None())?; + updates.set_item("context_injection_role", "system")?; + updates.set_item("ephemeral", false)?; + updates.set_item("append_to_last_tool_result", false)?; + } else { + updates.set_item("action", "inject_context")?; + updates.set_item( + "context_injection", + residual + .iter() + .map(|item| item.content.as_str()) + .collect::>() + .join("\n\n"), + )?; + updates.set_item("context_injection_role", &residual[0].role)?; + updates.set_item("ephemeral", true)?; + updates.set_item( + "append_to_last_tool_result", + residual.iter().any(|item| item.append_to_last_tool_result), + )?; + } + values.call_method1(py, "update", (updates,))?; + hook_result_cls.call_method1(py, "model_validate", (values,)) + }) + .ok_or_else(|| PyRuntimeError::new_err("Failed to create residual HookResult"))? + } + /// Construct a HookResult Python object with the given action and optional reason. /// /// Centralises the `Python::try_attach` + `PyDict` + `hook_result_cls.call` pattern diff --git a/crates/amplifier-core/src/bridges/grpc_hook.rs b/crates/amplifier-core/src/bridges/grpc_hook.rs index 5a7fa7d..0a1f8cb 100644 --- a/crates/amplifier-core/src/bridges/grpc_hook.rs +++ b/crates/amplifier-core/src/bridges/grpc_hook.rs @@ -194,6 +194,35 @@ impl GrpcHookBridge { } }; + let context_injections = proto + .context_injections + .into_iter() + .map(|injection| { + let role = match amplifier_module::ContextInjectionRole::try_from(injection.role) { + Ok(amplifier_module::ContextInjectionRole::System) => { + models::ContextInjectionRole::System + } + Ok(amplifier_module::ContextInjectionRole::User) => { + models::ContextInjectionRole::User + } + Ok(amplifier_module::ContextInjectionRole::Assistant) => { + models::ContextInjectionRole::Assistant + } + Ok(amplifier_module::ContextInjectionRole::Unspecified) | Err(_) => { + models::ContextInjectionRole::System + } + }; + models::ContextInjection { + content: injection.content, + role, + ephemeral: injection.ephemeral, + append_to_last_tool_result: injection.append_to_last_tool_result, + hook_name: injection.hook_name, + event: injection.event, + } + }) + .collect(); + let approval_prompt = if proto.approval_prompt.is_empty() { None } else { @@ -267,6 +296,7 @@ impl GrpcHookBridge { user_message_level, user_message_source, append_to_last_tool_result: proto.append_to_last_tool_result, + context_injections, extensions: HashMap::new(), } } @@ -333,6 +363,7 @@ mod tests { user_message_level: 0, user_message_source: String::new(), append_to_last_tool_result: false, + context_injections: vec![], } } diff --git a/crates/amplifier-core/src/generated/amplifier.module.rs b/crates/amplifier-core/src/generated/amplifier.module.rs index d510a11..b280ae7 100644 --- a/crates/amplifier-core/src/generated/amplifier.module.rs +++ b/crates/amplifier-core/src/generated/amplifier.module.rs @@ -405,6 +405,21 @@ pub struct ToolResult { pub error_json: ::prost::alloc::string::String, } #[derive(Clone, PartialEq, ::prost::Message)] +pub struct ContextInjection { + #[prost(string, tag = "1")] + pub content: ::prost::alloc::string::String, + #[prost(enumeration = "ContextInjectionRole", tag = "2")] + pub role: i32, + #[prost(bool, tag = "3")] + pub ephemeral: bool, + #[prost(bool, tag = "4")] + pub append_to_last_tool_result: bool, + #[prost(string, tag = "5")] + pub hook_name: ::prost::alloc::string::String, + #[prost(string, tag = "6")] + pub event: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] pub struct HookResult { #[prost(enumeration = "HookAction", tag = "1")] pub action: i32, @@ -437,6 +452,8 @@ pub struct HookResult { pub user_message_source: ::prost::alloc::string::String, #[prost(bool, tag = "15")] pub append_to_last_tool_result: bool, + #[prost(message, repeated, tag = "16")] + pub context_injections: ::prost::alloc::vec::Vec, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct ModelInfo { diff --git a/crates/amplifier-core/src/generated/conversions.rs b/crates/amplifier-core/src/generated/conversions.rs index 39b06cf..fdc8ae3 100644 --- a/crates/amplifier-core/src/generated/conversions.rs +++ b/crates/amplifier-core/src/generated/conversions.rs @@ -602,6 +602,27 @@ pub fn native_hook_result_to_proto( .map(|d| to_json_or_warn(d, "HookResult data")) .unwrap_or_default(); + let context_injections = result + .context_injections + .iter() + .map(|injection| amplifier_module::ContextInjection { + content: injection.content.clone(), + role: match injection.role { + ContextInjectionRole::System => { + amplifier_module::ContextInjectionRole::System as i32 + } + ContextInjectionRole::User => amplifier_module::ContextInjectionRole::User as i32, + ContextInjectionRole::Assistant => { + amplifier_module::ContextInjectionRole::Assistant as i32 + } + }, + ephemeral: injection.ephemeral, + append_to_last_tool_result: injection.append_to_last_tool_result, + hook_name: injection.hook_name.clone(), + event: injection.event.clone(), + }) + .collect(); + amplifier_module::HookResult { action, data_json, @@ -618,6 +639,7 @@ pub fn native_hook_result_to_proto( user_message_level, user_message_source: result.user_message_source.clone().unwrap_or_default(), append_to_last_tool_result: result.append_to_last_tool_result, + context_injections, } } @@ -2289,7 +2311,8 @@ mod tests { fn hook_result_roundtrip_via_bridge_reverse() { use crate::bridges::grpc_hook::GrpcHookBridge; use crate::models::{ - ApprovalDefault, ContextInjectionRole, HookAction, HookResult, UserMessageLevel, + ApprovalDefault, ContextInjection, ContextInjectionRole, HookAction, HookResult, + UserMessageLevel, }; let original = HookResult { @@ -2308,6 +2331,14 @@ mod tests { user_message_level: UserMessageLevel::Warning, user_message_source: Some("approval-hook".to_string()), append_to_last_tool_result: false, + context_injections: vec![ContextInjection { + content: "ordered injection".to_string(), + role: ContextInjectionRole::Assistant, + ephemeral: true, + append_to_last_tool_result: true, + hook_name: "registered-hook".to_string(), + event: "tool:post".to_string(), + }], extensions: HashMap::new(), }; @@ -2334,6 +2365,7 @@ mod tests { restored.append_to_last_tool_result, original.append_to_last_tool_result ); + assert_eq!(restored.context_injections, original.context_injections); } #[test] diff --git a/crates/amplifier-core/src/generated/equivalence_tests.rs b/crates/amplifier-core/src/generated/equivalence_tests.rs index 87b4775..9a844fa 100644 --- a/crates/amplifier-core/src/generated/equivalence_tests.rs +++ b/crates/amplifier-core/src/generated/equivalence_tests.rs @@ -59,7 +59,7 @@ mod tests { } #[test] - fn proto_hook_result_has_all_15_fields() { + fn proto_hook_result_has_all_16_fields() { let result = HookResult { action: HookAction::Modify as i32, data_json: r#"{"modified": true}"#.into(), @@ -76,6 +76,7 @@ mod tests { user_message_level: UserMessageLevel::Warning as i32, user_message_source: "content-filter".into(), append_to_last_tool_result: true, + context_injections: vec![], }; assert_eq!(result.action, HookAction::Modify as i32); assert_eq!(result.data_json, r#"{"modified": true}"#); diff --git a/crates/amplifier-core/src/hooks.rs b/crates/amplifier-core/src/hooks.rs index d10b113..9c90469 100644 --- a/crates/amplifier-core/src/hooks.rs +++ b/crates/amplifier-core/src/hooks.rs @@ -31,7 +31,7 @@ use std::time::Duration; use serde_json::Value; -use crate::models::{HookAction, HookResult}; +use crate::models::{ContextInjection, HookAction, HookResult}; use crate::traits::HookHandler; // --------------------------------------------------------------------------- @@ -209,7 +209,7 @@ impl HookRegistry { let mut inject_context_results: Vec = Vec::new(); for (handler, name) in &entries { - let result = match handler.handle(event, current_data.clone()).await { + let mut result = match handler.handle(event, current_data.clone()).await { Ok(r) => r, Err(e) => { // Error in handler -- log and continue (matches Python behaviour). @@ -222,6 +222,8 @@ impl HookRegistry { } }; + normalize_inject_context_result(&mut result, name, event); + // Deny short-circuits immediately if result.action == HookAction::Deny { return result; @@ -241,7 +243,7 @@ impl HookRegistry { } // Collect inject_context for merging at end - if result.action == HookAction::InjectContext && result.context_injection.is_some() { + if result.action == HookAction::InjectContext && !result.context_injections.is_empty() { inject_context_results.push(result.clone()); } @@ -402,58 +404,92 @@ fn value_to_map(value: &Value) -> HashMap { /// Merge multiple inject_context HookResults into a single result. /// -/// Combines injections with `"\n\n"` separator. `context_injection_role` and -/// `suppress_output` are taken from the first result (these are display/role -/// settings where "first wins" is a reasonable, harmless default). `ephemeral` -/// and `append_to_last_tool_result` instead use OR semantics across ALL -/// results, not just the first: -/// -/// `ephemeral` in particular MUST be the logical OR of every contributing -/// result, not `first.ephemeral`. The combined `context_injection` string is -/// the concatenation of every hook's content -- if even one of those hooks -/// marked its own contribution `ephemeral: true` (regenerated per turn), the -/// resulting combined string is regenerated per turn too, byte-for-byte -/// identical only when ALL contributors are stable. Taking only the first -/// result's `ephemeral` flag meant a single non-ephemeral hook running before -/// an ephemeral one (pure registration-order luck) would silently downgrade -/// the merged result to `ephemeral: false` -- causing every downstream -/// consumer that trusts `Message.metadata["ephemeral"]` (e.g. the Anthropic -/// provider's conversation-region prompt-cache breakpoint placement) to lose -/// the ephemeral signal for the ENTIRE combined injection, not just the -/// stable part. See amplifier_module_provider_anthropic's -/// `_count_trailing_ephemeral_messages` / `_apply_conversation_cache_control`. +/// Keeps the ordered list lossless and projects the legacy scalar fields from +/// every item: bodies are joined with `"\n\n"`, the first role wins, and both +/// boolean flags use OR semantics. fn merge_inject_context_results(results: &[HookResult]) -> HookResult { if results.is_empty() { return HookResult::default(); } - if results.len() == 1 { - return results[0].clone(); + let context_injections: Vec = results + .iter() + .flat_map(|result| { + if result.context_injections.is_empty() { + result + .context_injection + .as_ref() + .map(|content| { + vec![ContextInjection { + content: content.clone(), + role: result.context_injection_role.clone(), + ephemeral: result.ephemeral, + append_to_last_tool_result: result.append_to_last_tool_result, + hook_name: "unknown".to_string(), + event: String::new(), + }] + }) + .unwrap_or_default() + } else { + result.context_injections.clone() + } + }) + .collect(); + + if context_injections.is_empty() { + return HookResult::default(); } - // Combine all injections - let combined_content: String = results + let combined_content = context_injections .iter() - .filter_map(|r| r.context_injection.as_deref()) + .map(|injection| injection.content.as_str()) .collect::>() .join("\n\n"); - - // role/suppress_output: "first wins" -- harmless display-only settings. let first = &results[0]; + let first_injection = &context_injections[0]; HookResult { action: HookAction::InjectContext, context_injection: Some(combined_content), - context_injection_role: first.context_injection_role.clone(), - // OR semantics: ANY contributing result marking itself ephemeral - // makes the whole merged injection ephemeral (see doc comment above). - ephemeral: results.iter().any(|r| r.ephemeral), + context_injection_role: first_injection.role.clone(), + ephemeral: context_injections + .iter() + .any(|injection| injection.ephemeral), suppress_output: first.suppress_output, - append_to_last_tool_result: results.iter().any(|r| r.append_to_last_tool_result), + append_to_last_tool_result: context_injections + .iter() + .any(|injection| injection.append_to_last_tool_result), + context_injections, ..Default::default() } } +/// Bind injection provenance at the trusted registration boundary and promote +/// legacy scalar injections into the structured carrier. +fn normalize_inject_context_result(result: &mut HookResult, handler_name: &str, event: &str) { + if result.action != HookAction::InjectContext { + return; + } + + if result.context_injections.is_empty() { + if let Some(content) = result.context_injection.clone() { + result.context_injections.push(ContextInjection { + content, + role: result.context_injection_role.clone(), + ephemeral: result.ephemeral, + append_to_last_tool_result: result.append_to_last_tool_result, + hook_name: handler_name.to_string(), + event: event.to_string(), + }); + } + } + + for injection in &mut result.context_injections { + injection.hook_name = handler_name.to_string(); + injection.event = event.to_string(); + } +} + // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- @@ -1257,6 +1293,39 @@ mod tests { ); } + #[tokio::test] + async fn registered_mixed_legacy_injections_preserve_item_lifetime() { + let registry = HookRegistry::new(); + let durable = Arc::new(SimpleHandler(HookResult { + action: HookAction::InjectContext, + context_injection: Some("durable".into()), + context_injection_role: crate::models::ContextInjectionRole::Assistant, + ephemeral: false, + ..Default::default() + })); + let temporary = Arc::new(SimpleHandler(HookResult { + action: HookAction::InjectContext, + context_injection: Some("temporary".into()), + context_injection_role: crate::models::ContextInjectionRole::User, + ephemeral: true, + ..Default::default() + })); + + let _ = registry.register("test:mixed", durable, 0, Some("durable-hook".into())); + let _ = registry.register("test:mixed", temporary, 10, Some("temporary-hook".into())); + + let result = registry.emit("test:mixed", serde_json::json!({})).await; + assert_eq!(result.context_injections.len(), 2); + assert_eq!(result.context_injections[0].content, "durable"); + assert!(!result.context_injections[0].ephemeral); + assert_eq!(result.context_injections[0].hook_name, "durable-hook"); + assert_eq!(result.context_injections[0].event, "test:mixed"); + assert_eq!(result.context_injections[1].content, "temporary"); + assert!(result.context_injections[1].ephemeral); + assert_eq!(result.context_injections[1].hook_name, "temporary-hook"); + assert_eq!(result.context_injections[1].event, "test:mixed"); + } + /// Verify that the `log` crate is available and usable from amplifier-core. /// This ensures Rust code can emit structured log messages that will be /// routed through Python's logging module via pyo3-log in the bindings. diff --git a/crates/amplifier-core/src/lib.rs b/crates/amplifier-core/src/lib.rs index 3390987..3891f64 100644 --- a/crates/amplifier-core/src/lib.rs +++ b/crates/amplifier-core/src/lib.rs @@ -51,8 +51,8 @@ pub use errors::{AmplifierError, ContextError, HookError, ProviderError, Session // Core data models pub use models::{ ApprovalDefault, ApprovalRequest, ApprovalResponse, ConfigField, ConfigFieldType, - ContextInjectionRole, HookAction, HookResult, ModelInfo, ModuleInfo, ModuleType, ProviderInfo, - SessionState, SessionStatus, ToolResult, UserMessageLevel, + ContextInjection, ContextInjectionRole, HookAction, HookResult, ModelInfo, ModuleInfo, + ModuleType, ProviderInfo, SessionState, SessionStatus, ToolResult, UserMessageLevel, }; // Chat protocol models diff --git a/crates/amplifier-core/src/models.rs b/crates/amplifier-core/src/models.rs index 266c62e..6c3c50d 100644 --- a/crates/amplifier-core/src/models.rs +++ b/crates/amplifier-core/src/models.rs @@ -106,6 +106,40 @@ pub enum SessionState { // Structs // --------------------------------------------------------------------------- +/// One independently processed context injection from a hook. +/// +/// The registry binds `hook_name` and `event` from the registered handler that +/// emitted this item. Consumers must not rely on handler-provided provenance. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct ContextInjection { + /// Text to inject into the agent's conversation context. + pub content: String, + + /// Role for the injected message in conversation. + #[serde(default)] + pub role: ContextInjectionRole, + + /// Whether this injection is request-only rather than durable history. + #[serde(default)] + pub ephemeral: bool, + + /// Carrier for post-tool-result placement; interpreted by the loop layer. + #[serde(default)] + pub append_to_last_tool_result: bool, + + /// Registered handler name, bound by the hook registry. + #[serde(default = "default_unknown_hook_name")] + pub hook_name: String, + + /// Event that emitted the injection, bound by the hook registry. + #[serde(default)] + pub event: String, +} + +fn default_unknown_hook_name() -> String { + "unknown".to_string() +} + /// Result from hook execution with enhanced capabilities. /// /// Hooks can observe, block, modify operations, inject context to the agent, @@ -187,6 +221,13 @@ pub struct HookResult { #[serde(default)] pub append_to_last_tool_result: bool, + /// Ordered, lossless context injection items. + /// + /// The legacy scalar context fields remain for backwards compatibility and + /// are projected from this list by the hook registry. + #[serde(default)] + pub context_injections: Vec, + /// Extension fields for forward-compatibility. /// Captures any unknown JSON keys during deserialization. #[serde(flatten)] @@ -215,6 +256,7 @@ impl Default for HookResult { user_message_level: UserMessageLevel::default(), user_message_source: None, append_to_last_tool_result: false, + context_injections: Vec::new(), extensions: HashMap::new(), } } @@ -560,6 +602,23 @@ mod tests { assert!(result.context_injection.is_none()); assert_eq!(result.context_injection_role, ContextInjectionRole::System); assert!(!result.ephemeral); + assert!(result.context_injections.is_empty()); + } + + #[test] + fn context_injection_serialization_roundtrip() { + let injection = ContextInjection { + content: "temporary reminder".into(), + role: ContextInjectionRole::User, + ephemeral: true, + append_to_last_tool_result: true, + hook_name: "registered-hook".into(), + event: "tool:post".into(), + }; + + let json = serde_json::to_string(&injection).unwrap(); + let restored: ContextInjection = serde_json::from_str(&json).unwrap(); + assert_eq!(restored, injection); } #[test] diff --git a/proto/amplifier_module.proto b/proto/amplifier_module.proto index e12f988..cd47b85 100644 --- a/proto/amplifier_module.proto +++ b/proto/amplifier_module.proto @@ -389,6 +389,15 @@ message ToolResult { string error_json = 3; } +message ContextInjection { + string content = 1; + ContextInjectionRole role = 2; + bool ephemeral = 3; + bool append_to_last_tool_result = 4; + string hook_name = 5; + string event = 6; +} + message HookResult { HookAction action = 1; string data_json = 2; @@ -406,6 +415,7 @@ message HookResult { UserMessageLevel user_message_level = 13; string user_message_source = 14; bool append_to_last_tool_result = 15; + repeated ContextInjection context_injections = 16; } message ModelInfo { diff --git a/python/amplifier_core/__init__.py b/python/amplifier_core/__init__.py index b4e014c..ca80776 100644 --- a/python/amplifier_core/__init__.py +++ b/python/amplifier_core/__init__.py @@ -74,6 +74,7 @@ from .message_models import ToolSpec from .message_models import Usage from .models import ConfigField +from .models import ContextInjection from .models import HookResult from .models import ModelInfo from .models import ModuleInfo @@ -114,6 +115,7 @@ "HookRegistry", "ToolCall", "ToolResult", + "ContextInjection", "HookResult", "ConfigField", "ModelInfo", diff --git a/python/amplifier_core/_grpc_gen/amplifier_module_pb2.py b/python/amplifier_core/_grpc_gen/amplifier_module_pb2.py index 6f4d751..f1ac9dd 100644 --- a/python/amplifier_core/_grpc_gen/amplifier_module_pb2.py +++ b/python/amplifier_core/_grpc_gen/amplifier_module_pb2.py @@ -24,7 +24,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x61mplifier_module.proto\x12\x10\x61mplifier.module\"\x07\n\x05\x45mpty\"F\n\x08ToolSpec\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"9\n\x12ToolExecuteRequest\x12\r\n\x05input\x18\x01 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x02 \x01(\t\"[\n\x13ToolExecuteResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0e\n\x06output\x18\x02 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x03 \x01(\t\x12\r\n\x05\x65rror\x18\x04 \x01(\t\"\xd6\x01\n\nModuleInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x31\n\x0bmodule_type\x18\x04 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\x12\x13\n\x0bmount_point\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x06 \x01(\t\x12\x1a\n\x12\x63onfig_schema_json\x18\x07 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x08 \x03(\t\x12\x0e\n\x06\x61uthor\x18\t \x01(\t\"\x8c\x01\n\x0cMountRequest\x12:\n\x06\x63onfig\x18\x01 \x03(\x0b\x32*.amplifier.module.MountRequest.ConfigEntry\x12\x11\n\tmodule_id\x18\x02 \x01(\t\x1a-\n\x0b\x43onfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"_\n\rMountResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12.\n\x06status\x18\x03 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\"V\n\x13HealthCheckResponse\x12.\n\x06status\x18\x01 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xca\x02\n\x0b\x43onfigField\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x35\n\nfield_type\x18\x03 \x01(\x0e\x32!.amplifier.module.ConfigFieldType\x12\x0e\n\x06prompt\x18\x04 \x01(\t\x12\x0f\n\x07\x65nv_var\x18\x05 \x01(\t\x12\x0f\n\x07\x63hoices\x18\x06 \x03(\t\x12\x10\n\x08required\x18\x07 \x01(\x08\x12\x15\n\rdefault_value\x18\x08 \x01(\t\x12>\n\tshow_when\x18\t \x03(\x0b\x32+.amplifier.module.ConfigField.ShowWhenEntry\x12\x16\n\x0erequires_model\x18\n \x01(\x08\x1a/\n\rShowWhenEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xbc\x01\n\rProviderError\x12\x37\n\nerror_type\x18\x01 \x01(\x0e\x32#.amplifier.module.ProviderErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x15\n\rprovider_name\x18\x03 \x01(\t\x12\r\n\x05model\x18\x04 \x01(\t\x12\x13\n\x0bstatus_code\x18\x05 \x01(\x05\x12\x11\n\tretryable\x18\x06 \x01(\x08\x12\x13\n\x0bretry_after\x18\x07 \x01(\x01\"\x97\x01\n\tToolError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.ToolErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\ttool_name\x18\x03 \x01(\t\x12\x0e\n\x06stdout\x18\x04 \x01(\t\x12\x0e\n\x06stderr\x18\x05 \x01(\t\x12\x11\n\texit_code\x18\x06 \x01(\x05\"d\n\tHookError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.HookErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\thook_name\x18\x03 \x01(\t\"\xef\x01\n\x0e\x41mplifierError\x12\x39\n\x0eprovider_error\x18\x01 \x01(\x0b\x32\x1f.amplifier.module.ProviderErrorH\x00\x12\x31\n\ntool_error\x18\x02 \x01(\x0b\x32\x1b.amplifier.module.ToolErrorH\x00\x12\x31\n\nhook_error\x18\x03 \x01(\x0b\x32\x1b.amplifier.module.HookErrorH\x00\x12\x17\n\rgeneric_error\x18\x04 \x01(\tH\x00\x12\x1a\n\x10validation_error\x18\x05 \x01(\tH\x00\x42\x07\n\x05\x65rror\"\x19\n\tTextBlock\x12\x0c\n\x04text\x18\x01 \x01(\t\"E\n\rThinkingBlock\x12\x10\n\x08thinking\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\t\"%\n\x15RedactedThinkingBlock\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\t\"=\n\rToolCallBlock\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x12\n\ninput_json\x18\x03 \x01(\t\"<\n\x0fToolResultBlock\x12\x14\n\x0ctool_call_id\x18\x01 \x01(\t\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\"C\n\nImageBlock\x12\x12\n\nmedia_type\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x13\n\x0bsource_json\x18\x03 \x01(\t\"2\n\x0eReasoningBlock\x12\x0f\n\x07\x63ontent\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x03(\t\"\xf1\x03\n\x0c\x43ontentBlock\x12\x31\n\ntext_block\x18\x01 \x01(\x0b\x32\x1b.amplifier.module.TextBlockH\x00\x12\x39\n\x0ethinking_block\x18\x02 \x01(\x0b\x32\x1f.amplifier.module.ThinkingBlockH\x00\x12J\n\x17redacted_thinking_block\x18\x03 \x01(\x0b\x32\'.amplifier.module.RedactedThinkingBlockH\x00\x12:\n\x0ftool_call_block\x18\x04 \x01(\x0b\x32\x1f.amplifier.module.ToolCallBlockH\x00\x12>\n\x11tool_result_block\x18\x05 \x01(\x0b\x32!.amplifier.module.ToolResultBlockH\x00\x12\x33\n\x0bimage_block\x18\x06 \x01(\x0b\x32\x1c.amplifier.module.ImageBlockH\x00\x12;\n\x0freasoning_block\x18\x07 \x01(\x0b\x32 .amplifier.module.ReasoningBlockH\x00\x12\x30\n\nvisibility\x18\x08 \x01(\x0e\x32\x1c.amplifier.module.VisibilityB\x07\n\x05\x62lock\"B\n\x10\x43ontentBlockList\x12.\n\x06\x62locks\x18\x01 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xca\x01\n\x07Message\x12$\n\x04role\x18\x01 \x01(\x0e\x32\x16.amplifier.module.Role\x12\x16\n\x0ctext_content\x18\x02 \x01(\tH\x00\x12;\n\rblock_content\x18\x03 \x01(\x0b\x32\".amplifier.module.ContentBlockListH\x00\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x14\n\x0ctool_call_id\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\tB\t\n\x07\x63ontent\"C\n\x0fToolCallMessage\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x16\n\x0e\x61rguments_json\x18\x03 \x01(\t\"K\n\rToolSpecProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"7\n\x10JsonSchemaFormat\x12\x13\n\x0bschema_json\x18\x01 \x01(\t\x12\x0e\n\x06strict\x18\x02 \x01(\x08\"u\n\x0eResponseFormat\x12\x0e\n\x04text\x18\x01 \x01(\x08H\x00\x12\x0e\n\x04json\x18\x02 \x01(\x08H\x00\x12\x39\n\x0bjson_schema\x18\x03 \x01(\x0b\x32\".amplifier.module.JsonSchemaFormatH\x00\x42\x08\n\x06\x66ormat\"\xf7\x01\n\x05Usage\x12\x15\n\rprompt_tokens\x18\x01 \x01(\x05\x12\x19\n\x11\x63ompletion_tokens\x18\x02 \x01(\x05\x12\x14\n\x0ctotal_tokens\x18\x03 \x01(\x05\x12\x1d\n\x10reasoning_tokens\x18\x04 \x01(\x05H\x00\x88\x01\x01\x12\x1e\n\x11\x63\x61\x63he_read_tokens\x18\x05 \x01(\x05H\x01\x88\x01\x01\x12\"\n\x15\x63\x61\x63he_creation_tokens\x18\x06 \x01(\x05H\x02\x88\x01\x01\x42\x13\n\x11_reasoning_tokensB\x14\n\x12_cache_read_tokensB\x18\n\x16_cache_creation_tokens\"@\n\x0b\x44\x65gradation\x12\x11\n\trequested\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tual\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x81\x03\n\x0b\x43hatRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\x12.\n\x05tools\x18\x02 \x03(\x0b\x32\x1f.amplifier.module.ToolSpecProto\x12\x39\n\x0fresponse_format\x18\x03 \x01(\x0b\x32 .amplifier.module.ResponseFormat\x12\x13\n\x0btemperature\x18\x04 \x01(\x01\x12\r\n\x05top_p\x18\x05 \x01(\x01\x12\x19\n\x11max_output_tokens\x18\x06 \x01(\x05\x12\x17\n\x0f\x63onversation_id\x18\x07 \x01(\t\x12\x0e\n\x06stream\x18\x08 \x01(\x08\x12\x15\n\rmetadata_json\x18\t \x01(\t\x12\r\n\x05model\x18\n \x01(\t\x12\x13\n\x0btool_choice\x18\x0b \x01(\t\x12\x0c\n\x04stop\x18\x0c \x03(\t\x12\x18\n\x10reasoning_effort\x18\r \x01(\t\x12\x0f\n\x07timeout\x18\x0e \x01(\x01\"\x98\x02\n\x0c\x43hatResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x35\n\ntool_calls\x18\x02 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\x12&\n\x05usage\x18\x03 \x01(\x0b\x32\x17.amplifier.module.Usage\x12\x32\n\x0b\x64\x65gradation\x18\x04 \x01(\x0b\x32\x1d.amplifier.module.Degradation\x12\x15\n\rfinish_reason\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x07 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"F\n\nToolResult\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\x12\x12\n\nerror_json\x18\x03 \x01(\t\"\xa7\x04\n\nHookResult\x12,\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x1c.amplifier.module.HookAction\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x19\n\x11\x63ontext_injection\x18\x04 \x01(\t\x12\x46\n\x16\x63ontext_injection_role\x18\x05 \x01(\x0e\x32&.amplifier.module.ContextInjectionRole\x12\x11\n\tephemeral\x18\x06 \x01(\x08\x12\x17\n\x0f\x61pproval_prompt\x18\x07 \x01(\t\x12\x18\n\x10\x61pproval_options\x18\x08 \x03(\t\x12\x1d\n\x10\x61pproval_timeout\x18\t \x01(\x01H\x00\x88\x01\x01\x12;\n\x10\x61pproval_default\x18\n \x01(\x0e\x32!.amplifier.module.ApprovalDefault\x12\x17\n\x0fsuppress_output\x18\x0b \x01(\x08\x12\x14\n\x0cuser_message\x18\x0c \x01(\t\x12>\n\x12user_message_level\x18\r \x01(\x0e\x32\".amplifier.module.UserMessageLevel\x12\x1b\n\x13user_message_source\x18\x0e \x01(\t\x12\"\n\x1a\x61ppend_to_last_tool_result\x18\x0f \x01(\x08\x42\x13\n\x11_approval_timeout\"\x8d\x01\n\tModelInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x16\n\x0e\x63ontext_window\x18\x03 \x01(\x05\x12\x19\n\x11max_output_tokens\x18\x04 \x01(\x05\x12\x14\n\x0c\x63\x61pabilities\x18\x05 \x03(\t\x12\x15\n\rdefaults_json\x18\x06 \x01(\t\"\xb0\x01\n\x0cProviderInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x1b\n\x13\x63redential_env_vars\x18\x03 \x03(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x04 \x03(\t\x12\x15\n\rdefaults_json\x18\x05 \x01(\t\x12\x34\n\rconfig_fields\x18\x06 \x03(\x0b\x32\x1d.amplifier.module.ConfigField\"\x80\x01\n\x0f\x41pprovalRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x14\n\x0c\x64\x65tails_json\x18\x03 \x01(\t\x12\x12\n\nrisk_level\x18\x04 \x01(\t\x12\x14\n\x07timeout\x18\x05 \x01(\x01H\x00\x88\x01\x01\x42\n\n\x08_timeout\"F\n\x10\x41pprovalResponse\x12\x10\n\x08\x61pproved\x18\x01 \x01(\x08\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x10\n\x08remember\x18\x03 \x01(\x08\"A\n\x12ListModelsResponse\x12+\n\x06models\x18\x01 \x03(\x0b\x32\x1b.amplifier.module.ModelInfo\"O\n\x16ParseToolCallsResponse\x12\x35\n\ntool_calls\x18\x01 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\"@\n\x1aOrchestratorExecuteRequest\x12\x0e\n\x06prompt\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\">\n\x1bOrchestratorExecuteResponse\x12\x10\n\x08response\x18\x01 \x01(\t\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"?\n\x11\x41\x64\x64MessageRequest\x12*\n\x07message\x18\x01 \x01(\x0b\x32\x19.amplifier.module.Message\"B\n\x13GetMessagesResponse\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"J\n\x1bGetMessagesForRequestParams\x12\x14\n\x0ctoken_budget\x18\x01 \x01(\x05\x12\x15\n\rprovider_name\x18\x02 \x01(\t\"A\n\x12SetMessagesRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"5\n\x11HookHandleRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\".\n\x17GetSubscriptionsRequest\x12\x13\n\x0b\x63onfig_json\x18\x01 \x01(\t\"V\n\x18GetSubscriptionsResponse\x12:\n\rsubscriptions\x18\x01 \x03(\x0b\x32#.amplifier.module.EventSubscription\"B\n\x11\x45ventSubscription\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x10\n\x08priority\x18\x02 \x01(\x05\x12\x0c\n\x04name\x18\x03 \x01(\t\"d\n\x1b\x43ompleteWithProviderRequest\x12\x15\n\rprovider_name\x18\x01 \x01(\t\x12.\n\x07request\x18\x02 \x01(\x0b\x32\x1d.amplifier.module.ChatRequest\";\n\x12\x45xecuteToolRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x12\n\ninput_json\x18\x02 \x01(\t\"3\n\x0f\x45mitHookRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\"V\n\x19\x45mitHookAndCollectRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x17\n\x0ftimeout_seconds\x18\x03 \x01(\x01\"4\n\x1a\x45mitHookAndCollectResponse\x12\x16\n\x0eresponses_json\x18\x01 \x03(\t\"(\n\x12GetMessagesRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\"Y\n\x17KernelAddMessageRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12*\n\x07message\x18\x02 \x01(\x0b\x32\x19.amplifier.module.Message\"a\n\x17GetMountedModuleRequest\x12\x13\n\x0bmodule_name\x18\x01 \x01(\t\x12\x31\n\x0bmodule_type\x18\x02 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\"U\n\x18GetMountedModuleResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12*\n\x04info\x18\x02 \x01(\x0b\x32\x1c.amplifier.module.ModuleInfo\"=\n\x19RegisterCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\nvalue_json\x18\x02 \x01(\t\"$\n\x14GetCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\":\n\x15GetCapabilityResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12\x12\n\nvalue_json\x18\x02 \x01(\t*\xbc\x01\n\nModuleType\x12\x1b\n\x17MODULE_TYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14MODULE_TYPE_PROVIDER\x10\x01\x12\x14\n\x10MODULE_TYPE_TOOL\x10\x02\x12\x14\n\x10MODULE_TYPE_HOOK\x10\x03\x12\x16\n\x12MODULE_TYPE_MEMORY\x10\x04\x12\x19\n\x15MODULE_TYPE_GUARDRAIL\x10\x05\x12\x18\n\x14MODULE_TYPE_APPROVAL\x10\x06*\x82\x01\n\x0cHealthStatus\x12\x1d\n\x19HEALTH_STATUS_UNSPECIFIED\x10\x00\x12\x19\n\x15HEALTH_STATUS_SERVING\x10\x01\x12\x1d\n\x19HEALTH_STATUS_NOT_SERVING\x10\x02\x12\x19\n\x15HEALTH_STATUS_UNKNOWN\x10\x03*\xad\x01\n\x0f\x43onfigFieldType\x12!\n\x1d\x43ONFIG_FIELD_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_STRING\x10\x01\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_NUMBER\x10\x02\x12\x1d\n\x19\x43ONFIG_FIELD_TYPE_BOOLEAN\x10\x03\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_SECRET\x10\x04*\xd8\x02\n\x11ProviderErrorType\x12#\n\x1fPROVIDER_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18PROVIDER_ERROR_TYPE_AUTH\x10\x01\x12\"\n\x1ePROVIDER_ERROR_TYPE_RATE_LIMIT\x10\x02\x12&\n\"PROVIDER_ERROR_TYPE_CONTEXT_LENGTH\x10\x03\x12\'\n#PROVIDER_ERROR_TYPE_INVALID_REQUEST\x10\x04\x12&\n\"PROVIDER_ERROR_TYPE_CONTENT_FILTER\x10\x05\x12#\n\x1fPROVIDER_ERROR_TYPE_UNAVAILABLE\x10\x06\x12\x1f\n\x1bPROVIDER_ERROR_TYPE_TIMEOUT\x10\x07\x12\x1d\n\x19PROVIDER_ERROR_TYPE_OTHER\x10\x08*\x8c\x01\n\rToolErrorType\x12\x1f\n\x1bTOOL_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19TOOL_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aTOOL_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17TOOL_ERROR_TYPE_TIMEOUT\x10\x03*\x8c\x01\n\rHookErrorType\x12\x1f\n\x1bHOOK_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19HOOK_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aHOOK_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17HOOK_ERROR_TYPE_TIMEOUT\x10\x03*\x86\x01\n\x04Role\x12\x14\n\x10ROLE_UNSPECIFIED\x10\x00\x12\x0f\n\x0bROLE_SYSTEM\x10\x01\x12\r\n\tROLE_USER\x10\x02\x12\x12\n\x0eROLE_ASSISTANT\x10\x03\x12\r\n\tROLE_TOOL\x10\x04\x12\x11\n\rROLE_FUNCTION\x10\x05\x12\x12\n\x0eROLE_DEVELOPER\x10\x06*o\n\nVisibility\x12\x1a\n\x16VISIBILITY_UNSPECIFIED\x10\x00\x12\x12\n\x0eVISIBILITY_ALL\x10\x01\x12\x17\n\x13VISIBILITY_LLM_ONLY\x10\x02\x12\x18\n\x14VISIBILITY_USER_ONLY\x10\x03*\xab\x01\n\nHookAction\x12\x1b\n\x17HOOK_ACTION_UNSPECIFIED\x10\x00\x12\x18\n\x14HOOK_ACTION_CONTINUE\x10\x01\x12\x16\n\x12HOOK_ACTION_MODIFY\x10\x02\x12\x14\n\x10HOOK_ACTION_DENY\x10\x03\x12\x1e\n\x1aHOOK_ACTION_INJECT_CONTEXT\x10\x04\x12\x18\n\x14HOOK_ACTION_ASK_USER\x10\x05*\xa8\x01\n\x14\x43ontextInjectionRole\x12&\n\"CONTEXT_INJECTION_ROLE_UNSPECIFIED\x10\x00\x12!\n\x1d\x43ONTEXT_INJECTION_ROLE_SYSTEM\x10\x01\x12\x1f\n\x1b\x43ONTEXT_INJECTION_ROLE_USER\x10\x02\x12$\n CONTEXT_INJECTION_ROLE_ASSISTANT\x10\x03*l\n\x0f\x41pprovalDefault\x12 \n\x1c\x41PPROVAL_DEFAULT_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x41PPROVAL_DEFAULT_APPROVE\x10\x01\x12\x19\n\x15\x41PPROVAL_DEFAULT_DENY\x10\x02*\x91\x01\n\x10UserMessageLevel\x12\"\n\x1eUSER_MESSAGE_LEVEL_UNSPECIFIED\x10\x00\x12\x1b\n\x17USER_MESSAGE_LEVEL_INFO\x10\x01\x12\x1e\n\x1aUSER_MESSAGE_LEVEL_WARNING\x10\x02\x12\x1c\n\x18USER_MESSAGE_LEVEL_ERROR\x10\x03\x32\xa5\x01\n\x0bToolService\x12>\n\x07GetSpec\x12\x17.amplifier.module.Empty\x1a\x1a.amplifier.module.ToolSpec\x12V\n\x07\x45xecute\x12$.amplifier.module.ToolExecuteRequest\x1a%.amplifier.module.ToolExecuteResponse2\x9f\x03\n\x0fProviderService\x12\x42\n\x07GetInfo\x12\x17.amplifier.module.Empty\x1a\x1e.amplifier.module.ProviderInfo\x12K\n\nListModels\x12\x17.amplifier.module.Empty\x1a$.amplifier.module.ListModelsResponse\x12I\n\x08\x43omplete\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse\x12T\n\x11\x43ompleteStreaming\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Z\n\x0eParseToolCalls\x12\x1e.amplifier.module.ChatResponse\x1a(.amplifier.module.ParseToolCallsResponse2}\n\x13OrchestratorService\x12\x66\n\x07\x45xecute\x12,.amplifier.module.OrchestratorExecuteRequest\x1a-.amplifier.module.OrchestratorExecuteResponse2\xa3\x03\n\x0e\x43ontextService\x12J\n\nAddMessage\x12#.amplifier.module.AddMessageRequest\x1a\x17.amplifier.module.Empty\x12M\n\x0bGetMessages\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.GetMessagesResponse\x12m\n\x15GetMessagesForRequest\x12-.amplifier.module.GetMessagesForRequestParams\x1a%.amplifier.module.GetMessagesResponse\x12L\n\x0bSetMessages\x12$.amplifier.module.SetMessagesRequest\x1a\x17.amplifier.module.Empty\x12\x39\n\x05\x43lear\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty2\xc5\x01\n\x0bHookService\x12K\n\x06Handle\x12#.amplifier.module.HookHandleRequest\x1a\x1c.amplifier.module.HookResult\x12i\n\x10GetSubscriptions\x12).amplifier.module.GetSubscriptionsRequest\x1a*.amplifier.module.GetSubscriptionsResponse2k\n\x0f\x41pprovalService\x12X\n\x0fRequestApproval\x12!.amplifier.module.ApprovalRequest\x1a\".amplifier.module.ApprovalResponse2\xd0\x07\n\rKernelService\x12\x65\n\x14\x43ompleteWithProvider\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse\x12p\n\x1d\x43ompleteWithProviderStreaming\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Q\n\x0b\x45xecuteTool\x12$.amplifier.module.ExecuteToolRequest\x1a\x1c.amplifier.module.ToolResult\x12K\n\x08\x45mitHook\x12!.amplifier.module.EmitHookRequest\x1a\x1c.amplifier.module.HookResult\x12o\n\x12\x45mitHookAndCollect\x12+.amplifier.module.EmitHookAndCollectRequest\x1a,.amplifier.module.EmitHookAndCollectResponse\x12Z\n\x0bGetMessages\x12$.amplifier.module.GetMessagesRequest\x1a%.amplifier.module.GetMessagesResponse\x12P\n\nAddMessage\x12).amplifier.module.KernelAddMessageRequest\x1a\x17.amplifier.module.Empty\x12i\n\x10GetMountedModule\x12).amplifier.module.GetMountedModuleRequest\x1a*.amplifier.module.GetMountedModuleResponse\x12Z\n\x12RegisterCapability\x12+.amplifier.module.RegisterCapabilityRequest\x1a\x17.amplifier.module.Empty\x12`\n\rGetCapability\x12&.amplifier.module.GetCapabilityRequest\x1a\'.amplifier.module.GetCapabilityResponse2\xaf\x02\n\x0fModuleLifecycle\x12H\n\x05Mount\x12\x1e.amplifier.module.MountRequest\x1a\x1f.amplifier.module.MountResponse\x12;\n\x07\x43leanup\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty\x12M\n\x0bHealthCheck\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.HealthCheckResponse\x12\x46\n\rGetModuleInfo\x12\x17.amplifier.module.Empty\x1a\x1c.amplifier.module.ModuleInfob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x61mplifier_module.proto\x12\x10\x61mplifier.module\"\x07\n\x05\x45mpty\"F\n\x08ToolSpec\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"9\n\x12ToolExecuteRequest\x12\r\n\x05input\x18\x01 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x02 \x01(\t\"[\n\x13ToolExecuteResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0e\n\x06output\x18\x02 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x03 \x01(\t\x12\r\n\x05\x65rror\x18\x04 \x01(\t\"\xd6\x01\n\nModuleInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x31\n\x0bmodule_type\x18\x04 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\x12\x13\n\x0bmount_point\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x06 \x01(\t\x12\x1a\n\x12\x63onfig_schema_json\x18\x07 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x08 \x03(\t\x12\x0e\n\x06\x61uthor\x18\t \x01(\t\"\x8c\x01\n\x0cMountRequest\x12:\n\x06\x63onfig\x18\x01 \x03(\x0b\x32*.amplifier.module.MountRequest.ConfigEntry\x12\x11\n\tmodule_id\x18\x02 \x01(\t\x1a-\n\x0b\x43onfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"_\n\rMountResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12.\n\x06status\x18\x03 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\"V\n\x13HealthCheckResponse\x12.\n\x06status\x18\x01 \x01(\x0e\x32\x1e.amplifier.module.HealthStatus\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xca\x02\n\x0b\x43onfigField\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x35\n\nfield_type\x18\x03 \x01(\x0e\x32!.amplifier.module.ConfigFieldType\x12\x0e\n\x06prompt\x18\x04 \x01(\t\x12\x0f\n\x07\x65nv_var\x18\x05 \x01(\t\x12\x0f\n\x07\x63hoices\x18\x06 \x03(\t\x12\x10\n\x08required\x18\x07 \x01(\x08\x12\x15\n\rdefault_value\x18\x08 \x01(\t\x12>\n\tshow_when\x18\t \x03(\x0b\x32+.amplifier.module.ConfigField.ShowWhenEntry\x12\x16\n\x0erequires_model\x18\n \x01(\x08\x1a/\n\rShowWhenEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xbc\x01\n\rProviderError\x12\x37\n\nerror_type\x18\x01 \x01(\x0e\x32#.amplifier.module.ProviderErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x15\n\rprovider_name\x18\x03 \x01(\t\x12\r\n\x05model\x18\x04 \x01(\t\x12\x13\n\x0bstatus_code\x18\x05 \x01(\x05\x12\x11\n\tretryable\x18\x06 \x01(\x08\x12\x13\n\x0bretry_after\x18\x07 \x01(\x01\"\x97\x01\n\tToolError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.ToolErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\ttool_name\x18\x03 \x01(\t\x12\x0e\n\x06stdout\x18\x04 \x01(\t\x12\x0e\n\x06stderr\x18\x05 \x01(\t\x12\x11\n\texit_code\x18\x06 \x01(\x05\"d\n\tHookError\x12\x33\n\nerror_type\x18\x01 \x01(\x0e\x32\x1f.amplifier.module.HookErrorType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\thook_name\x18\x03 \x01(\t\"\xef\x01\n\x0e\x41mplifierError\x12\x39\n\x0eprovider_error\x18\x01 \x01(\x0b\x32\x1f.amplifier.module.ProviderErrorH\x00\x12\x31\n\ntool_error\x18\x02 \x01(\x0b\x32\x1b.amplifier.module.ToolErrorH\x00\x12\x31\n\nhook_error\x18\x03 \x01(\x0b\x32\x1b.amplifier.module.HookErrorH\x00\x12\x17\n\rgeneric_error\x18\x04 \x01(\tH\x00\x12\x1a\n\x10validation_error\x18\x05 \x01(\tH\x00\x42\x07\n\x05\x65rror\"\x19\n\tTextBlock\x12\x0c\n\x04text\x18\x01 \x01(\t\"E\n\rThinkingBlock\x12\x10\n\x08thinking\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\t\"%\n\x15RedactedThinkingBlock\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\t\"=\n\rToolCallBlock\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x12\n\ninput_json\x18\x03 \x01(\t\"<\n\x0fToolResultBlock\x12\x14\n\x0ctool_call_id\x18\x01 \x01(\t\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\"C\n\nImageBlock\x12\x12\n\nmedia_type\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x13\n\x0bsource_json\x18\x03 \x01(\t\"2\n\x0eReasoningBlock\x12\x0f\n\x07\x63ontent\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x03(\t\"\xf1\x03\n\x0c\x43ontentBlock\x12\x31\n\ntext_block\x18\x01 \x01(\x0b\x32\x1b.amplifier.module.TextBlockH\x00\x12\x39\n\x0ethinking_block\x18\x02 \x01(\x0b\x32\x1f.amplifier.module.ThinkingBlockH\x00\x12J\n\x17redacted_thinking_block\x18\x03 \x01(\x0b\x32\'.amplifier.module.RedactedThinkingBlockH\x00\x12:\n\x0ftool_call_block\x18\x04 \x01(\x0b\x32\x1f.amplifier.module.ToolCallBlockH\x00\x12>\n\x11tool_result_block\x18\x05 \x01(\x0b\x32!.amplifier.module.ToolResultBlockH\x00\x12\x33\n\x0bimage_block\x18\x06 \x01(\x0b\x32\x1c.amplifier.module.ImageBlockH\x00\x12;\n\x0freasoning_block\x18\x07 \x01(\x0b\x32 .amplifier.module.ReasoningBlockH\x00\x12\x30\n\nvisibility\x18\x08 \x01(\x0e\x32\x1c.amplifier.module.VisibilityB\x07\n\x05\x62lock\"B\n\x10\x43ontentBlockList\x12.\n\x06\x62locks\x18\x01 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"\xca\x01\n\x07Message\x12$\n\x04role\x18\x01 \x01(\x0e\x32\x16.amplifier.module.Role\x12\x16\n\x0ctext_content\x18\x02 \x01(\tH\x00\x12;\n\rblock_content\x18\x03 \x01(\x0b\x32\".amplifier.module.ContentBlockListH\x00\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x14\n\x0ctool_call_id\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\tB\t\n\x07\x63ontent\"C\n\x0fToolCallMessage\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x16\n\x0e\x61rguments_json\x18\x03 \x01(\t\"K\n\rToolSpecProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x17\n\x0fparameters_json\x18\x03 \x01(\t\"7\n\x10JsonSchemaFormat\x12\x13\n\x0bschema_json\x18\x01 \x01(\t\x12\x0e\n\x06strict\x18\x02 \x01(\x08\"u\n\x0eResponseFormat\x12\x0e\n\x04text\x18\x01 \x01(\x08H\x00\x12\x0e\n\x04json\x18\x02 \x01(\x08H\x00\x12\x39\n\x0bjson_schema\x18\x03 \x01(\x0b\x32\".amplifier.module.JsonSchemaFormatH\x00\x42\x08\n\x06\x66ormat\"\xf7\x01\n\x05Usage\x12\x15\n\rprompt_tokens\x18\x01 \x01(\x05\x12\x19\n\x11\x63ompletion_tokens\x18\x02 \x01(\x05\x12\x14\n\x0ctotal_tokens\x18\x03 \x01(\x05\x12\x1d\n\x10reasoning_tokens\x18\x04 \x01(\x05H\x00\x88\x01\x01\x12\x1e\n\x11\x63\x61\x63he_read_tokens\x18\x05 \x01(\x05H\x01\x88\x01\x01\x12\"\n\x15\x63\x61\x63he_creation_tokens\x18\x06 \x01(\x05H\x02\x88\x01\x01\x42\x13\n\x11_reasoning_tokensB\x14\n\x12_cache_read_tokensB\x18\n\x16_cache_creation_tokens\"@\n\x0b\x44\x65gradation\x12\x11\n\trequested\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tual\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\x81\x03\n\x0b\x43hatRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\x12.\n\x05tools\x18\x02 \x03(\x0b\x32\x1f.amplifier.module.ToolSpecProto\x12\x39\n\x0fresponse_format\x18\x03 \x01(\x0b\x32 .amplifier.module.ResponseFormat\x12\x13\n\x0btemperature\x18\x04 \x01(\x01\x12\r\n\x05top_p\x18\x05 \x01(\x01\x12\x19\n\x11max_output_tokens\x18\x06 \x01(\x05\x12\x17\n\x0f\x63onversation_id\x18\x07 \x01(\t\x12\x0e\n\x06stream\x18\x08 \x01(\x08\x12\x15\n\rmetadata_json\x18\t \x01(\t\x12\r\n\x05model\x18\n \x01(\t\x12\x13\n\x0btool_choice\x18\x0b \x01(\t\x12\x0c\n\x04stop\x18\x0c \x03(\t\x12\x18\n\x10reasoning_effort\x18\r \x01(\t\x12\x0f\n\x07timeout\x18\x0e \x01(\x01\"\x98\x02\n\x0c\x43hatResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x35\n\ntool_calls\x18\x02 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\x12&\n\x05usage\x18\x03 \x01(\x0b\x32\x17.amplifier.module.Usage\x12\x32\n\x0b\x64\x65gradation\x18\x04 \x01(\x0b\x32\x1d.amplifier.module.Degradation\x12\x15\n\rfinish_reason\x18\x05 \x01(\t\x12\x15\n\rmetadata_json\x18\x06 \x01(\t\x12\x36\n\x0e\x63ontent_blocks\x18\x07 \x03(\x0b\x32\x1e.amplifier.module.ContentBlock\"F\n\nToolResult\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x13\n\x0boutput_json\x18\x02 \x01(\t\x12\x12\n\nerror_json\x18\x03 \x01(\t\"\xb2\x01\n\x10\x43ontextInjection\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x34\n\x04role\x18\x02 \x01(\x0e\x32&.amplifier.module.ContextInjectionRole\x12\x11\n\tephemeral\x18\x03 \x01(\x08\x12\"\n\x1a\x61ppend_to_last_tool_result\x18\x04 \x01(\x08\x12\x11\n\thook_name\x18\x05 \x01(\t\x12\r\n\x05\x65vent\x18\x06 \x01(\t\"\xe7\x04\n\nHookResult\x12,\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x1c.amplifier.module.HookAction\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x19\n\x11\x63ontext_injection\x18\x04 \x01(\t\x12\x46\n\x16\x63ontext_injection_role\x18\x05 \x01(\x0e\x32&.amplifier.module.ContextInjectionRole\x12\x11\n\tephemeral\x18\x06 \x01(\x08\x12\x17\n\x0f\x61pproval_prompt\x18\x07 \x01(\t\x12\x18\n\x10\x61pproval_options\x18\x08 \x03(\t\x12\x1d\n\x10\x61pproval_timeout\x18\t \x01(\x01H\x00\x88\x01\x01\x12;\n\x10\x61pproval_default\x18\n \x01(\x0e\x32!.amplifier.module.ApprovalDefault\x12\x17\n\x0fsuppress_output\x18\x0b \x01(\x08\x12\x14\n\x0cuser_message\x18\x0c \x01(\t\x12>\n\x12user_message_level\x18\r \x01(\x0e\x32\".amplifier.module.UserMessageLevel\x12\x1b\n\x13user_message_source\x18\x0e \x01(\t\x12\"\n\x1a\x61ppend_to_last_tool_result\x18\x0f \x01(\x08\x12>\n\x12\x63ontext_injections\x18\x10 \x03(\x0b\x32\".amplifier.module.ContextInjectionB\x13\n\x11_approval_timeout\"\x8d\x01\n\tModelInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x16\n\x0e\x63ontext_window\x18\x03 \x01(\x05\x12\x19\n\x11max_output_tokens\x18\x04 \x01(\x05\x12\x14\n\x0c\x63\x61pabilities\x18\x05 \x03(\t\x12\x15\n\rdefaults_json\x18\x06 \x01(\t\"\xb0\x01\n\x0cProviderInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x1b\n\x13\x63redential_env_vars\x18\x03 \x03(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x04 \x03(\t\x12\x15\n\rdefaults_json\x18\x05 \x01(\t\x12\x34\n\rconfig_fields\x18\x06 \x03(\x0b\x32\x1d.amplifier.module.ConfigField\"\x80\x01\n\x0f\x41pprovalRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x14\n\x0c\x64\x65tails_json\x18\x03 \x01(\t\x12\x12\n\nrisk_level\x18\x04 \x01(\t\x12\x14\n\x07timeout\x18\x05 \x01(\x01H\x00\x88\x01\x01\x42\n\n\x08_timeout\"F\n\x10\x41pprovalResponse\x12\x10\n\x08\x61pproved\x18\x01 \x01(\x08\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x10\n\x08remember\x18\x03 \x01(\x08\"A\n\x12ListModelsResponse\x12+\n\x06models\x18\x01 \x03(\x0b\x32\x1b.amplifier.module.ModelInfo\"O\n\x16ParseToolCallsResponse\x12\x35\n\ntool_calls\x18\x01 \x03(\x0b\x32!.amplifier.module.ToolCallMessage\"@\n\x1aOrchestratorExecuteRequest\x12\x0e\n\x06prompt\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\">\n\x1bOrchestratorExecuteResponse\x12\x10\n\x08response\x18\x01 \x01(\t\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"?\n\x11\x41\x64\x64MessageRequest\x12*\n\x07message\x18\x01 \x01(\x0b\x32\x19.amplifier.module.Message\"B\n\x13GetMessagesResponse\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"J\n\x1bGetMessagesForRequestParams\x12\x14\n\x0ctoken_budget\x18\x01 \x01(\x05\x12\x15\n\rprovider_name\x18\x02 \x01(\t\"A\n\x12SetMessagesRequest\x12+\n\x08messages\x18\x01 \x03(\x0b\x32\x19.amplifier.module.Message\"5\n\x11HookHandleRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\".\n\x17GetSubscriptionsRequest\x12\x13\n\x0b\x63onfig_json\x18\x01 \x01(\t\"V\n\x18GetSubscriptionsResponse\x12:\n\rsubscriptions\x18\x01 \x03(\x0b\x32#.amplifier.module.EventSubscription\"B\n\x11\x45ventSubscription\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x10\n\x08priority\x18\x02 \x01(\x05\x12\x0c\n\x04name\x18\x03 \x01(\t\"d\n\x1b\x43ompleteWithProviderRequest\x12\x15\n\rprovider_name\x18\x01 \x01(\t\x12.\n\x07request\x18\x02 \x01(\x0b\x32\x1d.amplifier.module.ChatRequest\";\n\x12\x45xecuteToolRequest\x12\x11\n\ttool_name\x18\x01 \x01(\t\x12\x12\n\ninput_json\x18\x02 \x01(\t\"3\n\x0f\x45mitHookRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\"V\n\x19\x45mitHookAndCollectRequest\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x11\n\tdata_json\x18\x02 \x01(\t\x12\x17\n\x0ftimeout_seconds\x18\x03 \x01(\x01\"4\n\x1a\x45mitHookAndCollectResponse\x12\x16\n\x0eresponses_json\x18\x01 \x03(\t\"(\n\x12GetMessagesRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\"Y\n\x17KernelAddMessageRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12*\n\x07message\x18\x02 \x01(\x0b\x32\x19.amplifier.module.Message\"a\n\x17GetMountedModuleRequest\x12\x13\n\x0bmodule_name\x18\x01 \x01(\t\x12\x31\n\x0bmodule_type\x18\x02 \x01(\x0e\x32\x1c.amplifier.module.ModuleType\"U\n\x18GetMountedModuleResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12*\n\x04info\x18\x02 \x01(\x0b\x32\x1c.amplifier.module.ModuleInfo\"=\n\x19RegisterCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\nvalue_json\x18\x02 \x01(\t\"$\n\x14GetCapabilityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\":\n\x15GetCapabilityResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12\x12\n\nvalue_json\x18\x02 \x01(\t*\xbc\x01\n\nModuleType\x12\x1b\n\x17MODULE_TYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14MODULE_TYPE_PROVIDER\x10\x01\x12\x14\n\x10MODULE_TYPE_TOOL\x10\x02\x12\x14\n\x10MODULE_TYPE_HOOK\x10\x03\x12\x16\n\x12MODULE_TYPE_MEMORY\x10\x04\x12\x19\n\x15MODULE_TYPE_GUARDRAIL\x10\x05\x12\x18\n\x14MODULE_TYPE_APPROVAL\x10\x06*\x82\x01\n\x0cHealthStatus\x12\x1d\n\x19HEALTH_STATUS_UNSPECIFIED\x10\x00\x12\x19\n\x15HEALTH_STATUS_SERVING\x10\x01\x12\x1d\n\x19HEALTH_STATUS_NOT_SERVING\x10\x02\x12\x19\n\x15HEALTH_STATUS_UNKNOWN\x10\x03*\xad\x01\n\x0f\x43onfigFieldType\x12!\n\x1d\x43ONFIG_FIELD_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_STRING\x10\x01\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_NUMBER\x10\x02\x12\x1d\n\x19\x43ONFIG_FIELD_TYPE_BOOLEAN\x10\x03\x12\x1c\n\x18\x43ONFIG_FIELD_TYPE_SECRET\x10\x04*\xd8\x02\n\x11ProviderErrorType\x12#\n\x1fPROVIDER_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18PROVIDER_ERROR_TYPE_AUTH\x10\x01\x12\"\n\x1ePROVIDER_ERROR_TYPE_RATE_LIMIT\x10\x02\x12&\n\"PROVIDER_ERROR_TYPE_CONTEXT_LENGTH\x10\x03\x12\'\n#PROVIDER_ERROR_TYPE_INVALID_REQUEST\x10\x04\x12&\n\"PROVIDER_ERROR_TYPE_CONTENT_FILTER\x10\x05\x12#\n\x1fPROVIDER_ERROR_TYPE_UNAVAILABLE\x10\x06\x12\x1f\n\x1bPROVIDER_ERROR_TYPE_TIMEOUT\x10\x07\x12\x1d\n\x19PROVIDER_ERROR_TYPE_OTHER\x10\x08*\x8c\x01\n\rToolErrorType\x12\x1f\n\x1bTOOL_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19TOOL_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aTOOL_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17TOOL_ERROR_TYPE_TIMEOUT\x10\x03*\x8c\x01\n\rHookErrorType\x12\x1f\n\x1bHOOK_ERROR_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19HOOK_ERROR_TYPE_EXECUTION\x10\x01\x12\x1e\n\x1aHOOK_ERROR_TYPE_VALIDATION\x10\x02\x12\x1b\n\x17HOOK_ERROR_TYPE_TIMEOUT\x10\x03*\x86\x01\n\x04Role\x12\x14\n\x10ROLE_UNSPECIFIED\x10\x00\x12\x0f\n\x0bROLE_SYSTEM\x10\x01\x12\r\n\tROLE_USER\x10\x02\x12\x12\n\x0eROLE_ASSISTANT\x10\x03\x12\r\n\tROLE_TOOL\x10\x04\x12\x11\n\rROLE_FUNCTION\x10\x05\x12\x12\n\x0eROLE_DEVELOPER\x10\x06*o\n\nVisibility\x12\x1a\n\x16VISIBILITY_UNSPECIFIED\x10\x00\x12\x12\n\x0eVISIBILITY_ALL\x10\x01\x12\x17\n\x13VISIBILITY_LLM_ONLY\x10\x02\x12\x18\n\x14VISIBILITY_USER_ONLY\x10\x03*\xab\x01\n\nHookAction\x12\x1b\n\x17HOOK_ACTION_UNSPECIFIED\x10\x00\x12\x18\n\x14HOOK_ACTION_CONTINUE\x10\x01\x12\x16\n\x12HOOK_ACTION_MODIFY\x10\x02\x12\x14\n\x10HOOK_ACTION_DENY\x10\x03\x12\x1e\n\x1aHOOK_ACTION_INJECT_CONTEXT\x10\x04\x12\x18\n\x14HOOK_ACTION_ASK_USER\x10\x05*\xa8\x01\n\x14\x43ontextInjectionRole\x12&\n\"CONTEXT_INJECTION_ROLE_UNSPECIFIED\x10\x00\x12!\n\x1d\x43ONTEXT_INJECTION_ROLE_SYSTEM\x10\x01\x12\x1f\n\x1b\x43ONTEXT_INJECTION_ROLE_USER\x10\x02\x12$\n CONTEXT_INJECTION_ROLE_ASSISTANT\x10\x03*l\n\x0f\x41pprovalDefault\x12 \n\x1c\x41PPROVAL_DEFAULT_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x41PPROVAL_DEFAULT_APPROVE\x10\x01\x12\x19\n\x15\x41PPROVAL_DEFAULT_DENY\x10\x02*\x91\x01\n\x10UserMessageLevel\x12\"\n\x1eUSER_MESSAGE_LEVEL_UNSPECIFIED\x10\x00\x12\x1b\n\x17USER_MESSAGE_LEVEL_INFO\x10\x01\x12\x1e\n\x1aUSER_MESSAGE_LEVEL_WARNING\x10\x02\x12\x1c\n\x18USER_MESSAGE_LEVEL_ERROR\x10\x03\x32\xa5\x01\n\x0bToolService\x12>\n\x07GetSpec\x12\x17.amplifier.module.Empty\x1a\x1a.amplifier.module.ToolSpec\x12V\n\x07\x45xecute\x12$.amplifier.module.ToolExecuteRequest\x1a%.amplifier.module.ToolExecuteResponse2\x9f\x03\n\x0fProviderService\x12\x42\n\x07GetInfo\x12\x17.amplifier.module.Empty\x1a\x1e.amplifier.module.ProviderInfo\x12K\n\nListModels\x12\x17.amplifier.module.Empty\x1a$.amplifier.module.ListModelsResponse\x12I\n\x08\x43omplete\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse\x12T\n\x11\x43ompleteStreaming\x12\x1d.amplifier.module.ChatRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Z\n\x0eParseToolCalls\x12\x1e.amplifier.module.ChatResponse\x1a(.amplifier.module.ParseToolCallsResponse2}\n\x13OrchestratorService\x12\x66\n\x07\x45xecute\x12,.amplifier.module.OrchestratorExecuteRequest\x1a-.amplifier.module.OrchestratorExecuteResponse2\xa3\x03\n\x0e\x43ontextService\x12J\n\nAddMessage\x12#.amplifier.module.AddMessageRequest\x1a\x17.amplifier.module.Empty\x12M\n\x0bGetMessages\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.GetMessagesResponse\x12m\n\x15GetMessagesForRequest\x12-.amplifier.module.GetMessagesForRequestParams\x1a%.amplifier.module.GetMessagesResponse\x12L\n\x0bSetMessages\x12$.amplifier.module.SetMessagesRequest\x1a\x17.amplifier.module.Empty\x12\x39\n\x05\x43lear\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty2\xc5\x01\n\x0bHookService\x12K\n\x06Handle\x12#.amplifier.module.HookHandleRequest\x1a\x1c.amplifier.module.HookResult\x12i\n\x10GetSubscriptions\x12).amplifier.module.GetSubscriptionsRequest\x1a*.amplifier.module.GetSubscriptionsResponse2k\n\x0f\x41pprovalService\x12X\n\x0fRequestApproval\x12!.amplifier.module.ApprovalRequest\x1a\".amplifier.module.ApprovalResponse2\xd0\x07\n\rKernelService\x12\x65\n\x14\x43ompleteWithProvider\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse\x12p\n\x1d\x43ompleteWithProviderStreaming\x12-.amplifier.module.CompleteWithProviderRequest\x1a\x1e.amplifier.module.ChatResponse0\x01\x12Q\n\x0b\x45xecuteTool\x12$.amplifier.module.ExecuteToolRequest\x1a\x1c.amplifier.module.ToolResult\x12K\n\x08\x45mitHook\x12!.amplifier.module.EmitHookRequest\x1a\x1c.amplifier.module.HookResult\x12o\n\x12\x45mitHookAndCollect\x12+.amplifier.module.EmitHookAndCollectRequest\x1a,.amplifier.module.EmitHookAndCollectResponse\x12Z\n\x0bGetMessages\x12$.amplifier.module.GetMessagesRequest\x1a%.amplifier.module.GetMessagesResponse\x12P\n\nAddMessage\x12).amplifier.module.KernelAddMessageRequest\x1a\x17.amplifier.module.Empty\x12i\n\x10GetMountedModule\x12).amplifier.module.GetMountedModuleRequest\x1a*.amplifier.module.GetMountedModuleResponse\x12Z\n\x12RegisterCapability\x12+.amplifier.module.RegisterCapabilityRequest\x1a\x17.amplifier.module.Empty\x12`\n\rGetCapability\x12&.amplifier.module.GetCapabilityRequest\x1a\'.amplifier.module.GetCapabilityResponse2\xaf\x02\n\x0fModuleLifecycle\x12H\n\x05Mount\x12\x1e.amplifier.module.MountRequest\x1a\x1f.amplifier.module.MountResponse\x12;\n\x07\x43leanup\x12\x17.amplifier.module.Empty\x1a\x17.amplifier.module.Empty\x12M\n\x0bHealthCheck\x12\x17.amplifier.module.Empty\x1a%.amplifier.module.HealthCheckResponse\x12\x46\n\rGetModuleInfo\x12\x17.amplifier.module.Empty\x1a\x1c.amplifier.module.ModuleInfob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -35,30 +35,30 @@ _globals['_MOUNTREQUEST_CONFIGENTRY']._serialized_options = b'8\001' _globals['_CONFIGFIELD_SHOWWHENENTRY']._loaded_options = None _globals['_CONFIGFIELD_SHOWWHENENTRY']._serialized_options = b'8\001' - _globals['_MODULETYPE']._serialized_start=7113 - _globals['_MODULETYPE']._serialized_end=7301 - _globals['_HEALTHSTATUS']._serialized_start=7304 - _globals['_HEALTHSTATUS']._serialized_end=7434 - _globals['_CONFIGFIELDTYPE']._serialized_start=7437 - _globals['_CONFIGFIELDTYPE']._serialized_end=7610 - _globals['_PROVIDERERRORTYPE']._serialized_start=7613 - _globals['_PROVIDERERRORTYPE']._serialized_end=7957 - _globals['_TOOLERRORTYPE']._serialized_start=7960 - _globals['_TOOLERRORTYPE']._serialized_end=8100 - _globals['_HOOKERRORTYPE']._serialized_start=8103 - _globals['_HOOKERRORTYPE']._serialized_end=8243 - _globals['_ROLE']._serialized_start=8246 - _globals['_ROLE']._serialized_end=8380 - _globals['_VISIBILITY']._serialized_start=8382 - _globals['_VISIBILITY']._serialized_end=8493 - _globals['_HOOKACTION']._serialized_start=8496 - _globals['_HOOKACTION']._serialized_end=8667 - _globals['_CONTEXTINJECTIONROLE']._serialized_start=8670 - _globals['_CONTEXTINJECTIONROLE']._serialized_end=8838 - _globals['_APPROVALDEFAULT']._serialized_start=8840 - _globals['_APPROVALDEFAULT']._serialized_end=8948 - _globals['_USERMESSAGELEVEL']._serialized_start=8951 - _globals['_USERMESSAGELEVEL']._serialized_end=9096 + _globals['_MODULETYPE']._serialized_start=7358 + _globals['_MODULETYPE']._serialized_end=7546 + _globals['_HEALTHSTATUS']._serialized_start=7549 + _globals['_HEALTHSTATUS']._serialized_end=7679 + _globals['_CONFIGFIELDTYPE']._serialized_start=7682 + _globals['_CONFIGFIELDTYPE']._serialized_end=7855 + _globals['_PROVIDERERRORTYPE']._serialized_start=7858 + _globals['_PROVIDERERRORTYPE']._serialized_end=8202 + _globals['_TOOLERRORTYPE']._serialized_start=8205 + _globals['_TOOLERRORTYPE']._serialized_end=8345 + _globals['_HOOKERRORTYPE']._serialized_start=8348 + _globals['_HOOKERRORTYPE']._serialized_end=8488 + _globals['_ROLE']._serialized_start=8491 + _globals['_ROLE']._serialized_end=8625 + _globals['_VISIBILITY']._serialized_start=8627 + _globals['_VISIBILITY']._serialized_end=8738 + _globals['_HOOKACTION']._serialized_start=8741 + _globals['_HOOKACTION']._serialized_end=8912 + _globals['_CONTEXTINJECTIONROLE']._serialized_start=8915 + _globals['_CONTEXTINJECTIONROLE']._serialized_end=9083 + _globals['_APPROVALDEFAULT']._serialized_start=9085 + _globals['_APPROVALDEFAULT']._serialized_end=9193 + _globals['_USERMESSAGELEVEL']._serialized_start=9196 + _globals['_USERMESSAGELEVEL']._serialized_end=9341 _globals['_EMPTY']._serialized_start=44 _globals['_EMPTY']._serialized_end=51 _globals['_TOOLSPEC']._serialized_start=53 @@ -127,78 +127,80 @@ _globals['_CHATRESPONSE']._serialized_end=4307 _globals['_TOOLRESULT']._serialized_start=4309 _globals['_TOOLRESULT']._serialized_end=4379 - _globals['_HOOKRESULT']._serialized_start=4382 - _globals['_HOOKRESULT']._serialized_end=4933 - _globals['_MODELINFO']._serialized_start=4936 - _globals['_MODELINFO']._serialized_end=5077 - _globals['_PROVIDERINFO']._serialized_start=5080 - _globals['_PROVIDERINFO']._serialized_end=5256 - _globals['_APPROVALREQUEST']._serialized_start=5259 - _globals['_APPROVALREQUEST']._serialized_end=5387 - _globals['_APPROVALRESPONSE']._serialized_start=5389 - _globals['_APPROVALRESPONSE']._serialized_end=5459 - _globals['_LISTMODELSRESPONSE']._serialized_start=5461 - _globals['_LISTMODELSRESPONSE']._serialized_end=5526 - _globals['_PARSETOOLCALLSRESPONSE']._serialized_start=5528 - _globals['_PARSETOOLCALLSRESPONSE']._serialized_end=5607 - _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_start=5609 - _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_end=5673 - _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_start=5675 - _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_end=5737 - _globals['_ADDMESSAGEREQUEST']._serialized_start=5739 - _globals['_ADDMESSAGEREQUEST']._serialized_end=5802 - _globals['_GETMESSAGESRESPONSE']._serialized_start=5804 - _globals['_GETMESSAGESRESPONSE']._serialized_end=5870 - _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_start=5872 - _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_end=5946 - _globals['_SETMESSAGESREQUEST']._serialized_start=5948 - _globals['_SETMESSAGESREQUEST']._serialized_end=6013 - _globals['_HOOKHANDLEREQUEST']._serialized_start=6015 - _globals['_HOOKHANDLEREQUEST']._serialized_end=6068 - _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_start=6070 - _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_end=6116 - _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_start=6118 - _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_end=6204 - _globals['_EVENTSUBSCRIPTION']._serialized_start=6206 - _globals['_EVENTSUBSCRIPTION']._serialized_end=6272 - _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_start=6274 - _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_end=6374 - _globals['_EXECUTETOOLREQUEST']._serialized_start=6376 - _globals['_EXECUTETOOLREQUEST']._serialized_end=6435 - _globals['_EMITHOOKREQUEST']._serialized_start=6437 - _globals['_EMITHOOKREQUEST']._serialized_end=6488 - _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_start=6490 - _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_end=6576 - _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_start=6578 - _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_end=6630 - _globals['_GETMESSAGESREQUEST']._serialized_start=6632 - _globals['_GETMESSAGESREQUEST']._serialized_end=6672 - _globals['_KERNELADDMESSAGEREQUEST']._serialized_start=6674 - _globals['_KERNELADDMESSAGEREQUEST']._serialized_end=6763 - _globals['_GETMOUNTEDMODULEREQUEST']._serialized_start=6765 - _globals['_GETMOUNTEDMODULEREQUEST']._serialized_end=6862 - _globals['_GETMOUNTEDMODULERESPONSE']._serialized_start=6864 - _globals['_GETMOUNTEDMODULERESPONSE']._serialized_end=6949 - _globals['_REGISTERCAPABILITYREQUEST']._serialized_start=6951 - _globals['_REGISTERCAPABILITYREQUEST']._serialized_end=7012 - _globals['_GETCAPABILITYREQUEST']._serialized_start=7014 - _globals['_GETCAPABILITYREQUEST']._serialized_end=7050 - _globals['_GETCAPABILITYRESPONSE']._serialized_start=7052 - _globals['_GETCAPABILITYRESPONSE']._serialized_end=7110 - _globals['_TOOLSERVICE']._serialized_start=9099 - _globals['_TOOLSERVICE']._serialized_end=9264 - _globals['_PROVIDERSERVICE']._serialized_start=9267 - _globals['_PROVIDERSERVICE']._serialized_end=9682 - _globals['_ORCHESTRATORSERVICE']._serialized_start=9684 - _globals['_ORCHESTRATORSERVICE']._serialized_end=9809 - _globals['_CONTEXTSERVICE']._serialized_start=9812 - _globals['_CONTEXTSERVICE']._serialized_end=10231 - _globals['_HOOKSERVICE']._serialized_start=10234 - _globals['_HOOKSERVICE']._serialized_end=10431 - _globals['_APPROVALSERVICE']._serialized_start=10433 - _globals['_APPROVALSERVICE']._serialized_end=10540 - _globals['_KERNELSERVICE']._serialized_start=10543 - _globals['_KERNELSERVICE']._serialized_end=11519 - _globals['_MODULELIFECYCLE']._serialized_start=11522 - _globals['_MODULELIFECYCLE']._serialized_end=11825 + _globals['_CONTEXTINJECTION']._serialized_start=4382 + _globals['_CONTEXTINJECTION']._serialized_end=4560 + _globals['_HOOKRESULT']._serialized_start=4563 + _globals['_HOOKRESULT']._serialized_end=5178 + _globals['_MODELINFO']._serialized_start=5181 + _globals['_MODELINFO']._serialized_end=5322 + _globals['_PROVIDERINFO']._serialized_start=5325 + _globals['_PROVIDERINFO']._serialized_end=5501 + _globals['_APPROVALREQUEST']._serialized_start=5504 + _globals['_APPROVALREQUEST']._serialized_end=5632 + _globals['_APPROVALRESPONSE']._serialized_start=5634 + _globals['_APPROVALRESPONSE']._serialized_end=5704 + _globals['_LISTMODELSRESPONSE']._serialized_start=5706 + _globals['_LISTMODELSRESPONSE']._serialized_end=5771 + _globals['_PARSETOOLCALLSRESPONSE']._serialized_start=5773 + _globals['_PARSETOOLCALLSRESPONSE']._serialized_end=5852 + _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_start=5854 + _globals['_ORCHESTRATOREXECUTEREQUEST']._serialized_end=5918 + _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_start=5920 + _globals['_ORCHESTRATOREXECUTERESPONSE']._serialized_end=5982 + _globals['_ADDMESSAGEREQUEST']._serialized_start=5984 + _globals['_ADDMESSAGEREQUEST']._serialized_end=6047 + _globals['_GETMESSAGESRESPONSE']._serialized_start=6049 + _globals['_GETMESSAGESRESPONSE']._serialized_end=6115 + _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_start=6117 + _globals['_GETMESSAGESFORREQUESTPARAMS']._serialized_end=6191 + _globals['_SETMESSAGESREQUEST']._serialized_start=6193 + _globals['_SETMESSAGESREQUEST']._serialized_end=6258 + _globals['_HOOKHANDLEREQUEST']._serialized_start=6260 + _globals['_HOOKHANDLEREQUEST']._serialized_end=6313 + _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_start=6315 + _globals['_GETSUBSCRIPTIONSREQUEST']._serialized_end=6361 + _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_start=6363 + _globals['_GETSUBSCRIPTIONSRESPONSE']._serialized_end=6449 + _globals['_EVENTSUBSCRIPTION']._serialized_start=6451 + _globals['_EVENTSUBSCRIPTION']._serialized_end=6517 + _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_start=6519 + _globals['_COMPLETEWITHPROVIDERREQUEST']._serialized_end=6619 + _globals['_EXECUTETOOLREQUEST']._serialized_start=6621 + _globals['_EXECUTETOOLREQUEST']._serialized_end=6680 + _globals['_EMITHOOKREQUEST']._serialized_start=6682 + _globals['_EMITHOOKREQUEST']._serialized_end=6733 + _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_start=6735 + _globals['_EMITHOOKANDCOLLECTREQUEST']._serialized_end=6821 + _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_start=6823 + _globals['_EMITHOOKANDCOLLECTRESPONSE']._serialized_end=6875 + _globals['_GETMESSAGESREQUEST']._serialized_start=6877 + _globals['_GETMESSAGESREQUEST']._serialized_end=6917 + _globals['_KERNELADDMESSAGEREQUEST']._serialized_start=6919 + _globals['_KERNELADDMESSAGEREQUEST']._serialized_end=7008 + _globals['_GETMOUNTEDMODULEREQUEST']._serialized_start=7010 + _globals['_GETMOUNTEDMODULEREQUEST']._serialized_end=7107 + _globals['_GETMOUNTEDMODULERESPONSE']._serialized_start=7109 + _globals['_GETMOUNTEDMODULERESPONSE']._serialized_end=7194 + _globals['_REGISTERCAPABILITYREQUEST']._serialized_start=7196 + _globals['_REGISTERCAPABILITYREQUEST']._serialized_end=7257 + _globals['_GETCAPABILITYREQUEST']._serialized_start=7259 + _globals['_GETCAPABILITYREQUEST']._serialized_end=7295 + _globals['_GETCAPABILITYRESPONSE']._serialized_start=7297 + _globals['_GETCAPABILITYRESPONSE']._serialized_end=7355 + _globals['_TOOLSERVICE']._serialized_start=9344 + _globals['_TOOLSERVICE']._serialized_end=9509 + _globals['_PROVIDERSERVICE']._serialized_start=9512 + _globals['_PROVIDERSERVICE']._serialized_end=9927 + _globals['_ORCHESTRATORSERVICE']._serialized_start=9929 + _globals['_ORCHESTRATORSERVICE']._serialized_end=10054 + _globals['_CONTEXTSERVICE']._serialized_start=10057 + _globals['_CONTEXTSERVICE']._serialized_end=10476 + _globals['_HOOKSERVICE']._serialized_start=10479 + _globals['_HOOKSERVICE']._serialized_end=10676 + _globals['_APPROVALSERVICE']._serialized_start=10678 + _globals['_APPROVALSERVICE']._serialized_end=10785 + _globals['_KERNELSERVICE']._serialized_start=10788 + _globals['_KERNELSERVICE']._serialized_end=11764 + _globals['_MODULELIFECYCLE']._serialized_start=11767 + _globals['_MODULELIFECYCLE']._serialized_end=12070 # @@protoc_insertion_point(module_scope) diff --git a/python/amplifier_core/_grpc_gen/amplifier_module_pb2_grpc.py b/python/amplifier_core/_grpc_gen/amplifier_module_pb2_grpc.py index 1a2a1bf..e6b7668 100644 --- a/python/amplifier_core/_grpc_gen/amplifier_module_pb2_grpc.py +++ b/python/amplifier_core/_grpc_gen/amplifier_module_pb2_grpc.py @@ -3,7 +3,7 @@ import grpc import warnings -from . import amplifier_module_pb2 as amplifier__module__pb2 +import amplifier_module_pb2 as amplifier__module__pb2 GRPC_GENERATED_VERSION = '1.78.0' GRPC_VERSION = grpc.__version__ diff --git a/python/amplifier_core/hooks.py b/python/amplifier_core/hooks.py index b8c8b79..c6e7492 100644 --- a/python/amplifier_core/hooks.py +++ b/python/amplifier_core/hooks.py @@ -1,5 +1,5 @@ """Thin alias: re-exports RustHookRegistry (as HookRegistry) and HookResult.""" from ._engine import RustHookRegistry as HookRegistry -from .models import HookResult +from .models import ContextInjection, HookResult -__all__ = ["HookRegistry", "HookResult"] +__all__ = ["HookRegistry", "ContextInjection", "HookResult"] diff --git a/python/amplifier_core/models.py b/python/amplifier_core/models.py index 2bf3330..be568f1 100644 --- a/python/amplifier_core/models.py +++ b/python/amplifier_core/models.py @@ -124,6 +124,17 @@ def get_serialized_output(self) -> str: return "Success" +class ContextInjection(BaseModel): + """One ordered context injection emitted by a hook.""" + + content: str + role: Literal["system", "user", "assistant"] = "system" + ephemeral: bool = False + append_to_last_tool_result: bool = False + hook_name: str = "unknown" + event: str = "" + + class HookResult(BaseModel): """ Result from hook execution with enhanced capabilities. @@ -323,6 +334,13 @@ class HookResult(BaseModel): "Only applicable when action='inject_context' and ephemeral=True." ), ) + context_injections: list[ContextInjection] = Field( + default_factory=list, + description=( + "Ordered, lossless hook context injections. The hook registry projects " + "the legacy scalar context fields from these items for compatibility." + ), + ) class ModelInfo(BaseModel): diff --git a/tests/test_injection_items.py b/tests/test_injection_items.py new file mode 100644 index 0000000..604d629 --- /dev/null +++ b/tests/test_injection_items.py @@ -0,0 +1,20 @@ +"""Regression coverage for lossless mixed hook context injections.""" + +from amplifier_core.models import ContextInjection, HookResult + + +def test_mixed_durable_and_ephemeral_injections_remain_distinct() -> None: + result = HookResult( + action="inject_context", + context_injections=[ + ContextInjection(content="durable", role="assistant", ephemeral=False), + ContextInjection(content="temporary", role="user", ephemeral=True), + ], + ) + + assert [item.content for item in result.context_injections] == [ + "durable", + "temporary", + ] + assert [item.ephemeral for item in result.context_injections] == [False, True] + assert [item.role for item in result.context_injections] == ["assistant", "user"] \ No newline at end of file From d78cb22f7bb15a7e4661c5fc7d2b362deae7ea4d Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Sat, 12 Sep 2026 11:26:15 -0700 Subject: [PATCH 23/27] fix: repair generated gRPC imports and hook result compatibility Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- .github/workflows/proto-check.yml | 30 ++--- README.md | 8 ++ crates/amplifier-core/src/hooks.rs | 104 ++++++++++++++++++ python/amplifier_core/_grpc_gen/__init__.py | 5 +- .../_grpc_gen/amplifier_module_pb2_grpc.py | 2 +- python/amplifier_core/loader_grpc.py | 3 +- scripts/generate_grpc_stubs.py | 63 +++++++++++ tests/test_proto_compilation.py | 30 +++++ 8 files changed, 218 insertions(+), 27 deletions(-) create mode 100644 scripts/generate_grpc_stubs.py diff --git a/.github/workflows/proto-check.yml b/.github/workflows/proto-check.yml index c7e24af..25de234 100644 --- a/.github/workflows/proto-check.yml +++ b/.github/workflows/proto-check.yml @@ -7,12 +7,16 @@ on: - 'proto/**' - 'python/amplifier_core/_grpc_gen/**' - 'crates/amplifier-core/src/generated/**' + - 'scripts/generate_grpc_stubs.py' + - '.github/workflows/proto-check.yml' pull_request: branches: [main] paths: - 'proto/**' - 'python/amplifier_core/_grpc_gen/**' - 'crates/amplifier-core/src/generated/**' + - 'scripts/generate_grpc_stubs.py' + - '.github/workflows/proto-check.yml' jobs: proto-sync: @@ -32,32 +36,18 @@ jobs: python-version: '3.12' - name: Install grpcio-tools - run: pip install grpcio-tools + run: pip install grpcio-tools==1.78.0 - name: Regenerate Python stubs run: | - mkdir -p /tmp/proto_check - python -m grpc_tools.protoc \ - -Iproto \ - --python_out=/tmp/proto_check \ - --grpc_python_out=/tmp/proto_check \ - proto/amplifier_module.proto + python scripts/generate_grpc_stubs.py --output /tmp/proto_check - name: Compare with committed stubs run: | - # Check that key message classes exist in regenerated output - python -c " - import importlib.util, sys - spec = importlib.util.spec_from_file_location('pb2', '/tmp/proto_check/amplifier_module_pb2.py') - mod = importlib.util.module_from_spec(spec) - spec.loader.exec_module(mod) - required = ['ChatRequest', 'ChatResponse', 'ToolResult', 'HookResult', 'ModuleInfo'] - missing = [t for t in required if not hasattr(mod, t)] - if missing: - print(f'FAIL: Missing types in regenerated stubs: {missing}') - sys.exit(1) - print('PASS: Regenerated stubs contain all required types') - " + diff -u python/amplifier_core/_grpc_gen/amplifier_module_pb2.py \ + /tmp/proto_check/amplifier_module_pb2.py + diff -u python/amplifier_core/_grpc_gen/amplifier_module_pb2_grpc.py \ + /tmp/proto_check/amplifier_module_pb2_grpc.py rust-build: name: Rust build and test diff --git a/README.md b/README.md index a9c65f8..3f76052 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,14 @@ maturin develop uv run maturin develop ``` +To regenerate the committed Python gRPC stubs, install the pinned generator +and run the canonical command: + +```bash +python -m pip install grpcio-tools==1.78.0 +python scripts/generate_grpc_stubs.py +``` + See [docs/RUST_CORE_TESTING.md](docs/RUST_CORE_TESTING.md) for the full development setup guide. **Build dependencies**: Rust 1.70+, maturin diff --git a/crates/amplifier-core/src/hooks.rs b/crates/amplifier-core/src/hooks.rs index 9c90469..3f115ff 100644 --- a/crates/amplifier-core/src/hooks.rs +++ b/crates/amplifier-core/src/hooks.rs @@ -448,6 +448,25 @@ fn merge_inject_context_results(results: &[HookResult]) -> HookResult { let first = &results[0]; let first_injection = &context_injections[0]; + // A single handler may return multiple structured injections. This is + // normalization, not aggregation: retain the handler's other result-level + // fields (including UI and approval metadata) while updating only the + // legacy injection projection. + if results.len() == 1 { + let mut result = first.clone(); + result.action = HookAction::InjectContext; + result.context_injection = Some(combined_content); + result.context_injection_role = first_injection.role.clone(); + result.ephemeral = context_injections + .iter() + .any(|injection| injection.ephemeral); + result.append_to_last_tool_result = context_injections + .iter() + .any(|injection| injection.append_to_last_tool_result); + result.context_injections = context_injections; + return result; + } + HookResult { action: HookAction::InjectContext, context_injection: Some(combined_content), @@ -866,6 +885,91 @@ mod tests { assert!(injection.contains("second injection")); } + #[tokio::test] + async fn single_structured_injection_result_preserves_non_injection_fields() { + use crate::models::{ApprovalDefault, ContextInjectionRole, UserMessageLevel}; + + let registry = HookRegistry::new(); + let original_data = HashMap::from([("keep".to_string(), serde_json::json!(true))]); + let original_extensions = + HashMap::from([("future_field".to_string(), serde_json::json!("preserved"))]); + let handler = Arc::new(SimpleHandler(HookResult { + action: HookAction::InjectContext, + data: Some(original_data.clone()), + reason: Some("keep reason".into()), + // The scalar fields are intentionally stale: the list is canonical. + context_injection: Some("stale scalar".into()), + context_injection_role: ContextInjectionRole::System, + ephemeral: false, + approval_prompt: Some("keep approval".into()), + approval_options: Some(vec!["allow".into(), "deny".into()]), + approval_timeout: 42.0, + approval_default: ApprovalDefault::Allow, + suppress_output: true, + user_message: Some("Context injected".into()), + user_message_level: UserMessageLevel::Warning, + user_message_source: Some("status-context".into()), + append_to_last_tool_result: false, + context_injections: vec![ + ContextInjection { + content: "first".into(), + role: ContextInjectionRole::User, + ephemeral: false, + append_to_last_tool_result: false, + hook_name: "untrusted".into(), + event: "untrusted".into(), + }, + ContextInjection { + content: "second".into(), + role: ContextInjectionRole::Assistant, + ephemeral: true, + append_to_last_tool_result: true, + hook_name: "untrusted".into(), + event: "untrusted".into(), + }, + ], + extensions: original_extensions.clone(), + })); + let _ = registry.register( + "test:structured", + handler, + 0, + Some("structured-hook".into()), + ); + + let result = registry + .emit("test:structured", serde_json::json!({})) + .await; + + assert_eq!(result.action, HookAction::InjectContext); + assert_eq!(result.context_injection.as_deref(), Some("first\n\nsecond")); + assert_eq!(result.context_injection_role, ContextInjectionRole::User); + assert!(result.ephemeral); + assert!(result.append_to_last_tool_result); + assert_eq!(result.context_injections.len(), 2); + assert_eq!(result.context_injections[0].hook_name, "structured-hook"); + assert_eq!(result.context_injections[0].event, "test:structured"); + assert_eq!(result.context_injections[1].hook_name, "structured-hook"); + assert_eq!(result.context_injections[1].event, "test:structured"); + assert_eq!(result.data, Some(original_data)); + assert_eq!(result.reason.as_deref(), Some("keep reason")); + assert_eq!(result.approval_prompt.as_deref(), Some("keep approval")); + assert_eq!( + result.approval_options, + Some(vec!["allow".into(), "deny".into()]) + ); + assert_eq!(result.approval_timeout, 42.0); + assert_eq!(result.approval_default, ApprovalDefault::Allow); + assert!(result.suppress_output); + assert_eq!(result.user_message.as_deref(), Some("Context injected")); + assert_eq!(result.user_message_level, UserMessageLevel::Warning); + assert_eq!( + result.user_message_source.as_deref(), + Some("status-context") + ); + assert_eq!(result.extensions, original_extensions); + } + // --------------------------------------------------------------- // Unregister // --------------------------------------------------------------- diff --git a/python/amplifier_core/_grpc_gen/__init__.py b/python/amplifier_core/_grpc_gen/__init__.py index 4b20486..8e32a4e 100644 --- a/python/amplifier_core/_grpc_gen/__init__.py +++ b/python/amplifier_core/_grpc_gen/__init__.py @@ -1,8 +1,5 @@ """Generated gRPC stubs for amplifier_module.proto. Do not edit these files directly. Regenerate with: - python -m grpc_tools.protoc -I proto \ - --python_out=python/amplifier_core/_grpc_gen \ - --grpc_python_out=python/amplifier_core/_grpc_gen \ - proto/amplifier_module.proto + python scripts/generate_grpc_stubs.py """ diff --git a/python/amplifier_core/_grpc_gen/amplifier_module_pb2_grpc.py b/python/amplifier_core/_grpc_gen/amplifier_module_pb2_grpc.py index e6b7668..1a2a1bf 100644 --- a/python/amplifier_core/_grpc_gen/amplifier_module_pb2_grpc.py +++ b/python/amplifier_core/_grpc_gen/amplifier_module_pb2_grpc.py @@ -3,7 +3,7 @@ import grpc import warnings -import amplifier_module_pb2 as amplifier__module__pb2 +from . import amplifier_module_pb2 as amplifier__module__pb2 GRPC_GENERATED_VERSION = '1.78.0' GRPC_VERSION = grpc.__version__ diff --git a/python/amplifier_core/loader_grpc.py b/python/amplifier_core/loader_grpc.py index 41b0c3c..4759336 100644 --- a/python/amplifier_core/loader_grpc.py +++ b/python/amplifier_core/loader_grpc.py @@ -195,8 +195,7 @@ async def load_grpc_module( except ImportError: raise ImportError( "gRPC proto stubs not generated. Run: " - "python -m grpc_tools.protoc -I proto --python_out=python/amplifier_core/_grpc_gen " - "--grpc_python_out=python/amplifier_core/_grpc_gen proto/amplifier_module.proto" + "python scripts/generate_grpc_stubs.py" ) stub = amplifier_module_pb2_grpc.ToolServiceStub(channel) diff --git a/scripts/generate_grpc_stubs.py b/scripts/generate_grpc_stubs.py new file mode 100644 index 0000000..11ad49e --- /dev/null +++ b/scripts/generate_grpc_stubs.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +"""Generate package-safe Python gRPC stubs from the canonical proto.""" + +from __future__ import annotations + +import argparse +from importlib.metadata import version +import subprocess +import sys +from pathlib import Path + + +PROJECT_ROOT = Path(__file__).resolve().parent.parent +PROTO_PATH = PROJECT_ROOT / "proto" / "amplifier_module.proto" +RAW_IMPORT = "import amplifier_module_pb2 as amplifier__module__pb2" +PACKAGE_IMPORT = "from . import amplifier_module_pb2 as amplifier__module__pb2" +GRPCIO_TOOLS_VERSION = "1.78.0" + + +def main() -> None: + if version("grpcio-tools") != GRPCIO_TOOLS_VERSION: + raise RuntimeError( + f"grpcio-tools=={GRPCIO_TOOLS_VERSION} is required; install it with " + f"`python -m pip install grpcio-tools=={GRPCIO_TOOLS_VERSION}`." + ) + parser = argparse.ArgumentParser( + description="Generate package-safe Python stubs for amplifier_module.proto." + ) + parser.add_argument( + "--output", + type=Path, + default=Path("python/amplifier_core/_grpc_gen"), + help="Output directory, relative to the repository root by default.", + ) + args = parser.parse_args() + output = args.output if args.output.is_absolute() else PROJECT_ROOT / args.output + output.mkdir(parents=True, exist_ok=True) + + subprocess.run( + [ + sys.executable, + "-m", + "grpc_tools.protoc", + "-Iproto", + f"--python_out={output}", + f"--grpc_python_out={output}", + str(PROTO_PATH.relative_to(PROJECT_ROOT)), + ], + cwd=PROJECT_ROOT, + check=True, + ) + + grpc_stub = output / "amplifier_module_pb2_grpc.py" + generated = grpc_stub.read_text() + if RAW_IMPORT not in generated: + raise RuntimeError( + f"Expected generated import not found in {grpc_stub}: {RAW_IMPORT!r}" + ) + grpc_stub.write_text(generated.replace(RAW_IMPORT, PACKAGE_IMPORT, 1)) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tests/test_proto_compilation.py b/tests/test_proto_compilation.py index 9a0084e..db8bfdc 100644 --- a/tests/test_proto_compilation.py +++ b/tests/test_proto_compilation.py @@ -4,6 +4,16 @@ service messages, all 8 gRPC service stubs, and enum value assignments. """ +import shutil +import subprocess +import sys +from pathlib import Path + + +GRPC_GEN_DIR = ( + Path(__file__).parent.parent / "python" / "amplifier_core" / "_grpc_gen" +) + class TestProtoCompilation: """Verify proto-generated modules import cleanly and expose expected symbols.""" @@ -16,6 +26,26 @@ def test_pb2_grpc_module_imports(self): """The pb2_grpc module imports without error.""" from amplifier_core._grpc_gen import amplifier_module_pb2_grpc # noqa: F401 + def test_pb2_grpc_imports_as_a_package(self, tmp_path): + """The stub resolves its sibling pb2 module without a flat module path.""" + package_root = tmp_path / "isolated_package" + package_root.mkdir() + (package_root / "__init__.py").touch() + shutil.copytree(GRPC_GEN_DIR, package_root / "_grpc_gen") + + result = subprocess.run( + [ + sys.executable, + "-c", + "from isolated_package._grpc_gen import amplifier_module_pb2_grpc", + ], + cwd=tmp_path, + capture_output=True, + text=True, + ) + + assert result.returncode == 0, result.stderr + def test_tool_service_messages_exist(self): """Backward-compat: original ToolService messages are present.""" from amplifier_core._grpc_gen import amplifier_module_pb2 as pb2 From 667fe3caa6f5c2c9f5c4b132172561e706996ab0 Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Sat, 12 Sep 2026 12:02:29 -0700 Subject: [PATCH 24/27] test: cover hook injection compatibility Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- tests/test_generated_equivalence.py | 34 ++++++++++--- tests/test_hooks_alias.py | 8 +-- tests/test_hooks_request_id.py | 79 +++++++++++++++++++---------- 3 files changed, 85 insertions(+), 36 deletions(-) diff --git a/tests/test_generated_equivalence.py b/tests/test_generated_equivalence.py index 18e643e..42ff9f4 100644 --- a/tests/test_generated_equivalence.py +++ b/tests/test_generated_equivalence.py @@ -32,7 +32,7 @@ def test_proto_tool_result_field_count(self): class TestHookResultEquivalence: - """Proto HookResult has all 15 fields matching native HookResult.""" + """Proto HookResult has all 16 fields matching native HookResult.""" def test_proto_hook_result_has_all_fields(self): hr = pb2.HookResult() @@ -52,15 +52,37 @@ def test_proto_hook_result_has_all_fields(self): "user_message_level", "user_message_source", "append_to_last_tool_result", + "context_injections", ] - for field in expected_fields: - assert hasattr(hr, field), f"HookResult missing field: {field}" + assert [field.name for field in hr.DESCRIPTOR.fields] == expected_fields def test_proto_hook_result_field_count(self): - """HookResult must have exactly 15 fields.""" + """HookResult must have exactly 16 append-only fields.""" hr = pb2.HookResult() - fields = [f.name for f in hr.DESCRIPTOR.fields] - assert len(fields) == 15, f"Expected 15 fields, got {len(fields)}: {fields}" + expected_numbers = { + "action": 1, + "data_json": 2, + "reason": 3, + "context_injection": 4, + "context_injection_role": 5, + "ephemeral": 6, + "approval_prompt": 7, + "approval_options": 8, + "approval_timeout": 9, + "approval_default": 10, + "suppress_output": 11, + "user_message": 12, + "user_message_level": 13, + "user_message_source": 14, + "append_to_last_tool_result": 15, + "context_injections": 16, + } + assert {field.name: field.number for field in hr.DESCRIPTOR.fields} == expected_numbers + + injections = hr.DESCRIPTOR.fields_by_name["context_injections"] + assert injections.is_repeated + assert injections.type == injections.TYPE_MESSAGE + assert injections.message_type.full_name == pb2.ContextInjection.DESCRIPTOR.full_name class TestHookActionEnumEquivalence: diff --git a/tests/test_hooks_alias.py b/tests/test_hooks_alias.py index 29449f5..1133a5a 100644 --- a/tests/test_hooks_alias.py +++ b/tests/test_hooks_alias.py @@ -3,7 +3,8 @@ """ import amplifier_core.hooks as hooks_module -from amplifier_core.hooks import HookRegistry, HookResult +from amplifier_core.hooks import ContextInjection, HookRegistry, HookResult +from amplifier_core.models import ContextInjection as ModelContextInjection def test_import_hook_registry(): @@ -37,7 +38,8 @@ def test_hook_registry_instantiation(): def test_all_exports(): - """__all__ contains exactly HookRegistry and HookResult.""" + """__all__ exposes every public hook result type.""" all_exports = getattr(hooks_module, "__all__", None) assert all_exports is not None, "hooks module should define __all__" - assert set(all_exports) == {"HookRegistry", "HookResult"} + assert set(all_exports) == {"HookRegistry", "ContextInjection", "HookResult"} + assert ContextInjection is ModelContextInjection diff --git a/tests/test_hooks_request_id.py b/tests/test_hooks_request_id.py index 89657a2..c85885f 100644 --- a/tests/test_hooks_request_id.py +++ b/tests/test_hooks_request_id.py @@ -209,45 +209,77 @@ async def test_emit_carries_request_id_onto_the_error_path(): async def test_concurrent_calls_get_distinct_ids_and_pair_correctly(): """The case FIFO gets wrong. - Interleaving is forced to reproduce the measured trace (agent request, - summarizer request, summarizer response, agent response) where positional - pairing charges each response to the other caller. + Both request callbacks enter and suspend before either can resume. This + deterministically proves concurrent in-flight calls without relying on + task scheduling order. """ registry = HookRegistry() seen = [] - for event in ("llm:request", "llm:response"): - registry.register(event, _recorder(seen), name=f"cap-{event}") + request_ids_before_await = {} + request_ids_after_await = {} + callback_trace = [] + all_request_callbacks_entered = asyncio.Event() + release_request_callbacks = asyncio.Event() + + async def capture(event, data): + payload = dict(data) + caller = payload["caller"] + if event == "llm:request": + request_ids_before_await[caller] = payload[REQUEST_ID_FIELD] + callback_trace.append(("request-start", caller)) + if len(request_ids_before_await) == 2: + all_request_callbacks_entered.set() + await release_request_callbacks.wait() + request_ids_after_await[caller] = payload[REQUEST_ID_FIELD] + callback_trace.append(("request-end", caller)) + seen.append((event, payload)) + return HookResult(action="continue") - summarizer_requested = asyncio.Event() - summarizer_responded = asyncio.Event() + for event in ("llm:request", "llm:response"): + registry.register(event, capture, name=f"cap-{event}") async def agent_call(): await registry.emit("llm:request", {"caller": "agent"}) - await summarizer_responded.wait() await registry.emit("llm:response", {"caller": "agent"}) async def summarizer_call(): - await summarizer_requested.wait() await registry.emit("llm:request", {"caller": "summarizer"}) await registry.emit("llm:response", {"caller": "summarizer"}) - summarizer_responded.set() async def run(): task_agent = asyncio.create_task(agent_call()) task_summarizer = asyncio.create_task(summarizer_call()) - await asyncio.sleep(0) - summarizer_requested.set() - await asyncio.gather(task_agent, task_summarizer) + try: + await asyncio.wait_for(all_request_callbacks_entered.wait(), timeout=10) + except TimeoutError: + release_request_callbacks.set() + await asyncio.gather(task_agent, task_summarizer, return_exceptions=True) + pytest.fail( + "Concurrent emits did not enter both request callbacks before either resumed" + ) + release_request_callbacks.set() + try: + await asyncio.wait_for( + asyncio.gather(task_agent, task_summarizer), + timeout=10, + ) + except TimeoutError: + pytest.fail("Concurrent emits did not complete after callbacks were released") await run() - order = [(event, payload["caller"]) for event, payload in seen] - assert order == [ - ("llm:request", "agent"), - ("llm:request", "summarizer"), - ("llm:response", "summarizer"), - ("llm:response", "agent"), - ], "expected the interleaving that defeats positional pairing" + assert {caller for phase, caller in callback_trace if phase == "request-start"} == { + "agent", + "summarizer", + } + first_end = next( + index + for index, (phase, _) in enumerate(callback_trace) + if phase == "request-end" + ) + assert all(phase == "request-start" for phase, _ in callback_trace[:first_end]) + assert set(request_ids_before_await) == {"agent", "summarizer"} + assert request_ids_before_await == request_ids_after_await by_caller = {} for event, payload in seen: @@ -260,13 +292,6 @@ async def run(): assert summarizer["llm:request"] == summarizer["llm:response"] assert agent["llm:request"] != summarizer["llm:request"] - # And the positional pairing this replaces would have crossed them: - # FIFO joins the first request to the first response, which here belong - # to different callers. - requests = [p[REQUEST_ID_FIELD] for e, p in seen if e == "llm:request"] - responses = [p[REQUEST_ID_FIELD] for e, p in seen if e == "llm:response"] - assert requests[0] != responses[0], "expected FIFO to mis-pair this trace" - @pytest.mark.asyncio async def test_many_concurrent_calls_all_pair_correctly(): From e2cf2a6f1ff1aa2082b3b849b10f7e99e700bdd2 Mon Sep 17 00:00:00 2001 From: Brian Krabach Date: Sun, 20 Sep 2026 21:50:30 -0700 Subject: [PATCH 25/27] Expose safe module load failure categories (#112) --- docs/module-failure-reasons.md | 9 +++++ python/amplifier_core/_session_init.py | 2 + python/amplifier_core/loader.py | 56 +++++++++++++++++++++----- tests/test_module_failure_reasons.py | 56 ++++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 9 deletions(-) create mode 100644 docs/module-failure-reasons.md create mode 100644 tests/test_module_failure_reasons.py diff --git a/docs/module-failure-reasons.md b/docs/module-failure-reasons.md new file mode 100644 index 0000000..f987a01 --- /dev/null +++ b/docs/module-failure-reasons.md @@ -0,0 +1,9 @@ +# Module-load failure categories + +The `module:load_failed` event includes an optional `reason_code` in addition to its existing module type, ID, and legacy error text. Hosts can expose the bounded category without forwarding exception text or configuration. + +Categories are `invalid_package_layout`, `missing_source`, `invalid_entry_point`, `invalid_module_metadata`, `validation_failed`, and `unknown`. Codes originate from typed loader failures and named validation checks, never from exception-string parsing. Unknown exceptions fall back to `unknown`. Missing metadata still uses the existing naming fallback; explicitly invalid declared types are rejected instead of swallowing the validation error and falling back. + +The legacy `error` field remains unchanged for compatibility and is not a safe public diagnostic. Consumers should allowlist `reason_code` and supply their own remediation text. This addition changes neither the provider/tool/hook nonfatal session policy nor host decisions about incomplete configured sessions. + +Validation used Python source over the installed native kernel: 44 loader and session-initialization tests passed, including fixtures for the four requested categories, unknown and forged codes, and event compatibility. No native code or provider protocol changed. diff --git a/python/amplifier_core/_session_init.py b/python/amplifier_core/_session_init.py index 03ede4b..98002b3 100644 --- a/python/amplifier_core/_session_init.py +++ b/python/amplifier_core/_session_init.py @@ -32,6 +32,7 @@ async def _emit_module_load_failed( failure must never suppress the original WARNING log. """ from .events import MODULE_LOAD_FAILED + from .loader import module_failure_reason try: await coordinator.hooks.emit( @@ -40,6 +41,7 @@ async def _emit_module_load_failed( "module_type": module_type, "module_id": module_id, "error": _safe_exception_str(error), + "reason_code": module_failure_reason(error), }, ) except Exception: diff --git a/python/amplifier_core/loader.py b/python/amplifier_core/loader.py index ef6f064..24dec40 100644 --- a/python/amplifier_core/loader.py +++ b/python/amplifier_core/loader.py @@ -42,7 +42,34 @@ class ModuleValidationError(Exception): """Raised when a module fails validation at load time.""" - pass + def __init__(self, message: str, *, reason_code: str = "validation_failed"): + super().__init__(message) + self.reason_code = reason_code + + +class _ModuleDiscoveryError(ValueError): + def __init__(self, message: str, *, reason_code: str): + super().__init__(message) + self.reason_code = reason_code + + +MODULE_FAILURE_REASONS = frozenset({ + "invalid_package_layout", "missing_source", "invalid_entry_point", + "invalid_module_metadata", "validation_failed", "unknown", +}) + + +def module_failure_reason(error: BaseException) -> str: + """Return a bounded category without parsing or copying exception text.""" + from .module_sources import ModuleNotFoundError as SourceNotFoundError + + if isinstance(error, SourceNotFoundError): + return "missing_source" + if isinstance(error, (ModuleValidationError, _ModuleDiscoveryError)): + code = error.reason_code + if isinstance(code, str) and code in MODULE_FAILURE_REASONS: + return code + return "unknown" class ModuleLoader: @@ -236,8 +263,9 @@ async def mount_with_config_cached( mount_closure = await self._load_direct(module_id, config) if mount_closure: return mount_closure - raise ValueError( - f"Module '{module_id}' not found via entry points or filesystem" + raise _ModuleDiscoveryError( + f"Module '{module_id}' not found via entry points or filesystem", + reason_code="missing_source", ) # Try async resolution first (supports lazy activation) @@ -352,8 +380,9 @@ async def mount_with_config_fs( return mount_with_config_fs - raise ValueError( - f"Module '{module_id}' found at {module_path} but failed to load" + raise _ModuleDiscoveryError( + f"Module '{module_id}' found at {module_path} but failed to load", + reason_code="invalid_entry_point", ) except Exception as e: @@ -541,11 +570,12 @@ def _get_module_metadata( if module_type: # Derive mount point from type (kernel mechanism) - mount_point = TYPE_TO_MOUNT_POINT.get(module_type) + mount_point = TYPE_TO_MOUNT_POINT.get(module_type) if isinstance(module_type, str) else None if not mount_point: raise ModuleValidationError( f"Module '{module_id}' has unknown type '{module_type}'. " - f"Valid types: {list(TYPE_TO_MOUNT_POINT.keys())}" + f"Valid types: {list(TYPE_TO_MOUNT_POINT.keys())}", + reason_code="invalid_module_metadata", ) logger.debug( @@ -558,6 +588,8 @@ def _get_module_metadata( if added: sys.path.remove(path_str) + except ModuleValidationError: + raise except Exception as e: logger.debug(f"Could not inspect module '{module_id}': {e}") @@ -650,7 +682,8 @@ async def _validate_module( package_path = self._find_package_dir(module_id, module_path) if package_path is None: raise ModuleValidationError( - f"Module '{module_id}' has no valid Python package at {module_path}" + f"Module '{module_id}' has no valid Python package at {module_path}", + reason_code="invalid_package_layout" if module_path.exists() else "missing_source", ) # Run validation @@ -659,8 +692,13 @@ async def _validate_module( if not result.passed: error_details = "; ".join(f"{e.name}: {e.message}" for e in result.errors) + reason = "invalid_entry_point" if any( + check.name in {"mount_exists", "mount_signature", "on_session_ready_async"} + for check in result.errors + ) else "validation_failed" raise ModuleValidationError( - f"Module '{module_id}' failed validation: {result.summary()}. Errors: {error_details}" + f"Module '{module_id}' failed validation: {result.summary()}. Errors: {error_details}", + reason_code=reason, ) logger.info(f"[module:validated] {module_id} - {result.summary()}") diff --git a/tests/test_module_failure_reasons.py b/tests/test_module_failure_reasons.py new file mode 100644 index 0000000..8c848d6 --- /dev/null +++ b/tests/test_module_failure_reasons.py @@ -0,0 +1,56 @@ +"""Safe categories at the failure origin and event boundary; no text parsing.""" +import json +from pathlib import Path +from unittest.mock import AsyncMock, MagicMock + +import pytest + +from amplifier_core._session_init import _emit_module_load_failed +from amplifier_core.loader import ModuleLoader, ModuleValidationError, module_failure_reason +from amplifier_core.module_sources import ModuleNotFoundError as SourceNotFoundError + + +@pytest.mark.asyncio +@pytest.mark.parametrize('content,expected', [ + (None, 'invalid_package_layout'), + ('__amplifier_module_type__ = "tool"\n', 'invalid_entry_point'), + ('__amplifier_module_type__ = "secret-invalid-type"\n', 'invalid_module_metadata'), + ('__amplifier_module_type__ = {"secret": "value"}\n', 'invalid_module_metadata'), +]) +async def test_loader_assigns_safe_reason_at_source(tmp_path, monkeypatch, content, expected): + name = 'amplifier_module_tool_fixture' + package = tmp_path / name + if content is not None: + package.mkdir() + (package / '__init__.py').write_text(content) + monkeypatch.syspath_prepend(str(tmp_path)) + monkeypatch.delitem(__import__('sys').modules, name, raising=False) + with pytest.raises(ModuleValidationError) as caught: + await ModuleLoader()._validate_module('tool-fixture', tmp_path) + assert module_failure_reason(caught.value) == expected + monkeypatch.delitem(__import__('sys').modules, name, raising=False) + + +@pytest.mark.asyncio +async def test_missing_source_and_unknown_exception_have_stable_categories(tmp_path): + with pytest.raises(ModuleValidationError) as caught: + await ModuleLoader()._validate_module('tool-fixture', tmp_path / 'absent') + assert module_failure_reason(caught.value) == 'missing_source' + assert module_failure_reason(SourceNotFoundError('/private/secret?token=credential')) == 'missing_source' + error = RuntimeError('invalid package layout /private/token') + error.reason_code = 'invalid_package_layout' + assert module_failure_reason(error) == 'unknown' + assert module_failure_reason(ModuleValidationError('secret', reason_code='https://secret')) == 'unknown' + + +@pytest.mark.asyncio +async def test_event_adds_only_allowlisted_reason_without_changing_legacy_error(): + coordinator = MagicMock() + coordinator.hooks.emit = AsyncMock() + error = ModuleValidationError('private legacy error', reason_code='invalid_entry_point') + await _emit_module_load_failed(coordinator, 'tool', 'tool-fixture', error) + event, data = coordinator.hooks.emit.call_args.args + assert event == 'module:load_failed' + assert data['error'] == 'private legacy error' # Existing event remains compatible. + assert data['reason_code'] == 'invalid_entry_point' + assert json.dumps({'reason_code': data['reason_code']}) == '{"reason_code": "invalid_entry_point"}' From 7dc49efcd3fa25afcf6b3aa03286cc8d5e8ec40d Mon Sep 17 00:00:00 2001 From: Brian Krabach Date: Mon, 21 Sep 2026 03:34:43 -0700 Subject: [PATCH 26/27] chore: prepare Core 2.0 migration and combined-state release validation --- Cargo.lock | 4 +- bindings/python/Cargo.toml | 2 +- crates/amplifier-core/Cargo.toml | 2 +- docs/MIGRATING_TO_2_0.md | 70 +++ pyproject.toml | 2 +- python/amplifier_core/__init__.py | 2 +- scripts/e2e-smoke-test.sh | 4 + tests/test_injection_coordinator_native.py | 119 ++++ uv.lock | 640 ++++++++++----------- 9 files changed, 519 insertions(+), 326 deletions(-) create mode 100644 docs/MIGRATING_TO_2_0.md create mode 100644 tests/test_injection_coordinator_native.py diff --git a/Cargo.lock b/Cargo.lock index d35f6b7..505ed7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,7 +40,7 @@ checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" [[package]] name = "amplifier-core" -version = "1.6.1" +version = "2.0.0" dependencies = [ "base64", "chrono", @@ -77,7 +77,7 @@ dependencies = [ [[package]] name = "amplifier-core-py" -version = "1.6.1" +version = "2.0.0" dependencies = [ "amplifier-core", "log", diff --git a/bindings/python/Cargo.toml b/bindings/python/Cargo.toml index d43c48b..9de970b 100644 --- a/bindings/python/Cargo.toml +++ b/bindings/python/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "amplifier-core-py" -version = "1.6.1" +version = "2.0.0" edition = "2021" description = "PyO3 bridge for amplifier-core Rust kernel" license = "MIT" diff --git a/crates/amplifier-core/Cargo.toml b/crates/amplifier-core/Cargo.toml index db3add7..f959282 100644 --- a/crates/amplifier-core/Cargo.toml +++ b/crates/amplifier-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "amplifier-core" -version = "1.6.1" +version = "2.0.0" edition = "2021" description = "Pure Rust kernel for the Amplifier modular AI agent system" license = "MIT" diff --git a/docs/MIGRATING_TO_2_0.md b/docs/MIGRATING_TO_2_0.md new file mode 100644 index 0000000..7221b7b --- /dev/null +++ b/docs/MIGRATING_TO_2_0.md @@ -0,0 +1,70 @@ +# Migrating to amplifier-core 2.0 + +This release combines rich tool-result content, native session metadata, model +pricing and precise usage cost, canonical source-module imports, and ordered +hook context injections. Existing JSON/Python payloads can omit the new optional +fields. The major version reflects changes to public Rust construction and +conversion APIs and stricter module identity checks. + +## Upgrade the CLI before Core + +Use an amplifier-app-cli version containing the configured-source credential +preflight and import-free provider banner. Older CLI preflight can import a +provider from a different checkout before Core sees the configured source. Core +2.0 rejects that conflicting package identity rather than executing mixed code. + +Install and validate the CLI companion first; then upgrade Core using a newly +built native wheel. Do not substitute only Python source while retaining an old +native extension. Restart workers after updating their environment so already +imported modules cannot outlive the installed version. + +## Rust callers + +- `ToolResult` has a new `content` field. Prefer `ToolResult::new(...)`, or add + `content: None` to existing struct literals. Construct rich content through + `ToolResult::normalize_content`; `ToolResultContent` keeps its vector private + so invalid content cannot bypass validation. +- Conversions between the public Rust and generated protobuf `ToolResult` now + implement `TryFrom`. Replace `.into()` with `.try_into()` and propagate or + handle `ToolResultContentError`. +- Add `pricing: None` to existing `ModelInfo` literals, `cost_usd: None` to + `Usage` literals, and `context_injections: Vec::new()` to `HookResult` literals. + `HookResult` and `ToolResult` also support default-based construction. +- `Usage.cost_usd` is an optional decimal string. Preserve its precision instead + of converting it through a floating-point number. + +## Rich tool results + +The optional `ToolResult.content` carries an ordered, non-empty list of text and +base64 image blocks. Only the supported canonical block shapes are accepted; +URL images and unrelated message-block variants are rejected. Legacy +`success`, `output`, and `error` fields remain available. Callers should handle +validation failures rather than dropping invalid content silently. + +## Source-module identity + +Source modules import under their canonical Python package names, including +relative and sibling imports. Do not preload the same package from a different +checkout, or mutate `sys.modules` to hide a conflict. Resolve and activate the +configured source before importing provider metadata. A worker restart is +required when changing the source of a package already imported in that worker. + +## Ordered hook injections + +`HookResult.context_injections` is the authoritative ordered list when present. +Each `ContextInjection` has its own content, role, lifetime, placement flag, and +registry-bound provenance. Consumers must process each item once; do not also +apply the scalar compatibility projection. + +The native coordinator validates the batch, persists durable items in order, +and returns only ephemeral items for the orchestrator to apply to the current +request. An all-durable result is consumed and returns `continue`. Legacy scalar +hook results remain accepted. Custom loops that bypass the coordinator must +honor each item's lifetime and placement rather than flattening the whole batch. + +## Release ordering + +Land the backwards-compatible CLI companion first, validate the combined Core +source with current consumers, and then land the Core carrier. Publishing the +version tag and native wheels is a separate release action. A source merge +alone is not evidence that a running host or worker has upgraded. diff --git a/pyproject.toml b/pyproject.toml index 194e602..5269da7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "amplifier-core" -version = "1.6.1" +version = "2.0.0" description = "Rust kernel with Python bindings for the Amplifier modular AI agent framework" license = "MIT" readme = "README.md" diff --git a/python/amplifier_core/__init__.py b/python/amplifier_core/__init__.py index db39b63..3727942 100644 --- a/python/amplifier_core/__init__.py +++ b/python/amplifier_core/__init__.py @@ -6,7 +6,7 @@ AmplifierSession`) still give the pure-Python implementations. """ -__version__ = "1.6.1" +__version__ = "2.0.0" # --- Rust-backed primary types (THE SWITCHOVER) --- # These four were previously imported from their Python submodules. diff --git a/scripts/e2e-smoke-test.sh b/scripts/e2e-smoke-test.sh index 5aaee9a..75f92d7 100755 --- a/scripts/e2e-smoke-test.sh +++ b/scripts/e2e-smoke-test.sh @@ -352,6 +352,10 @@ if [[ "$SMOKE_EXIT_CODE" -eq 124 ]]; then fail "Smoke test TIMED OUT after ${TIMEOUT_SECONDS}s — increase SMOKE_TIMEOUT or investigate" fi +if [[ "$SMOKE_EXIT_CODE" -ne 0 ]]; then + fail "Smoke test FAILED — amplifier exited with status ${SMOKE_EXIT_CODE}" +fi + # If we got here: no exceptions, no tool failures, no timeout echo "" pass "========================================================" diff --git a/tests/test_injection_coordinator_native.py b/tests/test_injection_coordinator_native.py new file mode 100644 index 0000000..0b4d1dd --- /dev/null +++ b/tests/test_injection_coordinator_native.py @@ -0,0 +1,119 @@ +"""Exercise ordered injections through the production registry/coordinator.""" + +from types import SimpleNamespace + +import pytest + +from amplifier_core import HookRegistry +from amplifier_core._engine import RustCoordinator +from amplifier_core.models import ContextInjection, HookResult + + +class RecordingContext: + def __init__(self): + self.messages = [] + + async def add_message(self, message): + self.messages.append(message) + + +def coordinator(**settings): + session = SimpleNamespace( + session_id="injection-review", + parent_id=None, + config={"session": {"orchestrator": "loop-basic", **settings}}, + ) + return RustCoordinator(session) + + +@pytest.mark.asyncio +async def test_mixed_hooks_persist_only_durable_items_and_return_ephemeral_residual(): + registry = HookRegistry() + + async def durable(event, data): + return HookResult( + action="inject_context", + context_injection="durable context", + context_injection_role="assistant", + ) + + async def ephemeral(event, data): + return HookResult( + action="inject_context", + context_injections=[ContextInjection( + content="request reminder", role="user", ephemeral=True, + append_to_last_tool_result=True, + hook_name="forged", event="forged", + )], + ) + + registry.register("tool:post", durable, name="durable-hook", priority=0) + registry.register("tool:post", ephemeral, name="ephemeral-hook", priority=10) + merged = await registry.emit("tool:post", {}) + assert [item.hook_name for item in merged.context_injections] == [ + "durable-hook", "ephemeral-hook", + ] + + coord = coordinator() + context = RecordingContext() + await coord.mount("context", context) + residual = await coord.process_hook_result(merged, event="tool:post") + + assert len(context.messages) == 1 + saved = context.messages[0] + assert saved["content"] == "durable context" + assert saved["role"] == "assistant" + assert saved["metadata"]["hook_name"] == "durable-hook" + assert saved["metadata"]["persisted"] is True + assert saved["metadata"]["ephemeral"] is False + assert residual.action == "inject_context" + assert residual.context_injection == "request reminder" + assert residual.context_injection_role == "user" + assert residual.ephemeral is True + assert residual.append_to_last_tool_result is True + assert len(residual.context_injections) == 1 + assert residual.context_injections[0].hook_name == "ephemeral-hook" + assert residual.context_injections[0].event == "tool:post" + + +@pytest.mark.asyncio +async def test_later_invalid_item_prevents_partial_context_write_and_budget_charge(): + coord = coordinator(injection_size_limit=10) + context = RecordingContext() + await coord.mount("context", context) + result = HookResult( + action="inject_context", + context_injections=[ + ContextInjection(content="valid"), + ContextInjection(content="x" * 11, ephemeral=True), + ], + ) + with pytest.raises(ValueError, match="exceeds 10 characters"): + await coord.process_hook_result(result, event="tool:post") + assert context.messages == [] + assert coord._current_turn_injections == 0 + + +@pytest.mark.asyncio +async def test_all_durable_items_are_consumed_in_order_and_preserve_result_metadata(): + coord = coordinator() + context = RecordingContext() + await coord.mount("context", context) + result = HookResult( + action="inject_context", reason="original reason", suppress_output=True, + approval_prompt="preserved metadata", + context_injections=[ + ContextInjection(content="one", role="user"), + ContextInjection(content="two", role="assistant"), + ], + ) + residual = await coord.process_hook_result(result, event="tool:post") + assert [(m["content"], m["role"]) for m in context.messages] == [ + ("one", "user"), ("two", "assistant"), + ] + assert residual.action == "continue" + assert residual.context_injection is None + assert residual.context_injections == [] + assert residual.reason == "original reason" + assert residual.suppress_output is True + assert residual.approval_prompt == "preserved metadata" diff --git a/uv.lock b/uv.lock index 90a6a16..3adcd05 100644 --- a/uv.lock +++ b/uv.lock @@ -4,7 +4,7 @@ requires-python = ">=3.11" [[package]] name = "amplifier-core" -version = "1.6.1" +version = "2.0.0" source = { editable = "." } dependencies = [ { name = "click" }, @@ -46,317 +46,317 @@ dev = [ [[package]] name = "annotated-types" version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/annotated-types/0.7/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/annotated-types/0.7/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53" }, ] [[package]] name = "click" version = "8.3.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/click/8.3.1/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/click/8.3.1/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6" }, ] [[package]] name = "colorama" version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/colorama/0.4.6/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/colorama/0.4.6/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" }, ] [[package]] name = "grpcio" version = "1.78.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/06/8a/3d098f35c143a89520e568e6539cc098fcd294495910e359889ce8741c84/grpcio-1.78.0.tar.gz", hash = "sha256:7382b95189546f375c174f53a5fa873cef91c4b8005faa05cc5b3beea9c4f1c5", size = 12852416, upload-time = "2026-02-06T09:57:18.093Z" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0.tar.gz", hash = "sha256:7382b95189546f375c174f53a5fa873cef91c4b8005faa05cc5b3beea9c4f1c5" } wheels = [ - { url = "https://files.pythonhosted.org/packages/86/c7/d0b780a29b0837bf4ca9580904dfb275c1fc321ded7897d620af7047ec57/grpcio-1.78.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2777b783f6c13b92bd7b716667452c329eefd646bfb3f2e9dabea2e05dbd34f6", size = 5951525, upload-time = "2026-02-06T09:55:01.989Z" }, - { url = "https://files.pythonhosted.org/packages/c5/b1/96920bf2ee61df85a9503cb6f733fe711c0ff321a5a697d791b075673281/grpcio-1.78.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:9dca934f24c732750389ce49d638069c3892ad065df86cb465b3fa3012b70c9e", size = 11830418, upload-time = "2026-02-06T09:55:04.462Z" }, - { url = "https://files.pythonhosted.org/packages/83/0c/7c1528f098aeb75a97de2bae18c530f56959fb7ad6c882db45d9884d6edc/grpcio-1.78.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:459ab414b35f4496138d0ecd735fed26f1318af5e52cb1efbc82a09f0d5aa911", size = 6524477, upload-time = "2026-02-06T09:55:07.111Z" }, - { url = "https://files.pythonhosted.org/packages/8d/52/e7c1f3688f949058e19a011c4e0dec973da3d0ae5e033909677f967ae1f4/grpcio-1.78.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:082653eecbdf290e6e3e2c276ab2c54b9e7c299e07f4221872380312d8cf395e", size = 7198266, upload-time = "2026-02-06T09:55:10.016Z" }, - { url = "https://files.pythonhosted.org/packages/e5/61/8ac32517c1e856677282c34f2e7812d6c328fa02b8f4067ab80e77fdc9c9/grpcio-1.78.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85f93781028ec63f383f6bc90db785a016319c561cc11151fbb7b34e0d012303", size = 6730552, upload-time = "2026-02-06T09:55:12.207Z" }, - { url = "https://files.pythonhosted.org/packages/bd/98/b8ee0158199250220734f620b12e4a345955ac7329cfd908d0bf0fda77f0/grpcio-1.78.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f12857d24d98441af6a1d5c87442d624411db486f7ba12550b07788f74b67b04", size = 7304296, upload-time = "2026-02-06T09:55:15.044Z" }, - { url = "https://files.pythonhosted.org/packages/bd/0f/7b72762e0d8840b58032a56fdbd02b78fc645b9fa993d71abf04edbc54f4/grpcio-1.78.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5397fff416b79e4b284959642a4e95ac4b0f1ece82c9993658e0e477d40551ec", size = 8288298, upload-time = "2026-02-06T09:55:17.276Z" }, - { url = "https://files.pythonhosted.org/packages/24/ae/ae4ce56bc5bb5caa3a486d60f5f6083ac3469228faa734362487176c15c5/grpcio-1.78.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fbe6e89c7ffb48518384068321621b2a69cab509f58e40e4399fdd378fa6d074", size = 7730953, upload-time = "2026-02-06T09:55:19.545Z" }, - { url = "https://files.pythonhosted.org/packages/b5/6e/8052e3a28eb6a820c372b2eb4b5e32d195c661e137d3eca94d534a4cfd8a/grpcio-1.78.0-cp311-cp311-win32.whl", hash = "sha256:6092beabe1966a3229f599d7088b38dfc8ffa1608b5b5cdda31e591e6500f856", size = 4076503, upload-time = "2026-02-06T09:55:21.521Z" }, - { url = "https://files.pythonhosted.org/packages/08/62/f22c98c5265dfad327251fa2f840b591b1df5f5e15d88b19c18c86965b27/grpcio-1.78.0-cp311-cp311-win_amd64.whl", hash = "sha256:1afa62af6e23f88629f2b29ec9e52ec7c65a7176c1e0a83292b93c76ca882558", size = 4799767, upload-time = "2026-02-06T09:55:24.107Z" }, - { url = "https://files.pythonhosted.org/packages/4e/f4/7384ed0178203d6074446b3c4f46c90a22ddf7ae0b3aee521627f54cfc2a/grpcio-1.78.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:f9ab915a267fc47c7e88c387a3a28325b58c898e23d4995f765728f4e3dedb97", size = 5913985, upload-time = "2026-02-06T09:55:26.832Z" }, - { url = "https://files.pythonhosted.org/packages/81/ed/be1caa25f06594463f685b3790b320f18aea49b33166f4141bfdc2bfb236/grpcio-1.78.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3f8904a8165ab21e07e58bf3e30a73f4dffc7a1e0dbc32d51c61b5360d26f43e", size = 11811853, upload-time = "2026-02-06T09:55:29.224Z" }, - { url = "https://files.pythonhosted.org/packages/24/a7/f06d151afc4e64b7e3cc3e872d331d011c279aaab02831e40a81c691fb65/grpcio-1.78.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:859b13906ce098c0b493af92142ad051bf64c7870fa58a123911c88606714996", size = 6475766, upload-time = "2026-02-06T09:55:31.825Z" }, - { url = "https://files.pythonhosted.org/packages/8a/a8/4482922da832ec0082d0f2cc3a10976d84a7424707f25780b82814aafc0a/grpcio-1.78.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b2342d87af32790f934a79c3112641e7b27d63c261b8b4395350dad43eff1dc7", size = 7170027, upload-time = "2026-02-06T09:55:34.7Z" }, - { url = "https://files.pythonhosted.org/packages/54/bf/f4a3b9693e35d25b24b0b39fa46d7d8a3c439e0a3036c3451764678fec20/grpcio-1.78.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12a771591ae40bc65ba67048fa52ef4f0e6db8279e595fd349f9dfddeef571f9", size = 6690766, upload-time = "2026-02-06T09:55:36.902Z" }, - { url = "https://files.pythonhosted.org/packages/c7/b9/521875265cc99fe5ad4c5a17010018085cae2810a928bf15ebe7d8bcd9cc/grpcio-1.78.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:185dea0d5260cbb2d224c507bf2a5444d5abbb1fa3594c1ed7e4c709d5eb8383", size = 7266161, upload-time = "2026-02-06T09:55:39.824Z" }, - { url = "https://files.pythonhosted.org/packages/05/86/296a82844fd40a4ad4a95f100b55044b4f817dece732bf686aea1a284147/grpcio-1.78.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51b13f9aed9d59ee389ad666b8c2214cc87b5de258fa712f9ab05f922e3896c6", size = 8253303, upload-time = "2026-02-06T09:55:42.353Z" }, - { url = "https://files.pythonhosted.org/packages/f3/e4/ea3c0caf5468537f27ad5aab92b681ed7cc0ef5f8c9196d3fd42c8c2286b/grpcio-1.78.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd5f135b1bd58ab088930b3c613455796dfa0393626a6972663ccdda5b4ac6ce", size = 7698222, upload-time = "2026-02-06T09:55:44.629Z" }, - { url = "https://files.pythonhosted.org/packages/d7/47/7f05f81e4bb6b831e93271fb12fd52ba7b319b5402cbc101d588f435df00/grpcio-1.78.0-cp312-cp312-win32.whl", hash = "sha256:94309f498bcc07e5a7d16089ab984d42ad96af1d94b5a4eb966a266d9fcabf68", size = 4066123, upload-time = "2026-02-06T09:55:47.644Z" }, - { url = "https://files.pythonhosted.org/packages/ad/e7/d6914822c88aa2974dbbd10903d801a28a19ce9cd8bad7e694cbbcf61528/grpcio-1.78.0-cp312-cp312-win_amd64.whl", hash = "sha256:9566fe4ababbb2610c39190791e5b829869351d14369603702e890ef3ad2d06e", size = 4797657, upload-time = "2026-02-06T09:55:49.86Z" }, - { url = "https://files.pythonhosted.org/packages/05/a9/8f75894993895f361ed8636cd9237f4ab39ef87fd30db17467235ed1c045/grpcio-1.78.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:ce3a90455492bf8bfa38e56fbbe1dbd4f872a3d8eeaf7337dc3b1c8aa28c271b", size = 5920143, upload-time = "2026-02-06T09:55:52.035Z" }, - { url = "https://files.pythonhosted.org/packages/55/06/0b78408e938ac424100100fd081189451b472236e8a3a1f6500390dc4954/grpcio-1.78.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:2bf5e2e163b356978b23652c4818ce4759d40f4712ee9ec5a83c4be6f8c23a3a", size = 11803926, upload-time = "2026-02-06T09:55:55.494Z" }, - { url = "https://files.pythonhosted.org/packages/88/93/b59fe7832ff6ae3c78b813ea43dac60e295fa03606d14d89d2e0ec29f4f3/grpcio-1.78.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8f2ac84905d12918e4e55a16da17939eb63e433dc11b677267c35568aa63fc84", size = 6478628, upload-time = "2026-02-06T09:55:58.533Z" }, - { url = "https://files.pythonhosted.org/packages/ed/df/e67e3734527f9926b7d9c0dde6cd998d1d26850c3ed8eeec81297967ac67/grpcio-1.78.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b58f37edab4a3881bc6c9bca52670610e0c9ca14e2ea3cf9debf185b870457fb", size = 7173574, upload-time = "2026-02-06T09:56:01.786Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/cc03fffb07bfba982a9ec097b164e8835546980aec25ecfa5f9c1a47e022/grpcio-1.78.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:735e38e176a88ce41840c21bb49098ab66177c64c82426e24e0082500cc68af5", size = 6692639, upload-time = "2026-02-06T09:56:04.529Z" }, - { url = "https://files.pythonhosted.org/packages/bf/9a/289c32e301b85bdb67d7ec68b752155e674ee3ba2173a1858f118e399ef3/grpcio-1.78.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2045397e63a7a0ee7957c25f7dbb36ddc110e0cfb418403d110c0a7a68a844e9", size = 7268838, upload-time = "2026-02-06T09:56:08.397Z" }, - { url = "https://files.pythonhosted.org/packages/0e/79/1be93f32add280461fa4773880196572563e9c8510861ac2da0ea0f892b6/grpcio-1.78.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9f136fbafe7ccf4ac7e8e0c28b31066e810be52d6e344ef954a3a70234e1702", size = 8251878, upload-time = "2026-02-06T09:56:10.914Z" }, - { url = "https://files.pythonhosted.org/packages/65/65/793f8e95296ab92e4164593674ae6291b204bb5f67f9d4a711489cd30ffa/grpcio-1.78.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:748b6138585379c737adc08aeffd21222abbda1a86a0dca2a39682feb9196c20", size = 7695412, upload-time = "2026-02-06T09:56:13.593Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/1e233fe697ecc82845942c2822ed06bb522e70d6771c28d5528e4c50f6a4/grpcio-1.78.0-cp313-cp313-win32.whl", hash = "sha256:271c73e6e5676afe4fc52907686670c7cea22ab2310b76a59b678403ed40d670", size = 4064899, upload-time = "2026-02-06T09:56:15.601Z" }, - { url = "https://files.pythonhosted.org/packages/4d/27/d86b89e36de8a951501fb06a0f38df19853210f341d0b28f83f4aa0ffa08/grpcio-1.78.0-cp313-cp313-win_amd64.whl", hash = "sha256:f2d4e43ee362adfc05994ed479334d5a451ab7bc3f3fee1b796b8ca66895acb4", size = 4797393, upload-time = "2026-02-06T09:56:17.882Z" }, - { url = "https://files.pythonhosted.org/packages/29/f2/b56e43e3c968bfe822fa6ce5bca10d5c723aa40875b48791ce1029bb78c7/grpcio-1.78.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:e87cbc002b6f440482b3519e36e1313eb5443e9e9e73d6a52d43bd2004fcfd8e", size = 5920591, upload-time = "2026-02-06T09:56:20.758Z" }, - { url = "https://files.pythonhosted.org/packages/5d/81/1f3b65bd30c334167bfa8b0d23300a44e2725ce39bba5b76a2460d85f745/grpcio-1.78.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:c41bc64626db62e72afec66b0c8a0da76491510015417c127bfc53b2fe6d7f7f", size = 11813685, upload-time = "2026-02-06T09:56:24.315Z" }, - { url = "https://files.pythonhosted.org/packages/0e/1c/bbe2f8216a5bd3036119c544d63c2e592bdf4a8ec6e4a1867592f4586b26/grpcio-1.78.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8dfffba826efcf366b1e3ccc37e67afe676f290e13a3b48d31a46739f80a8724", size = 6487803, upload-time = "2026-02-06T09:56:27.367Z" }, - { url = "https://files.pythonhosted.org/packages/16/5c/a6b2419723ea7ddce6308259a55e8e7593d88464ce8db9f4aa857aba96fa/grpcio-1.78.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:74be1268d1439eaaf552c698cdb11cd594f0c49295ae6bb72c34ee31abbe611b", size = 7173206, upload-time = "2026-02-06T09:56:29.876Z" }, - { url = "https://files.pythonhosted.org/packages/df/1e/b8801345629a415ea7e26c83d75eb5dbe91b07ffe5210cc517348a8d4218/grpcio-1.78.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be63c88b32e6c0f1429f1398ca5c09bc64b0d80950c8bb7807d7d7fb36fb84c7", size = 6693826, upload-time = "2026-02-06T09:56:32.305Z" }, - { url = "https://files.pythonhosted.org/packages/34/84/0de28eac0377742679a510784f049738a80424b17287739fc47d63c2439e/grpcio-1.78.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3c586ac70e855c721bda8f548d38c3ca66ac791dc49b66a8281a1f99db85e452", size = 7277897, upload-time = "2026-02-06T09:56:34.915Z" }, - { url = "https://files.pythonhosted.org/packages/ca/9c/ad8685cfe20559a9edb66f735afdcb2b7d3de69b13666fdfc542e1916ebd/grpcio-1.78.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:35eb275bf1751d2ffbd8f57cdbc46058e857cf3971041521b78b7db94bdaf127", size = 8252404, upload-time = "2026-02-06T09:56:37.553Z" }, - { url = "https://files.pythonhosted.org/packages/3c/05/33a7a4985586f27e1de4803887c417ec7ced145ebd069bc38a9607059e2b/grpcio-1.78.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:207db540302c884b8848036b80db352a832b99dfdf41db1eb554c2c2c7800f65", size = 7696837, upload-time = "2026-02-06T09:56:40.173Z" }, - { url = "https://files.pythonhosted.org/packages/73/77/7382241caf88729b106e49e7d18e3116216c778e6a7e833826eb96de22f7/grpcio-1.78.0-cp314-cp314-win32.whl", hash = "sha256:57bab6deef2f4f1ca76cc04565df38dc5713ae6c17de690721bdf30cb1e0545c", size = 4142439, upload-time = "2026-02-06T09:56:43.258Z" }, - { url = "https://files.pythonhosted.org/packages/48/b2/b096ccce418882fbfda4f7496f9357aaa9a5af1896a9a7f60d9f2b275a06/grpcio-1.78.0-cp314-cp314-win_amd64.whl", hash = "sha256:dce09d6116df20a96acfdbf85e4866258c3758180e8c49845d6ba8248b6d0bbb", size = 4929852, upload-time = "2026-02-06T09:56:45.885Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2777b783f6c13b92bd7b716667452c329eefd646bfb3f2e9dabea2e05dbd34f6" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:9dca934f24c732750389ce49d638069c3892ad065df86cb465b3fa3012b70c9e" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:459ab414b35f4496138d0ecd735fed26f1318af5e52cb1efbc82a09f0d5aa911" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:082653eecbdf290e6e3e2c276ab2c54b9e7c299e07f4221872380312d8cf395e" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85f93781028ec63f383f6bc90db785a016319c561cc11151fbb7b34e0d012303" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f12857d24d98441af6a1d5c87442d624411db486f7ba12550b07788f74b67b04" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5397fff416b79e4b284959642a4e95ac4b0f1ece82c9993658e0e477d40551ec" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fbe6e89c7ffb48518384068321621b2a69cab509f58e40e4399fdd378fa6d074" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-win32.whl", hash = "sha256:6092beabe1966a3229f599d7088b38dfc8ffa1608b5b5cdda31e591e6500f856" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-win_amd64.whl", hash = "sha256:1afa62af6e23f88629f2b29ec9e52ec7c65a7176c1e0a83292b93c76ca882558" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:f9ab915a267fc47c7e88c387a3a28325b58c898e23d4995f765728f4e3dedb97" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3f8904a8165ab21e07e58bf3e30a73f4dffc7a1e0dbc32d51c61b5360d26f43e" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:859b13906ce098c0b493af92142ad051bf64c7870fa58a123911c88606714996" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b2342d87af32790f934a79c3112641e7b27d63c261b8b4395350dad43eff1dc7" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12a771591ae40bc65ba67048fa52ef4f0e6db8279e595fd349f9dfddeef571f9" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:185dea0d5260cbb2d224c507bf2a5444d5abbb1fa3594c1ed7e4c709d5eb8383" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51b13f9aed9d59ee389ad666b8c2214cc87b5de258fa712f9ab05f922e3896c6" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd5f135b1bd58ab088930b3c613455796dfa0393626a6972663ccdda5b4ac6ce" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-win32.whl", hash = "sha256:94309f498bcc07e5a7d16089ab984d42ad96af1d94b5a4eb966a266d9fcabf68" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-win_amd64.whl", hash = "sha256:9566fe4ababbb2610c39190791e5b829869351d14369603702e890ef3ad2d06e" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:ce3a90455492bf8bfa38e56fbbe1dbd4f872a3d8eeaf7337dc3b1c8aa28c271b" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:2bf5e2e163b356978b23652c4818ce4759d40f4712ee9ec5a83c4be6f8c23a3a" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8f2ac84905d12918e4e55a16da17939eb63e433dc11b677267c35568aa63fc84" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b58f37edab4a3881bc6c9bca52670610e0c9ca14e2ea3cf9debf185b870457fb" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:735e38e176a88ce41840c21bb49098ab66177c64c82426e24e0082500cc68af5" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2045397e63a7a0ee7957c25f7dbb36ddc110e0cfb418403d110c0a7a68a844e9" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9f136fbafe7ccf4ac7e8e0c28b31066e810be52d6e344ef954a3a70234e1702" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:748b6138585379c737adc08aeffd21222abbda1a86a0dca2a39682feb9196c20" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-win32.whl", hash = "sha256:271c73e6e5676afe4fc52907686670c7cea22ab2310b76a59b678403ed40d670" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-win_amd64.whl", hash = "sha256:f2d4e43ee362adfc05994ed479334d5a451ab7bc3f3fee1b796b8ca66895acb4" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:e87cbc002b6f440482b3519e36e1313eb5443e9e9e73d6a52d43bd2004fcfd8e" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:c41bc64626db62e72afec66b0c8a0da76491510015417c127bfc53b2fe6d7f7f" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8dfffba826efcf366b1e3ccc37e67afe676f290e13a3b48d31a46739f80a8724" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:74be1268d1439eaaf552c698cdb11cd594f0c49295ae6bb72c34ee31abbe611b" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be63c88b32e6c0f1429f1398ca5c09bc64b0d80950c8bb7807d7d7fb36fb84c7" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3c586ac70e855c721bda8f548d38c3ca66ac791dc49b66a8281a1f99db85e452" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:35eb275bf1751d2ffbd8f57cdbc46058e857cf3971041521b78b7db94bdaf127" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:207db540302c884b8848036b80db352a832b99dfdf41db1eb554c2c2c7800f65" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-win32.whl", hash = "sha256:57bab6deef2f4f1ca76cc04565df38dc5713ae6c17de690721bdf30cb1e0545c" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-win_amd64.whl", hash = "sha256:dce09d6116df20a96acfdbf85e4866258c3758180e8c49845d6ba8248b6d0bbb" }, ] [[package]] name = "grpcio-tools" version = "1.78.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } dependencies = [ { name = "grpcio" }, { name = "protobuf" }, { name = "setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/d1/cbefe328653f746fd319c4377836a25ba64226e41c6a1d7d5cdbc87a459f/grpcio_tools-1.78.0.tar.gz", hash = "sha256:4b0dd86560274316e155d925158276f8564508193088bc43e20d3f5dff956b2b", size = 5393026, upload-time = "2026-02-06T09:59:59.53Z" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0.tar.gz", hash = "sha256:4b0dd86560274316e155d925158276f8564508193088bc43e20d3f5dff956b2b" } wheels = [ - { url = "https://files.pythonhosted.org/packages/75/78/280184d19242ed6762bf453c47a70b869b3c5c72a24dc5bf2bf43909faa3/grpcio_tools-1.78.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:6a8b8b7b49f319d29dbcf507f62984fa382d1d10437d75c3f26db5f09c4ac0af", size = 2545904, upload-time = "2026-02-06T09:57:52.769Z" }, - { url = "https://files.pythonhosted.org/packages/5b/51/3c46dea5113f68fe879961cae62d34bb7a3c308a774301b45d614952ee98/grpcio_tools-1.78.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:d62cf3b68372b0c6d722a6165db41b976869811abeabc19c8522182978d8db10", size = 5709078, upload-time = "2026-02-06T09:57:56.389Z" }, - { url = "https://files.pythonhosted.org/packages/e0/2c/dc1ae9ec53182c96d56dfcbf3bcd3e55a8952ad508b188c75bf5fc8993d4/grpcio_tools-1.78.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fa9056742efeaf89d5fe14198af71e5cbc4fbf155d547b89507e19d6025906c6", size = 2591744, upload-time = "2026-02-06T09:57:58.341Z" }, - { url = "https://files.pythonhosted.org/packages/04/63/9b53fc9a9151dd24386785171a4191ee7cb5afb4d983b6a6a87408f41b28/grpcio_tools-1.78.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e3191af125dcb705aa6bc3856ba81ba99b94121c1b6ebee152e66ea084672831", size = 2905113, upload-time = "2026-02-06T09:58:00.38Z" }, - { url = "https://files.pythonhosted.org/packages/96/b2/0ad8d789f3a2a00893131c140865605fa91671a6e6fcf9da659e1fabba10/grpcio_tools-1.78.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:283239ddbb67ae83fac111c61b25d8527a1dbd355b377cbc8383b79f1329944d", size = 2656436, upload-time = "2026-02-06T09:58:03.038Z" }, - { url = "https://files.pythonhosted.org/packages/09/4d/580f47ce2fc61b093ade747b378595f51b4f59972dd39949f7444b464a03/grpcio_tools-1.78.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ac977508c0db15301ef36d6c79769ec1a6cc4e3bc75735afca7fe7e360cead3a", size = 3106128, upload-time = "2026-02-06T09:58:05.064Z" }, - { url = "https://files.pythonhosted.org/packages/c9/29/d83b2d89f8d10e438bad36b1eb29356510fb97e81e6a608b22ae1890e8e6/grpcio_tools-1.78.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4ff605e25652a0bd13aa8a73a09bc48669c68170902f5d2bf1468a57d5e78771", size = 3654953, upload-time = "2026-02-06T09:58:07.15Z" }, - { url = "https://files.pythonhosted.org/packages/08/71/917ce85633311e54fefd7e6eb1224fb780ef317a4d092766f5630c3fc419/grpcio_tools-1.78.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0197d7b561c79be78ab93d0fe2836c8def470683df594bae3ac89dd8e5c821b2", size = 3322630, upload-time = "2026-02-06T09:58:10.305Z" }, - { url = "https://files.pythonhosted.org/packages/b2/55/3fbf6b26ab46fc79e1e6f7f4e0993cf540263dad639290299fad374a0829/grpcio_tools-1.78.0-cp311-cp311-win32.whl", hash = "sha256:28f71f591f7f39555863ced84fcc209cbf4454e85ef957232f43271ee99af577", size = 993804, upload-time = "2026-02-06T09:58:13.698Z" }, - { url = "https://files.pythonhosted.org/packages/73/86/4affe006d9e1e9e1c6653d6aafe2f8b9188acb2b563cd8ed3a2c7c0e8aec/grpcio_tools-1.78.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a6de495dabf86a3b40b9a7492994e1232b077af9d63080811838b781abbe4e8", size = 1158566, upload-time = "2026-02-06T09:58:15.721Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ae/5b1fa5dd8d560a6925aa52de0de8731d319f121c276e35b9b2af7cc220a2/grpcio_tools-1.78.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:9eb122da57d4cad7d339fc75483116f0113af99e8d2c67f3ef9cae7501d806e4", size = 2546823, upload-time = "2026-02-06T09:58:17.944Z" }, - { url = "https://files.pythonhosted.org/packages/a7/ed/d33ccf7fa701512efea7e7e23333b748848a123e9d3bbafde4e126784546/grpcio_tools-1.78.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:d0c501b8249940b886420e6935045c44cb818fa6f265f4c2b97d5cff9cb5e796", size = 5706776, upload-time = "2026-02-06T09:58:20.944Z" }, - { url = "https://files.pythonhosted.org/packages/c6/69/4285583f40b37af28277fc6b867d636e3b10e1b6a7ebd29391a856e1279b/grpcio_tools-1.78.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:77e5aa2d2a7268d55b1b113f958264681ef1994c970f69d48db7d4683d040f57", size = 2593972, upload-time = "2026-02-06T09:58:23.29Z" }, - { url = "https://files.pythonhosted.org/packages/d7/eb/ecc1885bd6b3147f0a1b7dff5565cab72f01c8f8aa458f682a1c77a9fb08/grpcio_tools-1.78.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:8e3c0b0e6ba5275322ba29a97bf890565a55f129f99a21b121145e9e93a22525", size = 2905531, upload-time = "2026-02-06T09:58:25.406Z" }, - { url = "https://files.pythonhosted.org/packages/ae/a9/511d0040ced66960ca10ba0f082d6b2d2ee6dd61837b1709636fdd8e23b4/grpcio_tools-1.78.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975d4cb48694e20ebd78e1643e5f1cd94cdb6a3d38e677a8e84ae43665aa4790", size = 2656909, upload-time = "2026-02-06T09:58:28.022Z" }, - { url = "https://files.pythonhosted.org/packages/06/a3/3d2c707e7dee8df842c96fbb24feb2747e506e39f4a81b661def7fed107c/grpcio_tools-1.78.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:553ff18c5d52807dedecf25045ae70bad7a3dbba0b27a9a3cdd9bcf0a1b7baec", size = 3109778, upload-time = "2026-02-06T09:58:30.091Z" }, - { url = "https://files.pythonhosted.org/packages/1f/4b/646811ba241bf05da1f0dc6f25764f1c837f78f75b4485a4210c84b79eae/grpcio_tools-1.78.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8c7f5e4af5a84d2e96c862b1a65e958a538237e268d5f8203a3a784340975b51", size = 3658763, upload-time = "2026-02-06T09:58:32.875Z" }, - { url = "https://files.pythonhosted.org/packages/45/de/0a5ef3b3e79d1011375f5580dfee3a9c1ccb96c5f5d1c74c8cee777a2483/grpcio_tools-1.78.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:96183e2b44afc3f9a761e9d0f985c3b44e03e8bb98e626241a6cbfb3b6f7e88f", size = 3325116, upload-time = "2026-02-06T09:58:34.894Z" }, - { url = "https://files.pythonhosted.org/packages/95/d2/6391b241ad571bc3e71d63f957c0b1860f0c47932d03c7f300028880f9b8/grpcio_tools-1.78.0-cp312-cp312-win32.whl", hash = "sha256:2250e8424c565a88573f7dc10659a0b92802e68c2a1d57e41872c9b88ccea7a6", size = 993493, upload-time = "2026-02-06T09:58:37.242Z" }, - { url = "https://files.pythonhosted.org/packages/7c/8f/7d0d3a39ecad76ccc136be28274daa660569b244fa7d7d0bbb24d68e5ece/grpcio_tools-1.78.0-cp312-cp312-win_amd64.whl", hash = "sha256:217d1fa29de14d9c567d616ead7cb0fef33cde36010edff5a9390b00d52e5094", size = 1158423, upload-time = "2026-02-06T09:58:40.072Z" }, - { url = "https://files.pythonhosted.org/packages/53/ce/17311fb77530420e2f441e916b347515133e83d21cd6cc77be04ce093d5b/grpcio_tools-1.78.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:2d6de1cc23bdc1baafc23e201b1e48c617b8c1418b4d8e34cebf72141676e5fb", size = 2546284, upload-time = "2026-02-06T09:58:43.073Z" }, - { url = "https://files.pythonhosted.org/packages/1d/d3/79e101483115f0e78223397daef71751b75eba7e92a32060c10aae11ca64/grpcio_tools-1.78.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:2afeaad88040894c76656202ff832cb151bceb05c0e6907e539d129188b1e456", size = 5705653, upload-time = "2026-02-06T09:58:45.533Z" }, - { url = "https://files.pythonhosted.org/packages/8b/a7/52fa3ccb39ceeee6adc010056eadfbca8198651c113e418dafebbdf2b306/grpcio_tools-1.78.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:33cc593735c93c03d63efe7a8ba25f3c66f16c52f0651910712490244facad72", size = 2592788, upload-time = "2026-02-06T09:58:48.918Z" }, - { url = "https://files.pythonhosted.org/packages/68/08/682ff6bb548225513d73dc9403742d8975439d7469c673bc534b9bbc83a7/grpcio_tools-1.78.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2921d7989c4d83b71f03130ab415fa4d66e6693b8b8a1fcbb7a1c67cff19b812", size = 2905157, upload-time = "2026-02-06T09:58:51.478Z" }, - { url = "https://files.pythonhosted.org/packages/b2/66/264f3836a96423b7018e5ada79d62576a6401f6da4e1f4975b18b2be1265/grpcio_tools-1.78.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e6a0df438e82c804c7b95e3f311c97c2f876dcc36376488d5b736b7bcf5a9b45", size = 2656166, upload-time = "2026-02-06T09:58:54.117Z" }, - { url = "https://files.pythonhosted.org/packages/f3/6b/f108276611522e03e98386b668cc7e575eff6952f2db9caa15b2a3b3e883/grpcio_tools-1.78.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9c6070a9500798225191ef25d0055a15d2c01c9c8f2ee7b681fffa99c98c822", size = 3109110, upload-time = "2026-02-06T09:58:56.891Z" }, - { url = "https://files.pythonhosted.org/packages/6f/c7/cf048dbcd64b3396b3c860a2ffbcc67a8f8c87e736aaa74c2e505a7eee4c/grpcio_tools-1.78.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:394e8b57d85370a62e5b0a4d64c96fcf7568345c345d8590c821814d227ecf1d", size = 3657863, upload-time = "2026-02-06T09:58:59.176Z" }, - { url = "https://files.pythonhosted.org/packages/b6/37/e2736912c8fda57e2e57a66ea5e0bc8eb9a5fb7ded00e866ad22d50afb08/grpcio_tools-1.78.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3ef700293ab375e111a2909d87434ed0a0b086adf0ce67a8d9cf12ea7765e63", size = 3324748, upload-time = "2026-02-06T09:59:01.242Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/726abc75bb5bfc2841e88ea05896e42f51ca7c30cb56da5c5b63058b3867/grpcio_tools-1.78.0-cp313-cp313-win32.whl", hash = "sha256:6993b960fec43a8d840ee5dc20247ef206c1a19587ea49fe5e6cc3d2a09c1585", size = 993074, upload-time = "2026-02-06T09:59:03.085Z" }, - { url = "https://files.pythonhosted.org/packages/c5/68/91b400bb360faf9b177ffb5540ec1c4d06ca923691ddf0f79e2c9683f4da/grpcio_tools-1.78.0-cp313-cp313-win_amd64.whl", hash = "sha256:275ce3c2978842a8cf9dd88dce954e836e590cf7029649ad5d1145b779039ed5", size = 1158185, upload-time = "2026-02-06T09:59:05.036Z" }, - { url = "https://files.pythonhosted.org/packages/cf/5e/278f3831c8d56bae02e3acc570465648eccf0a6bbedcb1733789ac966803/grpcio_tools-1.78.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:8b080d0d072e6032708a3a91731b808074d7ab02ca8fb9847b6a011fdce64cd9", size = 2546270, upload-time = "2026-02-06T09:59:07.426Z" }, - { url = "https://files.pythonhosted.org/packages/a3/d9/68582f2952b914b60dddc18a2e3f9c6f09af9372b6f6120d6cf3ec7f8b4e/grpcio_tools-1.78.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8c0ad8f8f133145cd7008b49cb611a5c6a9d89ab276c28afa17050516e801f79", size = 5705731, upload-time = "2026-02-06T09:59:09.856Z" }, - { url = "https://files.pythonhosted.org/packages/70/68/feb0f9a48818ee1df1e8b644069379a1e6ef5447b9b347c24e96fd258e5d/grpcio_tools-1.78.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2f8ea092a7de74c6359335d36f0674d939a3c7e1a550f4c2c9e80e0226de8fe4", size = 2593896, upload-time = "2026-02-06T09:59:12.23Z" }, - { url = "https://files.pythonhosted.org/packages/1f/08/a430d8d06e1b8d33f3e48d3f0cc28236723af2f35e37bd5c8db05df6c3aa/grpcio_tools-1.78.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:da422985e0cac822b41822f43429c19ecb27c81ffe3126d0b74e77edec452608", size = 2905298, upload-time = "2026-02-06T09:59:14.458Z" }, - { url = "https://files.pythonhosted.org/packages/71/0a/348c36a3eae101ca0c090c9c3bc96f2179adf59ee0c9262d11cdc7bfe7db/grpcio_tools-1.78.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4fab1faa3fbcb246263e68da7a8177d73772283f9db063fb8008517480888d26", size = 2656186, upload-time = "2026-02-06T09:59:16.949Z" }, - { url = "https://files.pythonhosted.org/packages/1d/3f/18219f331536fad4af6207ade04142292faa77b5cb4f4463787988963df8/grpcio_tools-1.78.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dd9c094f73f734becae3f20f27d4944d3cd8fb68db7338ee6c58e62fc5c3d99f", size = 3109859, upload-time = "2026-02-06T09:59:19.202Z" }, - { url = "https://files.pythonhosted.org/packages/5b/d9/341ea20a44c8e5a3a18acc820b65014c2e3ea5b4f32a53d14864bcd236bc/grpcio_tools-1.78.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:2ed51ce6b833068f6c580b73193fc2ec16468e6bc18354bc2f83a58721195a58", size = 3657915, upload-time = "2026-02-06T09:59:21.839Z" }, - { url = "https://files.pythonhosted.org/packages/fb/f4/5978b0f91611a64371424c109dd0027b247e5b39260abad2eaee66b6aa37/grpcio_tools-1.78.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:05803a5cdafe77c8bdf36aa660ad7a6a1d9e49bc59ce45c1bade2a4698826599", size = 3324724, upload-time = "2026-02-06T09:59:24.402Z" }, - { url = "https://files.pythonhosted.org/packages/b2/80/96a324dba99cfbd20e291baf0b0ae719dbb62b76178c5ce6c788e7331cb1/grpcio_tools-1.78.0-cp314-cp314-win32.whl", hash = "sha256:f7c722e9ce6f11149ac5bddd5056e70aaccfd8168e74e9d34d8b8b588c3f5c7c", size = 1015505, upload-time = "2026-02-06T09:59:26.3Z" }, - { url = "https://files.pythonhosted.org/packages/3b/d1/909e6a05bfd44d46327dc4b8a78beb2bae4fb245ffab2772e350081aaf7e/grpcio_tools-1.78.0-cp314-cp314-win_amd64.whl", hash = "sha256:7d58ade518b546120ec8f0a8e006fc8076ae5df151250ebd7e82e9b5e152c229", size = 1190196, upload-time = "2026-02-06T09:59:28.359Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:6a8b8b7b49f319d29dbcf507f62984fa382d1d10437d75c3f26db5f09c4ac0af" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:d62cf3b68372b0c6d722a6165db41b976869811abeabc19c8522182978d8db10" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fa9056742efeaf89d5fe14198af71e5cbc4fbf155d547b89507e19d6025906c6" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e3191af125dcb705aa6bc3856ba81ba99b94121c1b6ebee152e66ea084672831" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:283239ddbb67ae83fac111c61b25d8527a1dbd355b377cbc8383b79f1329944d" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ac977508c0db15301ef36d6c79769ec1a6cc4e3bc75735afca7fe7e360cead3a" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4ff605e25652a0bd13aa8a73a09bc48669c68170902f5d2bf1468a57d5e78771" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0197d7b561c79be78ab93d0fe2836c8def470683df594bae3ac89dd8e5c821b2" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-win32.whl", hash = "sha256:28f71f591f7f39555863ced84fcc209cbf4454e85ef957232f43271ee99af577" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a6de495dabf86a3b40b9a7492994e1232b077af9d63080811838b781abbe4e8" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:9eb122da57d4cad7d339fc75483116f0113af99e8d2c67f3ef9cae7501d806e4" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:d0c501b8249940b886420e6935045c44cb818fa6f265f4c2b97d5cff9cb5e796" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:77e5aa2d2a7268d55b1b113f958264681ef1994c970f69d48db7d4683d040f57" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:8e3c0b0e6ba5275322ba29a97bf890565a55f129f99a21b121145e9e93a22525" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975d4cb48694e20ebd78e1643e5f1cd94cdb6a3d38e677a8e84ae43665aa4790" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:553ff18c5d52807dedecf25045ae70bad7a3dbba0b27a9a3cdd9bcf0a1b7baec" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8c7f5e4af5a84d2e96c862b1a65e958a538237e268d5f8203a3a784340975b51" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:96183e2b44afc3f9a761e9d0f985c3b44e03e8bb98e626241a6cbfb3b6f7e88f" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-win32.whl", hash = "sha256:2250e8424c565a88573f7dc10659a0b92802e68c2a1d57e41872c9b88ccea7a6" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-win_amd64.whl", hash = "sha256:217d1fa29de14d9c567d616ead7cb0fef33cde36010edff5a9390b00d52e5094" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:2d6de1cc23bdc1baafc23e201b1e48c617b8c1418b4d8e34cebf72141676e5fb" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:2afeaad88040894c76656202ff832cb151bceb05c0e6907e539d129188b1e456" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:33cc593735c93c03d63efe7a8ba25f3c66f16c52f0651910712490244facad72" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2921d7989c4d83b71f03130ab415fa4d66e6693b8b8a1fcbb7a1c67cff19b812" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e6a0df438e82c804c7b95e3f311c97c2f876dcc36376488d5b736b7bcf5a9b45" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9c6070a9500798225191ef25d0055a15d2c01c9c8f2ee7b681fffa99c98c822" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:394e8b57d85370a62e5b0a4d64c96fcf7568345c345d8590c821814d227ecf1d" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3ef700293ab375e111a2909d87434ed0a0b086adf0ce67a8d9cf12ea7765e63" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-win32.whl", hash = "sha256:6993b960fec43a8d840ee5dc20247ef206c1a19587ea49fe5e6cc3d2a09c1585" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-win_amd64.whl", hash = "sha256:275ce3c2978842a8cf9dd88dce954e836e590cf7029649ad5d1145b779039ed5" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:8b080d0d072e6032708a3a91731b808074d7ab02ca8fb9847b6a011fdce64cd9" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8c0ad8f8f133145cd7008b49cb611a5c6a9d89ab276c28afa17050516e801f79" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2f8ea092a7de74c6359335d36f0674d939a3c7e1a550f4c2c9e80e0226de8fe4" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:da422985e0cac822b41822f43429c19ecb27c81ffe3126d0b74e77edec452608" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4fab1faa3fbcb246263e68da7a8177d73772283f9db063fb8008517480888d26" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dd9c094f73f734becae3f20f27d4944d3cd8fb68db7338ee6c58e62fc5c3d99f" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:2ed51ce6b833068f6c580b73193fc2ec16468e6bc18354bc2f83a58721195a58" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:05803a5cdafe77c8bdf36aa660ad7a6a1d9e49bc59ce45c1bade2a4698826599" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-win32.whl", hash = "sha256:f7c722e9ce6f11149ac5bddd5056e70aaccfd8168e74e9d34d8b8b588c3f5c7c" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-win_amd64.whl", hash = "sha256:7d58ade518b546120ec8f0a8e006fc8076ae5df151250ebd7e82e9b5e152c229" }, ] [[package]] name = "iniconfig" version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/iniconfig/2.1/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/iniconfig/2.1/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760" }, ] [[package]] name = "maturin" version = "1.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/21/85e8189ca40f97885abc5154950490b821e590fd2aebea0c9bfb92b1c353/maturin-1.12.0.tar.gz", hash = "sha256:170e695ead35d33fa537078deea2a91dead31ee909fac454079a5df006786e01", size = 252430, upload-time = "2026-02-14T08:49:41.794Z" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0.tar.gz", hash = "sha256:170e695ead35d33fa537078deea2a91dead31ee909fac454079a5df006786e01" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/c8/acbcea1290b0b335b6457df80cc946cf81d521261b1a6a3885548fcc7120/maturin-1.12.0-py3-none-linux_armv6l.whl", hash = "sha256:f16d0db5000b37fb9e3ed252b7ffbc021274be34fad9abeaf1b98b723c233e4a", size = 9632654, upload-time = "2026-02-14T08:49:42.879Z" }, - { url = "https://files.pythonhosted.org/packages/6e/61/6bd544ca1e3c58300dbbdc5449b3de99e7638044039eeae1af5bbc814390/maturin-1.12.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d1724aee0fecc39cc74f8cff53936f0a6c34d1878caa5e407d568ad8f56d674b", size = 18839071, upload-time = "2026-02-14T08:49:19.859Z" }, - { url = "https://files.pythonhosted.org/packages/74/63/e48f5057248b597aa8528a64aaef4828e87a23ad9b924387d54060a030bb/maturin-1.12.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c0b054ee615f2ba7e2219ead94831f8fa7abeef34b09aa3f4fbdca3553c74dc1", size = 9737439, upload-time = "2026-02-14T08:49:28.605Z" }, - { url = "https://files.pythonhosted.org/packages/20/f3/39c47bcda041d71aee597147e4237197b878164bfe30533b3ea30f82c796/maturin-1.12.0-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:e1046cad6d7bde0d6fa592857f805b4a5101db39a720407af01aa7ff439650b9", size = 9684568, upload-time = "2026-02-14T08:49:35.599Z" }, - { url = "https://files.pythonhosted.org/packages/4a/b5/105d230350fe011f885d8e6fcecdb326999924d343137a1c14c756324710/maturin-1.12.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:6df159ac1520621cc9750a0d37ef09c0444b5983c7adc012bb0029e3d5a2a095", size = 10183157, upload-time = "2026-02-14T08:49:30.884Z" }, - { url = "https://files.pythonhosted.org/packages/04/19/fc170394256aafeabf97622b1ef286f521984940b5483b018f473dbaea32/maturin-1.12.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:978249d5dcf26eaad9440f9d87d691b0b55dbef0e45abf85cae2072b80fb0074", size = 9558768, upload-time = "2026-02-14T08:49:37.445Z" }, - { url = "https://files.pythonhosted.org/packages/46/4a/7bdd7ccbb0bb71da2122d0d5992efff5a55ff3e5ac60eba1fa16f28867eb/maturin-1.12.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:b12a5daf91d16db44381f5338a31f308dfcf85524321f72cad2428a5e0a806c6", size = 9467723, upload-time = "2026-02-14T08:49:45.178Z" }, - { url = "https://files.pythonhosted.org/packages/ff/93/8a73b927c7f710bc1aafae7a195ee44da6ad0e42ba73db79f3258569deab/maturin-1.12.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:d5fa15c769b56f98e2f8fded6dfe5a2c19680a340213e3fb28c8dd507bec85cb", size = 12557142, upload-time = "2026-02-14T08:49:47.438Z" }, - { url = "https://files.pythonhosted.org/packages/37/42/32f78aa187babf815ffee86f2d9dfeb66d596aa3e47a8163017cdb34cc2f/maturin-1.12.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d9d9cf7d4a3e043336ae2ad01c5e9e6a7062be43c8f09b0cd28dc5f6dce742b", size = 10301378, upload-time = "2026-02-14T08:49:33.126Z" }, - { url = "https://files.pythonhosted.org/packages/ae/5b/be0ff96087f245cacaf360ec94e6bea4c76055d87e230f9a16f8bf209449/maturin-1.12.0-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:45a5078c3c2f02b2885e38a2735eb9dd0c6e9dd06ba70c05845d5cd5ad7128db", size = 10008035, upload-time = "2026-02-14T08:49:26.236Z" }, - { url = "https://files.pythonhosted.org/packages/10/6e/cb255296223a38c3b48243dacc93343ee6473d37b5d938359f6bf858fcb1/maturin-1.12.0-py3-none-win32.whl", hash = "sha256:1eeed88f3021d15c426490af49198e8da82a34ace1a15d41dfc8fffa5e4c2967", size = 8468757, upload-time = "2026-02-14T08:49:24.542Z" }, - { url = "https://files.pythonhosted.org/packages/96/77/697dd0d6ca69728c31e59ef217a08128fa63656d003adfcd82d4e3d7ffd0/maturin-1.12.0-py3-none-win_amd64.whl", hash = "sha256:976519fd01354025da4b494d4ee9ca697ef296e7add8e0b5f2eb199da9275ee7", size = 9813092, upload-time = "2026-02-14T08:49:39.819Z" }, - { url = "https://files.pythonhosted.org/packages/c6/79/780b0af1780080780f618d2e095ce2192047bd6ae631a382e86a8d632d98/maturin-1.12.0-py3-none-win_arm64.whl", hash = "sha256:38764453c5a77100bd174c467cc1549530cc63f5acfa93abc9ef1c0253489839", size = 8524150, upload-time = "2026-02-14T08:49:22.377Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-linux_armv6l.whl", hash = "sha256:f16d0db5000b37fb9e3ed252b7ffbc021274be34fad9abeaf1b98b723c233e4a" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d1724aee0fecc39cc74f8cff53936f0a6c34d1878caa5e407d568ad8f56d674b" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c0b054ee615f2ba7e2219ead94831f8fa7abeef34b09aa3f4fbdca3553c74dc1" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:e1046cad6d7bde0d6fa592857f805b4a5101db39a720407af01aa7ff439650b9" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:6df159ac1520621cc9750a0d37ef09c0444b5983c7adc012bb0029e3d5a2a095" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:978249d5dcf26eaad9440f9d87d691b0b55dbef0e45abf85cae2072b80fb0074" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:b12a5daf91d16db44381f5338a31f308dfcf85524321f72cad2428a5e0a806c6" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:d5fa15c769b56f98e2f8fded6dfe5a2c19680a340213e3fb28c8dd507bec85cb" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d9d9cf7d4a3e043336ae2ad01c5e9e6a7062be43c8f09b0cd28dc5f6dce742b" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:45a5078c3c2f02b2885e38a2735eb9dd0c6e9dd06ba70c05845d5cd5ad7128db" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-win32.whl", hash = "sha256:1eeed88f3021d15c426490af49198e8da82a34ace1a15d41dfc8fffa5e4c2967" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-win_amd64.whl", hash = "sha256:976519fd01354025da4b494d4ee9ca697ef296e7add8e0b5f2eb199da9275ee7" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-win_arm64.whl", hash = "sha256:38764453c5a77100bd174c467cc1549530cc63f5acfa93abc9ef1c0253489839" }, ] [[package]] name = "packaging" version = "25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/packaging/25/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/packaging/25/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484" }, ] [[package]] name = "pluggy" version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pluggy/1.6/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pluggy/1.6/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746" }, ] [[package]] name = "protobuf" version = "6.33.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/66/70/e908e9c5e52ef7c3a6c7902c9dfbb34c7e29c25d2f81ade3856445fd5c94/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135", size = 444531, upload-time = "2026-03-18T19:05:00.988Z" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/protobuf/6.33.6/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/9f/2f509339e89cfa6f6a4c4ff50438db9ca488dec341f7e454adad60150b00/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3", size = 425739, upload-time = "2026-03-18T19:04:48.373Z" }, - { url = "https://files.pythonhosted.org/packages/76/5d/683efcd4798e0030c1bab27374fd13a89f7c2515fb1f3123efdfaa5eab57/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326", size = 437089, upload-time = "2026-03-18T19:04:50.381Z" }, - { url = "https://files.pythonhosted.org/packages/5c/01/a3c3ed5cd186f39e7880f8303cc51385a198a81469d53d0fdecf1f64d929/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a", size = 427737, upload-time = "2026-03-18T19:04:51.866Z" }, - { url = "https://files.pythonhosted.org/packages/ee/90/b3c01fdec7d2f627b3a6884243ba328c1217ed2d978def5c12dc50d328a3/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2", size = 324610, upload-time = "2026-03-18T19:04:53.096Z" }, - { url = "https://files.pythonhosted.org/packages/9b/ca/25afc144934014700c52e05103c2421997482d561f3101ff352e1292fb81/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3", size = 339381, upload-time = "2026-03-18T19:04:54.616Z" }, - { url = "https://files.pythonhosted.org/packages/16/92/d1e32e3e0d894fe00b15ce28ad4944ab692713f2e7f0a99787405e43533a/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593", size = 323436, upload-time = "2026-03-18T19:04:55.768Z" }, - { url = "https://files.pythonhosted.org/packages/c4/72/02445137af02769918a93807b2b7890047c32bfb9f90371cbc12688819eb/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901", size = 170656, upload-time = "2026-03-18T19:04:59.826Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/protobuf/6.33.6/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/protobuf/6.33.6/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/protobuf/6.33.6/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/protobuf/6.33.6/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/protobuf/6.33.6/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/protobuf/6.33.6/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/protobuf/6.33.6/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901" }, ] [[package]] name = "pydantic" version = "2.12.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c3/da/b8a7ee04378a53f6fefefc0c5e05570a3ebfdfa0523a878bcd3b475683ee/pydantic-2.12.0.tar.gz", hash = "sha256:c1a077e6270dbfb37bfd8b498b3981e2bb18f68103720e51fa6c306a5a9af563", size = 814760, upload-time = "2025-10-07T15:58:03.467Z" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic/2.12/pydantic-2.12.0.tar.gz", hash = "sha256:c1a077e6270dbfb37bfd8b498b3981e2bb18f68103720e51fa6c306a5a9af563" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/9d/d5c855424e2e5b6b626fbc6ec514d8e655a600377ce283008b115abb7445/pydantic-2.12.0-py3-none-any.whl", hash = "sha256:f6a1da352d42790537e95e83a8bdfb91c7efbae63ffd0b86fa823899e807116f", size = 459730, upload-time = "2025-10-07T15:58:01.576Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic/2.12/pydantic-2.12.0-py3-none-any.whl", hash = "sha256:f6a1da352d42790537e95e83a8bdfb91c7efbae63ffd0b86fa823899e807116f" }, ] [[package]] name = "pydantic-core" version = "2.41.1" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/14/12b4a0d2b0b10d8e1d9a24ad94e7bbb43335eaf29c0c4e57860e8a30734a/pydantic_core-2.41.1.tar.gz", hash = "sha256:1ad375859a6d8c356b7704ec0f547a58e82ee80bb41baa811ad710e124bc8f2f", size = 454870, upload-time = "2025-10-07T10:50:45.974Z" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1.tar.gz", hash = "sha256:1ad375859a6d8c356b7704ec0f547a58e82ee80bb41baa811ad710e124bc8f2f" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/a9/ec440f02e57beabdfd804725ef1e38ac1ba00c49854d298447562e119513/pydantic_core-2.41.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4f276a6134fe1fc1daa692642a3eaa2b7b858599c49a7610816388f5e37566a1", size = 2111456, upload-time = "2025-10-06T21:10:09.824Z" }, - { url = "https://files.pythonhosted.org/packages/f0/f9/6bc15bacfd8dcfc073a1820a564516d9c12a435a9a332d4cbbfd48828ddd/pydantic_core-2.41.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07588570a805296ece009c59d9a679dc08fab72fb337365afb4f3a14cfbfc176", size = 1915012, upload-time = "2025-10-06T21:10:11.599Z" }, - { url = "https://files.pythonhosted.org/packages/38/8a/d9edcdcdfe80bade17bed424284427c08bea892aaec11438fa52eaeaf79c/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28527e4b53400cd60ffbd9812ccb2b5135d042129716d71afd7e45bf42b855c0", size = 1973762, upload-time = "2025-10-06T21:10:13.154Z" }, - { url = "https://files.pythonhosted.org/packages/d5/b3/ff225c6d49fba4279de04677c1c876fc3dc6562fd0c53e9bfd66f58c51a8/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46a1c935c9228bad738c8a41de06478770927baedf581d172494ab36a6b96575", size = 2065386, upload-time = "2025-10-06T21:10:14.436Z" }, - { url = "https://files.pythonhosted.org/packages/47/ba/183e8c0be4321314af3fd1ae6bfc7eafdd7a49bdea5da81c56044a207316/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:447ddf56e2b7d28d200d3e9eafa936fe40485744b5a824b67039937580b3cb20", size = 2252317, upload-time = "2025-10-06T21:10:15.719Z" }, - { url = "https://files.pythonhosted.org/packages/57/c5/aab61e94fd02f45c65f1f8c9ec38bb3b33fbf001a1837c74870e97462572/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63892ead40c1160ac860b5debcc95c95c5a0035e543a8b5a4eac70dd22e995f4", size = 2373405, upload-time = "2025-10-06T21:10:17.017Z" }, - { url = "https://files.pythonhosted.org/packages/e5/4f/3aaa3bd1ea420a15acc42d7d3ccb3b0bbc5444ae2f9dbc1959f8173e16b8/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4a9543ca355e6df8fbe9c83e9faab707701e9103ae857ecb40f1c0cf8b0e94d", size = 2073794, upload-time = "2025-10-06T21:10:18.383Z" }, - { url = "https://files.pythonhosted.org/packages/58/bd/e3975cdebe03ec080ef881648de316c73f2a6be95c14fc4efb2f7bdd0d41/pydantic_core-2.41.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2611bdb694116c31e551ed82e20e39a90bea9b7ad9e54aaf2d045ad621aa7a1", size = 2194430, upload-time = "2025-10-06T21:10:19.638Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b8/6b7e7217f147d3b3105b57fb1caec3c4f667581affdfaab6d1d277e1f749/pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fecc130893a9b5f7bfe230be1bb8c61fe66a19db8ab704f808cb25a82aad0bc9", size = 2154611, upload-time = "2025-10-06T21:10:21.28Z" }, - { url = "https://files.pythonhosted.org/packages/fe/7b/239c2fe76bd8b7eef9ae2140d737368a3c6fea4fd27f8f6b4cde6baa3ce9/pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:1e2df5f8344c99b6ea5219f00fdc8950b8e6f2c422fbc1cc122ec8641fac85a1", size = 2329809, upload-time = "2025-10-06T21:10:22.678Z" }, - { url = "https://files.pythonhosted.org/packages/bd/2e/77a821a67ff0786f2f14856d6bd1348992f695ee90136a145d7a445c1ff6/pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:35291331e9d8ed94c257bab6be1cb3a380b5eee570a2784bffc055e18040a2ea", size = 2327907, upload-time = "2025-10-06T21:10:24.447Z" }, - { url = "https://files.pythonhosted.org/packages/fd/9a/b54512bb9df7f64c586b369328c30481229b70ca6a5fcbb90b715e15facf/pydantic_core-2.41.1-cp311-cp311-win32.whl", hash = "sha256:2876a095292668d753f1a868c4a57c4ac9f6acbd8edda8debe4218d5848cf42f", size = 1989964, upload-time = "2025-10-06T21:10:25.676Z" }, - { url = "https://files.pythonhosted.org/packages/9d/72/63c9a4f1a5c950e65dd522d7dd67f167681f9d4f6ece3b80085a0329f08f/pydantic_core-2.41.1-cp311-cp311-win_amd64.whl", hash = "sha256:b92d6c628e9a338846a28dfe3fcdc1a3279388624597898b105e078cdfc59298", size = 2025158, upload-time = "2025-10-06T21:10:27.522Z" }, - { url = "https://files.pythonhosted.org/packages/d8/16/4e2706184209f61b50c231529257c12eb6bd9eb36e99ea1272e4815d2200/pydantic_core-2.41.1-cp311-cp311-win_arm64.whl", hash = "sha256:7d82ae99409eb69d507a89835488fb657faa03ff9968a9379567b0d2e2e56bc5", size = 1972297, upload-time = "2025-10-06T21:10:28.814Z" }, - { url = "https://files.pythonhosted.org/packages/ee/bc/5f520319ee1c9e25010412fac4154a72e0a40d0a19eb00281b1f200c0947/pydantic_core-2.41.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:db2f82c0ccbce8f021ad304ce35cbe02aa2f95f215cac388eed542b03b4d5eb4", size = 2099300, upload-time = "2025-10-06T21:10:30.463Z" }, - { url = "https://files.pythonhosted.org/packages/31/14/010cd64c5c3814fb6064786837ec12604be0dd46df3327cf8474e38abbbd/pydantic_core-2.41.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47694a31c710ced9205d5f1e7e8af3ca57cbb8a503d98cb9e33e27c97a501601", size = 1910179, upload-time = "2025-10-06T21:10:31.782Z" }, - { url = "https://files.pythonhosted.org/packages/8e/2e/23fc2a8a93efad52df302fdade0a60f471ecc0c7aac889801ac24b4c07d6/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e9decce94daf47baf9e9d392f5f2557e783085f7c5e522011545d9d6858e00", size = 1957225, upload-time = "2025-10-06T21:10:33.11Z" }, - { url = "https://files.pythonhosted.org/packages/b9/b6/6db08b2725b2432b9390844852e11d320281e5cea8a859c52c68001975fa/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab0adafdf2b89c8b84f847780a119437a0931eca469f7b44d356f2b426dd9741", size = 2053315, upload-time = "2025-10-06T21:10:34.87Z" }, - { url = "https://files.pythonhosted.org/packages/61/d9/4de44600f2d4514b44f3f3aeeda2e14931214b6b5bf52479339e801ce748/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5da98cc81873f39fd56882e1569c4677940fbc12bce6213fad1ead784192d7c8", size = 2224298, upload-time = "2025-10-06T21:10:36.233Z" }, - { url = "https://files.pythonhosted.org/packages/7a/ae/dbe51187a7f35fc21b283c5250571a94e36373eb557c1cba9f29a9806dcf/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:209910e88afb01fd0fd403947b809ba8dba0e08a095e1f703294fda0a8fdca51", size = 2351797, upload-time = "2025-10-06T21:10:37.601Z" }, - { url = "https://files.pythonhosted.org/packages/b5/a7/975585147457c2e9fb951c7c8dab56deeb6aa313f3aa72c2fc0df3f74a49/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365109d1165d78d98e33c5bfd815a9b5d7d070f578caefaabcc5771825b4ecb5", size = 2074921, upload-time = "2025-10-06T21:10:38.927Z" }, - { url = "https://files.pythonhosted.org/packages/62/37/ea94d1d0c01dec1b7d236c7cec9103baab0021f42500975de3d42522104b/pydantic_core-2.41.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:706abf21e60a2857acdb09502bc853ee5bce732955e7b723b10311114f033115", size = 2187767, upload-time = "2025-10-06T21:10:40.651Z" }, - { url = "https://files.pythonhosted.org/packages/d3/fe/694cf9fdd3a777a618c3afd210dba7b414cb8a72b1bd29b199c2e5765fee/pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bf0bd5417acf7f6a7ec3b53f2109f587be176cb35f9cf016da87e6017437a72d", size = 2136062, upload-time = "2025-10-06T21:10:42.09Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ae/174aeabd89916fbd2988cc37b81a59e1186e952afd2a7ed92018c22f31ca/pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2e71b1c6ceb9c78424ae9f63a07292fb769fb890a4e7efca5554c47f33a60ea5", size = 2317819, upload-time = "2025-10-06T21:10:43.974Z" }, - { url = "https://files.pythonhosted.org/packages/65/e8/e9aecafaebf53fc456314f72886068725d6fba66f11b013532dc21259343/pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80745b9770b4a38c25015b517451c817799bfb9d6499b0d13d8227ec941cb513", size = 2312267, upload-time = "2025-10-06T21:10:45.34Z" }, - { url = "https://files.pythonhosted.org/packages/35/2f/1c2e71d2a052f9bb2f2df5a6a05464a0eb800f9e8d9dd800202fe31219e1/pydantic_core-2.41.1-cp312-cp312-win32.whl", hash = "sha256:83b64d70520e7890453f1aa21d66fda44e7b35f1cfea95adf7b4289a51e2b479", size = 1990927, upload-time = "2025-10-06T21:10:46.738Z" }, - { url = "https://files.pythonhosted.org/packages/b1/78/562998301ff2588b9c6dcc5cb21f52fa919d6e1decc75a35055feb973594/pydantic_core-2.41.1-cp312-cp312-win_amd64.whl", hash = "sha256:377defd66ee2003748ee93c52bcef2d14fde48fe28a0b156f88c3dbf9bc49a50", size = 2034703, upload-time = "2025-10-06T21:10:48.524Z" }, - { url = "https://files.pythonhosted.org/packages/b2/53/d95699ce5a5cdb44bb470bd818b848b9beadf51459fd4ea06667e8ede862/pydantic_core-2.41.1-cp312-cp312-win_arm64.whl", hash = "sha256:c95caff279d49c1d6cdfe2996e6c2ad712571d3b9caaa209a404426c326c4bde", size = 1972719, upload-time = "2025-10-06T21:10:50.256Z" }, - { url = "https://files.pythonhosted.org/packages/27/8a/6d54198536a90a37807d31a156642aae7a8e1263ed9fe6fc6245defe9332/pydantic_core-2.41.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70e790fce5f05204ef4403159857bfcd587779da78627b0babb3654f75361ebf", size = 2105825, upload-time = "2025-10-06T21:10:51.719Z" }, - { url = "https://files.pythonhosted.org/packages/4f/2e/4784fd7b22ac9c8439db25bf98ffed6853d01e7e560a346e8af821776ccc/pydantic_core-2.41.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9cebf1ca35f10930612d60bd0f78adfacee824c30a880e3534ba02c207cceceb", size = 1910126, upload-time = "2025-10-06T21:10:53.145Z" }, - { url = "https://files.pythonhosted.org/packages/f3/92/31eb0748059ba5bd0aa708fb4bab9fcb211461ddcf9e90702a6542f22d0d/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:170406a37a5bc82c22c3274616bf6f17cc7df9c4a0a0a50449e559cb755db669", size = 1961472, upload-time = "2025-10-06T21:10:55.754Z" }, - { url = "https://files.pythonhosted.org/packages/ab/91/946527792275b5c4c7dde4cfa3e81241bf6900e9fee74fb1ba43e0c0f1ab/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12d4257fc9187a0ccd41b8b327d6a4e57281ab75e11dda66a9148ef2e1fb712f", size = 2063230, upload-time = "2025-10-06T21:10:57.179Z" }, - { url = "https://files.pythonhosted.org/packages/31/5d/a35c5d7b414e5c0749f1d9f0d159ee2ef4bab313f499692896b918014ee3/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a75a33b4db105dd1c8d57839e17ee12db8d5ad18209e792fa325dbb4baeb00f4", size = 2229469, upload-time = "2025-10-06T21:10:59.409Z" }, - { url = "https://files.pythonhosted.org/packages/21/4d/8713737c689afa57ecfefe38db78259d4484c97aa494979e6a9d19662584/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08a589f850803a74e0fcb16a72081cafb0d72a3cdda500106942b07e76b7bf62", size = 2347986, upload-time = "2025-10-06T21:11:00.847Z" }, - { url = "https://files.pythonhosted.org/packages/f6/ec/929f9a3a5ed5cda767081494bacd32f783e707a690ce6eeb5e0730ec4986/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a97939d6ea44763c456bd8a617ceada2c9b96bb5b8ab3dfa0d0827df7619014", size = 2072216, upload-time = "2025-10-06T21:11:02.43Z" }, - { url = "https://files.pythonhosted.org/packages/26/55/a33f459d4f9cc8786d9db42795dbecc84fa724b290d7d71ddc3d7155d46a/pydantic_core-2.41.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae423c65c556f09569524b80ffd11babff61f33055ef9773d7c9fabc11ed8d", size = 2193047, upload-time = "2025-10-06T21:11:03.787Z" }, - { url = "https://files.pythonhosted.org/packages/77/af/d5c6959f8b089f2185760a2779079e3c2c411bfc70ea6111f58367851629/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:4dc703015fbf8764d6a8001c327a87f1823b7328d40b47ce6000c65918ad2b4f", size = 2140613, upload-time = "2025-10-06T21:11:05.607Z" }, - { url = "https://files.pythonhosted.org/packages/58/e5/2c19bd2a14bffe7fabcf00efbfbd3ac430aaec5271b504a938ff019ac7be/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:968e4ffdfd35698a5fe659e5e44c508b53664870a8e61c8f9d24d3d145d30257", size = 2327641, upload-time = "2025-10-06T21:11:07.143Z" }, - { url = "https://files.pythonhosted.org/packages/93/ef/e0870ccda798c54e6b100aff3c4d49df5458fd64217e860cb9c3b0a403f4/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:fff2b76c8e172d34771cd4d4f0ade08072385310f214f823b5a6ad4006890d32", size = 2318229, upload-time = "2025-10-06T21:11:08.73Z" }, - { url = "https://files.pythonhosted.org/packages/b1/4b/c3b991d95f5deb24d0bd52e47bcf716098fa1afe0ce2d4bd3125b38566ba/pydantic_core-2.41.1-cp313-cp313-win32.whl", hash = "sha256:a38a5263185407ceb599f2f035faf4589d57e73c7146d64f10577f6449e8171d", size = 1997911, upload-time = "2025-10-06T21:11:10.329Z" }, - { url = "https://files.pythonhosted.org/packages/a7/ce/5c316fd62e01f8d6be1b7ee6b54273214e871772997dc2c95e204997a055/pydantic_core-2.41.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42ae7fd6760782c975897e1fdc810f483b021b32245b0105d40f6e7a3803e4b", size = 2034301, upload-time = "2025-10-06T21:11:12.113Z" }, - { url = "https://files.pythonhosted.org/packages/29/41/902640cfd6a6523194123e2c3373c60f19006447f2fb06f76de4e8466c5b/pydantic_core-2.41.1-cp313-cp313-win_arm64.whl", hash = "sha256:ad4111acc63b7384e205c27a2f15e23ac0ee21a9d77ad6f2e9cb516ec90965fb", size = 1977238, upload-time = "2025-10-06T21:11:14.1Z" }, - { url = "https://files.pythonhosted.org/packages/04/04/28b040e88c1b89d851278478842f0bdf39c7a05da9e850333c6c8cbe7dfa/pydantic_core-2.41.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:440d0df7415b50084a4ba9d870480c16c5f67c0d1d4d5119e3f70925533a0edc", size = 1875626, upload-time = "2025-10-06T21:11:15.69Z" }, - { url = "https://files.pythonhosted.org/packages/d6/58/b41dd3087505220bb58bc81be8c3e8cbc037f5710cd3c838f44f90bdd704/pydantic_core-2.41.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71eaa38d342099405dae6484216dcf1e8e4b0bebd9b44a4e08c9b43db6a2ab67", size = 2045708, upload-time = "2025-10-06T21:11:17.258Z" }, - { url = "https://files.pythonhosted.org/packages/d7/b8/760f23754e40bf6c65b94a69b22c394c24058a0ef7e2aa471d2e39219c1a/pydantic_core-2.41.1-cp313-cp313t-win_amd64.whl", hash = "sha256:555ecf7e50f1161d3f693bc49f23c82cf6cdeafc71fa37a06120772a09a38795", size = 1997171, upload-time = "2025-10-06T21:11:18.822Z" }, - { url = "https://files.pythonhosted.org/packages/41/12/cec246429ddfa2778d2d6301eca5362194dc8749ecb19e621f2f65b5090f/pydantic_core-2.41.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:05226894a26f6f27e1deb735d7308f74ef5fa3a6de3e0135bb66cdcaee88f64b", size = 2107836, upload-time = "2025-10-06T21:11:20.432Z" }, - { url = "https://files.pythonhosted.org/packages/20/39/baba47f8d8b87081302498e610aefc37142ce6a1cc98b2ab6b931a162562/pydantic_core-2.41.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:85ff7911c6c3e2fd8d3779c50925f6406d770ea58ea6dde9c230d35b52b16b4a", size = 1904449, upload-time = "2025-10-06T21:11:22.185Z" }, - { url = "https://files.pythonhosted.org/packages/50/32/9a3d87cae2c75a5178334b10358d631bd094b916a00a5993382222dbfd92/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47f1f642a205687d59b52dc1a9a607f45e588f5a2e9eeae05edd80c7a8c47674", size = 1961750, upload-time = "2025-10-06T21:11:24.348Z" }, - { url = "https://files.pythonhosted.org/packages/27/42/a96c9d793a04cf2a9773bff98003bb154087b94f5530a2ce6063ecfec583/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df11c24e138876ace5ec6043e5cae925e34cf38af1a1b3d63589e8f7b5f5cdc4", size = 2063305, upload-time = "2025-10-06T21:11:26.556Z" }, - { url = "https://files.pythonhosted.org/packages/3e/8d/028c4b7d157a005b1f52c086e2d4b0067886b213c86220c1153398dbdf8f/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f0bf7f5c8f7bf345c527e8a0d72d6b26eda99c1227b0c34e7e59e181260de31", size = 2228959, upload-time = "2025-10-06T21:11:28.426Z" }, - { url = "https://files.pythonhosted.org/packages/08/f7/ee64cda8fcc9ca3f4716e6357144f9ee71166775df582a1b6b738bf6da57/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82b887a711d341c2c47352375d73b029418f55b20bd7815446d175a70effa706", size = 2345421, upload-time = "2025-10-06T21:11:30.226Z" }, - { url = "https://files.pythonhosted.org/packages/13/c0/e8ec05f0f5ee7a3656973ad9cd3bc73204af99f6512c1a4562f6fb4b3f7d/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5f1d5d6bbba484bdf220c72d8ecd0be460f4bd4c5e534a541bb2cd57589fb8b", size = 2065288, upload-time = "2025-10-06T21:11:32.019Z" }, - { url = "https://files.pythonhosted.org/packages/0a/25/d77a73ff24e2e4fcea64472f5e39b0402d836da9b08b5361a734d0153023/pydantic_core-2.41.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bf1917385ebe0f968dc5c6ab1375886d56992b93ddfe6bf52bff575d03662be", size = 2189759, upload-time = "2025-10-06T21:11:33.753Z" }, - { url = "https://files.pythonhosted.org/packages/66/45/4a4ebaaae12a740552278d06fe71418c0f2869537a369a89c0e6723b341d/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:4f94f3ab188f44b9a73f7295663f3ecb8f2e2dd03a69c8f2ead50d37785ecb04", size = 2140747, upload-time = "2025-10-06T21:11:35.781Z" }, - { url = "https://files.pythonhosted.org/packages/da/6d/b727ce1022f143194a36593243ff244ed5a1eb3c9122296bf7e716aa37ba/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:3925446673641d37c30bd84a9d597e49f72eacee8b43322c8999fa17d5ae5bc4", size = 2327416, upload-time = "2025-10-06T21:11:37.75Z" }, - { url = "https://files.pythonhosted.org/packages/6f/8c/02df9d8506c427787059f87c6c7253435c6895e12472a652d9616ee0fc95/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:49bd51cc27adb980c7b97357ae036ce9b3c4d0bb406e84fbe16fb2d368b602a8", size = 2318138, upload-time = "2025-10-06T21:11:39.463Z" }, - { url = "https://files.pythonhosted.org/packages/98/67/0cf429a7d6802536941f430e6e3243f6d4b68f41eeea4b242372f1901794/pydantic_core-2.41.1-cp314-cp314-win32.whl", hash = "sha256:a31ca0cd0e4d12ea0df0077df2d487fc3eb9d7f96bbb13c3c5b88dcc21d05159", size = 1998429, upload-time = "2025-10-06T21:11:41.989Z" }, - { url = "https://files.pythonhosted.org/packages/38/60/742fef93de5d085022d2302a6317a2b34dbfe15258e9396a535c8a100ae7/pydantic_core-2.41.1-cp314-cp314-win_amd64.whl", hash = "sha256:1b5c4374a152e10a22175d7790e644fbd8ff58418890e07e2073ff9d4414efae", size = 2028870, upload-time = "2025-10-06T21:11:43.66Z" }, - { url = "https://files.pythonhosted.org/packages/31/38/cdd8ccb8555ef7720bd7715899bd6cfbe3c29198332710e1b61b8f5dd8b8/pydantic_core-2.41.1-cp314-cp314-win_arm64.whl", hash = "sha256:4fee76d757639b493eb600fba668f1e17475af34c17dd61db7a47e824d464ca9", size = 1974275, upload-time = "2025-10-06T21:11:45.476Z" }, - { url = "https://files.pythonhosted.org/packages/e7/7e/8ac10ccb047dc0221aa2530ec3c7c05ab4656d4d4bd984ee85da7f3d5525/pydantic_core-2.41.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f9b9c968cfe5cd576fdd7361f47f27adeb120517e637d1b189eea1c3ece573f4", size = 1875124, upload-time = "2025-10-06T21:11:47.591Z" }, - { url = "https://files.pythonhosted.org/packages/c3/e4/7d9791efeb9c7d97e7268f8d20e0da24d03438a7fa7163ab58f1073ba968/pydantic_core-2.41.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ebc7ab67b856384aba09ed74e3e977dded40e693de18a4f197c67d0d4e6d8e", size = 2043075, upload-time = "2025-10-06T21:11:49.542Z" }, - { url = "https://files.pythonhosted.org/packages/2d/c3/3f6e6b2342ac11ac8cd5cb56e24c7b14afa27c010e82a765ffa5f771884a/pydantic_core-2.41.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8ae0dc57b62a762985bc7fbf636be3412394acc0ddb4ade07fe104230f1b9762", size = 1995341, upload-time = "2025-10-06T21:11:51.497Z" }, - { url = "https://files.pythonhosted.org/packages/16/89/d0afad37ba25f5801735af1472e650b86baad9fe807a42076508e4824a2a/pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:68f2251559b8efa99041bb63571ec7cdd2d715ba74cc82b3bc9eff824ebc8bf0", size = 2124001, upload-time = "2025-10-07T10:49:54.369Z" }, - { url = "https://files.pythonhosted.org/packages/8e/c4/08609134b34520568ddebb084d9ed0a2a3f5f52b45739e6e22cb3a7112eb/pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:c7bc140c596097cb53b30546ca257dbe3f19282283190b1b5142928e5d5d3a20", size = 1941841, upload-time = "2025-10-07T10:49:56.248Z" }, - { url = "https://files.pythonhosted.org/packages/2a/43/94a4877094e5fe19a3f37e7e817772263e2c573c94f1e3fa2b1eee56ef3b/pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2896510fce8f4725ec518f8b9d7f015a00db249d2fd40788f442af303480063d", size = 1961129, upload-time = "2025-10-07T10:49:58.298Z" }, - { url = "https://files.pythonhosted.org/packages/a2/30/23a224d7e25260eb5f69783a63667453037e07eb91ff0e62dabaadd47128/pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ced20e62cfa0f496ba68fa5d6c7ee71114ea67e2a5da3114d6450d7f4683572a", size = 2148770, upload-time = "2025-10-07T10:49:59.959Z" }, - { url = "https://files.pythonhosted.org/packages/2b/3e/a51c5f5d37b9288ba30683d6e96f10fa8f1defad1623ff09f1020973b577/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b04fa9ed049461a7398138c604b00550bc89e3e1151d84b81ad6dc93e39c4c06", size = 2115344, upload-time = "2025-10-07T10:50:02.466Z" }, - { url = "https://files.pythonhosted.org/packages/5a/bd/389504c9e0600ef4502cd5238396b527afe6ef8981a6a15cd1814fc7b434/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b3b7d9cfbfdc43c80a16638c6dc2768e3956e73031fca64e8e1a3ae744d1faeb", size = 1927994, upload-time = "2025-10-07T10:50:04.379Z" }, - { url = "https://files.pythonhosted.org/packages/ff/9c/5111c6b128861cb792a4c082677e90dac4f2e090bb2e2fe06aa5b2d39027/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eec83fc6abef04c7f9bec616e2d76ee9a6a4ae2a359b10c21d0f680e24a247ca", size = 1959394, upload-time = "2025-10-07T10:50:06.335Z" }, - { url = "https://files.pythonhosted.org/packages/14/3f/cfec8b9a0c48ce5d64409ec5e1903cb0b7363da38f14b41de2fcb3712700/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6771a2d9f83c4038dfad5970a3eef215940682b2175e32bcc817bdc639019b28", size = 2147365, upload-time = "2025-10-07T10:50:07.978Z" }, - { url = "https://files.pythonhosted.org/packages/e6/6c/fa3e45c2b054a1e627a89a364917f12cbe3abc3e91b9004edaae16e7b3c5/pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:af2385d3f98243fb733862f806c5bb9122e5fba05b373e3af40e3c82d711cef1", size = 2112094, upload-time = "2025-10-07T10:50:25.513Z" }, - { url = "https://files.pythonhosted.org/packages/e5/17/7eebc38b4658cc8e6902d0befc26388e4c2a5f2e179c561eeb43e1922c7b/pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6550617a0c2115be56f90c31a5370261d8ce9dbf051c3ed53b51172dd34da696", size = 1935300, upload-time = "2025-10-07T10:50:27.715Z" }, - { url = "https://files.pythonhosted.org/packages/2b/00/9fe640194a1717a464ab861d43595c268830f98cb1e2705aa134b3544b70/pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc17b6ecf4983d298686014c92ebc955a9f9baf9f57dad4065e7906e7bee6222", size = 1970417, upload-time = "2025-10-07T10:50:29.573Z" }, - { url = "https://files.pythonhosted.org/packages/b2/ad/f4cdfaf483b78ee65362363e73b6b40c48e067078d7b146e8816d5945ad6/pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42ae9352cf211f08b04ea110563d6b1e415878eea5b4c70f6bdb17dca3b932d2", size = 2190745, upload-time = "2025-10-07T10:50:31.48Z" }, - { url = "https://files.pythonhosted.org/packages/cb/c1/18f416d40a10f44e9387497ba449f40fdb1478c61ba05c4b6bdb82300362/pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e82947de92068b0a21681a13dd2102387197092fbe7defcfb8453e0913866506", size = 2150888, upload-time = "2025-10-07T10:50:33.477Z" }, - { url = "https://files.pythonhosted.org/packages/42/30/134c8a921630d8a88d6f905a562495a6421e959a23c19b0f49b660801d67/pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e244c37d5471c9acdcd282890c6c4c83747b77238bfa19429b8473586c907656", size = 2324489, upload-time = "2025-10-07T10:50:36.48Z" }, - { url = "https://files.pythonhosted.org/packages/9c/48/a9263aeaebdec81e941198525b43edb3b44f27cfa4cb8005b8d3eb8dec72/pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1e798b4b304a995110d41ec93653e57975620ccb2842ba9420037985e7d7284e", size = 2322763, upload-time = "2025-10-07T10:50:38.751Z" }, - { url = "https://files.pythonhosted.org/packages/1d/62/755d2bd2593f701c5839fc084e9c2c5e2418f460383ad04e3b5d0befc3ca/pydantic_core-2.41.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f1fc716c0eb1663c59699b024428ad5ec2bcc6b928527b8fe28de6cb89f47efb", size = 2144046, upload-time = "2025-10-07T10:50:40.686Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4f276a6134fe1fc1daa692642a3eaa2b7b858599c49a7610816388f5e37566a1" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07588570a805296ece009c59d9a679dc08fab72fb337365afb4f3a14cfbfc176" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28527e4b53400cd60ffbd9812ccb2b5135d042129716d71afd7e45bf42b855c0" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46a1c935c9228bad738c8a41de06478770927baedf581d172494ab36a6b96575" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:447ddf56e2b7d28d200d3e9eafa936fe40485744b5a824b67039937580b3cb20" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63892ead40c1160ac860b5debcc95c95c5a0035e543a8b5a4eac70dd22e995f4" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4a9543ca355e6df8fbe9c83e9faab707701e9103ae857ecb40f1c0cf8b0e94d" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2611bdb694116c31e551ed82e20e39a90bea9b7ad9e54aaf2d045ad621aa7a1" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fecc130893a9b5f7bfe230be1bb8c61fe66a19db8ab704f808cb25a82aad0bc9" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:1e2df5f8344c99b6ea5219f00fdc8950b8e6f2c422fbc1cc122ec8641fac85a1" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:35291331e9d8ed94c257bab6be1cb3a380b5eee570a2784bffc055e18040a2ea" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-win32.whl", hash = "sha256:2876a095292668d753f1a868c4a57c4ac9f6acbd8edda8debe4218d5848cf42f" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-win_amd64.whl", hash = "sha256:b92d6c628e9a338846a28dfe3fcdc1a3279388624597898b105e078cdfc59298" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-win_arm64.whl", hash = "sha256:7d82ae99409eb69d507a89835488fb657faa03ff9968a9379567b0d2e2e56bc5" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:db2f82c0ccbce8f021ad304ce35cbe02aa2f95f215cac388eed542b03b4d5eb4" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47694a31c710ced9205d5f1e7e8af3ca57cbb8a503d98cb9e33e27c97a501601" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e9decce94daf47baf9e9d392f5f2557e783085f7c5e522011545d9d6858e00" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab0adafdf2b89c8b84f847780a119437a0931eca469f7b44d356f2b426dd9741" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5da98cc81873f39fd56882e1569c4677940fbc12bce6213fad1ead784192d7c8" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:209910e88afb01fd0fd403947b809ba8dba0e08a095e1f703294fda0a8fdca51" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365109d1165d78d98e33c5bfd815a9b5d7d070f578caefaabcc5771825b4ecb5" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:706abf21e60a2857acdb09502bc853ee5bce732955e7b723b10311114f033115" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bf0bd5417acf7f6a7ec3b53f2109f587be176cb35f9cf016da87e6017437a72d" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2e71b1c6ceb9c78424ae9f63a07292fb769fb890a4e7efca5554c47f33a60ea5" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80745b9770b4a38c25015b517451c817799bfb9d6499b0d13d8227ec941cb513" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-win32.whl", hash = "sha256:83b64d70520e7890453f1aa21d66fda44e7b35f1cfea95adf7b4289a51e2b479" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-win_amd64.whl", hash = "sha256:377defd66ee2003748ee93c52bcef2d14fde48fe28a0b156f88c3dbf9bc49a50" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-win_arm64.whl", hash = "sha256:c95caff279d49c1d6cdfe2996e6c2ad712571d3b9caaa209a404426c326c4bde" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70e790fce5f05204ef4403159857bfcd587779da78627b0babb3654f75361ebf" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9cebf1ca35f10930612d60bd0f78adfacee824c30a880e3534ba02c207cceceb" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:170406a37a5bc82c22c3274616bf6f17cc7df9c4a0a0a50449e559cb755db669" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12d4257fc9187a0ccd41b8b327d6a4e57281ab75e11dda66a9148ef2e1fb712f" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a75a33b4db105dd1c8d57839e17ee12db8d5ad18209e792fa325dbb4baeb00f4" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08a589f850803a74e0fcb16a72081cafb0d72a3cdda500106942b07e76b7bf62" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a97939d6ea44763c456bd8a617ceada2c9b96bb5b8ab3dfa0d0827df7619014" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae423c65c556f09569524b80ffd11babff61f33055ef9773d7c9fabc11ed8d" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:4dc703015fbf8764d6a8001c327a87f1823b7328d40b47ce6000c65918ad2b4f" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:968e4ffdfd35698a5fe659e5e44c508b53664870a8e61c8f9d24d3d145d30257" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:fff2b76c8e172d34771cd4d4f0ade08072385310f214f823b5a6ad4006890d32" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-win32.whl", hash = "sha256:a38a5263185407ceb599f2f035faf4589d57e73c7146d64f10577f6449e8171d" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42ae7fd6760782c975897e1fdc810f483b021b32245b0105d40f6e7a3803e4b" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-win_arm64.whl", hash = "sha256:ad4111acc63b7384e205c27a2f15e23ac0ee21a9d77ad6f2e9cb516ec90965fb" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:440d0df7415b50084a4ba9d870480c16c5f67c0d1d4d5119e3f70925533a0edc" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71eaa38d342099405dae6484216dcf1e8e4b0bebd9b44a4e08c9b43db6a2ab67" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313t-win_amd64.whl", hash = "sha256:555ecf7e50f1161d3f693bc49f23c82cf6cdeafc71fa37a06120772a09a38795" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:05226894a26f6f27e1deb735d7308f74ef5fa3a6de3e0135bb66cdcaee88f64b" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:85ff7911c6c3e2fd8d3779c50925f6406d770ea58ea6dde9c230d35b52b16b4a" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47f1f642a205687d59b52dc1a9a607f45e588f5a2e9eeae05edd80c7a8c47674" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df11c24e138876ace5ec6043e5cae925e34cf38af1a1b3d63589e8f7b5f5cdc4" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f0bf7f5c8f7bf345c527e8a0d72d6b26eda99c1227b0c34e7e59e181260de31" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82b887a711d341c2c47352375d73b029418f55b20bd7815446d175a70effa706" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5f1d5d6bbba484bdf220c72d8ecd0be460f4bd4c5e534a541bb2cd57589fb8b" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bf1917385ebe0f968dc5c6ab1375886d56992b93ddfe6bf52bff575d03662be" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:4f94f3ab188f44b9a73f7295663f3ecb8f2e2dd03a69c8f2ead50d37785ecb04" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:3925446673641d37c30bd84a9d597e49f72eacee8b43322c8999fa17d5ae5bc4" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:49bd51cc27adb980c7b97357ae036ce9b3c4d0bb406e84fbe16fb2d368b602a8" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-win32.whl", hash = "sha256:a31ca0cd0e4d12ea0df0077df2d487fc3eb9d7f96bbb13c3c5b88dcc21d05159" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-win_amd64.whl", hash = "sha256:1b5c4374a152e10a22175d7790e644fbd8ff58418890e07e2073ff9d4414efae" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-win_arm64.whl", hash = "sha256:4fee76d757639b493eb600fba668f1e17475af34c17dd61db7a47e824d464ca9" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f9b9c968cfe5cd576fdd7361f47f27adeb120517e637d1b189eea1c3ece573f4" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ebc7ab67b856384aba09ed74e3e977dded40e693de18a4f197c67d0d4e6d8e" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8ae0dc57b62a762985bc7fbf636be3412394acc0ddb4ade07fe104230f1b9762" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:68f2251559b8efa99041bb63571ec7cdd2d715ba74cc82b3bc9eff824ebc8bf0" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:c7bc140c596097cb53b30546ca257dbe3f19282283190b1b5142928e5d5d3a20" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2896510fce8f4725ec518f8b9d7f015a00db249d2fd40788f442af303480063d" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ced20e62cfa0f496ba68fa5d6c7ee71114ea67e2a5da3114d6450d7f4683572a" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b04fa9ed049461a7398138c604b00550bc89e3e1151d84b81ad6dc93e39c4c06" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b3b7d9cfbfdc43c80a16638c6dc2768e3956e73031fca64e8e1a3ae744d1faeb" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eec83fc6abef04c7f9bec616e2d76ee9a6a4ae2a359b10c21d0f680e24a247ca" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6771a2d9f83c4038dfad5970a3eef215940682b2175e32bcc817bdc639019b28" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:af2385d3f98243fb733862f806c5bb9122e5fba05b373e3af40e3c82d711cef1" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6550617a0c2115be56f90c31a5370261d8ce9dbf051c3ed53b51172dd34da696" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc17b6ecf4983d298686014c92ebc955a9f9baf9f57dad4065e7906e7bee6222" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42ae9352cf211f08b04ea110563d6b1e415878eea5b4c70f6bdb17dca3b932d2" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e82947de92068b0a21681a13dd2102387197092fbe7defcfb8453e0913866506" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e244c37d5471c9acdcd282890c6c4c83747b77238bfa19429b8473586c907656" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1e798b4b304a995110d41ec93653e57975620ccb2842ba9420037985e7d7284e" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f1fc716c0eb1663c59699b024428ad5ec2bcc6b928527b8fe28de6cb89f47efb" }, ] [[package]] name = "pygments" version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pygments/2.20/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pygments/2.20/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176" }, ] [[package]] name = "pytest" version = "9.0.3" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "iniconfig" }, @@ -364,154 +364,154 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pytest/9.0.3/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pytest/9.0.3/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9" }, ] [[package]] name = "pytest-asyncio" version = "1.3.0" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } dependencies = [ { name = "pytest" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pytest-asyncio/1.3/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pytest-asyncio/1.3/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5" }, ] [[package]] name = "pyyaml" version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, - { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, - { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, - { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, - { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, - { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, - { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, - { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, - { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b" }, ] [[package]] name = "setuptools" version = "83.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/34/26/f5d29e25ffdb535afef2d35cdb55b325298f96debd670da4c325e08d70f4/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef", size = 1154254, upload-time = "2026-07-04T15:31:22.699Z" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/setuptools/83/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3", size = 1008090, upload-time = "2026-07-04T15:31:20.885Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/setuptools/83/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3" }, ] [[package]] name = "tomli" version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" }, - { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" }, - { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" }, - { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" }, - { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" }, - { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" }, - { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" }, - { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" }, - { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" }, - { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" }, - { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" }, - { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" }, - { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" }, - { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" }, - { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" }, - { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" }, - { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" }, - { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" }, - { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" }, - { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" }, - { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" }, - { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" }, - { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" }, - { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" }, - { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" }, - { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" }, - { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" }, - { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" }, - { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" }, - { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" }, - { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" }, - { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" }, - { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" }, - { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" }, - { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" }, - { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" }, - { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" }, - { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" }, - { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" }, - { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b" }, ] [[package]] name = "typing-extensions" version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/typing-extensions/4.15/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/typing-extensions/4.15/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548" }, ] [[package]] name = "typing-inspection" version = "0.4.2" -source = { registry = "https://pypi.org/simple" } +source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/typing-inspection/0.4.2/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, + { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/typing-inspection/0.4.2/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7" }, ] From 35f7aaed5816c76fa698ae685649b51e3acd8cc9 Mon Sep 17 00:00:00 2001 From: Brian Krabach Date: Mon, 21 Sep 2026 03:35:28 -0700 Subject: [PATCH 27/27] chore: preserve public registry lock metadata for version bump --- uv.lock | 638 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 319 insertions(+), 319 deletions(-) diff --git a/uv.lock b/uv.lock index 3adcd05..1f55b0f 100644 --- a/uv.lock +++ b/uv.lock @@ -46,317 +46,317 @@ dev = [ [[package]] name = "annotated-types" version = "0.7.0" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/annotated-types/0.7/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/annotated-types/0.7/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53" }, + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] [[package]] name = "click" version = "8.3.1" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/click/8.3.1/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/click/8.3.1/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6" }, + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, ] [[package]] name = "colorama" version = "0.4.6" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/colorama/0.4.6/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/colorama/0.4.6/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] name = "grpcio" version = "1.78.0" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0.tar.gz", hash = "sha256:7382b95189546f375c174f53a5fa873cef91c4b8005faa05cc5b3beea9c4f1c5" } +sdist = { url = "https://files.pythonhosted.org/packages/06/8a/3d098f35c143a89520e568e6539cc098fcd294495910e359889ce8741c84/grpcio-1.78.0.tar.gz", hash = "sha256:7382b95189546f375c174f53a5fa873cef91c4b8005faa05cc5b3beea9c4f1c5", size = 12852416, upload-time = "2026-02-06T09:57:18.093Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2777b783f6c13b92bd7b716667452c329eefd646bfb3f2e9dabea2e05dbd34f6" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:9dca934f24c732750389ce49d638069c3892ad065df86cb465b3fa3012b70c9e" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:459ab414b35f4496138d0ecd735fed26f1318af5e52cb1efbc82a09f0d5aa911" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:082653eecbdf290e6e3e2c276ab2c54b9e7c299e07f4221872380312d8cf395e" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85f93781028ec63f383f6bc90db785a016319c561cc11151fbb7b34e0d012303" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f12857d24d98441af6a1d5c87442d624411db486f7ba12550b07788f74b67b04" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5397fff416b79e4b284959642a4e95ac4b0f1ece82c9993658e0e477d40551ec" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fbe6e89c7ffb48518384068321621b2a69cab509f58e40e4399fdd378fa6d074" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-win32.whl", hash = "sha256:6092beabe1966a3229f599d7088b38dfc8ffa1608b5b5cdda31e591e6500f856" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp311-cp311-win_amd64.whl", hash = "sha256:1afa62af6e23f88629f2b29ec9e52ec7c65a7176c1e0a83292b93c76ca882558" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:f9ab915a267fc47c7e88c387a3a28325b58c898e23d4995f765728f4e3dedb97" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3f8904a8165ab21e07e58bf3e30a73f4dffc7a1e0dbc32d51c61b5360d26f43e" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:859b13906ce098c0b493af92142ad051bf64c7870fa58a123911c88606714996" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b2342d87af32790f934a79c3112641e7b27d63c261b8b4395350dad43eff1dc7" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12a771591ae40bc65ba67048fa52ef4f0e6db8279e595fd349f9dfddeef571f9" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:185dea0d5260cbb2d224c507bf2a5444d5abbb1fa3594c1ed7e4c709d5eb8383" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51b13f9aed9d59ee389ad666b8c2214cc87b5de258fa712f9ab05f922e3896c6" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd5f135b1bd58ab088930b3c613455796dfa0393626a6972663ccdda5b4ac6ce" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-win32.whl", hash = "sha256:94309f498bcc07e5a7d16089ab984d42ad96af1d94b5a4eb966a266d9fcabf68" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp312-cp312-win_amd64.whl", hash = "sha256:9566fe4ababbb2610c39190791e5b829869351d14369603702e890ef3ad2d06e" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:ce3a90455492bf8bfa38e56fbbe1dbd4f872a3d8eeaf7337dc3b1c8aa28c271b" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:2bf5e2e163b356978b23652c4818ce4759d40f4712ee9ec5a83c4be6f8c23a3a" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8f2ac84905d12918e4e55a16da17939eb63e433dc11b677267c35568aa63fc84" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b58f37edab4a3881bc6c9bca52670610e0c9ca14e2ea3cf9debf185b870457fb" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:735e38e176a88ce41840c21bb49098ab66177c64c82426e24e0082500cc68af5" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2045397e63a7a0ee7957c25f7dbb36ddc110e0cfb418403d110c0a7a68a844e9" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9f136fbafe7ccf4ac7e8e0c28b31066e810be52d6e344ef954a3a70234e1702" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:748b6138585379c737adc08aeffd21222abbda1a86a0dca2a39682feb9196c20" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-win32.whl", hash = "sha256:271c73e6e5676afe4fc52907686670c7cea22ab2310b76a59b678403ed40d670" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp313-cp313-win_amd64.whl", hash = "sha256:f2d4e43ee362adfc05994ed479334d5a451ab7bc3f3fee1b796b8ca66895acb4" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:e87cbc002b6f440482b3519e36e1313eb5443e9e9e73d6a52d43bd2004fcfd8e" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:c41bc64626db62e72afec66b0c8a0da76491510015417c127bfc53b2fe6d7f7f" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8dfffba826efcf366b1e3ccc37e67afe676f290e13a3b48d31a46739f80a8724" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:74be1268d1439eaaf552c698cdb11cd594f0c49295ae6bb72c34ee31abbe611b" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be63c88b32e6c0f1429f1398ca5c09bc64b0d80950c8bb7807d7d7fb36fb84c7" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3c586ac70e855c721bda8f548d38c3ca66ac791dc49b66a8281a1f99db85e452" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:35eb275bf1751d2ffbd8f57cdbc46058e857cf3971041521b78b7db94bdaf127" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:207db540302c884b8848036b80db352a832b99dfdf41db1eb554c2c2c7800f65" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-win32.whl", hash = "sha256:57bab6deef2f4f1ca76cc04565df38dc5713ae6c17de690721bdf30cb1e0545c" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio/1.78/grpcio-1.78.0-cp314-cp314-win_amd64.whl", hash = "sha256:dce09d6116df20a96acfdbf85e4866258c3758180e8c49845d6ba8248b6d0bbb" }, + { url = "https://files.pythonhosted.org/packages/86/c7/d0b780a29b0837bf4ca9580904dfb275c1fc321ded7897d620af7047ec57/grpcio-1.78.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2777b783f6c13b92bd7b716667452c329eefd646bfb3f2e9dabea2e05dbd34f6", size = 5951525, upload-time = "2026-02-06T09:55:01.989Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b1/96920bf2ee61df85a9503cb6f733fe711c0ff321a5a697d791b075673281/grpcio-1.78.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:9dca934f24c732750389ce49d638069c3892ad065df86cb465b3fa3012b70c9e", size = 11830418, upload-time = "2026-02-06T09:55:04.462Z" }, + { url = "https://files.pythonhosted.org/packages/83/0c/7c1528f098aeb75a97de2bae18c530f56959fb7ad6c882db45d9884d6edc/grpcio-1.78.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:459ab414b35f4496138d0ecd735fed26f1318af5e52cb1efbc82a09f0d5aa911", size = 6524477, upload-time = "2026-02-06T09:55:07.111Z" }, + { url = "https://files.pythonhosted.org/packages/8d/52/e7c1f3688f949058e19a011c4e0dec973da3d0ae5e033909677f967ae1f4/grpcio-1.78.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:082653eecbdf290e6e3e2c276ab2c54b9e7c299e07f4221872380312d8cf395e", size = 7198266, upload-time = "2026-02-06T09:55:10.016Z" }, + { url = "https://files.pythonhosted.org/packages/e5/61/8ac32517c1e856677282c34f2e7812d6c328fa02b8f4067ab80e77fdc9c9/grpcio-1.78.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85f93781028ec63f383f6bc90db785a016319c561cc11151fbb7b34e0d012303", size = 6730552, upload-time = "2026-02-06T09:55:12.207Z" }, + { url = "https://files.pythonhosted.org/packages/bd/98/b8ee0158199250220734f620b12e4a345955ac7329cfd908d0bf0fda77f0/grpcio-1.78.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f12857d24d98441af6a1d5c87442d624411db486f7ba12550b07788f74b67b04", size = 7304296, upload-time = "2026-02-06T09:55:15.044Z" }, + { url = "https://files.pythonhosted.org/packages/bd/0f/7b72762e0d8840b58032a56fdbd02b78fc645b9fa993d71abf04edbc54f4/grpcio-1.78.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5397fff416b79e4b284959642a4e95ac4b0f1ece82c9993658e0e477d40551ec", size = 8288298, upload-time = "2026-02-06T09:55:17.276Z" }, + { url = "https://files.pythonhosted.org/packages/24/ae/ae4ce56bc5bb5caa3a486d60f5f6083ac3469228faa734362487176c15c5/grpcio-1.78.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fbe6e89c7ffb48518384068321621b2a69cab509f58e40e4399fdd378fa6d074", size = 7730953, upload-time = "2026-02-06T09:55:19.545Z" }, + { url = "https://files.pythonhosted.org/packages/b5/6e/8052e3a28eb6a820c372b2eb4b5e32d195c661e137d3eca94d534a4cfd8a/grpcio-1.78.0-cp311-cp311-win32.whl", hash = "sha256:6092beabe1966a3229f599d7088b38dfc8ffa1608b5b5cdda31e591e6500f856", size = 4076503, upload-time = "2026-02-06T09:55:21.521Z" }, + { url = "https://files.pythonhosted.org/packages/08/62/f22c98c5265dfad327251fa2f840b591b1df5f5e15d88b19c18c86965b27/grpcio-1.78.0-cp311-cp311-win_amd64.whl", hash = "sha256:1afa62af6e23f88629f2b29ec9e52ec7c65a7176c1e0a83292b93c76ca882558", size = 4799767, upload-time = "2026-02-06T09:55:24.107Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f4/7384ed0178203d6074446b3c4f46c90a22ddf7ae0b3aee521627f54cfc2a/grpcio-1.78.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:f9ab915a267fc47c7e88c387a3a28325b58c898e23d4995f765728f4e3dedb97", size = 5913985, upload-time = "2026-02-06T09:55:26.832Z" }, + { url = "https://files.pythonhosted.org/packages/81/ed/be1caa25f06594463f685b3790b320f18aea49b33166f4141bfdc2bfb236/grpcio-1.78.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3f8904a8165ab21e07e58bf3e30a73f4dffc7a1e0dbc32d51c61b5360d26f43e", size = 11811853, upload-time = "2026-02-06T09:55:29.224Z" }, + { url = "https://files.pythonhosted.org/packages/24/a7/f06d151afc4e64b7e3cc3e872d331d011c279aaab02831e40a81c691fb65/grpcio-1.78.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:859b13906ce098c0b493af92142ad051bf64c7870fa58a123911c88606714996", size = 6475766, upload-time = "2026-02-06T09:55:31.825Z" }, + { url = "https://files.pythonhosted.org/packages/8a/a8/4482922da832ec0082d0f2cc3a10976d84a7424707f25780b82814aafc0a/grpcio-1.78.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b2342d87af32790f934a79c3112641e7b27d63c261b8b4395350dad43eff1dc7", size = 7170027, upload-time = "2026-02-06T09:55:34.7Z" }, + { url = "https://files.pythonhosted.org/packages/54/bf/f4a3b9693e35d25b24b0b39fa46d7d8a3c439e0a3036c3451764678fec20/grpcio-1.78.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12a771591ae40bc65ba67048fa52ef4f0e6db8279e595fd349f9dfddeef571f9", size = 6690766, upload-time = "2026-02-06T09:55:36.902Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b9/521875265cc99fe5ad4c5a17010018085cae2810a928bf15ebe7d8bcd9cc/grpcio-1.78.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:185dea0d5260cbb2d224c507bf2a5444d5abbb1fa3594c1ed7e4c709d5eb8383", size = 7266161, upload-time = "2026-02-06T09:55:39.824Z" }, + { url = "https://files.pythonhosted.org/packages/05/86/296a82844fd40a4ad4a95f100b55044b4f817dece732bf686aea1a284147/grpcio-1.78.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51b13f9aed9d59ee389ad666b8c2214cc87b5de258fa712f9ab05f922e3896c6", size = 8253303, upload-time = "2026-02-06T09:55:42.353Z" }, + { url = "https://files.pythonhosted.org/packages/f3/e4/ea3c0caf5468537f27ad5aab92b681ed7cc0ef5f8c9196d3fd42c8c2286b/grpcio-1.78.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd5f135b1bd58ab088930b3c613455796dfa0393626a6972663ccdda5b4ac6ce", size = 7698222, upload-time = "2026-02-06T09:55:44.629Z" }, + { url = "https://files.pythonhosted.org/packages/d7/47/7f05f81e4bb6b831e93271fb12fd52ba7b319b5402cbc101d588f435df00/grpcio-1.78.0-cp312-cp312-win32.whl", hash = "sha256:94309f498bcc07e5a7d16089ab984d42ad96af1d94b5a4eb966a266d9fcabf68", size = 4066123, upload-time = "2026-02-06T09:55:47.644Z" }, + { url = "https://files.pythonhosted.org/packages/ad/e7/d6914822c88aa2974dbbd10903d801a28a19ce9cd8bad7e694cbbcf61528/grpcio-1.78.0-cp312-cp312-win_amd64.whl", hash = "sha256:9566fe4ababbb2610c39190791e5b829869351d14369603702e890ef3ad2d06e", size = 4797657, upload-time = "2026-02-06T09:55:49.86Z" }, + { url = "https://files.pythonhosted.org/packages/05/a9/8f75894993895f361ed8636cd9237f4ab39ef87fd30db17467235ed1c045/grpcio-1.78.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:ce3a90455492bf8bfa38e56fbbe1dbd4f872a3d8eeaf7337dc3b1c8aa28c271b", size = 5920143, upload-time = "2026-02-06T09:55:52.035Z" }, + { url = "https://files.pythonhosted.org/packages/55/06/0b78408e938ac424100100fd081189451b472236e8a3a1f6500390dc4954/grpcio-1.78.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:2bf5e2e163b356978b23652c4818ce4759d40f4712ee9ec5a83c4be6f8c23a3a", size = 11803926, upload-time = "2026-02-06T09:55:55.494Z" }, + { url = "https://files.pythonhosted.org/packages/88/93/b59fe7832ff6ae3c78b813ea43dac60e295fa03606d14d89d2e0ec29f4f3/grpcio-1.78.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8f2ac84905d12918e4e55a16da17939eb63e433dc11b677267c35568aa63fc84", size = 6478628, upload-time = "2026-02-06T09:55:58.533Z" }, + { url = "https://files.pythonhosted.org/packages/ed/df/e67e3734527f9926b7d9c0dde6cd998d1d26850c3ed8eeec81297967ac67/grpcio-1.78.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b58f37edab4a3881bc6c9bca52670610e0c9ca14e2ea3cf9debf185b870457fb", size = 7173574, upload-time = "2026-02-06T09:56:01.786Z" }, + { url = "https://files.pythonhosted.org/packages/a6/62/cc03fffb07bfba982a9ec097b164e8835546980aec25ecfa5f9c1a47e022/grpcio-1.78.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:735e38e176a88ce41840c21bb49098ab66177c64c82426e24e0082500cc68af5", size = 6692639, upload-time = "2026-02-06T09:56:04.529Z" }, + { url = "https://files.pythonhosted.org/packages/bf/9a/289c32e301b85bdb67d7ec68b752155e674ee3ba2173a1858f118e399ef3/grpcio-1.78.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2045397e63a7a0ee7957c25f7dbb36ddc110e0cfb418403d110c0a7a68a844e9", size = 7268838, upload-time = "2026-02-06T09:56:08.397Z" }, + { url = "https://files.pythonhosted.org/packages/0e/79/1be93f32add280461fa4773880196572563e9c8510861ac2da0ea0f892b6/grpcio-1.78.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9f136fbafe7ccf4ac7e8e0c28b31066e810be52d6e344ef954a3a70234e1702", size = 8251878, upload-time = "2026-02-06T09:56:10.914Z" }, + { url = "https://files.pythonhosted.org/packages/65/65/793f8e95296ab92e4164593674ae6291b204bb5f67f9d4a711489cd30ffa/grpcio-1.78.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:748b6138585379c737adc08aeffd21222abbda1a86a0dca2a39682feb9196c20", size = 7695412, upload-time = "2026-02-06T09:56:13.593Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9f/1e233fe697ecc82845942c2822ed06bb522e70d6771c28d5528e4c50f6a4/grpcio-1.78.0-cp313-cp313-win32.whl", hash = "sha256:271c73e6e5676afe4fc52907686670c7cea22ab2310b76a59b678403ed40d670", size = 4064899, upload-time = "2026-02-06T09:56:15.601Z" }, + { url = "https://files.pythonhosted.org/packages/4d/27/d86b89e36de8a951501fb06a0f38df19853210f341d0b28f83f4aa0ffa08/grpcio-1.78.0-cp313-cp313-win_amd64.whl", hash = "sha256:f2d4e43ee362adfc05994ed479334d5a451ab7bc3f3fee1b796b8ca66895acb4", size = 4797393, upload-time = "2026-02-06T09:56:17.882Z" }, + { url = "https://files.pythonhosted.org/packages/29/f2/b56e43e3c968bfe822fa6ce5bca10d5c723aa40875b48791ce1029bb78c7/grpcio-1.78.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:e87cbc002b6f440482b3519e36e1313eb5443e9e9e73d6a52d43bd2004fcfd8e", size = 5920591, upload-time = "2026-02-06T09:56:20.758Z" }, + { url = "https://files.pythonhosted.org/packages/5d/81/1f3b65bd30c334167bfa8b0d23300a44e2725ce39bba5b76a2460d85f745/grpcio-1.78.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:c41bc64626db62e72afec66b0c8a0da76491510015417c127bfc53b2fe6d7f7f", size = 11813685, upload-time = "2026-02-06T09:56:24.315Z" }, + { url = "https://files.pythonhosted.org/packages/0e/1c/bbe2f8216a5bd3036119c544d63c2e592bdf4a8ec6e4a1867592f4586b26/grpcio-1.78.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8dfffba826efcf366b1e3ccc37e67afe676f290e13a3b48d31a46739f80a8724", size = 6487803, upload-time = "2026-02-06T09:56:27.367Z" }, + { url = "https://files.pythonhosted.org/packages/16/5c/a6b2419723ea7ddce6308259a55e8e7593d88464ce8db9f4aa857aba96fa/grpcio-1.78.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:74be1268d1439eaaf552c698cdb11cd594f0c49295ae6bb72c34ee31abbe611b", size = 7173206, upload-time = "2026-02-06T09:56:29.876Z" }, + { url = "https://files.pythonhosted.org/packages/df/1e/b8801345629a415ea7e26c83d75eb5dbe91b07ffe5210cc517348a8d4218/grpcio-1.78.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be63c88b32e6c0f1429f1398ca5c09bc64b0d80950c8bb7807d7d7fb36fb84c7", size = 6693826, upload-time = "2026-02-06T09:56:32.305Z" }, + { url = "https://files.pythonhosted.org/packages/34/84/0de28eac0377742679a510784f049738a80424b17287739fc47d63c2439e/grpcio-1.78.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3c586ac70e855c721bda8f548d38c3ca66ac791dc49b66a8281a1f99db85e452", size = 7277897, upload-time = "2026-02-06T09:56:34.915Z" }, + { url = "https://files.pythonhosted.org/packages/ca/9c/ad8685cfe20559a9edb66f735afdcb2b7d3de69b13666fdfc542e1916ebd/grpcio-1.78.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:35eb275bf1751d2ffbd8f57cdbc46058e857cf3971041521b78b7db94bdaf127", size = 8252404, upload-time = "2026-02-06T09:56:37.553Z" }, + { url = "https://files.pythonhosted.org/packages/3c/05/33a7a4985586f27e1de4803887c417ec7ced145ebd069bc38a9607059e2b/grpcio-1.78.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:207db540302c884b8848036b80db352a832b99dfdf41db1eb554c2c2c7800f65", size = 7696837, upload-time = "2026-02-06T09:56:40.173Z" }, + { url = "https://files.pythonhosted.org/packages/73/77/7382241caf88729b106e49e7d18e3116216c778e6a7e833826eb96de22f7/grpcio-1.78.0-cp314-cp314-win32.whl", hash = "sha256:57bab6deef2f4f1ca76cc04565df38dc5713ae6c17de690721bdf30cb1e0545c", size = 4142439, upload-time = "2026-02-06T09:56:43.258Z" }, + { url = "https://files.pythonhosted.org/packages/48/b2/b096ccce418882fbfda4f7496f9357aaa9a5af1896a9a7f60d9f2b275a06/grpcio-1.78.0-cp314-cp314-win_amd64.whl", hash = "sha256:dce09d6116df20a96acfdbf85e4866258c3758180e8c49845d6ba8248b6d0bbb", size = 4929852, upload-time = "2026-02-06T09:56:45.885Z" }, ] [[package]] name = "grpcio-tools" version = "1.78.0" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "grpcio" }, { name = "protobuf" }, { name = "setuptools" }, ] -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0.tar.gz", hash = "sha256:4b0dd86560274316e155d925158276f8564508193088bc43e20d3f5dff956b2b" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/d1/cbefe328653f746fd319c4377836a25ba64226e41c6a1d7d5cdbc87a459f/grpcio_tools-1.78.0.tar.gz", hash = "sha256:4b0dd86560274316e155d925158276f8564508193088bc43e20d3f5dff956b2b", size = 5393026, upload-time = "2026-02-06T09:59:59.53Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:6a8b8b7b49f319d29dbcf507f62984fa382d1d10437d75c3f26db5f09c4ac0af" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:d62cf3b68372b0c6d722a6165db41b976869811abeabc19c8522182978d8db10" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fa9056742efeaf89d5fe14198af71e5cbc4fbf155d547b89507e19d6025906c6" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e3191af125dcb705aa6bc3856ba81ba99b94121c1b6ebee152e66ea084672831" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:283239ddbb67ae83fac111c61b25d8527a1dbd355b377cbc8383b79f1329944d" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ac977508c0db15301ef36d6c79769ec1a6cc4e3bc75735afca7fe7e360cead3a" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4ff605e25652a0bd13aa8a73a09bc48669c68170902f5d2bf1468a57d5e78771" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0197d7b561c79be78ab93d0fe2836c8def470683df594bae3ac89dd8e5c821b2" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-win32.whl", hash = "sha256:28f71f591f7f39555863ced84fcc209cbf4454e85ef957232f43271ee99af577" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a6de495dabf86a3b40b9a7492994e1232b077af9d63080811838b781abbe4e8" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:9eb122da57d4cad7d339fc75483116f0113af99e8d2c67f3ef9cae7501d806e4" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:d0c501b8249940b886420e6935045c44cb818fa6f265f4c2b97d5cff9cb5e796" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:77e5aa2d2a7268d55b1b113f958264681ef1994c970f69d48db7d4683d040f57" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:8e3c0b0e6ba5275322ba29a97bf890565a55f129f99a21b121145e9e93a22525" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975d4cb48694e20ebd78e1643e5f1cd94cdb6a3d38e677a8e84ae43665aa4790" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:553ff18c5d52807dedecf25045ae70bad7a3dbba0b27a9a3cdd9bcf0a1b7baec" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8c7f5e4af5a84d2e96c862b1a65e958a538237e268d5f8203a3a784340975b51" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:96183e2b44afc3f9a761e9d0f985c3b44e03e8bb98e626241a6cbfb3b6f7e88f" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-win32.whl", hash = "sha256:2250e8424c565a88573f7dc10659a0b92802e68c2a1d57e41872c9b88ccea7a6" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp312-cp312-win_amd64.whl", hash = "sha256:217d1fa29de14d9c567d616ead7cb0fef33cde36010edff5a9390b00d52e5094" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:2d6de1cc23bdc1baafc23e201b1e48c617b8c1418b4d8e34cebf72141676e5fb" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:2afeaad88040894c76656202ff832cb151bceb05c0e6907e539d129188b1e456" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:33cc593735c93c03d63efe7a8ba25f3c66f16c52f0651910712490244facad72" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2921d7989c4d83b71f03130ab415fa4d66e6693b8b8a1fcbb7a1c67cff19b812" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e6a0df438e82c804c7b95e3f311c97c2f876dcc36376488d5b736b7bcf5a9b45" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9c6070a9500798225191ef25d0055a15d2c01c9c8f2ee7b681fffa99c98c822" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:394e8b57d85370a62e5b0a4d64c96fcf7568345c345d8590c821814d227ecf1d" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3ef700293ab375e111a2909d87434ed0a0b086adf0ce67a8d9cf12ea7765e63" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-win32.whl", hash = "sha256:6993b960fec43a8d840ee5dc20247ef206c1a19587ea49fe5e6cc3d2a09c1585" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp313-cp313-win_amd64.whl", hash = "sha256:275ce3c2978842a8cf9dd88dce954e836e590cf7029649ad5d1145b779039ed5" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:8b080d0d072e6032708a3a91731b808074d7ab02ca8fb9847b6a011fdce64cd9" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8c0ad8f8f133145cd7008b49cb611a5c6a9d89ab276c28afa17050516e801f79" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2f8ea092a7de74c6359335d36f0674d939a3c7e1a550f4c2c9e80e0226de8fe4" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:da422985e0cac822b41822f43429c19ecb27c81ffe3126d0b74e77edec452608" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4fab1faa3fbcb246263e68da7a8177d73772283f9db063fb8008517480888d26" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dd9c094f73f734becae3f20f27d4944d3cd8fb68db7338ee6c58e62fc5c3d99f" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:2ed51ce6b833068f6c580b73193fc2ec16468e6bc18354bc2f83a58721195a58" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:05803a5cdafe77c8bdf36aa660ad7a6a1d9e49bc59ce45c1bade2a4698826599" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-win32.whl", hash = "sha256:f7c722e9ce6f11149ac5bddd5056e70aaccfd8168e74e9d34d8b8b588c3f5c7c" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/grpcio-tools/1.78/grpcio_tools-1.78.0-cp314-cp314-win_amd64.whl", hash = "sha256:7d58ade518b546120ec8f0a8e006fc8076ae5df151250ebd7e82e9b5e152c229" }, + { url = "https://files.pythonhosted.org/packages/75/78/280184d19242ed6762bf453c47a70b869b3c5c72a24dc5bf2bf43909faa3/grpcio_tools-1.78.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:6a8b8b7b49f319d29dbcf507f62984fa382d1d10437d75c3f26db5f09c4ac0af", size = 2545904, upload-time = "2026-02-06T09:57:52.769Z" }, + { url = "https://files.pythonhosted.org/packages/5b/51/3c46dea5113f68fe879961cae62d34bb7a3c308a774301b45d614952ee98/grpcio_tools-1.78.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:d62cf3b68372b0c6d722a6165db41b976869811abeabc19c8522182978d8db10", size = 5709078, upload-time = "2026-02-06T09:57:56.389Z" }, + { url = "https://files.pythonhosted.org/packages/e0/2c/dc1ae9ec53182c96d56dfcbf3bcd3e55a8952ad508b188c75bf5fc8993d4/grpcio_tools-1.78.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fa9056742efeaf89d5fe14198af71e5cbc4fbf155d547b89507e19d6025906c6", size = 2591744, upload-time = "2026-02-06T09:57:58.341Z" }, + { url = "https://files.pythonhosted.org/packages/04/63/9b53fc9a9151dd24386785171a4191ee7cb5afb4d983b6a6a87408f41b28/grpcio_tools-1.78.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e3191af125dcb705aa6bc3856ba81ba99b94121c1b6ebee152e66ea084672831", size = 2905113, upload-time = "2026-02-06T09:58:00.38Z" }, + { url = "https://files.pythonhosted.org/packages/96/b2/0ad8d789f3a2a00893131c140865605fa91671a6e6fcf9da659e1fabba10/grpcio_tools-1.78.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:283239ddbb67ae83fac111c61b25d8527a1dbd355b377cbc8383b79f1329944d", size = 2656436, upload-time = "2026-02-06T09:58:03.038Z" }, + { url = "https://files.pythonhosted.org/packages/09/4d/580f47ce2fc61b093ade747b378595f51b4f59972dd39949f7444b464a03/grpcio_tools-1.78.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ac977508c0db15301ef36d6c79769ec1a6cc4e3bc75735afca7fe7e360cead3a", size = 3106128, upload-time = "2026-02-06T09:58:05.064Z" }, + { url = "https://files.pythonhosted.org/packages/c9/29/d83b2d89f8d10e438bad36b1eb29356510fb97e81e6a608b22ae1890e8e6/grpcio_tools-1.78.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4ff605e25652a0bd13aa8a73a09bc48669c68170902f5d2bf1468a57d5e78771", size = 3654953, upload-time = "2026-02-06T09:58:07.15Z" }, + { url = "https://files.pythonhosted.org/packages/08/71/917ce85633311e54fefd7e6eb1224fb780ef317a4d092766f5630c3fc419/grpcio_tools-1.78.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0197d7b561c79be78ab93d0fe2836c8def470683df594bae3ac89dd8e5c821b2", size = 3322630, upload-time = "2026-02-06T09:58:10.305Z" }, + { url = "https://files.pythonhosted.org/packages/b2/55/3fbf6b26ab46fc79e1e6f7f4e0993cf540263dad639290299fad374a0829/grpcio_tools-1.78.0-cp311-cp311-win32.whl", hash = "sha256:28f71f591f7f39555863ced84fcc209cbf4454e85ef957232f43271ee99af577", size = 993804, upload-time = "2026-02-06T09:58:13.698Z" }, + { url = "https://files.pythonhosted.org/packages/73/86/4affe006d9e1e9e1c6653d6aafe2f8b9188acb2b563cd8ed3a2c7c0e8aec/grpcio_tools-1.78.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a6de495dabf86a3b40b9a7492994e1232b077af9d63080811838b781abbe4e8", size = 1158566, upload-time = "2026-02-06T09:58:15.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ae/5b1fa5dd8d560a6925aa52de0de8731d319f121c276e35b9b2af7cc220a2/grpcio_tools-1.78.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:9eb122da57d4cad7d339fc75483116f0113af99e8d2c67f3ef9cae7501d806e4", size = 2546823, upload-time = "2026-02-06T09:58:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ed/d33ccf7fa701512efea7e7e23333b748848a123e9d3bbafde4e126784546/grpcio_tools-1.78.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:d0c501b8249940b886420e6935045c44cb818fa6f265f4c2b97d5cff9cb5e796", size = 5706776, upload-time = "2026-02-06T09:58:20.944Z" }, + { url = "https://files.pythonhosted.org/packages/c6/69/4285583f40b37af28277fc6b867d636e3b10e1b6a7ebd29391a856e1279b/grpcio_tools-1.78.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:77e5aa2d2a7268d55b1b113f958264681ef1994c970f69d48db7d4683d040f57", size = 2593972, upload-time = "2026-02-06T09:58:23.29Z" }, + { url = "https://files.pythonhosted.org/packages/d7/eb/ecc1885bd6b3147f0a1b7dff5565cab72f01c8f8aa458f682a1c77a9fb08/grpcio_tools-1.78.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:8e3c0b0e6ba5275322ba29a97bf890565a55f129f99a21b121145e9e93a22525", size = 2905531, upload-time = "2026-02-06T09:58:25.406Z" }, + { url = "https://files.pythonhosted.org/packages/ae/a9/511d0040ced66960ca10ba0f082d6b2d2ee6dd61837b1709636fdd8e23b4/grpcio_tools-1.78.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975d4cb48694e20ebd78e1643e5f1cd94cdb6a3d38e677a8e84ae43665aa4790", size = 2656909, upload-time = "2026-02-06T09:58:28.022Z" }, + { url = "https://files.pythonhosted.org/packages/06/a3/3d2c707e7dee8df842c96fbb24feb2747e506e39f4a81b661def7fed107c/grpcio_tools-1.78.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:553ff18c5d52807dedecf25045ae70bad7a3dbba0b27a9a3cdd9bcf0a1b7baec", size = 3109778, upload-time = "2026-02-06T09:58:30.091Z" }, + { url = "https://files.pythonhosted.org/packages/1f/4b/646811ba241bf05da1f0dc6f25764f1c837f78f75b4485a4210c84b79eae/grpcio_tools-1.78.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8c7f5e4af5a84d2e96c862b1a65e958a538237e268d5f8203a3a784340975b51", size = 3658763, upload-time = "2026-02-06T09:58:32.875Z" }, + { url = "https://files.pythonhosted.org/packages/45/de/0a5ef3b3e79d1011375f5580dfee3a9c1ccb96c5f5d1c74c8cee777a2483/grpcio_tools-1.78.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:96183e2b44afc3f9a761e9d0f985c3b44e03e8bb98e626241a6cbfb3b6f7e88f", size = 3325116, upload-time = "2026-02-06T09:58:34.894Z" }, + { url = "https://files.pythonhosted.org/packages/95/d2/6391b241ad571bc3e71d63f957c0b1860f0c47932d03c7f300028880f9b8/grpcio_tools-1.78.0-cp312-cp312-win32.whl", hash = "sha256:2250e8424c565a88573f7dc10659a0b92802e68c2a1d57e41872c9b88ccea7a6", size = 993493, upload-time = "2026-02-06T09:58:37.242Z" }, + { url = "https://files.pythonhosted.org/packages/7c/8f/7d0d3a39ecad76ccc136be28274daa660569b244fa7d7d0bbb24d68e5ece/grpcio_tools-1.78.0-cp312-cp312-win_amd64.whl", hash = "sha256:217d1fa29de14d9c567d616ead7cb0fef33cde36010edff5a9390b00d52e5094", size = 1158423, upload-time = "2026-02-06T09:58:40.072Z" }, + { url = "https://files.pythonhosted.org/packages/53/ce/17311fb77530420e2f441e916b347515133e83d21cd6cc77be04ce093d5b/grpcio_tools-1.78.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:2d6de1cc23bdc1baafc23e201b1e48c617b8c1418b4d8e34cebf72141676e5fb", size = 2546284, upload-time = "2026-02-06T09:58:43.073Z" }, + { url = "https://files.pythonhosted.org/packages/1d/d3/79e101483115f0e78223397daef71751b75eba7e92a32060c10aae11ca64/grpcio_tools-1.78.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:2afeaad88040894c76656202ff832cb151bceb05c0e6907e539d129188b1e456", size = 5705653, upload-time = "2026-02-06T09:58:45.533Z" }, + { url = "https://files.pythonhosted.org/packages/8b/a7/52fa3ccb39ceeee6adc010056eadfbca8198651c113e418dafebbdf2b306/grpcio_tools-1.78.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:33cc593735c93c03d63efe7a8ba25f3c66f16c52f0651910712490244facad72", size = 2592788, upload-time = "2026-02-06T09:58:48.918Z" }, + { url = "https://files.pythonhosted.org/packages/68/08/682ff6bb548225513d73dc9403742d8975439d7469c673bc534b9bbc83a7/grpcio_tools-1.78.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2921d7989c4d83b71f03130ab415fa4d66e6693b8b8a1fcbb7a1c67cff19b812", size = 2905157, upload-time = "2026-02-06T09:58:51.478Z" }, + { url = "https://files.pythonhosted.org/packages/b2/66/264f3836a96423b7018e5ada79d62576a6401f6da4e1f4975b18b2be1265/grpcio_tools-1.78.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e6a0df438e82c804c7b95e3f311c97c2f876dcc36376488d5b736b7bcf5a9b45", size = 2656166, upload-time = "2026-02-06T09:58:54.117Z" }, + { url = "https://files.pythonhosted.org/packages/f3/6b/f108276611522e03e98386b668cc7e575eff6952f2db9caa15b2a3b3e883/grpcio_tools-1.78.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9c6070a9500798225191ef25d0055a15d2c01c9c8f2ee7b681fffa99c98c822", size = 3109110, upload-time = "2026-02-06T09:58:56.891Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c7/cf048dbcd64b3396b3c860a2ffbcc67a8f8c87e736aaa74c2e505a7eee4c/grpcio_tools-1.78.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:394e8b57d85370a62e5b0a4d64c96fcf7568345c345d8590c821814d227ecf1d", size = 3657863, upload-time = "2026-02-06T09:58:59.176Z" }, + { url = "https://files.pythonhosted.org/packages/b6/37/e2736912c8fda57e2e57a66ea5e0bc8eb9a5fb7ded00e866ad22d50afb08/grpcio_tools-1.78.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3ef700293ab375e111a2909d87434ed0a0b086adf0ce67a8d9cf12ea7765e63", size = 3324748, upload-time = "2026-02-06T09:59:01.242Z" }, + { url = "https://files.pythonhosted.org/packages/1c/5d/726abc75bb5bfc2841e88ea05896e42f51ca7c30cb56da5c5b63058b3867/grpcio_tools-1.78.0-cp313-cp313-win32.whl", hash = "sha256:6993b960fec43a8d840ee5dc20247ef206c1a19587ea49fe5e6cc3d2a09c1585", size = 993074, upload-time = "2026-02-06T09:59:03.085Z" }, + { url = "https://files.pythonhosted.org/packages/c5/68/91b400bb360faf9b177ffb5540ec1c4d06ca923691ddf0f79e2c9683f4da/grpcio_tools-1.78.0-cp313-cp313-win_amd64.whl", hash = "sha256:275ce3c2978842a8cf9dd88dce954e836e590cf7029649ad5d1145b779039ed5", size = 1158185, upload-time = "2026-02-06T09:59:05.036Z" }, + { url = "https://files.pythonhosted.org/packages/cf/5e/278f3831c8d56bae02e3acc570465648eccf0a6bbedcb1733789ac966803/grpcio_tools-1.78.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:8b080d0d072e6032708a3a91731b808074d7ab02ca8fb9847b6a011fdce64cd9", size = 2546270, upload-time = "2026-02-06T09:59:07.426Z" }, + { url = "https://files.pythonhosted.org/packages/a3/d9/68582f2952b914b60dddc18a2e3f9c6f09af9372b6f6120d6cf3ec7f8b4e/grpcio_tools-1.78.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8c0ad8f8f133145cd7008b49cb611a5c6a9d89ab276c28afa17050516e801f79", size = 5705731, upload-time = "2026-02-06T09:59:09.856Z" }, + { url = "https://files.pythonhosted.org/packages/70/68/feb0f9a48818ee1df1e8b644069379a1e6ef5447b9b347c24e96fd258e5d/grpcio_tools-1.78.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2f8ea092a7de74c6359335d36f0674d939a3c7e1a550f4c2c9e80e0226de8fe4", size = 2593896, upload-time = "2026-02-06T09:59:12.23Z" }, + { url = "https://files.pythonhosted.org/packages/1f/08/a430d8d06e1b8d33f3e48d3f0cc28236723af2f35e37bd5c8db05df6c3aa/grpcio_tools-1.78.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:da422985e0cac822b41822f43429c19ecb27c81ffe3126d0b74e77edec452608", size = 2905298, upload-time = "2026-02-06T09:59:14.458Z" }, + { url = "https://files.pythonhosted.org/packages/71/0a/348c36a3eae101ca0c090c9c3bc96f2179adf59ee0c9262d11cdc7bfe7db/grpcio_tools-1.78.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4fab1faa3fbcb246263e68da7a8177d73772283f9db063fb8008517480888d26", size = 2656186, upload-time = "2026-02-06T09:59:16.949Z" }, + { url = "https://files.pythonhosted.org/packages/1d/3f/18219f331536fad4af6207ade04142292faa77b5cb4f4463787988963df8/grpcio_tools-1.78.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dd9c094f73f734becae3f20f27d4944d3cd8fb68db7338ee6c58e62fc5c3d99f", size = 3109859, upload-time = "2026-02-06T09:59:19.202Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d9/341ea20a44c8e5a3a18acc820b65014c2e3ea5b4f32a53d14864bcd236bc/grpcio_tools-1.78.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:2ed51ce6b833068f6c580b73193fc2ec16468e6bc18354bc2f83a58721195a58", size = 3657915, upload-time = "2026-02-06T09:59:21.839Z" }, + { url = "https://files.pythonhosted.org/packages/fb/f4/5978b0f91611a64371424c109dd0027b247e5b39260abad2eaee66b6aa37/grpcio_tools-1.78.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:05803a5cdafe77c8bdf36aa660ad7a6a1d9e49bc59ce45c1bade2a4698826599", size = 3324724, upload-time = "2026-02-06T09:59:24.402Z" }, + { url = "https://files.pythonhosted.org/packages/b2/80/96a324dba99cfbd20e291baf0b0ae719dbb62b76178c5ce6c788e7331cb1/grpcio_tools-1.78.0-cp314-cp314-win32.whl", hash = "sha256:f7c722e9ce6f11149ac5bddd5056e70aaccfd8168e74e9d34d8b8b588c3f5c7c", size = 1015505, upload-time = "2026-02-06T09:59:26.3Z" }, + { url = "https://files.pythonhosted.org/packages/3b/d1/909e6a05bfd44d46327dc4b8a78beb2bae4fb245ffab2772e350081aaf7e/grpcio_tools-1.78.0-cp314-cp314-win_amd64.whl", hash = "sha256:7d58ade518b546120ec8f0a8e006fc8076ae5df151250ebd7e82e9b5e152c229", size = 1190196, upload-time = "2026-02-06T09:59:28.359Z" }, ] [[package]] name = "iniconfig" version = "2.1.0" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/iniconfig/2.1/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/iniconfig/2.1/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760" }, + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, ] [[package]] name = "maturin" version = "1.12.0" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0.tar.gz", hash = "sha256:170e695ead35d33fa537078deea2a91dead31ee909fac454079a5df006786e01" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/21/85e8189ca40f97885abc5154950490b821e590fd2aebea0c9bfb92b1c353/maturin-1.12.0.tar.gz", hash = "sha256:170e695ead35d33fa537078deea2a91dead31ee909fac454079a5df006786e01", size = 252430, upload-time = "2026-02-14T08:49:41.794Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-linux_armv6l.whl", hash = "sha256:f16d0db5000b37fb9e3ed252b7ffbc021274be34fad9abeaf1b98b723c233e4a" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d1724aee0fecc39cc74f8cff53936f0a6c34d1878caa5e407d568ad8f56d674b" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c0b054ee615f2ba7e2219ead94831f8fa7abeef34b09aa3f4fbdca3553c74dc1" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:e1046cad6d7bde0d6fa592857f805b4a5101db39a720407af01aa7ff439650b9" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:6df159ac1520621cc9750a0d37ef09c0444b5983c7adc012bb0029e3d5a2a095" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:978249d5dcf26eaad9440f9d87d691b0b55dbef0e45abf85cae2072b80fb0074" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:b12a5daf91d16db44381f5338a31f308dfcf85524321f72cad2428a5e0a806c6" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:d5fa15c769b56f98e2f8fded6dfe5a2c19680a340213e3fb28c8dd507bec85cb" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d9d9cf7d4a3e043336ae2ad01c5e9e6a7062be43c8f09b0cd28dc5f6dce742b" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:45a5078c3c2f02b2885e38a2735eb9dd0c6e9dd06ba70c05845d5cd5ad7128db" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-win32.whl", hash = "sha256:1eeed88f3021d15c426490af49198e8da82a34ace1a15d41dfc8fffa5e4c2967" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-win_amd64.whl", hash = "sha256:976519fd01354025da4b494d4ee9ca697ef296e7add8e0b5f2eb199da9275ee7" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/maturin/1.12/maturin-1.12.0-py3-none-win_arm64.whl", hash = "sha256:38764453c5a77100bd174c467cc1549530cc63f5acfa93abc9ef1c0253489839" }, + { url = "https://files.pythonhosted.org/packages/6e/c8/acbcea1290b0b335b6457df80cc946cf81d521261b1a6a3885548fcc7120/maturin-1.12.0-py3-none-linux_armv6l.whl", hash = "sha256:f16d0db5000b37fb9e3ed252b7ffbc021274be34fad9abeaf1b98b723c233e4a", size = 9632654, upload-time = "2026-02-14T08:49:42.879Z" }, + { url = "https://files.pythonhosted.org/packages/6e/61/6bd544ca1e3c58300dbbdc5449b3de99e7638044039eeae1af5bbc814390/maturin-1.12.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d1724aee0fecc39cc74f8cff53936f0a6c34d1878caa5e407d568ad8f56d674b", size = 18839071, upload-time = "2026-02-14T08:49:19.859Z" }, + { url = "https://files.pythonhosted.org/packages/74/63/e48f5057248b597aa8528a64aaef4828e87a23ad9b924387d54060a030bb/maturin-1.12.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c0b054ee615f2ba7e2219ead94831f8fa7abeef34b09aa3f4fbdca3553c74dc1", size = 9737439, upload-time = "2026-02-14T08:49:28.605Z" }, + { url = "https://files.pythonhosted.org/packages/20/f3/39c47bcda041d71aee597147e4237197b878164bfe30533b3ea30f82c796/maturin-1.12.0-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:e1046cad6d7bde0d6fa592857f805b4a5101db39a720407af01aa7ff439650b9", size = 9684568, upload-time = "2026-02-14T08:49:35.599Z" }, + { url = "https://files.pythonhosted.org/packages/4a/b5/105d230350fe011f885d8e6fcecdb326999924d343137a1c14c756324710/maturin-1.12.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:6df159ac1520621cc9750a0d37ef09c0444b5983c7adc012bb0029e3d5a2a095", size = 10183157, upload-time = "2026-02-14T08:49:30.884Z" }, + { url = "https://files.pythonhosted.org/packages/04/19/fc170394256aafeabf97622b1ef286f521984940b5483b018f473dbaea32/maturin-1.12.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:978249d5dcf26eaad9440f9d87d691b0b55dbef0e45abf85cae2072b80fb0074", size = 9558768, upload-time = "2026-02-14T08:49:37.445Z" }, + { url = "https://files.pythonhosted.org/packages/46/4a/7bdd7ccbb0bb71da2122d0d5992efff5a55ff3e5ac60eba1fa16f28867eb/maturin-1.12.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:b12a5daf91d16db44381f5338a31f308dfcf85524321f72cad2428a5e0a806c6", size = 9467723, upload-time = "2026-02-14T08:49:45.178Z" }, + { url = "https://files.pythonhosted.org/packages/ff/93/8a73b927c7f710bc1aafae7a195ee44da6ad0e42ba73db79f3258569deab/maturin-1.12.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:d5fa15c769b56f98e2f8fded6dfe5a2c19680a340213e3fb28c8dd507bec85cb", size = 12557142, upload-time = "2026-02-14T08:49:47.438Z" }, + { url = "https://files.pythonhosted.org/packages/37/42/32f78aa187babf815ffee86f2d9dfeb66d596aa3e47a8163017cdb34cc2f/maturin-1.12.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d9d9cf7d4a3e043336ae2ad01c5e9e6a7062be43c8f09b0cd28dc5f6dce742b", size = 10301378, upload-time = "2026-02-14T08:49:33.126Z" }, + { url = "https://files.pythonhosted.org/packages/ae/5b/be0ff96087f245cacaf360ec94e6bea4c76055d87e230f9a16f8bf209449/maturin-1.12.0-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:45a5078c3c2f02b2885e38a2735eb9dd0c6e9dd06ba70c05845d5cd5ad7128db", size = 10008035, upload-time = "2026-02-14T08:49:26.236Z" }, + { url = "https://files.pythonhosted.org/packages/10/6e/cb255296223a38c3b48243dacc93343ee6473d37b5d938359f6bf858fcb1/maturin-1.12.0-py3-none-win32.whl", hash = "sha256:1eeed88f3021d15c426490af49198e8da82a34ace1a15d41dfc8fffa5e4c2967", size = 8468757, upload-time = "2026-02-14T08:49:24.542Z" }, + { url = "https://files.pythonhosted.org/packages/96/77/697dd0d6ca69728c31e59ef217a08128fa63656d003adfcd82d4e3d7ffd0/maturin-1.12.0-py3-none-win_amd64.whl", hash = "sha256:976519fd01354025da4b494d4ee9ca697ef296e7add8e0b5f2eb199da9275ee7", size = 9813092, upload-time = "2026-02-14T08:49:39.819Z" }, + { url = "https://files.pythonhosted.org/packages/c6/79/780b0af1780080780f618d2e095ce2192047bd6ae631a382e86a8d632d98/maturin-1.12.0-py3-none-win_arm64.whl", hash = "sha256:38764453c5a77100bd174c467cc1549530cc63f5acfa93abc9ef1c0253489839", size = 8524150, upload-time = "2026-02-14T08:49:22.377Z" }, ] [[package]] name = "packaging" version = "25.0" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/packaging/25/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/packaging/25/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484" }, + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] [[package]] name = "pluggy" version = "1.6.0" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pluggy/1.6/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pluggy/1.6/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746" }, + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] [[package]] name = "protobuf" version = "6.33.6" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/protobuf/6.33.6/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/70/e908e9c5e52ef7c3a6c7902c9dfbb34c7e29c25d2f81ade3856445fd5c94/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135", size = 444531, upload-time = "2026-03-18T19:05:00.988Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/protobuf/6.33.6/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/protobuf/6.33.6/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/protobuf/6.33.6/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/protobuf/6.33.6/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/protobuf/6.33.6/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/protobuf/6.33.6/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/protobuf/6.33.6/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901" }, + { url = "https://files.pythonhosted.org/packages/fc/9f/2f509339e89cfa6f6a4c4ff50438db9ca488dec341f7e454adad60150b00/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3", size = 425739, upload-time = "2026-03-18T19:04:48.373Z" }, + { url = "https://files.pythonhosted.org/packages/76/5d/683efcd4798e0030c1bab27374fd13a89f7c2515fb1f3123efdfaa5eab57/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326", size = 437089, upload-time = "2026-03-18T19:04:50.381Z" }, + { url = "https://files.pythonhosted.org/packages/5c/01/a3c3ed5cd186f39e7880f8303cc51385a198a81469d53d0fdecf1f64d929/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a", size = 427737, upload-time = "2026-03-18T19:04:51.866Z" }, + { url = "https://files.pythonhosted.org/packages/ee/90/b3c01fdec7d2f627b3a6884243ba328c1217ed2d978def5c12dc50d328a3/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2", size = 324610, upload-time = "2026-03-18T19:04:53.096Z" }, + { url = "https://files.pythonhosted.org/packages/9b/ca/25afc144934014700c52e05103c2421997482d561f3101ff352e1292fb81/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3", size = 339381, upload-time = "2026-03-18T19:04:54.616Z" }, + { url = "https://files.pythonhosted.org/packages/16/92/d1e32e3e0d894fe00b15ce28ad4944ab692713f2e7f0a99787405e43533a/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593", size = 323436, upload-time = "2026-03-18T19:04:55.768Z" }, + { url = "https://files.pythonhosted.org/packages/c4/72/02445137af02769918a93807b2b7890047c32bfb9f90371cbc12688819eb/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901", size = 170656, upload-time = "2026-03-18T19:04:59.826Z" }, ] [[package]] name = "pydantic" version = "2.12.0" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic/2.12/pydantic-2.12.0.tar.gz", hash = "sha256:c1a077e6270dbfb37bfd8b498b3981e2bb18f68103720e51fa6c306a5a9af563" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/da/b8a7ee04378a53f6fefefc0c5e05570a3ebfdfa0523a878bcd3b475683ee/pydantic-2.12.0.tar.gz", hash = "sha256:c1a077e6270dbfb37bfd8b498b3981e2bb18f68103720e51fa6c306a5a9af563", size = 814760, upload-time = "2025-10-07T15:58:03.467Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic/2.12/pydantic-2.12.0-py3-none-any.whl", hash = "sha256:f6a1da352d42790537e95e83a8bdfb91c7efbae63ffd0b86fa823899e807116f" }, + { url = "https://files.pythonhosted.org/packages/f4/9d/d5c855424e2e5b6b626fbc6ec514d8e655a600377ce283008b115abb7445/pydantic-2.12.0-py3-none-any.whl", hash = "sha256:f6a1da352d42790537e95e83a8bdfb91c7efbae63ffd0b86fa823899e807116f", size = 459730, upload-time = "2025-10-07T15:58:01.576Z" }, ] [[package]] name = "pydantic-core" version = "2.41.1" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1.tar.gz", hash = "sha256:1ad375859a6d8c356b7704ec0f547a58e82ee80bb41baa811ad710e124bc8f2f" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/14/12b4a0d2b0b10d8e1d9a24ad94e7bbb43335eaf29c0c4e57860e8a30734a/pydantic_core-2.41.1.tar.gz", hash = "sha256:1ad375859a6d8c356b7704ec0f547a58e82ee80bb41baa811ad710e124bc8f2f", size = 454870, upload-time = "2025-10-07T10:50:45.974Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4f276a6134fe1fc1daa692642a3eaa2b7b858599c49a7610816388f5e37566a1" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07588570a805296ece009c59d9a679dc08fab72fb337365afb4f3a14cfbfc176" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28527e4b53400cd60ffbd9812ccb2b5135d042129716d71afd7e45bf42b855c0" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46a1c935c9228bad738c8a41de06478770927baedf581d172494ab36a6b96575" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:447ddf56e2b7d28d200d3e9eafa936fe40485744b5a824b67039937580b3cb20" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63892ead40c1160ac860b5debcc95c95c5a0035e543a8b5a4eac70dd22e995f4" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4a9543ca355e6df8fbe9c83e9faab707701e9103ae857ecb40f1c0cf8b0e94d" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2611bdb694116c31e551ed82e20e39a90bea9b7ad9e54aaf2d045ad621aa7a1" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fecc130893a9b5f7bfe230be1bb8c61fe66a19db8ab704f808cb25a82aad0bc9" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:1e2df5f8344c99b6ea5219f00fdc8950b8e6f2c422fbc1cc122ec8641fac85a1" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:35291331e9d8ed94c257bab6be1cb3a380b5eee570a2784bffc055e18040a2ea" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-win32.whl", hash = "sha256:2876a095292668d753f1a868c4a57c4ac9f6acbd8edda8debe4218d5848cf42f" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-win_amd64.whl", hash = "sha256:b92d6c628e9a338846a28dfe3fcdc1a3279388624597898b105e078cdfc59298" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp311-cp311-win_arm64.whl", hash = "sha256:7d82ae99409eb69d507a89835488fb657faa03ff9968a9379567b0d2e2e56bc5" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:db2f82c0ccbce8f021ad304ce35cbe02aa2f95f215cac388eed542b03b4d5eb4" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47694a31c710ced9205d5f1e7e8af3ca57cbb8a503d98cb9e33e27c97a501601" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e9decce94daf47baf9e9d392f5f2557e783085f7c5e522011545d9d6858e00" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab0adafdf2b89c8b84f847780a119437a0931eca469f7b44d356f2b426dd9741" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5da98cc81873f39fd56882e1569c4677940fbc12bce6213fad1ead784192d7c8" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:209910e88afb01fd0fd403947b809ba8dba0e08a095e1f703294fda0a8fdca51" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365109d1165d78d98e33c5bfd815a9b5d7d070f578caefaabcc5771825b4ecb5" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:706abf21e60a2857acdb09502bc853ee5bce732955e7b723b10311114f033115" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bf0bd5417acf7f6a7ec3b53f2109f587be176cb35f9cf016da87e6017437a72d" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2e71b1c6ceb9c78424ae9f63a07292fb769fb890a4e7efca5554c47f33a60ea5" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80745b9770b4a38c25015b517451c817799bfb9d6499b0d13d8227ec941cb513" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-win32.whl", hash = "sha256:83b64d70520e7890453f1aa21d66fda44e7b35f1cfea95adf7b4289a51e2b479" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-win_amd64.whl", hash = "sha256:377defd66ee2003748ee93c52bcef2d14fde48fe28a0b156f88c3dbf9bc49a50" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp312-cp312-win_arm64.whl", hash = "sha256:c95caff279d49c1d6cdfe2996e6c2ad712571d3b9caaa209a404426c326c4bde" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70e790fce5f05204ef4403159857bfcd587779da78627b0babb3654f75361ebf" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9cebf1ca35f10930612d60bd0f78adfacee824c30a880e3534ba02c207cceceb" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:170406a37a5bc82c22c3274616bf6f17cc7df9c4a0a0a50449e559cb755db669" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12d4257fc9187a0ccd41b8b327d6a4e57281ab75e11dda66a9148ef2e1fb712f" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a75a33b4db105dd1c8d57839e17ee12db8d5ad18209e792fa325dbb4baeb00f4" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08a589f850803a74e0fcb16a72081cafb0d72a3cdda500106942b07e76b7bf62" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a97939d6ea44763c456bd8a617ceada2c9b96bb5b8ab3dfa0d0827df7619014" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae423c65c556f09569524b80ffd11babff61f33055ef9773d7c9fabc11ed8d" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:4dc703015fbf8764d6a8001c327a87f1823b7328d40b47ce6000c65918ad2b4f" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:968e4ffdfd35698a5fe659e5e44c508b53664870a8e61c8f9d24d3d145d30257" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:fff2b76c8e172d34771cd4d4f0ade08072385310f214f823b5a6ad4006890d32" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-win32.whl", hash = "sha256:a38a5263185407ceb599f2f035faf4589d57e73c7146d64f10577f6449e8171d" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42ae7fd6760782c975897e1fdc810f483b021b32245b0105d40f6e7a3803e4b" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313-win_arm64.whl", hash = "sha256:ad4111acc63b7384e205c27a2f15e23ac0ee21a9d77ad6f2e9cb516ec90965fb" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:440d0df7415b50084a4ba9d870480c16c5f67c0d1d4d5119e3f70925533a0edc" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71eaa38d342099405dae6484216dcf1e8e4b0bebd9b44a4e08c9b43db6a2ab67" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp313-cp313t-win_amd64.whl", hash = "sha256:555ecf7e50f1161d3f693bc49f23c82cf6cdeafc71fa37a06120772a09a38795" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:05226894a26f6f27e1deb735d7308f74ef5fa3a6de3e0135bb66cdcaee88f64b" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:85ff7911c6c3e2fd8d3779c50925f6406d770ea58ea6dde9c230d35b52b16b4a" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47f1f642a205687d59b52dc1a9a607f45e588f5a2e9eeae05edd80c7a8c47674" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df11c24e138876ace5ec6043e5cae925e34cf38af1a1b3d63589e8f7b5f5cdc4" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f0bf7f5c8f7bf345c527e8a0d72d6b26eda99c1227b0c34e7e59e181260de31" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82b887a711d341c2c47352375d73b029418f55b20bd7815446d175a70effa706" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5f1d5d6bbba484bdf220c72d8ecd0be460f4bd4c5e534a541bb2cd57589fb8b" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bf1917385ebe0f968dc5c6ab1375886d56992b93ddfe6bf52bff575d03662be" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:4f94f3ab188f44b9a73f7295663f3ecb8f2e2dd03a69c8f2ead50d37785ecb04" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:3925446673641d37c30bd84a9d597e49f72eacee8b43322c8999fa17d5ae5bc4" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:49bd51cc27adb980c7b97357ae036ce9b3c4d0bb406e84fbe16fb2d368b602a8" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-win32.whl", hash = "sha256:a31ca0cd0e4d12ea0df0077df2d487fc3eb9d7f96bbb13c3c5b88dcc21d05159" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-win_amd64.whl", hash = "sha256:1b5c4374a152e10a22175d7790e644fbd8ff58418890e07e2073ff9d4414efae" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314-win_arm64.whl", hash = "sha256:4fee76d757639b493eb600fba668f1e17475af34c17dd61db7a47e824d464ca9" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f9b9c968cfe5cd576fdd7361f47f27adeb120517e637d1b189eea1c3ece573f4" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ebc7ab67b856384aba09ed74e3e977dded40e693de18a4f197c67d0d4e6d8e" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8ae0dc57b62a762985bc7fbf636be3412394acc0ddb4ade07fe104230f1b9762" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:68f2251559b8efa99041bb63571ec7cdd2d715ba74cc82b3bc9eff824ebc8bf0" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:c7bc140c596097cb53b30546ca257dbe3f19282283190b1b5142928e5d5d3a20" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2896510fce8f4725ec518f8b9d7f015a00db249d2fd40788f442af303480063d" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ced20e62cfa0f496ba68fa5d6c7ee71114ea67e2a5da3114d6450d7f4683572a" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b04fa9ed049461a7398138c604b00550bc89e3e1151d84b81ad6dc93e39c4c06" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b3b7d9cfbfdc43c80a16638c6dc2768e3956e73031fca64e8e1a3ae744d1faeb" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eec83fc6abef04c7f9bec616e2d76ee9a6a4ae2a359b10c21d0f680e24a247ca" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6771a2d9f83c4038dfad5970a3eef215940682b2175e32bcc817bdc639019b28" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:af2385d3f98243fb733862f806c5bb9122e5fba05b373e3af40e3c82d711cef1" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6550617a0c2115be56f90c31a5370261d8ce9dbf051c3ed53b51172dd34da696" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc17b6ecf4983d298686014c92ebc955a9f9baf9f57dad4065e7906e7bee6222" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42ae9352cf211f08b04ea110563d6b1e415878eea5b4c70f6bdb17dca3b932d2" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e82947de92068b0a21681a13dd2102387197092fbe7defcfb8453e0913866506" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e244c37d5471c9acdcd282890c6c4c83747b77238bfa19429b8473586c907656" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1e798b4b304a995110d41ec93653e57975620ccb2842ba9420037985e7d7284e" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pydantic-core/2.41.1/pydantic_core-2.41.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f1fc716c0eb1663c59699b024428ad5ec2bcc6b928527b8fe28de6cb89f47efb" }, + { url = "https://files.pythonhosted.org/packages/f6/a9/ec440f02e57beabdfd804725ef1e38ac1ba00c49854d298447562e119513/pydantic_core-2.41.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4f276a6134fe1fc1daa692642a3eaa2b7b858599c49a7610816388f5e37566a1", size = 2111456, upload-time = "2025-10-06T21:10:09.824Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f9/6bc15bacfd8dcfc073a1820a564516d9c12a435a9a332d4cbbfd48828ddd/pydantic_core-2.41.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07588570a805296ece009c59d9a679dc08fab72fb337365afb4f3a14cfbfc176", size = 1915012, upload-time = "2025-10-06T21:10:11.599Z" }, + { url = "https://files.pythonhosted.org/packages/38/8a/d9edcdcdfe80bade17bed424284427c08bea892aaec11438fa52eaeaf79c/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28527e4b53400cd60ffbd9812ccb2b5135d042129716d71afd7e45bf42b855c0", size = 1973762, upload-time = "2025-10-06T21:10:13.154Z" }, + { url = "https://files.pythonhosted.org/packages/d5/b3/ff225c6d49fba4279de04677c1c876fc3dc6562fd0c53e9bfd66f58c51a8/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46a1c935c9228bad738c8a41de06478770927baedf581d172494ab36a6b96575", size = 2065386, upload-time = "2025-10-06T21:10:14.436Z" }, + { url = "https://files.pythonhosted.org/packages/47/ba/183e8c0be4321314af3fd1ae6bfc7eafdd7a49bdea5da81c56044a207316/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:447ddf56e2b7d28d200d3e9eafa936fe40485744b5a824b67039937580b3cb20", size = 2252317, upload-time = "2025-10-06T21:10:15.719Z" }, + { url = "https://files.pythonhosted.org/packages/57/c5/aab61e94fd02f45c65f1f8c9ec38bb3b33fbf001a1837c74870e97462572/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63892ead40c1160ac860b5debcc95c95c5a0035e543a8b5a4eac70dd22e995f4", size = 2373405, upload-time = "2025-10-06T21:10:17.017Z" }, + { url = "https://files.pythonhosted.org/packages/e5/4f/3aaa3bd1ea420a15acc42d7d3ccb3b0bbc5444ae2f9dbc1959f8173e16b8/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4a9543ca355e6df8fbe9c83e9faab707701e9103ae857ecb40f1c0cf8b0e94d", size = 2073794, upload-time = "2025-10-06T21:10:18.383Z" }, + { url = "https://files.pythonhosted.org/packages/58/bd/e3975cdebe03ec080ef881648de316c73f2a6be95c14fc4efb2f7bdd0d41/pydantic_core-2.41.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2611bdb694116c31e551ed82e20e39a90bea9b7ad9e54aaf2d045ad621aa7a1", size = 2194430, upload-time = "2025-10-06T21:10:19.638Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b8/6b7e7217f147d3b3105b57fb1caec3c4f667581affdfaab6d1d277e1f749/pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fecc130893a9b5f7bfe230be1bb8c61fe66a19db8ab704f808cb25a82aad0bc9", size = 2154611, upload-time = "2025-10-06T21:10:21.28Z" }, + { url = "https://files.pythonhosted.org/packages/fe/7b/239c2fe76bd8b7eef9ae2140d737368a3c6fea4fd27f8f6b4cde6baa3ce9/pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:1e2df5f8344c99b6ea5219f00fdc8950b8e6f2c422fbc1cc122ec8641fac85a1", size = 2329809, upload-time = "2025-10-06T21:10:22.678Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2e/77a821a67ff0786f2f14856d6bd1348992f695ee90136a145d7a445c1ff6/pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:35291331e9d8ed94c257bab6be1cb3a380b5eee570a2784bffc055e18040a2ea", size = 2327907, upload-time = "2025-10-06T21:10:24.447Z" }, + { url = "https://files.pythonhosted.org/packages/fd/9a/b54512bb9df7f64c586b369328c30481229b70ca6a5fcbb90b715e15facf/pydantic_core-2.41.1-cp311-cp311-win32.whl", hash = "sha256:2876a095292668d753f1a868c4a57c4ac9f6acbd8edda8debe4218d5848cf42f", size = 1989964, upload-time = "2025-10-06T21:10:25.676Z" }, + { url = "https://files.pythonhosted.org/packages/9d/72/63c9a4f1a5c950e65dd522d7dd67f167681f9d4f6ece3b80085a0329f08f/pydantic_core-2.41.1-cp311-cp311-win_amd64.whl", hash = "sha256:b92d6c628e9a338846a28dfe3fcdc1a3279388624597898b105e078cdfc59298", size = 2025158, upload-time = "2025-10-06T21:10:27.522Z" }, + { url = "https://files.pythonhosted.org/packages/d8/16/4e2706184209f61b50c231529257c12eb6bd9eb36e99ea1272e4815d2200/pydantic_core-2.41.1-cp311-cp311-win_arm64.whl", hash = "sha256:7d82ae99409eb69d507a89835488fb657faa03ff9968a9379567b0d2e2e56bc5", size = 1972297, upload-time = "2025-10-06T21:10:28.814Z" }, + { url = "https://files.pythonhosted.org/packages/ee/bc/5f520319ee1c9e25010412fac4154a72e0a40d0a19eb00281b1f200c0947/pydantic_core-2.41.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:db2f82c0ccbce8f021ad304ce35cbe02aa2f95f215cac388eed542b03b4d5eb4", size = 2099300, upload-time = "2025-10-06T21:10:30.463Z" }, + { url = "https://files.pythonhosted.org/packages/31/14/010cd64c5c3814fb6064786837ec12604be0dd46df3327cf8474e38abbbd/pydantic_core-2.41.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47694a31c710ced9205d5f1e7e8af3ca57cbb8a503d98cb9e33e27c97a501601", size = 1910179, upload-time = "2025-10-06T21:10:31.782Z" }, + { url = "https://files.pythonhosted.org/packages/8e/2e/23fc2a8a93efad52df302fdade0a60f471ecc0c7aac889801ac24b4c07d6/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e9decce94daf47baf9e9d392f5f2557e783085f7c5e522011545d9d6858e00", size = 1957225, upload-time = "2025-10-06T21:10:33.11Z" }, + { url = "https://files.pythonhosted.org/packages/b9/b6/6db08b2725b2432b9390844852e11d320281e5cea8a859c52c68001975fa/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab0adafdf2b89c8b84f847780a119437a0931eca469f7b44d356f2b426dd9741", size = 2053315, upload-time = "2025-10-06T21:10:34.87Z" }, + { url = "https://files.pythonhosted.org/packages/61/d9/4de44600f2d4514b44f3f3aeeda2e14931214b6b5bf52479339e801ce748/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5da98cc81873f39fd56882e1569c4677940fbc12bce6213fad1ead784192d7c8", size = 2224298, upload-time = "2025-10-06T21:10:36.233Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ae/dbe51187a7f35fc21b283c5250571a94e36373eb557c1cba9f29a9806dcf/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:209910e88afb01fd0fd403947b809ba8dba0e08a095e1f703294fda0a8fdca51", size = 2351797, upload-time = "2025-10-06T21:10:37.601Z" }, + { url = "https://files.pythonhosted.org/packages/b5/a7/975585147457c2e9fb951c7c8dab56deeb6aa313f3aa72c2fc0df3f74a49/pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365109d1165d78d98e33c5bfd815a9b5d7d070f578caefaabcc5771825b4ecb5", size = 2074921, upload-time = "2025-10-06T21:10:38.927Z" }, + { url = "https://files.pythonhosted.org/packages/62/37/ea94d1d0c01dec1b7d236c7cec9103baab0021f42500975de3d42522104b/pydantic_core-2.41.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:706abf21e60a2857acdb09502bc853ee5bce732955e7b723b10311114f033115", size = 2187767, upload-time = "2025-10-06T21:10:40.651Z" }, + { url = "https://files.pythonhosted.org/packages/d3/fe/694cf9fdd3a777a618c3afd210dba7b414cb8a72b1bd29b199c2e5765fee/pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bf0bd5417acf7f6a7ec3b53f2109f587be176cb35f9cf016da87e6017437a72d", size = 2136062, upload-time = "2025-10-06T21:10:42.09Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/174aeabd89916fbd2988cc37b81a59e1186e952afd2a7ed92018c22f31ca/pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2e71b1c6ceb9c78424ae9f63a07292fb769fb890a4e7efca5554c47f33a60ea5", size = 2317819, upload-time = "2025-10-06T21:10:43.974Z" }, + { url = "https://files.pythonhosted.org/packages/65/e8/e9aecafaebf53fc456314f72886068725d6fba66f11b013532dc21259343/pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80745b9770b4a38c25015b517451c817799bfb9d6499b0d13d8227ec941cb513", size = 2312267, upload-time = "2025-10-06T21:10:45.34Z" }, + { url = "https://files.pythonhosted.org/packages/35/2f/1c2e71d2a052f9bb2f2df5a6a05464a0eb800f9e8d9dd800202fe31219e1/pydantic_core-2.41.1-cp312-cp312-win32.whl", hash = "sha256:83b64d70520e7890453f1aa21d66fda44e7b35f1cfea95adf7b4289a51e2b479", size = 1990927, upload-time = "2025-10-06T21:10:46.738Z" }, + { url = "https://files.pythonhosted.org/packages/b1/78/562998301ff2588b9c6dcc5cb21f52fa919d6e1decc75a35055feb973594/pydantic_core-2.41.1-cp312-cp312-win_amd64.whl", hash = "sha256:377defd66ee2003748ee93c52bcef2d14fde48fe28a0b156f88c3dbf9bc49a50", size = 2034703, upload-time = "2025-10-06T21:10:48.524Z" }, + { url = "https://files.pythonhosted.org/packages/b2/53/d95699ce5a5cdb44bb470bd818b848b9beadf51459fd4ea06667e8ede862/pydantic_core-2.41.1-cp312-cp312-win_arm64.whl", hash = "sha256:c95caff279d49c1d6cdfe2996e6c2ad712571d3b9caaa209a404426c326c4bde", size = 1972719, upload-time = "2025-10-06T21:10:50.256Z" }, + { url = "https://files.pythonhosted.org/packages/27/8a/6d54198536a90a37807d31a156642aae7a8e1263ed9fe6fc6245defe9332/pydantic_core-2.41.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70e790fce5f05204ef4403159857bfcd587779da78627b0babb3654f75361ebf", size = 2105825, upload-time = "2025-10-06T21:10:51.719Z" }, + { url = "https://files.pythonhosted.org/packages/4f/2e/4784fd7b22ac9c8439db25bf98ffed6853d01e7e560a346e8af821776ccc/pydantic_core-2.41.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9cebf1ca35f10930612d60bd0f78adfacee824c30a880e3534ba02c207cceceb", size = 1910126, upload-time = "2025-10-06T21:10:53.145Z" }, + { url = "https://files.pythonhosted.org/packages/f3/92/31eb0748059ba5bd0aa708fb4bab9fcb211461ddcf9e90702a6542f22d0d/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:170406a37a5bc82c22c3274616bf6f17cc7df9c4a0a0a50449e559cb755db669", size = 1961472, upload-time = "2025-10-06T21:10:55.754Z" }, + { url = "https://files.pythonhosted.org/packages/ab/91/946527792275b5c4c7dde4cfa3e81241bf6900e9fee74fb1ba43e0c0f1ab/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12d4257fc9187a0ccd41b8b327d6a4e57281ab75e11dda66a9148ef2e1fb712f", size = 2063230, upload-time = "2025-10-06T21:10:57.179Z" }, + { url = "https://files.pythonhosted.org/packages/31/5d/a35c5d7b414e5c0749f1d9f0d159ee2ef4bab313f499692896b918014ee3/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a75a33b4db105dd1c8d57839e17ee12db8d5ad18209e792fa325dbb4baeb00f4", size = 2229469, upload-time = "2025-10-06T21:10:59.409Z" }, + { url = "https://files.pythonhosted.org/packages/21/4d/8713737c689afa57ecfefe38db78259d4484c97aa494979e6a9d19662584/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08a589f850803a74e0fcb16a72081cafb0d72a3cdda500106942b07e76b7bf62", size = 2347986, upload-time = "2025-10-06T21:11:00.847Z" }, + { url = "https://files.pythonhosted.org/packages/f6/ec/929f9a3a5ed5cda767081494bacd32f783e707a690ce6eeb5e0730ec4986/pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a97939d6ea44763c456bd8a617ceada2c9b96bb5b8ab3dfa0d0827df7619014", size = 2072216, upload-time = "2025-10-06T21:11:02.43Z" }, + { url = "https://files.pythonhosted.org/packages/26/55/a33f459d4f9cc8786d9db42795dbecc84fa724b290d7d71ddc3d7155d46a/pydantic_core-2.41.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae423c65c556f09569524b80ffd11babff61f33055ef9773d7c9fabc11ed8d", size = 2193047, upload-time = "2025-10-06T21:11:03.787Z" }, + { url = "https://files.pythonhosted.org/packages/77/af/d5c6959f8b089f2185760a2779079e3c2c411bfc70ea6111f58367851629/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:4dc703015fbf8764d6a8001c327a87f1823b7328d40b47ce6000c65918ad2b4f", size = 2140613, upload-time = "2025-10-06T21:11:05.607Z" }, + { url = "https://files.pythonhosted.org/packages/58/e5/2c19bd2a14bffe7fabcf00efbfbd3ac430aaec5271b504a938ff019ac7be/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:968e4ffdfd35698a5fe659e5e44c508b53664870a8e61c8f9d24d3d145d30257", size = 2327641, upload-time = "2025-10-06T21:11:07.143Z" }, + { url = "https://files.pythonhosted.org/packages/93/ef/e0870ccda798c54e6b100aff3c4d49df5458fd64217e860cb9c3b0a403f4/pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:fff2b76c8e172d34771cd4d4f0ade08072385310f214f823b5a6ad4006890d32", size = 2318229, upload-time = "2025-10-06T21:11:08.73Z" }, + { url = "https://files.pythonhosted.org/packages/b1/4b/c3b991d95f5deb24d0bd52e47bcf716098fa1afe0ce2d4bd3125b38566ba/pydantic_core-2.41.1-cp313-cp313-win32.whl", hash = "sha256:a38a5263185407ceb599f2f035faf4589d57e73c7146d64f10577f6449e8171d", size = 1997911, upload-time = "2025-10-06T21:11:10.329Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ce/5c316fd62e01f8d6be1b7ee6b54273214e871772997dc2c95e204997a055/pydantic_core-2.41.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42ae7fd6760782c975897e1fdc810f483b021b32245b0105d40f6e7a3803e4b", size = 2034301, upload-time = "2025-10-06T21:11:12.113Z" }, + { url = "https://files.pythonhosted.org/packages/29/41/902640cfd6a6523194123e2c3373c60f19006447f2fb06f76de4e8466c5b/pydantic_core-2.41.1-cp313-cp313-win_arm64.whl", hash = "sha256:ad4111acc63b7384e205c27a2f15e23ac0ee21a9d77ad6f2e9cb516ec90965fb", size = 1977238, upload-time = "2025-10-06T21:11:14.1Z" }, + { url = "https://files.pythonhosted.org/packages/04/04/28b040e88c1b89d851278478842f0bdf39c7a05da9e850333c6c8cbe7dfa/pydantic_core-2.41.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:440d0df7415b50084a4ba9d870480c16c5f67c0d1d4d5119e3f70925533a0edc", size = 1875626, upload-time = "2025-10-06T21:11:15.69Z" }, + { url = "https://files.pythonhosted.org/packages/d6/58/b41dd3087505220bb58bc81be8c3e8cbc037f5710cd3c838f44f90bdd704/pydantic_core-2.41.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71eaa38d342099405dae6484216dcf1e8e4b0bebd9b44a4e08c9b43db6a2ab67", size = 2045708, upload-time = "2025-10-06T21:11:17.258Z" }, + { url = "https://files.pythonhosted.org/packages/d7/b8/760f23754e40bf6c65b94a69b22c394c24058a0ef7e2aa471d2e39219c1a/pydantic_core-2.41.1-cp313-cp313t-win_amd64.whl", hash = "sha256:555ecf7e50f1161d3f693bc49f23c82cf6cdeafc71fa37a06120772a09a38795", size = 1997171, upload-time = "2025-10-06T21:11:18.822Z" }, + { url = "https://files.pythonhosted.org/packages/41/12/cec246429ddfa2778d2d6301eca5362194dc8749ecb19e621f2f65b5090f/pydantic_core-2.41.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:05226894a26f6f27e1deb735d7308f74ef5fa3a6de3e0135bb66cdcaee88f64b", size = 2107836, upload-time = "2025-10-06T21:11:20.432Z" }, + { url = "https://files.pythonhosted.org/packages/20/39/baba47f8d8b87081302498e610aefc37142ce6a1cc98b2ab6b931a162562/pydantic_core-2.41.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:85ff7911c6c3e2fd8d3779c50925f6406d770ea58ea6dde9c230d35b52b16b4a", size = 1904449, upload-time = "2025-10-06T21:11:22.185Z" }, + { url = "https://files.pythonhosted.org/packages/50/32/9a3d87cae2c75a5178334b10358d631bd094b916a00a5993382222dbfd92/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47f1f642a205687d59b52dc1a9a607f45e588f5a2e9eeae05edd80c7a8c47674", size = 1961750, upload-time = "2025-10-06T21:11:24.348Z" }, + { url = "https://files.pythonhosted.org/packages/27/42/a96c9d793a04cf2a9773bff98003bb154087b94f5530a2ce6063ecfec583/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df11c24e138876ace5ec6043e5cae925e34cf38af1a1b3d63589e8f7b5f5cdc4", size = 2063305, upload-time = "2025-10-06T21:11:26.556Z" }, + { url = "https://files.pythonhosted.org/packages/3e/8d/028c4b7d157a005b1f52c086e2d4b0067886b213c86220c1153398dbdf8f/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f0bf7f5c8f7bf345c527e8a0d72d6b26eda99c1227b0c34e7e59e181260de31", size = 2228959, upload-time = "2025-10-06T21:11:28.426Z" }, + { url = "https://files.pythonhosted.org/packages/08/f7/ee64cda8fcc9ca3f4716e6357144f9ee71166775df582a1b6b738bf6da57/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82b887a711d341c2c47352375d73b029418f55b20bd7815446d175a70effa706", size = 2345421, upload-time = "2025-10-06T21:11:30.226Z" }, + { url = "https://files.pythonhosted.org/packages/13/c0/e8ec05f0f5ee7a3656973ad9cd3bc73204af99f6512c1a4562f6fb4b3f7d/pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5f1d5d6bbba484bdf220c72d8ecd0be460f4bd4c5e534a541bb2cd57589fb8b", size = 2065288, upload-time = "2025-10-06T21:11:32.019Z" }, + { url = "https://files.pythonhosted.org/packages/0a/25/d77a73ff24e2e4fcea64472f5e39b0402d836da9b08b5361a734d0153023/pydantic_core-2.41.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bf1917385ebe0f968dc5c6ab1375886d56992b93ddfe6bf52bff575d03662be", size = 2189759, upload-time = "2025-10-06T21:11:33.753Z" }, + { url = "https://files.pythonhosted.org/packages/66/45/4a4ebaaae12a740552278d06fe71418c0f2869537a369a89c0e6723b341d/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:4f94f3ab188f44b9a73f7295663f3ecb8f2e2dd03a69c8f2ead50d37785ecb04", size = 2140747, upload-time = "2025-10-06T21:11:35.781Z" }, + { url = "https://files.pythonhosted.org/packages/da/6d/b727ce1022f143194a36593243ff244ed5a1eb3c9122296bf7e716aa37ba/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:3925446673641d37c30bd84a9d597e49f72eacee8b43322c8999fa17d5ae5bc4", size = 2327416, upload-time = "2025-10-06T21:11:37.75Z" }, + { url = "https://files.pythonhosted.org/packages/6f/8c/02df9d8506c427787059f87c6c7253435c6895e12472a652d9616ee0fc95/pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:49bd51cc27adb980c7b97357ae036ce9b3c4d0bb406e84fbe16fb2d368b602a8", size = 2318138, upload-time = "2025-10-06T21:11:39.463Z" }, + { url = "https://files.pythonhosted.org/packages/98/67/0cf429a7d6802536941f430e6e3243f6d4b68f41eeea4b242372f1901794/pydantic_core-2.41.1-cp314-cp314-win32.whl", hash = "sha256:a31ca0cd0e4d12ea0df0077df2d487fc3eb9d7f96bbb13c3c5b88dcc21d05159", size = 1998429, upload-time = "2025-10-06T21:11:41.989Z" }, + { url = "https://files.pythonhosted.org/packages/38/60/742fef93de5d085022d2302a6317a2b34dbfe15258e9396a535c8a100ae7/pydantic_core-2.41.1-cp314-cp314-win_amd64.whl", hash = "sha256:1b5c4374a152e10a22175d7790e644fbd8ff58418890e07e2073ff9d4414efae", size = 2028870, upload-time = "2025-10-06T21:11:43.66Z" }, + { url = "https://files.pythonhosted.org/packages/31/38/cdd8ccb8555ef7720bd7715899bd6cfbe3c29198332710e1b61b8f5dd8b8/pydantic_core-2.41.1-cp314-cp314-win_arm64.whl", hash = "sha256:4fee76d757639b493eb600fba668f1e17475af34c17dd61db7a47e824d464ca9", size = 1974275, upload-time = "2025-10-06T21:11:45.476Z" }, + { url = "https://files.pythonhosted.org/packages/e7/7e/8ac10ccb047dc0221aa2530ec3c7c05ab4656d4d4bd984ee85da7f3d5525/pydantic_core-2.41.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f9b9c968cfe5cd576fdd7361f47f27adeb120517e637d1b189eea1c3ece573f4", size = 1875124, upload-time = "2025-10-06T21:11:47.591Z" }, + { url = "https://files.pythonhosted.org/packages/c3/e4/7d9791efeb9c7d97e7268f8d20e0da24d03438a7fa7163ab58f1073ba968/pydantic_core-2.41.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ebc7ab67b856384aba09ed74e3e977dded40e693de18a4f197c67d0d4e6d8e", size = 2043075, upload-time = "2025-10-06T21:11:49.542Z" }, + { url = "https://files.pythonhosted.org/packages/2d/c3/3f6e6b2342ac11ac8cd5cb56e24c7b14afa27c010e82a765ffa5f771884a/pydantic_core-2.41.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8ae0dc57b62a762985bc7fbf636be3412394acc0ddb4ade07fe104230f1b9762", size = 1995341, upload-time = "2025-10-06T21:11:51.497Z" }, + { url = "https://files.pythonhosted.org/packages/16/89/d0afad37ba25f5801735af1472e650b86baad9fe807a42076508e4824a2a/pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:68f2251559b8efa99041bb63571ec7cdd2d715ba74cc82b3bc9eff824ebc8bf0", size = 2124001, upload-time = "2025-10-07T10:49:54.369Z" }, + { url = "https://files.pythonhosted.org/packages/8e/c4/08609134b34520568ddebb084d9ed0a2a3f5f52b45739e6e22cb3a7112eb/pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:c7bc140c596097cb53b30546ca257dbe3f19282283190b1b5142928e5d5d3a20", size = 1941841, upload-time = "2025-10-07T10:49:56.248Z" }, + { url = "https://files.pythonhosted.org/packages/2a/43/94a4877094e5fe19a3f37e7e817772263e2c573c94f1e3fa2b1eee56ef3b/pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2896510fce8f4725ec518f8b9d7f015a00db249d2fd40788f442af303480063d", size = 1961129, upload-time = "2025-10-07T10:49:58.298Z" }, + { url = "https://files.pythonhosted.org/packages/a2/30/23a224d7e25260eb5f69783a63667453037e07eb91ff0e62dabaadd47128/pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ced20e62cfa0f496ba68fa5d6c7ee71114ea67e2a5da3114d6450d7f4683572a", size = 2148770, upload-time = "2025-10-07T10:49:59.959Z" }, + { url = "https://files.pythonhosted.org/packages/2b/3e/a51c5f5d37b9288ba30683d6e96f10fa8f1defad1623ff09f1020973b577/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b04fa9ed049461a7398138c604b00550bc89e3e1151d84b81ad6dc93e39c4c06", size = 2115344, upload-time = "2025-10-07T10:50:02.466Z" }, + { url = "https://files.pythonhosted.org/packages/5a/bd/389504c9e0600ef4502cd5238396b527afe6ef8981a6a15cd1814fc7b434/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b3b7d9cfbfdc43c80a16638c6dc2768e3956e73031fca64e8e1a3ae744d1faeb", size = 1927994, upload-time = "2025-10-07T10:50:04.379Z" }, + { url = "https://files.pythonhosted.org/packages/ff/9c/5111c6b128861cb792a4c082677e90dac4f2e090bb2e2fe06aa5b2d39027/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eec83fc6abef04c7f9bec616e2d76ee9a6a4ae2a359b10c21d0f680e24a247ca", size = 1959394, upload-time = "2025-10-07T10:50:06.335Z" }, + { url = "https://files.pythonhosted.org/packages/14/3f/cfec8b9a0c48ce5d64409ec5e1903cb0b7363da38f14b41de2fcb3712700/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6771a2d9f83c4038dfad5970a3eef215940682b2175e32bcc817bdc639019b28", size = 2147365, upload-time = "2025-10-07T10:50:07.978Z" }, + { url = "https://files.pythonhosted.org/packages/e6/6c/fa3e45c2b054a1e627a89a364917f12cbe3abc3e91b9004edaae16e7b3c5/pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:af2385d3f98243fb733862f806c5bb9122e5fba05b373e3af40e3c82d711cef1", size = 2112094, upload-time = "2025-10-07T10:50:25.513Z" }, + { url = "https://files.pythonhosted.org/packages/e5/17/7eebc38b4658cc8e6902d0befc26388e4c2a5f2e179c561eeb43e1922c7b/pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6550617a0c2115be56f90c31a5370261d8ce9dbf051c3ed53b51172dd34da696", size = 1935300, upload-time = "2025-10-07T10:50:27.715Z" }, + { url = "https://files.pythonhosted.org/packages/2b/00/9fe640194a1717a464ab861d43595c268830f98cb1e2705aa134b3544b70/pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc17b6ecf4983d298686014c92ebc955a9f9baf9f57dad4065e7906e7bee6222", size = 1970417, upload-time = "2025-10-07T10:50:29.573Z" }, + { url = "https://files.pythonhosted.org/packages/b2/ad/f4cdfaf483b78ee65362363e73b6b40c48e067078d7b146e8816d5945ad6/pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42ae9352cf211f08b04ea110563d6b1e415878eea5b4c70f6bdb17dca3b932d2", size = 2190745, upload-time = "2025-10-07T10:50:31.48Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c1/18f416d40a10f44e9387497ba449f40fdb1478c61ba05c4b6bdb82300362/pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e82947de92068b0a21681a13dd2102387197092fbe7defcfb8453e0913866506", size = 2150888, upload-time = "2025-10-07T10:50:33.477Z" }, + { url = "https://files.pythonhosted.org/packages/42/30/134c8a921630d8a88d6f905a562495a6421e959a23c19b0f49b660801d67/pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e244c37d5471c9acdcd282890c6c4c83747b77238bfa19429b8473586c907656", size = 2324489, upload-time = "2025-10-07T10:50:36.48Z" }, + { url = "https://files.pythonhosted.org/packages/9c/48/a9263aeaebdec81e941198525b43edb3b44f27cfa4cb8005b8d3eb8dec72/pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1e798b4b304a995110d41ec93653e57975620ccb2842ba9420037985e7d7284e", size = 2322763, upload-time = "2025-10-07T10:50:38.751Z" }, + { url = "https://files.pythonhosted.org/packages/1d/62/755d2bd2593f701c5839fc084e9c2c5e2418f460383ad04e3b5d0befc3ca/pydantic_core-2.41.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f1fc716c0eb1663c59699b024428ad5ec2bcc6b928527b8fe28de6cb89f47efb", size = 2144046, upload-time = "2025-10-07T10:50:40.686Z" }, ] [[package]] name = "pygments" version = "2.20.0" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pygments/2.20/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pygments/2.20/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176" }, + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] [[package]] name = "pytest" version = "9.0.3" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "iniconfig" }, @@ -364,154 +364,154 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pytest/9.0.3/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pytest/9.0.3/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9" }, + { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, ] [[package]] name = "pytest-asyncio" version = "1.3.0" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pytest-asyncio/1.3/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5" } +sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pytest-asyncio/1.3/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5" }, + { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, ] [[package]] name = "pyyaml" version = "6.0.3" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b" }, + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] [[package]] name = "setuptools" version = "83.0.0" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/setuptools/83/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/34/26/f5d29e25ffdb535afef2d35cdb55b325298f96debd670da4c325e08d70f4/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef", size = 1154254, upload-time = "2026-07-04T15:31:22.699Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/setuptools/83/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3" }, + { url = "https://files.pythonhosted.org/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3", size = 1008090, upload-time = "2026-07-04T15:31:20.885Z" }, ] [[package]] name = "tomli" version = "2.3.0" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba" }, - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/tomli/2.3/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b" }, + { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" }, + { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" }, + { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" }, + { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" }, + { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" }, + { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" }, + { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" }, + { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" }, + { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" }, + { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" }, + { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" }, + { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" }, + { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" }, + { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" }, + { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" }, + { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" }, + { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" }, + { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" }, + { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" }, + { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" }, + { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" }, + { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" }, + { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" }, + { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" }, + { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" }, + { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" }, + { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" }, + { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" }, + { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" }, + { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" }, + { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" }, + { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" }, + { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" }, + { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" }, + { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" }, + { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" }, + { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" }, + { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, ] [[package]] name = "typing-extensions" version = "4.15.0" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/typing-extensions/4.15/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466" } +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/typing-extensions/4.15/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548" }, + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] [[package]] name = "typing-inspection" version = "0.4.2" -source = { registry = "https://packagefeedproxy.microsoft.io/pypi/simple/" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/typing-inspection/0.4.2/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464" } +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } wheels = [ - { url = "https://ms-feed-25.pkgs.visualstudio.com/6f084628-a36d-42cb-934d-057357e379dc/_packaging/49d7402f-07bb-4b18-a9ce-086e6e98a554/pypi/download/typing-inspection/0.4.2/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7" }, + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, ]