Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
6ddbcf5
Add typed tool media profiles and protected source disclosure foundation
Oct 13, 2026
acffda1
Retain protected media provenance and reauthorize native history deli…
Oct 13, 2026
2f5c30b
Require fresh source delivery for restricted text and preserve aggreg…
Oct 13, 2026
835e89b
Complete unavailable-source error code and assert image custody separ…
Oct 13, 2026
12de658
Revalidate protected sources at transport and enforce aggregate pixels
Oct 13, 2026
5c07ad6
Deliver reopened original pixels as typed continuation input
Oct 13, 2026
b2c581f
Keep unavailable protected sources closed in background retrieval
Oct 13, 2026
fe5bf70
Test retained effect reopening and genuine continuation image content
Oct 13, 2026
4017bb5
Use admitted JSON Schema in media owner fixture
Oct 13, 2026
801b7e8
Exercise approved media custody and bound complete history pixels
Oct 13, 2026
554add9
Retain completed outputs before media admission and use genuine appro…
Oct 13, 2026
2c7f2e2
Qualify native approval source history and current owner cancellation
Oct 13, 2026
0c9e808
Box bounded source passes and align native fixture with admitted prov…
Oct 13, 2026
91a1900
Reject owning image results that return beyond the resolution deadline
Oct 13, 2026
df759ac
Bind actual effect fences and close queued protected media delivery
Oct 13, 2026
09b6f0d
Keep retained lifecycle state out of native coordinator stack frames
Oct 13, 2026
29dc3e6
Fence Codex protected transport and retain completed output before ac…
Oct 13, 2026
0bfa4df
Use actual adapter capabilities in legacy factory regression
Oct 13, 2026
8251673
Bind complete media transport and retained executor provenance
Oct 13, 2026
c865e1c
Match owned attachment IDs and persistence feature bounds
Oct 13, 2026
3cfb4b3
Retain effective complete transport proof on mutable owning plan
Oct 13, 2026
8b9a7bb
Authorize native approved source wrappers and isolate effect future c…
Oct 13, 2026
45c49e8
Scope protected-source guard to actual approved continuation
Oct 13, 2026
982d421
Preflight exact protected tool outputs before legacy factory launch
Oct 13, 2026
af7d39e
Use a fresh valid legacy ordinary transport plan in preflight regression
Oct 13, 2026
e92b55d
Expose owning source matcher and keep image admission strict lint clean
Oct 13, 2026
42b2279
test(ai): fix typed media scoped lint diagnostics
Oct 13, 2026
d1b133b
test(ai): preserve edition 2024 fixture formatting
Oct 13, 2026
67927be
feat(ai): checkpoint fenced static offering continuation
Oct 13, 2026
a6bb444
feat(ai): preserve withheld native task context through verified cleanup
Oct 13, 2026
fc38838
fix(ai): correct frozen static continuation type diagnostics
Oct 13, 2026
08f0925
Bind reset continuation to its original task after later messages
Oct 13, 2026
5d629c2
Exercise owning quota retry after one approved retained effect
Oct 13, 2026
4822d1a
Require exact confirmed unstarted cleanup before offering retry
Oct 13, 2026
6038945
Count actual admitted pixels after native cursor reset
Oct 13, 2026
39c12b3
Document exact native retry custody and conservative quote contract
Oct 13, 2026
f3095f9
Correct native reset fixture types and source guard construction
Oct 13, 2026
85ec68f
Heap-bound private retry and retained reset futures
Oct 13, 2026
4056ed3
fix(ai): bound unstarted offering planner by lease and cancellation
Oct 13, 2026
cb4662d
fix(ai): preserve replacement freeze during custody recheck
Oct 13, 2026
fc7724f
test(ai): price complete native task continuation before quota retry
Oct 13, 2026
6d4b628
test(ai): map exact fixture projection errors explicitly
Oct 13, 2026
742b04a
fix(ai): include verified native result in whole inference proof
Oct 13, 2026
afd45a7
test(ai): assert wrong source withholding preserves exact task custody
Oct 13, 2026
ff404aa
test(ai): retain exact original event and client identity on withholding
Oct 13, 2026
738a393
fix(ai): require current owning session for original task context
Oct 13, 2026
59be331
test(ai): price complete direct-reset request within finite task limits
Oct 13, 2026
185e3fc
test(ai): retain owning native responder for task reset continuation
Oct 13, 2026
6155597
test(ai): distinguish cursor rebind from human message events
Oct 13, 2026
881cb8a
test(ai): apply ordinary policy to withheld source-free continuation
Oct 13, 2026
3fc71e7
docs(ai): prepare typed media workspace release notes
Oct 13, 2026
2255dab
docs(ai): document pre-1.0 source compatibility boundaries
Oct 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 77 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ futures = "0.3"
getrandom = "0.3"
graphql-composition = "=0.12.2"
graphql-orm = { path = "crates/graphql-orm", version = "0.43.1", default-features = false }
graphql-orm-ai-tool-profiles = { path = "crates/graphql-orm-ai-tool-profiles", version = "0.15.0" }
graphql-orm-ai-tool-profiles = { path = "crates/graphql-orm-ai-tool-profiles", version = "0.16.0" }
graphql-orm-backup = { path = "crates/graphql-orm-backup", version = "0.7.2", default-features = false }
graphql-orm-operation-catalog = { path = "crates/graphql-orm-operation-catalog", version = "0.4.0" }
graphql-orm-router-protocol = { path = "crates/graphql-orm-router-protocol", version = "0.2.1" }
Expand Down
16 changes: 16 additions & 0 deletions crates/graphql-orm-ai-tool-profiles/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ supersedes: []

