diff --git a/Cargo.lock b/Cargo.lock index 7aca4138..caab34e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3177,7 +3177,7 @@ dependencies = [ [[package]] name = "graphql-orm-ai" -version = "0.106.0" +version = "0.106.1" dependencies = [ "agql-auth", "async-graphql", diff --git a/crates/graphql-orm-ai/CHANGELOG.md b/crates/graphql-orm-ai/CHANGELOG.md index 7b33f535..821c0ca9 100644 --- a/crates/graphql-orm-ai/CHANGELOG.md +++ b/crates/graphql-orm-ai/CHANGELOG.md @@ -18,6 +18,15 @@ checkpoint facts. For the current workspace baseline and active gates, use the [implementation status](docs/implementation-status.md) and the central [AI production-readiness plan](../../docs/plans/active/ai-production-readiness/README.md). +## [0.106.1] - 2026-10-01 + +### Fixed + +- Raise the finite provider-session static bootstrap and Grok SDK definition + bounds from 64 to 128. Larger reviewed catalogues can start native sessions + without omitting tools; exact fingerprints, duplicate/schema rejection, + provider frame/byte limits and callback bounds remain mandatory. + ## [Unreleased] ### Added diff --git a/crates/graphql-orm-ai/Cargo.toml b/crates/graphql-orm-ai/Cargo.toml index bda0e129..6156495f 100644 --- a/crates/graphql-orm-ai/Cargo.toml +++ b/crates/graphql-orm-ai/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graphql-orm-ai" -version = "0.106.0" +version = "0.106.1" edition = "2024" authors = ["Toby Martin "] description = "Project-agnostic AI agent runtime for graphql-orm applications" diff --git a/crates/graphql-orm-ai/MIGRATION.md b/crates/graphql-orm-ai/MIGRATION.md index 75cdce69..bef806aa 100644 --- a/crates/graphql-orm-ai/MIGRATION.md +++ b/crates/graphql-orm-ai/MIGRATION.md @@ -19,6 +19,22 @@ they describe. For the current workspace baseline and active delivery gates, use [implementation status](docs/implementation-status.md) and the central [AI production-readiness plan](../../docs/plans/active/ai-production-readiness/README.md). +## 0.106.0 to 0.106.1 + +Provider-session bindings now admit up to 128 static bootstrap fingerprints; +the Grok SDK broker admits up to 128 exact definitions. Count the complete +projected provider surface, including fixed broker definitions, against its +own limit. This fixes admission of reviewed catalogues above the former 64-tool +ceiling without granting discovery or execution authority. Existing session +fingerprints remain byte-identical for unchanged inputs. + +No data migration, DDL, GraphQL SDL, protected payload or schema-module change +is required. The widened bound affects construction only; the persisted format +and fingerprint computation are unchanged. Older readers cannot construct a +new session with more than 64 bootstrap fingerprints, so retain this release +when configuring such a catalogue. All current-principal, approval, egress, +budget, provider byte/frame and callback checks remain authoritative. + ## 0.105.2 to 0.106.0 Hosts can return `AiToolAuthorizationDecision::require_authentication` for an diff --git a/crates/graphql-orm-ai/README.md b/crates/graphql-orm-ai/README.md index 2ea3ef5a..6a9c65c0 100644 --- a/crates/graphql-orm-ai/README.md +++ b/crates/graphql-orm-ai/README.md @@ -10,6 +10,12 @@ supersedes: [] # graphql-orm-ai +Version `0.106.1` admits up to 128 exact static bootstrap fingerprints and +Grok SDK definitions, matching the existing Codex definition ceiling. Hosts +with larger reviewed tool catalogues no longer fail provider-session admission +at the former 64-tool limit. Complete projected surfaces, including broker tools, +still obey each provider’s definition and byte limits. + Version `0.106.0` lets trusted host policy distinguish required authentication renewal from permission denial before native tool admission. The model receives a protected, audited, non-retryable preflight failure and can continue permitted diff --git a/crates/graphql-orm-ai/docs/capability-discovery-and-execution.md b/crates/graphql-orm-ai/docs/capability-discovery-and-execution.md index 6629e290..c4fca375 100644 --- a/crates/graphql-orm-ai/docs/capability-discovery-and-execution.md +++ b/crates/graphql-orm-ai/docs/capability-discovery-and-execution.md @@ -105,6 +105,11 @@ negotiation metadata, never authority. | `ProviderDeferred` | Already-filtered definitions marked for reviewed native deferred loading | Native provider tool search | | `FixedBroker` | Exact static bootstrap plus frozen discover/describe/execute definitions | Retained sessions whose generated definitions cannot change | +Provider-session bindings admit at most 128 exact static bootstrap fingerprints. +Grok SDK and Codex dynamic tools independently enforce 128-definition ceilings +on the **complete** surface, including fixed broker definitions. Fingerprint +admission does not relax provider byte/frame limits or application authority. + `prepare_client_deferred_continuation` accepts only definitions matching the crate-owned loaded bindings and the configured selection count. Fixed broker arguments use a closed scalar argument list and public selection paths; the diff --git a/crates/graphql-orm-ai/src/capability_delivery.rs b/crates/graphql-orm-ai/src/capability_delivery.rs index 7ed6a949..e3b12ada 100644 --- a/crates/graphql-orm-ai/src/capability_delivery.rs +++ b/crates/graphql-orm-ai/src/capability_delivery.rs @@ -481,7 +481,9 @@ impl AiProviderCapabilitySessionBinding { /// /// # Errors /// - /// Rejects missing, unbounded, or control-character-bearing values. + /// Rejects missing, unbounded, or control-character-bearing values and more + /// than 128 static bootstrap fingerprints. Provider-specific definition and + /// byte limits still apply independently to the complete projected surface. #[allow(clippy::too_many_arguments)] pub fn new( delivery_mode: AiCapabilityDeliveryMode, @@ -505,7 +507,7 @@ impl AiProviderCapabilitySessionBinding { } if !crate::valid_sha256(&capability_index_fingerprint) || !crate::valid_sha256(®istration_identity) - || static_bootstrap_tool_fingerprints.len() > 64 + || static_bootstrap_tool_fingerprints.len() > 128 || static_bootstrap_tool_fingerprints .iter() .any(|value| !crate::valid_sha256(value)) @@ -2787,6 +2789,30 @@ mod tests { )); } + #[test] + fn provider_binding_accepts_large_exact_bootstrap_and_keeps_a_finite_limit() { + let make = |count: usize| { + AiProviderCapabilitySessionBinding::new( + AiCapabilityDeliveryMode::FixedBroker, + "a".repeat(64), + (0..count).map(|index| format!("{index:064x}")).collect(), + "test-projection/v1", + "test-model", + ModelReasoningEffort::Low, + "b".repeat(64), + ) + }; + for count in [64, 65, 86, 128] { + let binding = make(count).expect("bounded larger host bootstrap should be admitted"); + assert_eq!(binding.static_bootstrap_tool_fingerprints().len(), count); + } + assert!(make(129).is_err()); + assert_ne!( + make(85).unwrap().fingerprint(), + make(86).unwrap().fingerprint() + ); + } + #[test] fn retained_binding_changes_for_every_execution_relevant_dimension() { let make = |mode, effort, model: &str| { diff --git a/crates/graphql-orm-ai/src/providers/grok_acp.rs b/crates/graphql-orm-ai/src/providers/grok_acp.rs index aa525b1b..870f9200 100644 --- a/crates/graphql-orm-ai/src/providers/grok_acp.rs +++ b/crates/graphql-orm-ai/src/providers/grok_acp.rs @@ -14,7 +14,7 @@ use crate::{ }; const MAX_FRAME_BYTES: usize = 16 * 1024 * 1024; -const MAX_DEFINITIONS: usize = 64; +const MAX_DEFINITIONS: usize = 128; pub(super) const MAX_SDK_CALLS: usize = 4096; fn rejected() -> ProviderError { @@ -81,7 +81,7 @@ impl AiGrokAcpSdkBroker { /// /// # Errors /// Rejects malformed identities, duplicate or invalid definitions, more - /// than 64 definitions or 4096 callbacks, frames over 16 MiB, or total + /// than 128 definitions or 4096 callbacks, frames over 16 MiB, or total /// bytes over 64 MiB. pub fn new( server_id: String, @@ -475,6 +475,39 @@ mod tests { )); } + #[test] + fn exact_definition_capacity_admits_large_bootstrap_and_rejects_overflow() { + let make = |count: usize| { + let tools = (0..count) + .map(|index| { + let mut tool = definition(); + tool.tool_id = format!("test.tool.{index}"); + tool.provider_name = format!("tool_{index}"); + tool.fingerprint = format!("{index:064x}"); + tool + }) + .collect(); + AiGrokAcpSdkBroker::new( + "sdk-1".into(), + "prompt-1".into(), + tools, + 1, + 1024 * 1024, + 4 * 1024 * 1024, + ) + }; + for count in [65, 90, 128] { + let mut broker = make(count).expect("bounded exact definitions should be admitted"); + initialize(&mut broker); + assert!(matches!( + broker.accept(&request(2, "tools/list", json!({}))), + Ok(AiGrokAcpSdkInbound::Response(_)) + )); + assert_eq!(broker.tools.len(), count); + } + assert!(matches!(make(129), Err(ProviderError::InvalidRequest))); + } + #[test] fn callback_capacity_is_independent_of_definition_count_and_remains_bounded() { let mut broker = AiGrokAcpSdkBroker::new( diff --git a/docs/reference/workspace-packages.md b/docs/reference/workspace-packages.md index 4b7cea44..040f2b7d 100644 --- a/docs/reference/workspace-packages.md +++ b/docs/reference/workspace-packages.md @@ -19,7 +19,7 @@ changes. | Package | Version | Path | Default features | Direct internal dependencies | | --- | --- | --- | --- | --- | | `graphql-orm` | `0.33.3` | `crates/graphql-orm` | `sqlite` | `graphql-orm-macros`, `graphql-orm-operation-catalog`, `graphql-orm-router-protocol` (dev-only) | -| `graphql-orm-ai` | `0.106.0` | `crates/graphql-orm-ai` | `sqlite` | `graphql-orm`, `graphql-orm-ai-tool-profiles`, `graphql-orm-storage` | +| `graphql-orm-ai` | `0.106.1` | `crates/graphql-orm-ai` | `sqlite` | `graphql-orm`, `graphql-orm-ai-tool-profiles`, `graphql-orm-storage` | | `graphql-orm-ai-tool-profiles` | `0.15.0` | `crates/graphql-orm-ai-tool-profiles` | none | `graphql-orm-operation-catalog`, `graphql-orm-router-protocol` (dev-only) | | `graphql-orm-backup` | `0.7.2` | `crates/graphql-orm-backup` | `local` | `graphql-orm` (optional), `graphql-orm-storage` | | `graphql-orm-macros` | `0.33.3` | `crates/graphql-orm-macros` | `sqlite` | none |