Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion Cargo.lock

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

21 changes: 21 additions & 0 deletions crates/graphql-orm-ai/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,27 @@ 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.88.0] - 2026-08-22

Persistent schema module: **0.63.0** (unchanged from 0.87.0).

### Changed

- The closed Codex dynamic-tool launch profile omits only the process-level
`--disable code_mode_host` argument, which Codex 0.148.0 was measured to
require for direct `dynamicToolCall` delivery to GPT-5.6 Luna.

### Security

- Code Mode, Code Mode-only routing, shell, files, MCP, browser, hosted web
search, and every other native surface remain disabled. The per-thread
configuration still sets `features.code_mode_host=false`, and the protocol
actor rejects command, file, MCP, collaboration, search, and image items at
both lifecycle boundaries.

There is no schema, data, protected-payload, GraphQL SDL, backup or restore
migration in this release.

## [0.87.0] - 2026-08-22

Persistent schema module: **0.63.0** (unchanged from 0.86.0).
Expand Down
2 changes: 1 addition & 1 deletion crates/graphql-orm-ai/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "graphql-orm-ai"
version = "0.87.0"
version = "0.88.0"
edition = "2024"
authors = ["Toby Martin <toby@dastari.net>"]
description = "Project-agnostic AI agent runtime for graphql-orm applications"
Expand Down
23 changes: 23 additions & 0 deletions crates/graphql-orm-ai/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,29 @@ 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.87.0 to 0.88.0: direct GPT-5.6 dynamic tools on Codex 0.148.0

Adopt `graphql-orm-ai` 0.88.0 from one reviewed full monorepo revision. The AI
schema module remains **0.63.0**. There is no database, data, table, column,
index, constraint, backfill, GraphQL SDL, protected-payload, backup or restore
migration.

Hosts must continue applying
`AiCodexAppServerLaunchProfile::codex_arguments()` unchanged. On Codex 0.148.0,
the profile now omits only `--disable code_mode_host`: an otherwise identical
GPT-5.6 Luna probe completed without calling its offered direct tool when that
argument was present, and emitted `dynamicToolCall` / `item/tool/call` when it
was absent. Do not infer that Code Mode or another native surface is admitted.
The actor still sends `features.code_mode_host=false`,
`features.code_mode=false`, `features.code_mode_only=false`, and every other
closed feature setting per thread; the process sandbox and protocol actor
continue to deny shell, file, MCP, browser, hosted-search, collaboration,
image, and arbitrary server-request items.

Before changing Codex versions, run a retained direct-tool readiness probe and
the negative native-item lifecycle tests. A non-dynamic route or any native
item must fail readiness rather than falling back to Code Mode or execution.

## 0.86.0 to 0.87.0: retained dynamic-tool readiness input

Adopt `graphql-orm-ai` 0.87.0 from one reviewed full monorepo revision. The AI
Expand Down
10 changes: 10 additions & 0 deletions crates/graphql-orm-ai/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,16 @@ message, tool call, or checkpoint. This discard guarantee is version-observed
for `codex-cli 0.148.0` with `gpt-5.4`; reverify it before upgrading Codex. Any
missing proof continues through the disclosed cleanup-and-rebind path.

The retained dynamic-tool launch profile is version-observed on Codex 0.148.0.
It disables Code Mode, Code Mode-only routing, shell, files, MCP, browser,
hosted search, and every other native item surface. Its sole process-level
exception is `code_mode_host`: `--disable code_mode_host` suppresses direct
`dynamicToolCall` delivery on that Codex version, so the launch arguments omit
only that flag while the per-thread configuration still sets the feature
false. The protocol actor rejects any native item that is nevertheless
emitted. Reverify both direct delivery and the negative native-item matrix
before upgrading Codex.

See the [session reliability adoption contract](docs/session-reliability-adoption.md).

## Features and capability boundary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,17 @@ collaboration, images, browser/computer use, and hosted search. This is defense
in depth: the process sandbox remains authoritative if a provider version
ignores a feature toggle.

The sole process-level exception is measured on Codex 0.148.0: adding
`--disable code_mode_host` to this otherwise identical profile made a retained
GPT-5.6 Luna turn complete without issuing its offered tool, while omitting
that one argument produced the direct `dynamicToolCall` / `item/tool/call`.
`codex_arguments()` therefore omits only that disable. The actor still sends
`features.code_mode_host=false`, `features.code_mode=false`, and
`features.code_mode_only=false` per thread; shell, file, MCP, browser, web, and
every other native item remain unavailable and are rejected by the protocol
actor if emitted. Re-run the direct-tool readiness probe and negative native-
item suite before adopting another Codex version.