# Changelog

## [0.16.0] - 2026-10-13

### Changed

- This pre-1.0 minor is source-breaking for public `AiToolDescriptor` literals:
initialize the new optional `result_media` field or use existing constructors.
Missing media preserves serialized descriptors and their fingerprints.

### Added

- Immutable result-media policies bind selected image-reference fields, exact
object paths, logical resolver, confidentiality and finite limits to tool
fingerprints. Secret/NeverExport ancestors and URL/path resolver IDs fail.
- `PreTransportToolSourceUnavailable` retains safe no-dispatch classification
for unavailable protected history after an unstarted reservation is released.

## [Unreleased]

### Added
Expand Down
2 changes: 1 addition & 1 deletion crates/graphql-orm-ai-tool-profiles/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "graphql-orm-ai-tool-profiles"
version = "0.15.0"
version = "0.16.0"
edition = "2024"
authors = ["Toby Martin <toby@dastari.net>"]
description = "Backend-neutral GraphQL AI tool profile compiler and manifest contracts"
Expand Down
20 changes: 20 additions & 0 deletions crates/graphql-orm-ai-tool-profiles/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,26 @@ supersedes: []

# Migration Guide

## 0.15.0 to 0.16.0

This is a source-breaking pre-1.0 minor release: the public
`AiToolDescriptor` gains the optional `result_media` field. Recompile consumers;
old struct literals must initialize it to `None` or use the existing
constructors. Serialized descriptors without media retain their previous
representation and fingerprints.

Profiles and descriptors gain an optional `AiToolResultMediaPolicy`. Construct
it from validated field bindings, exact selected object paths and finite limits;
use a logical owner resolver ID, never a URL or path. The declaration is included
in the immutable fingerprint and can only tighten result classification. Missing
policy preserves previous serialization/fingerprints. Consumers using public
struct literals must initialize `result_media` or use existing constructors.

Media metadata is descriptive and grants neither execution nor disclosure.
Adopt AI 0.107.0 from the same workspace release for the trusted byte resolver,
current source hooks, atomic budgets and typed provider transport. This package
requires no data migration; the owning AI package does require module 0.72.0.

## 0.14.0 to 0.15.0

Adopt the AI and tool-profile packages from one reviewed workspace release.
Expand Down
7 changes: 6 additions & 1 deletion crates/graphql-orm-ai-tool-profiles/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ are separate runtime decisions and must remain default-deny.

