Skip to content

Commit c12d594

Browse files
committed
Add ephemeral side conversations
1 parent 7117457 commit c12d594

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1483
-58
lines changed

codex-rs/app-server-protocol/schema/json/ClientRequest.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2784,6 +2784,16 @@
27842784
},
27852785
"threadId": {
27862786
"type": "string"
2787+
},
2788+
"toolAccessPolicy": {
2789+
"anyOf": [
2790+
{
2791+
"$ref": "#/definitions/ToolAccessPolicy"
2792+
},
2793+
{
2794+
"type": "null"
2795+
}
2796+
]
27872797
}
27882798
},
27892799
"required": [
@@ -3383,6 +3393,13 @@
33833393
],
33843394
"type": "object"
33853395
},
3396+
"ToolAccessPolicy": {
3397+
"enum": [
3398+
"default",
3399+
"noExternalTools"
3400+
],
3401+
"type": "string"
3402+
},
33863403
"TurnInterruptParams": {
33873404
"properties": {
33883405
"threadId": {

codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12964,6 +12964,16 @@
1296412964
},
1296512965
"threadId": {
1296612966
"type": "string"
12967+
},
12968+
"toolAccessPolicy": {
12969+
"anyOf": [
12970+
{
12971+
"$ref": "#/definitions/v2/ToolAccessPolicy"
12972+
},
12973+
{
12974+
"type": "null"
12975+
}
12976+
]
1296712977
}
1296812978
},
1296912979
"required": [
@@ -15003,6 +15013,13 @@
1500315013
],
1500415014
"type": "object"
1500515015
},
15016+
"ToolAccessPolicy": {
15017+
"enum": [
15018+
"default",
15019+
"noExternalTools"
15020+
],
15021+
"type": "string"
15022+
},
1500615023
"ToolsV2": {
1500715024
"properties": {
1500815025
"view_image": {

codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10808,6 +10808,16 @@
1080810808
},
1080910809
"threadId": {
1081010810
"type": "string"
10811+
},
10812+
"toolAccessPolicy": {
10813+
"anyOf": [
10814+
{
10815+
"$ref": "#/definitions/ToolAccessPolicy"
10816+
},
10817+
{
10818+
"type": "null"
10819+
}
10820+
]
1081110821
}
1081210822
},
1081310823
"required": [
@@ -12847,6 +12857,13 @@
1284712857
],
1284812858
"type": "object"
1284912859
},
12860+
"ToolAccessPolicy": {
12861+
"enum": [
12862+
"default",
12863+
"noExternalTools"
12864+
],
12865+
"type": "string"
12866+
},
1285012867
"ToolsV2": {
1285112868
"properties": {
1285212869
"view_image": {

codex-rs/app-server-protocol/schema/json/v2/ThreadForkParams.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@
7373
"flex"
7474
],
7575
"type": "string"
76+
},
77+
"ToolAccessPolicy": {
78+
"enum": [
79+
"default",
80+
"noExternalTools"
81+
],
82+
"type": "string"
7683
}
7784
},
7885
"description": "There are two ways to fork a thread: 1. By thread_id: load the thread from disk by thread_id and fork it into a new thread. 2. By path: load the thread from disk by path and fork it into a new thread.\n\nIf using path, the thread_id param will be ignored.\n\nPrefer using thread_id whenever possible.",
@@ -168,6 +175,16 @@
168175
},
169176
"threadId": {
170177
"type": "string"
178+
},
179+
"toolAccessPolicy": {
180+
"anyOf": [
181+
{
182+
"$ref": "#/definitions/ToolAccessPolicy"
183+
},
184+
{
185+
"type": "null"
186+
}
187+
]
171188
}
172189
},
173190
"required": [

codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkParams.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { JsonValue } from "../serde_json/JsonValue";
66
import type { ApprovalsReviewer } from "./ApprovalsReviewer";
77
import type { AskForApproval } from "./AskForApproval";
88
import type { SandboxMode } from "./SandboxMode";
9+
import type { ToolAccessPolicy } from "./ToolAccessPolicy";
910

1011
/**
1112
* There are two ways to fork a thread:
@@ -27,7 +28,7 @@ model?: string | null, modelProvider?: string | null, serviceTier?: ServiceTier
2728
* Override where approval requests are routed for review on this thread
2829
* and subsequent turns.
2930
*/
30-
approvalsReviewer?: ApprovalsReviewer | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, ephemeral?: boolean, /**
31+
approvalsReviewer?: ApprovalsReviewer | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, toolAccessPolicy?: ToolAccessPolicy | null, ephemeral?: boolean, /**
3132
* If true, persist additional rollout EventMsg variants required to
3233
* reconstruct a richer thread history on subsequent resume/fork/read.
3334
*/
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// GENERATED CODE! DO NOT MODIFY BY HAND!
2+
3+
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
4+
5+
export type ToolAccessPolicy = "default" | "noExternalTools";

codex-rs/app-server-protocol/schema/typescript/v2/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ export type { ThreadUnsubscribeParams } from "./ThreadUnsubscribeParams";
334334
export type { ThreadUnsubscribeResponse } from "./ThreadUnsubscribeResponse";
335335
export type { ThreadUnsubscribeStatus } from "./ThreadUnsubscribeStatus";
336336
export type { TokenUsageBreakdown } from "./TokenUsageBreakdown";
337+
export type { ToolAccessPolicy } from "./ToolAccessPolicy";
337338
export type { ToolRequestUserInputAnswer } from "./ToolRequestUserInputAnswer";
338339
export type { ToolRequestUserInputOption } from "./ToolRequestUserInputOption";
339340
export type { ToolRequestUserInputParams } from "./ToolRequestUserInputParams";

codex-rs/app-server-protocol/src/protocol/v2.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use codex_protocol::config_types::Personality;
2424
use codex_protocol::config_types::ReasoningSummary;
2525
use codex_protocol::config_types::SandboxMode as CoreSandboxMode;
2626
use codex_protocol::config_types::ServiceTier;
27+
use codex_protocol::config_types::ToolAccessPolicy as CoreToolAccessPolicy;
2728
use codex_protocol::config_types::Verbosity;
2829
use codex_protocol::config_types::WebSearchMode;
2930
use codex_protocol::config_types::WebSearchToolConfig;
@@ -357,6 +358,13 @@ impl From<CoreSandboxMode> for SandboxMode {
357358
}
358359
}
359360