Only a reviewed `Direct` model-tool declaration can construct this profile.
Codex models declared `CodeMode` or `CodeModeOnly` are rejected rather than
silently losing dynamic tools or requiring a native Code Mode host. Such a
Expand Down
82 changes: 78 additions & 4 deletions crates/graphql-orm-ai/src/providers/codex_app_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const OPTED_OUT_NOTIFICATION_METHODS: [&str; 5] = [
const REMOTE_CONTROL_STATUS_CHANGED: &str = "remoteControl/status/changed";
const RUNTIME_WARNING: &str = "warning";
const THREAD_TOKEN_USAGE_UPDATED: &str = "thread/tokenUsage/updated";
const CODE_MODE_HOST_FEATURE: &str = "code_mode_host";

const DYNAMIC_TOOLS_ONLY_DISABLED_FEATURES: &[&str] = &[
"apps",
Expand Down Expand Up @@ -274,15 +275,27 @@ impl AiCodexAppServerLaunchProfile {
///
/// The dynamic profile deliberately disables every native execution,
/// browser, hosted-search, connector, collaboration, image, plugin, and
/// interactive tool feature it relies on being absent. The factory must
/// also clear the environment, use a private configuration home containing
/// no project configuration or MCP servers, use an empty working
/// directory, and apply its reviewed external sandbox.
/// interactive tool feature it relies on being absent. Codex 0.148.0 is
/// the measured exception for the internal `code_mode_host` process gate:
/// disabling that one gate suppresses direct `dynamicToolCall` delivery,
/// so it remains available at process launch while Code Mode, Code
/// Mode-only routing, and the host itself remain false in the closed
/// per-thread configuration. The factory must also clear the environment,
/// use a private configuration home containing no project configuration
/// or MCP servers, use an empty working directory, and apply its reviewed
/// external sandbox.
#[must_use]
pub fn codex_arguments(self) -> Vec<&'static str> {
let mut arguments = vec!["app-server", "--stdio", "--strict-config"];
if self.supports_experimental_dynamic_tools() {
for feature in DYNAMIC_TOOLS_ONLY_DISABLED_FEATURES {
// Codex 0.148.0 suppresses direct `dynamicToolCall` delivery
// when this process-level feature gate is disabled. The
// per-thread configuration below still sets it false beside
// Code Mode and every model-native tool surface.
if *feature == CODE_MODE_HOST_FEATURE {
continue;
}
arguments.extend(["--disable", *feature]);
}
}
Expand Down Expand Up @@ -7426,6 +7439,15 @@ pub(crate) mod tests {
);
assert!(!arguments.contains(&"--enable"));
for feature in DYNAMIC_TOOLS_ONLY_DISABLED_FEATURES {
if *feature == CODE_MODE_HOST_FEATURE {
assert!(
!arguments
.windows(2)
.any(|pair| pair == ["--disable", *feature]),
"Codex 0.148.0 requires the process-level {feature} gate for direct dynamicToolCall delivery"
);
continue;
}
assert!(
arguments
.windows(2)
Expand Down Expand Up @@ -10359,6 +10381,43 @@ pub(crate) mod tests {
));
}

#[test]
fn direct_dynamic_tool_turn_rejects_every_native_item_surface() {
let input = AiCodexAppServerTurnInput::try_from_dynamic_request(dynamic_model_request())
.expect("dynamic request should convert");
for item_type in [
"commandExecution",
"fileChange",
"mcpToolCall",
"collabToolCall",
"webSearch",
"imageView",
] {
for (method, timestamp) in [
("item/started", "startedAtMs"),
("item/completed", "completedAtMs"),
] {
let mut actor = initialized_protocol_actor();
let thread_id = start_bound_dynamic_thread(&mut actor, input.tools());
start_bound_dynamic_turn(&mut actor, &thread_id, &input, "turn-native-1", 3);
let mut params = json!({
"item": {"type": item_type, "id": "native-item-1"},
"threadId": thread_id,
"turnId": "turn-native-1",
});
params
.as_object_mut()
.expect("native item params should be an object")
.insert(timestamp.to_owned(), json!(1));
let frame = lifecycle_notification(method, params);
assert!(
matches!(actor.accept(&frame), Err(ProviderError::Rejected)),
"native item {item_type} at {method} must fail the direct dynamic-tool turn"
);
}
}
}

#[test]
fn protocol_accepts_only_correlated_responses_and_allowlisted_notifications() {
let mut unbound =
Expand Down Expand Up @@ -10608,6 +10667,13 @@ pub(crate) mod tests {
assert!(start.contains("\"sandbox\":\"read-only\""));
let start_value: Value =
serde_json::from_str(start.trim()).expect("dynamic start should remain valid JSON");
for feature in DYNAMIC_TOOLS_ONLY_DISABLED_FEATURES {
assert_eq!(
start_value.pointer(&format!("/params/config/features.{feature}")),
Some(&Value::Bool(false)),
"thread config must disable native feature {feature}"
);
}
assert_eq!(
start_value.pointer("/params/environments"),
Some(&json!([]))
Expand All @@ -10624,6 +10690,14 @@ pub(crate) mod tests {
start_value.pointer("/params/config/features.code_mode"),
Some(&Value::Bool(false))
);
assert_eq!(
start_value.pointer("/params/config/features.code_mode_host"),
Some(&Value::Bool(false))
);
assert_eq!(
start_value.pointer("/params/config/features.code_mode_only"),
Some(&Value::Bool(false))
);
assert_eq!(
start_value.pointer("/params/config/features.apps"),
Some(&Value::Bool(false))
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/workspace-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ changes.
| Package | Version | Path | Default features | Direct internal dependencies |
| --- | --- | --- | --- | --- |
| `graphql-orm` | `0.23.0` | `crates/graphql-orm` | `sqlite` | `graphql-orm-macros`, `graphql-orm-operation-catalog`, `graphql-orm-router-protocol` (dev-only) |
| `graphql-orm-ai` | `0.87.0` | `crates/graphql-orm-ai` | `sqlite` | `graphql-orm`, `graphql-orm-ai-tool-profiles`, `graphql-orm-storage` |
| `graphql-orm-ai` | `0.88.0` | `crates/graphql-orm-ai` | `sqlite` | `graphql-orm`, `graphql-orm-ai-tool-profiles`, `graphql-orm-storage` |
| `graphql-orm-ai-tool-profiles` | `0.9.0` | `crates/graphql-orm-ai-tool-profiles` | none | `graphql-orm-operation-catalog`, `graphql-orm-router-protocol` (dev-only) |
| `graphql-orm-backup` | `0.7.1` | `crates/graphql-orm-backup` | `local` | `graphql-orm` (optional), `graphql-orm-storage` |
| `graphql-orm-macros` | `0.23.0` | `crates/graphql-orm-macros` | `sqlite` | none |
Expand Down
Loading