```toml
[dependencies]
graphql-orm-ai-tool-profiles = { git = "https://github.com/Dastari/graphql-orm.git", rev = "<reviewed-full-40-character-commit-sha>", version = "0.15.0" }
graphql-orm-ai-tool-profiles = { git = "https://github.com/Dastari/graphql-orm.git", rev = "<reviewed-full-40-character-commit-sha>", version = "0.16.0" }
serde_json = "1"
```

Expand Down Expand Up @@ -252,3 +252,8 @@ registered or remote binding helpers after independently verifying the owner's
authenticated evidence. The binding hash correlates one request/attempt; it
never proves remote authenticity or grants authority. Generic transport and
resolver failures remain uncertain for consequential operations.

`AiToolResultMediaPolicy` binds a logical owner resolver, exact selected opaque
image metadata, classification and finite limits to the tool fingerprint. See
the [AI media contract](../graphql-orm-ai/docs/tool-result-media.md); reference
JSON never supplies image bytes or resource authorization.
7 changes: 7 additions & 0 deletions crates/graphql-orm-ai-tool-profiles/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ pub enum AiError {
/// [`Self::ProviderFailed`] and remain recovery-required.
#[error("AI provider operation failed before dispatch")]
PreTransportProviderFailed,
/// Retained protected tool content became unavailable before provider dispatch. The
/// unstarted reservation was durably released. A coordinator may reset the old cursor and
/// send only independently reopened current human input with a truthful withholding notice;
/// this never authorizes replay of the original effects or the unavailable history.
#[error("retained tool content is unavailable before dispatch")]
PreTransportToolSourceUnavailable,
/// Input failed a public schema contract.
#[error("invalid AI input: {0}")]
InvalidInput(String),
Expand Down Expand Up @@ -119,6 +125,7 @@ impl AiError {
Self::BudgetDenied => "AI_BUDGET_DENIED",
Self::PreTransportBudgetDenied => "AI_BUDGET_DENIED",
Self::PreTransportProviderFailed => "AI_PROVIDER_FAILED",
Self::PreTransportToolSourceUnavailable => "AI_TOOL_SOURCE_UNAVAILABLE",
Self::InvalidInput(_) => "AI_INVALID_INPUT",
Self::ResultBudgetExceeded => "AI_RESULT_BUDGET_EXCEEDED",
Self::ReauthorizationFailed => "AI_REAUTHORIZATION_FAILED",
Expand Down
2 changes: 2 additions & 0 deletions crates/graphql-orm-ai-tool-profiles/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod execution;
mod execution_rejection;
mod profiles;
mod query_plans;
mod tool_media;
mod tools;

pub use capability_index::*;
Expand All @@ -26,4 +27,5 @@ pub use execution::*;
pub use execution_rejection::*;
pub use profiles::*;
pub use query_plans::*;
pub use tool_media::*;
pub use tools::*;
36 changes: 33 additions & 3 deletions crates/graphql-orm-ai-tool-profiles/src/profiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use sha2::{Digest, Sha256};
use crate::{
AiApprovalRule, AiBrowserResultPreviewPolicy, AiDisclosureDisposition, AiDisclosureSchema,
AiDisclosureShape, AiError, AiGeneratedGraphqlOperationPolicy, AiToolDescriptor,
AiToolOperationDomain, AiToolOperationKind, AiToolRisk, DataClassification,
GraphqlExecutionTargetId, GraphqlOperationContract, ToolMaturity,
AiToolOperationDomain, AiToolOperationKind, AiToolResultMediaPolicy, AiToolRisk,
DataClassification, GraphqlExecutionTargetId, GraphqlOperationContract, ToolMaturity,
canonical_json::canonical_json_bytes,
};

Expand Down Expand Up @@ -416,6 +416,8 @@ pub struct AiGraphqlToolProfile {
idempotent: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
browser_result_preview: Option<AiBrowserResultPreviewPolicy>,
#[serde(default, skip_serializing_if = "Option::is_none")]
result_media: Option<AiToolResultMediaPolicy>,
}

impl AiGraphqlToolProfile {
Expand Down Expand Up @@ -448,6 +450,7 @@ impl AiGraphqlToolProfile {
approval: AiApprovalRule::None,
idempotent: true,
browser_result_preview: None,
result_media: None,
}
}

Expand Down Expand Up @@ -492,6 +495,7 @@ impl AiGraphqlToolProfile {
approval: AiApprovalRule::OneShot,
idempotent,
browser_result_preview: None,
result_media: None,
})
}