361+
v2_enum_from_core!(
362+
pub enum ToolAccessPolicy from CoreToolAccessPolicy {
363+
Default,
364+
NoExternalTools
365+
}
366+
);
367+
360368
v2_enum_from_core!(
361369
pub enum ReviewDelivery from codex_protocol::protocol::ReviewDelivery {
362370
Inline, Detached
@@ -2898,6 +2906,8 @@ pub struct ThreadForkParams {
28982906
pub base_instructions: Option<String>,
28992907
#[ts(optional = nullable)]
29002908
pub developer_instructions: Option<String>,
2909+
#[ts(optional = nullable)]
2910+
pub tool_access_policy: Option<ToolAccessPolicy>,
29012911
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
29022912
pub ephemeral: bool,
29032913
/// If true, persist additional rollout EventMsg variants required to
@@ -8693,6 +8703,26 @@ mod tests {
86938703
assert_eq!(serialized_without_override.get("serviceTier"), None);
86948704
}
86958705

8706+
#[test]
8707+
fn thread_fork_params_round_trip_tool_access_policy() {
8708+
let params: ThreadForkParams = serde_json::from_value(json!({
8709+
"threadId": "thr_123",
8710+
"ephemeral": true,
8711+
"toolAccessPolicy": "noExternalTools"
8712+
}))
8713+
.expect("params should deserialize");
8714+
8715+
assert_eq!(
8716+
params.tool_access_policy,
8717+
Some(ToolAccessPolicy::NoExternalTools)
8718+
);
8719+
let serialized = serde_json::to_value(&params).expect("params should serialize");
8720+
assert_eq!(
8721+
serialized.get("toolAccessPolicy"),
8722+
Some(&json!("noExternalTools"))
8723+
);
8724+
}
8725+
86968726
#[test]
86978727
fn thread_lifecycle_responses_default_missing_instruction_sources() {
86988728
let response = json!({

codex-rs/app-server/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ To branch from a stored session, call `thread/fork` with the `thread.id`. This c
272272
{ "method": "thread/started", "params": { "thread": { } } }
273273
```
274274

275+
Ephemeral forks can also pass `toolAccessPolicy: "noExternalTools"` to disable MCP servers, app connector tools, and dynamic external tools for the fork while leaving built-in Codex tools available. This policy is rejected for non-ephemeral forks.
276+
275277
Experimental API: `thread/start`, `thread/resume`, and `thread/fork` accept `persistExtendedHistory: true` to persist a richer subset of ThreadItems for non-lossy history when calling `thread/read`, `thread/resume`, and `thread/fork` later. This does not backfill events that were not persisted previously.
276278

277279
### Example: List threads (with pagination & filters)

codex-rs/app-server/src/codex_message_processor.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ use codex_app_server_protocol::ThreadUnarchivedNotification;
183183
use codex_app_server_protocol::ThreadUnsubscribeParams;
184184
use codex_app_server_protocol::ThreadUnsubscribeResponse;
185185
use codex_app_server_protocol::ThreadUnsubscribeStatus;
186+
use codex_app_server_protocol::ToolAccessPolicy;
186187
use codex_app_server_protocol::Turn;
187188
use codex_app_server_protocol::TurnError;
188189
use codex_app_server_protocol::TurnInterruptParams;
@@ -4540,10 +4541,21 @@ impl CodexMessageProcessor {
45404541
config: cli_overrides,
45414542
base_instructions,
45424543
developer_instructions,
4544+
tool_access_policy,
45434545
ephemeral,
45444546
persist_extended_history,
45454547
} = params;
45464548

4549+
if matches!(tool_access_policy, Some(ToolAccessPolicy::NoExternalTools)) && !ephemeral {
4550+
self.send_invalid_request_error(
4551+
request_id,
4552+
"toolAccessPolicy=noExternalTools is only supported for ephemeral forks"
4553+
.to_string(),
4554+
)
4555+
.await;
4556+
return;
4557+
}
4558+
45474559
let (rollout_path, source_thread_id) = if let Some(path) = path {
45484560
(path, None)
45494561
} else {
@@ -4663,6 +4675,9 @@ impl CodexMessageProcessor {
46634675
config,
46644676
rollout_path.clone(),
46654677
persist_extended_history,
4678+
tool_access_policy
4679+
.map(ToolAccessPolicy::to_core)
4680+
.unwrap_or_default(),
46664681
self.request_trace_context(&request_id).await,
46674682
)
46684683
.await
@@ -7383,6 +7398,7 @@ impl CodexMessageProcessor {
73837398
config,
73847399
rollout_path,
73857400
/*persist_extended_history*/ false,
7401+
/*tool_access_policy*/ Default::default(),
73867402
self.request_trace_context(request_id).await,
73877403
)
73887404
.await

0 commit comments

Comments
 (0)