Expand Down Expand Up @@ -579,6 +583,14 @@ impl AiGraphqlToolProfile {
self
}

/// Declares exact selected output-image metadata and one deployment-owned resolver. The
/// compiler validates static disclosure paths and includes this policy in the tool fingerprint.
#[must_use]
pub fn with_result_media(mut self, policy: AiToolResultMediaPolicy) -> Self {
self.result_media = Some(policy);
self
}

/// Returns the stable host-authored profile identifier.
pub fn profile_id(&self) -> &str {
&self.profile_id
Expand Down Expand Up @@ -1000,6 +1012,9 @@ impl AiGraphqlToolManifestBuilder {
projected_shape,
)]));
validate_disclosure_shape(&profile.disclosure_schema.root, &response_shape)?;
if let Some(media) = &profile.result_media {
media.validate_disclosure(&profile.disclosure_schema.root)?;
}

let operation_name = operation_name(
&self.subgraph_id,
Expand Down Expand Up @@ -1089,6 +1104,10 @@ impl AiGraphqlToolManifestBuilder {
Some(policy) => descriptor.with_browser_result_preview(policy),
None => descriptor,
};
let descriptor = match profile.result_media {
Some(policy) => descriptor.with_result_media(policy)?,
None => descriptor,
};
Ok(AiGraphqlToolManifestEntry {
root_type: profile.root_type,
field_name: profile.field_name,
Expand Down Expand Up @@ -1524,6 +1543,9 @@ fn wrap_list(shape: ProjectedShape, maximum_items: Option<u32>) -> ProjectedShap
}

fn validate_profile_shape(profile: &AiGraphqlToolProfile) -> Result<(), AiError> {
if let Some(media) = &profile.result_media {
media.validate_disclosure(&profile.disclosure_schema.root)?;
}
validate_public_token(&profile.profile_id, "profile ID")?;
validate_graphql_name(&profile.field_name, "root field")?;
validate_model_description(&profile.description, "tool description")?;
Expand Down Expand Up @@ -1619,6 +1641,9 @@ fn validate_profile_shape(profile: &AiGraphqlToolProfile) -> Result<(), AiError>

fn validate_compiled_descriptor(entry: &AiGraphqlToolManifestEntry) -> Result<(), AiError> {
let descriptor = &entry.descriptor;
if let Some(media) = &descriptor.result_media {
media.validate_disclosure(&entry.disclosure_schema.root)?;
}
let safe_read = entry.root_type == AiGraphqlRootType::Query
&& descriptor.maturity == ToolMaturity::ReadOnly
&& descriptor.risk == AiToolRisk::ReadOnly
Expand All @@ -1643,7 +1668,12 @@ fn validate_compiled_descriptor(entry: &AiGraphqlToolManifestEntry) -> Result<()
|| descriptor.maximum_result_bytes == 0
|| descriptor.maximum_result_records == 0
|| descriptor.maximum_classification
!= maximum_classification(&entry.disclosure_schema.root)?
!= maximum_classification(&entry.disclosure_schema.root)?.max(
descriptor
.result_media
.as_ref()
.map_or(DataClassification::Public, |media| media.classification()),
)
|| descriptor.browser_result_preview.is_some_and(|preview| {
preview.maximum_bytes > descriptor.maximum_result_bytes
|| preview.maximum_records > descriptor.maximum_result_records
Expand Down
Loading
Loading