mirror of
https://github.com/openai/codex.git
synced 2026-10-22 17:58:46 +00:00
Merge remote-tracking branch 'origin/main' into dev/winston/credentialed-routes-discovery-review-20260618
# Conflicts: # codex-rs/backend-client/src/lib.rs # codex-rs/backend-client/src/types.rs
This commit is contained in:
Generated
+244
-229
File diff suppressed because it is too large
Load Diff
+20
-5
@@ -21,6 +21,9 @@ members = [
|
||||
"install-context",
|
||||
"codex-backend-openapi-models",
|
||||
"code-mode",
|
||||
"code-mode-host",
|
||||
"code-mode-protocol",
|
||||
"codex-home",
|
||||
"cloud-config",
|
||||
"cloud-tasks",
|
||||
"cloud-tasks-client",
|
||||
@@ -157,6 +160,8 @@ codex-cloud-config = { path = "cloud-config" }
|
||||
codex-cloud-tasks-client = { path = "cloud-tasks-client" }
|
||||
codex-cloud-tasks-mock-client = { path = "cloud-tasks-mock-client" }
|
||||
codex-code-mode = { path = "code-mode" }
|
||||
codex-code-mode-protocol = { path = "code-mode-protocol" }
|
||||
codex-home = { path = "codex-home" }
|
||||
codex-config = { path = "config" }
|
||||
codex-connectors = { path = "connectors" }
|
||||
codex-context-fragments = { path = "context-fragments" }
|
||||
@@ -203,7 +208,6 @@ codex-plugin = { path = "plugin" }
|
||||
codex-model-provider = { path = "model-provider" }
|
||||
codex-process-hardening = { path = "process-hardening" }
|
||||
codex-protocol = { path = "protocol" }
|
||||
codex-realtime-webrtc = { path = "realtime-webrtc" }
|
||||
codex-prompts = { path = "prompts" }
|
||||
codex-responses-api-proxy = { path = "responses-api-proxy" }
|
||||
codex-response-debug-context = { path = "response-debug-context" }
|
||||
@@ -237,6 +241,7 @@ codex-utils-json-to-toml = { path = "utils/json-to-toml" }
|
||||
codex-utils-oss = { path = "utils/oss" }
|
||||
codex-utils-output-truncation = { path = "utils/output-truncation" }
|
||||
codex-utils-path = { path = "utils/path-utils" }
|
||||
codex-utils-path-uri = { path = "utils/path-uri" }
|
||||
codex-utils-plugins = { path = "utils/plugins" }
|
||||
codex-utils-pty = { path = "utils/pty" }
|
||||
codex-utils-rustls-provider = { path = "utils/rustls-provider" }
|
||||
@@ -262,7 +267,6 @@ assert_matches = "1.5.0"
|
||||
async-channel = "2.3.1"
|
||||
async-io = "2.6.0"
|
||||
async-stream = "0.3.6"
|
||||
async-trait = "0.1.89"
|
||||
aws-config = "1"
|
||||
aws-credential-types = "1"
|
||||
aws-sigv4 = "1"
|
||||
@@ -275,6 +279,14 @@ chardetng = "0.1.17"
|
||||
chrono = "0.4.43"
|
||||
clap = "4"
|
||||
clap_complete = "4"
|
||||
clatter = { version = "2.2.0", default-features = false, features = [
|
||||
"alloc",
|
||||
"getrandom",
|
||||
"use-25519",
|
||||
"use-aes-gcm",
|
||||
"use-rust-crypto-ml-kem",
|
||||
"use-sha",
|
||||
] }
|
||||
color-eyre = "0.6.3"
|
||||
constant_time_eq = "0.3.1"
|
||||
crossbeam-channel = "0.5.15"
|
||||
@@ -319,6 +331,9 @@ keyring = { version = "3.6", default-features = false }
|
||||
landlock = "0.4.4"
|
||||
lazy_static = "1"
|
||||
libc = "0.2.182"
|
||||
# Keep SQLx's bundled SQLite on a version containing the WAL-reset corruption fix:
|
||||
# https://www.sqlite.org/wal.html#the_wal_reset_bug
|
||||
libsqlite3-sys = { version = "0.37", default-features = false }
|
||||
log = "0.4"
|
||||
lru = "0.16.3"
|
||||
maplit = "1.0.2"
|
||||
@@ -356,7 +371,7 @@ reqwest = { version = "0.12", features = ["cookies"] }
|
||||
rmcp = { version = "1.7.0", default-features = false }
|
||||
runfiles = { git = "https://github.com/dzbarsky/rules_rust", rev = "b56cbaa8465e74127f1ea216f813cd377295ad81" }
|
||||
rustls = { version = "0.23", default-features = false, features = [
|
||||
"ring",
|
||||
"aws_lc_rs",
|
||||
"std",
|
||||
] }
|
||||
rustls-native-certs = "0.8.3"
|
||||
@@ -511,8 +526,8 @@ split-debuginfo = "off"
|
||||
# sidecar symbols and stripped the binaries.
|
||||
strip = false
|
||||
|
||||
# See https://github.com/openai/codex/issues/1411 for details.
|
||||
codegen-units = 1
|
||||
# Balance parallel release code generation against binary size.
|
||||
codegen-units = 4
|
||||
|
||||
[profile.profiling]
|
||||
inherits = "release"
|
||||
|
||||
@@ -13,7 +13,6 @@ doctest = false
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
codex-protocol = { workspace = true }
|
||||
codex-state = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_state::StateRuntime;
|
||||
use std::sync::Arc;
|
||||
@@ -29,7 +28,6 @@ impl LocalAgentGraphStore {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl AgentGraphStore for LocalAgentGraphStore {
|
||||
async fn upsert_thread_spawn_edge(
|
||||
&self,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use async_trait::async_trait;
|
||||
use codex_protocol::ThreadId;
|
||||
|
||||
use crate::AgentGraphStoreResult;
|
||||
@@ -8,38 +7,37 @@ use crate::ThreadSpawnEdgeStatus;
|
||||
///
|
||||
/// Implementations are expected to return stable ordering for list methods so callers can merge
|
||||
/// persisted graph state with live in-memory state without introducing nondeterministic output.
|
||||
#[async_trait]
|
||||
pub trait AgentGraphStore: Send + Sync {
|
||||
/// Insert or replace the directional parent/child edge for a spawned thread.
|
||||
///
|
||||
/// `child_thread_id` has at most one persisted parent. Re-inserting the same child should
|
||||
/// update both the parent and status to match the supplied values.
|
||||
async fn upsert_thread_spawn_edge(
|
||||
fn upsert_thread_spawn_edge(
|
||||
&self,
|
||||
parent_thread_id: ThreadId,
|
||||
child_thread_id: ThreadId,
|
||||
status: ThreadSpawnEdgeStatus,
|
||||
) -> AgentGraphStoreResult<()>;
|
||||
) -> impl std::future::Future<Output = AgentGraphStoreResult<()>> + Send;
|
||||
|
||||
/// Update the persisted lifecycle status of a spawned thread's incoming edge.
|
||||
///
|
||||
/// Implementations should treat missing children as a successful no-op.
|
||||
async fn set_thread_spawn_edge_status(
|
||||
fn set_thread_spawn_edge_status(
|
||||
&self,
|
||||
child_thread_id: ThreadId,
|
||||
status: ThreadSpawnEdgeStatus,
|
||||
) -> AgentGraphStoreResult<()>;
|
||||
) -> impl std::future::Future<Output = AgentGraphStoreResult<()>> + Send;
|
||||
|
||||
/// List direct spawned children of a parent thread.
|
||||
///
|
||||
/// When `status_filter` is `Some`, only child edges with that exact status are returned. When
|
||||
/// it is `None`, all direct child edges are returned regardless of status, including statuses
|
||||
/// that may be added by a future store implementation.
|
||||
async fn list_thread_spawn_children(
|
||||
fn list_thread_spawn_children(
|
||||
&self,
|
||||
parent_thread_id: ThreadId,
|
||||
status_filter: Option<ThreadSpawnEdgeStatus>,
|
||||
) -> AgentGraphStoreResult<Vec<ThreadId>>;
|
||||
) -> impl std::future::Future<Output = AgentGraphStoreResult<Vec<ThreadId>>> + Send;
|
||||
|
||||
/// List spawned descendants breadth-first by depth, then by thread id.
|
||||
///
|
||||
@@ -47,9 +45,9 @@ pub trait AgentGraphStore: Send + Sync {
|
||||
/// For example, `Some(Open)` walks only open edges, so descendants under a closed edge are not
|
||||
/// included even if their own incoming edge is open. `None` walks and returns every persisted
|
||||
/// edge regardless of status.
|
||||
async fn list_thread_spawn_descendants(
|
||||
fn list_thread_spawn_descendants(
|
||||
&self,
|
||||
root_thread_id: ThreadId,
|
||||
status_filter: Option<ThreadSpawnEdgeStatus>,
|
||||
) -> AgentGraphStoreResult<Vec<ThreadId>>;
|
||||
) -> impl std::future::Future<Output = AgentGraphStoreResult<Vec<ThreadId>>> + Send;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Context;
|
||||
@@ -34,21 +36,66 @@ const AGENT_TASK_REGISTRATION_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
const AGENT_IDENTITY_JWKS_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
const AGENT_IDENTITY_JWT_AUDIENCE: &str = "codex-app-server";
|
||||
const AGENT_IDENTITY_JWT_ISSUER: &str = "https://chatgpt.com/codex-backend/agent-identity";
|
||||
const AGENT_REGISTRATION_TIMEOUT: Duration = Duration::from_secs(15);
|
||||
const PROD_AGENT_IDENTITY_AUTHAPI_BASE_URL: &str = "https://auth.openai.com/api/accounts";
|
||||
const STAGING_AGENT_IDENTITY_AUTHAPI_BASE_URL: &str = "https://auth.api.openai.org/api/accounts";
|
||||
const AGENT_IDENTITY_KEY_SEED_BYTES: usize = 64;
|
||||
const AGENT_IDENTITY_KEY_DERIVATION_CONTEXT: &[u8] = b"codex-agent-identity-ed25519-v1";
|
||||
|
||||
/// Stored key material for a registered agent identity.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum ChatGptEnvironment {
|
||||
#[default]
|
||||
Production,
|
||||
Staging,
|
||||
}
|
||||
|
||||
impl ChatGptEnvironment {
|
||||
pub fn from_chatgpt_base_url(chatgpt_base_url: &str) -> Result<Self> {
|
||||
match chatgpt_base_url.trim_end_matches('/') {
|
||||
"https://chatgpt.com"
|
||||
| "https://chatgpt.com/backend-api"
|
||||
| "https://chatgpt.com/codex"
|
||||
| "https://chatgpt.com/backend-api/codex"
|
||||
| "https://chat.openai.com"
|
||||
| "https://chat.openai.com/backend-api"
|
||||
| "https://chat.openai.com/codex"
|
||||
| "https://chat.openai.com/backend-api/codex" => Ok(Self::Production),
|
||||
"https://chatgpt-staging.com"
|
||||
| "https://chatgpt-staging.com/backend-api"
|
||||
| "https://chatgpt-staging.com/codex"
|
||||
| "https://chatgpt-staging.com/backend-api/codex" => Ok(Self::Staging),
|
||||
_ => anyhow::bail!(
|
||||
"Agent Identity only supports production and staging ChatGPT environments"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chatgpt_base_url(self) -> &'static str {
|
||||
match self {
|
||||
Self::Production => "https://chatgpt.com/backend-api",
|
||||
Self::Staging => "https://chatgpt-staging.com/backend-api",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn agent_identity_authapi_base_url(self) -> &'static str {
|
||||
match self {
|
||||
Self::Production => PROD_AGENT_IDENTITY_AUTHAPI_BASE_URL,
|
||||
Self::Staging => STAGING_AGENT_IDENTITY_AUTHAPI_BASE_URL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrowed durable signing material for a registered agent identity.
|
||||
///
|
||||
/// This intentionally does not include a task id. Task ids are scoped to a
|
||||
/// single Codex run, while the agent runtime id and private key are the
|
||||
/// reusable identity material used to register and sign that run task.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct AgentIdentityKey<'a> {
|
||||
pub agent_runtime_id: &'a str,
|
||||
pub private_key_pkcs8_base64: &'a str,
|
||||
}
|
||||
|
||||
/// Task binding to use when constructing a task-scoped AgentAssertion.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct AgentTaskAuthorizationTarget<'a> {
|
||||
pub agent_runtime_id: &'a str,
|
||||
pub task_id: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct AgentBillOfMaterials {
|
||||
pub agent_version: String,
|
||||
@@ -103,23 +150,92 @@ struct RegisterTaskResponse {
|
||||
encrypted_task_id_camel: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct RegisterAgentRequest {
|
||||
abom: AgentBillOfMaterials,
|
||||
agent_public_key: String,
|
||||
capabilities: Vec<String>,
|
||||
ttl: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct RegisterAgentResponse {
|
||||
agent_runtime_id: String,
|
||||
}
|
||||
|
||||
/// HTTP status failure returned by Agent Identity registration endpoints.
|
||||
#[derive(Debug)]
|
||||
pub struct AgentIdentityRegistrationHttpError {
|
||||
operation: &'static str,
|
||||
status: reqwest::StatusCode,
|
||||
body: String,
|
||||
}
|
||||
|
||||
impl AgentIdentityRegistrationHttpError {
|
||||
fn new(operation: &'static str, status: reqwest::StatusCode, body: String) -> Self {
|
||||
Self {
|
||||
operation,
|
||||
status,
|
||||
body,
|
||||
}
|
||||
}
|
||||
|
||||
/// HTTP status returned by the registration endpoint.
|
||||
pub fn status(&self) -> reqwest::StatusCode {
|
||||
self.status
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AgentIdentityRegistrationHttpError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.body.is_empty() {
|
||||
write!(f, "{} failed with status {}", self.operation, self.status)
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"{} failed with status {}: {}",
|
||||
self.operation, self.status, self.body
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for AgentIdentityRegistrationHttpError {}
|
||||
|
||||
/// Returns whether an Agent Identity registration error is safe to retry.
|
||||
pub fn is_retryable_registration_error(error: &anyhow::Error) -> bool {
|
||||
error.chain().any(is_retryable_registration_cause)
|
||||
}
|
||||
|
||||
fn is_retryable_registration_cause(cause: &(dyn StdError + 'static)) -> bool {
|
||||
if let Some(error) = cause.downcast_ref::<AgentIdentityRegistrationHttpError>() {
|
||||
return is_retryable_registration_status(error.status());
|
||||
}
|
||||
|
||||
if let Some(error) = cause.downcast_ref::<reqwest::Error>() {
|
||||
if let Some(status) = error.status() {
|
||||
return is_retryable_registration_status(status);
|
||||
}
|
||||
return error.is_timeout() || error.is_connect() || error.is_request();
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn is_retryable_registration_status(status: reqwest::StatusCode) -> bool {
|
||||
status == reqwest::StatusCode::TOO_MANY_REQUESTS || status.is_server_error()
|
||||
}
|
||||
|
||||
pub fn authorization_header_for_agent_task(
|
||||
key: AgentIdentityKey<'_>,
|
||||
target: AgentTaskAuthorizationTarget<'_>,
|
||||
task_id: &str,
|
||||
) -> Result<String> {
|
||||
anyhow::ensure!(
|
||||
key.agent_runtime_id == target.agent_runtime_id,
|
||||
"agent task runtime {} does not match stored agent identity {}",
|
||||
target.agent_runtime_id,
|
||||
key.agent_runtime_id
|
||||
);
|
||||
|
||||
let timestamp = Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true);
|
||||
let envelope = AgentAssertionEnvelope {
|
||||
agent_runtime_id: target.agent_runtime_id.to_string(),
|
||||
task_id: target.task_id.to_string(),
|
||||
agent_runtime_id: key.agent_runtime_id.to_string(),
|
||||
task_id: task_id.to_string(),
|
||||
timestamp: timestamp.clone(),
|
||||
signature: sign_agent_assertion_payload(key, target.task_id, ×tamp)?,
|
||||
signature: sign_agent_assertion_payload(key, task_id, ×tamp)?,
|
||||
};
|
||||
let serialized_assertion = serialize_agent_assertion(&envelope)?;
|
||||
Ok(format!("AgentAssertion {serialized_assertion}"))
|
||||
@@ -127,10 +243,10 @@ pub fn authorization_header_for_agent_task(
|
||||
|
||||
pub async fn fetch_agent_identity_jwks(
|
||||
client: &reqwest::Client,
|
||||
chatgpt_base_url: &str,
|
||||
agent_identity_jwt_base_url: &str,
|
||||
) -> Result<JwkSet> {
|
||||
let response = client
|
||||
.get(agent_identity_jwks_url(chatgpt_base_url))
|
||||
.get(agent_identity_jwks_url(agent_identity_jwt_base_url))
|
||||
.timeout(AGENT_IDENTITY_JWKS_TIMEOUT)
|
||||
.send()
|
||||
.await
|
||||
@@ -195,7 +311,7 @@ pub fn sign_task_registration_payload(
|
||||
|
||||
pub async fn register_agent_task(
|
||||
client: &reqwest::Client,
|
||||
chatgpt_base_url: &str,
|
||||
agent_identity_authapi_base_url: &str,
|
||||
key: AgentIdentityKey<'_>,
|
||||
) -> Result<String> {
|
||||
let timestamp = Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true);
|
||||
@@ -203,7 +319,7 @@ pub async fn register_agent_task(
|
||||
signature: sign_task_registration_payload(key, ×tamp)?,
|
||||
timestamp,
|
||||
};
|
||||
let url = agent_task_registration_url(chatgpt_base_url, key.agent_runtime_id);
|
||||
let url = agent_task_registration_url(agent_identity_authapi_base_url, key.agent_runtime_id);
|
||||
|
||||
let response = client
|
||||
.post(url)
|
||||
@@ -220,7 +336,12 @@ pub async fn register_agent_task(
|
||||
} else {
|
||||
body
|
||||
};
|
||||
anyhow::bail!("failed to register agent task with status {status}: {body}");
|
||||
return Err(AgentIdentityRegistrationHttpError::new(
|
||||
"agent task registration",
|
||||
status,
|
||||
body,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let response = response
|
||||
@@ -231,6 +352,45 @@ pub async fn register_agent_task(
|
||||
task_id_from_register_task_response(key, response)
|
||||
}
|
||||
|
||||
pub async fn register_agent_identity(
|
||||
client: &reqwest::Client,
|
||||
agent_identity_authapi_base_url: &str,
|
||||
access_token: &str,
|
||||
is_fedramp_account: bool,
|
||||
key_material: &GeneratedAgentKeyMaterial,
|
||||
abom: AgentBillOfMaterials,
|
||||
capabilities: Vec<String>,
|
||||
) -> Result<String> {
|
||||
let url = agent_registration_url(agent_identity_authapi_base_url);
|
||||
let request = RegisterAgentRequest {
|
||||
abom,
|
||||
agent_public_key: key_material.public_key_ssh.clone(),
|
||||
capabilities,
|
||||
ttl: None,
|
||||
};
|
||||
|
||||
let mut request_builder = client
|
||||
.post(&url)
|
||||
.bearer_auth(access_token)
|
||||
.json(&request)
|
||||
.timeout(AGENT_REGISTRATION_TIMEOUT);
|
||||
if is_fedramp_account {
|
||||
request_builder = request_builder.header("X-OpenAI-Fedramp", "true");
|
||||
}
|
||||
|
||||
let response = request_builder
|
||||
.send()
|
||||
.await
|
||||
.with_context(|| format!("failed to send agent identity registration request to {url}"))?
|
||||
.error_for_status()
|
||||
.with_context(|| format!("agent identity registration failed for {url}"))?
|
||||
.json::<RegisterAgentResponse>()
|
||||
.await
|
||||
.with_context(|| format!("failed to parse agent identity response from {url}"))?;
|
||||
|
||||
Ok(response.agent_runtime_id)
|
||||
}
|
||||
|
||||
fn task_id_from_register_task_response(
|
||||
key: AgentIdentityKey<'_>,
|
||||
response: RegisterTaskResponse,
|
||||
@@ -260,10 +420,17 @@ pub fn decrypt_task_id_response(
|
||||
}
|
||||
|
||||
pub fn generate_agent_key_material() -> Result<GeneratedAgentKeyMaterial> {
|
||||
let mut secret_key_bytes = [0u8; 32];
|
||||
let mut seed_material = [0u8; AGENT_IDENTITY_KEY_SEED_BYTES];
|
||||
OsRng
|
||||
.try_fill_bytes(&mut secret_key_bytes)
|
||||
.context("failed to generate agent identity private key bytes")?;
|
||||
.try_fill_bytes(&mut seed_material)
|
||||
.context("failed to generate agent identity private key seed material")?;
|
||||
// Ed25519 stores a 32-byte seed, so derive it from all sampled seed material.
|
||||
let mut digest = Sha512::new();
|
||||
digest.update(AGENT_IDENTITY_KEY_DERIVATION_CONTEXT);
|
||||
digest.update(seed_material);
|
||||
let digest = digest.finalize();
|
||||
let mut secret_key_bytes = [0u8; 32];
|
||||
secret_key_bytes.copy_from_slice(&digest[..32]);
|
||||
let signing_key = SigningKey::from_bytes(&secret_key_bytes);
|
||||
let private_key_pkcs8 = signing_key
|
||||
.to_pkcs8_der()
|
||||
@@ -296,23 +463,22 @@ pub fn curve25519_secret_key_from_private_key_pkcs8_base64(
|
||||
Ok(curve25519_secret_key_from_signing_key(&signing_key))
|
||||
}
|
||||
|
||||
pub fn agent_registration_url(chatgpt_base_url: &str) -> String {
|
||||
let trimmed = chatgpt_base_url.trim_end_matches('/');
|
||||
format!("{trimmed}/v1/agent/register")
|
||||
pub fn agent_registration_url(agent_identity_authapi_base_url: &str) -> String {
|
||||
agent_identity_authapi_url(agent_identity_authapi_base_url, "/v1/agent/register")
|
||||
}
|
||||
|
||||
pub fn agent_task_registration_url(chatgpt_base_url: &str, agent_runtime_id: &str) -> String {
|
||||
let trimmed = chatgpt_base_url.trim_end_matches('/');
|
||||
format!("{trimmed}/v1/agent/{agent_runtime_id}/task/register")
|
||||
pub fn agent_task_registration_url(
|
||||
agent_identity_authapi_base_url: &str,
|
||||
agent_runtime_id: &str,
|
||||
) -> String {
|
||||
agent_identity_authapi_url(
|
||||
agent_identity_authapi_base_url,
|
||||
&format!("/v1/agent/{agent_runtime_id}/task/register"),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn agent_identity_biscuit_url(chatgpt_base_url: &str) -> String {
|
||||
let trimmed = chatgpt_base_url.trim_end_matches('/');
|
||||
format!("{trimmed}/authenticate_app_v2")
|
||||
}
|
||||
|
||||
pub fn agent_identity_jwks_url(chatgpt_base_url: &str) -> String {
|
||||
let trimmed = chatgpt_base_url.trim_end_matches('/');
|
||||
pub fn agent_identity_jwks_url(agent_identity_jwt_base_url: &str) -> String {
|
||||
let trimmed = agent_identity_jwt_base_url.trim_end_matches('/');
|
||||
if trimmed.contains("/backend-api") {
|
||||
format!("{trimmed}/wham/agent-identities/jwks")
|
||||
} else {
|
||||
@@ -320,15 +486,9 @@ pub fn agent_identity_jwks_url(chatgpt_base_url: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn agent_identity_request_id() -> Result<String> {
|
||||
let mut request_id_bytes = [0u8; 16];
|
||||
OsRng
|
||||
.try_fill_bytes(&mut request_id_bytes)
|
||||
.context("failed to generate agent identity request id")?;
|
||||
Ok(format!(
|
||||
"codex-agent-identity-{}",
|
||||
URL_SAFE_NO_PAD.encode(request_id_bytes)
|
||||
))
|
||||
fn agent_identity_authapi_url(agent_identity_authapi_base_url: &str, api_path: &str) -> String {
|
||||
let base_url = agent_identity_authapi_base_url.trim_end_matches('/');
|
||||
format!("{base_url}{api_path}")
|
||||
}
|
||||
|
||||
pub fn build_abom(session_source: SessionSource) -> AgentBillOfMaterials {
|
||||
@@ -412,6 +572,24 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn register_task_request_uses_single_run_task_shape() {
|
||||
let request = RegisterTaskRequest {
|
||||
timestamp: "2026-04-23T00:00:00Z".to_string(),
|
||||
signature: "signature".to_string(),
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_value(request).expect("serialize request");
|
||||
|
||||
assert_eq!(
|
||||
serialized,
|
||||
serde_json::json!({
|
||||
"timestamp": "2026-04-23T00:00:00Z",
|
||||
"signature": "signature",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authorization_header_for_agent_task_serializes_signed_agent_assertion() {
|
||||
let signing_key = SigningKey::from_bytes(&[7u8; 32]);
|
||||
@@ -422,13 +600,9 @@ mod tests {
|
||||
agent_runtime_id: "agent-123",
|
||||
private_key_pkcs8_base64: &BASE64_STANDARD.encode(private_key.as_bytes()),
|
||||
};
|
||||
let target = AgentTaskAuthorizationTarget {
|
||||
agent_runtime_id: "agent-123",
|
||||
task_id: "task-123",
|
||||
};
|
||||
|
||||
let header =
|
||||
authorization_header_for_agent_task(key, target).expect("build agent assertion header");
|
||||
let header = authorization_header_for_agent_task(key, "task-123")
|
||||
.expect("build agent assertion header");
|
||||
let token = header
|
||||
.strip_prefix("AgentAssertion ")
|
||||
.expect("agent assertion scheme");
|
||||
@@ -464,31 +638,6 @@ mod tests {
|
||||
.expect("signature should verify");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authorization_header_for_agent_task_rejects_mismatched_runtime() {
|
||||
let signing_key = SigningKey::from_bytes(&[7u8; 32]);
|
||||
let private_key = signing_key
|
||||
.to_pkcs8_der()
|
||||
.expect("encode test key material");
|
||||
let private_key_pkcs8_base64 = BASE64_STANDARD.encode(private_key.as_bytes());
|
||||
let key = AgentIdentityKey {
|
||||
agent_runtime_id: "agent-123",
|
||||
private_key_pkcs8_base64: &private_key_pkcs8_base64,
|
||||
};
|
||||
let target = AgentTaskAuthorizationTarget {
|
||||
agent_runtime_id: "agent-456",
|
||||
task_id: "task-123",
|
||||
};
|
||||
|
||||
let error = authorization_header_for_agent_task(key, target)
|
||||
.expect_err("runtime mismatch should fail");
|
||||
|
||||
assert_eq!(
|
||||
error.to_string(),
|
||||
"agent task runtime agent-456 does not match stored agent identity agent-123"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_agent_identity_jwt_reads_claims() {
|
||||
let jwt = jwt_with_payload(serde_json::json!({
|
||||
@@ -704,7 +853,98 @@ J1bwkqKZTB5dHolX9A58e/xXnfZ5P8f3Z83+Izap3FwqQulk7b1WO1MQcHuVg2NN
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_identity_jwks_url_uses_backend_api_base_url() {
|
||||
fn chatgpt_environment_maps_known_urls_to_authapi() -> anyhow::Result<()> {
|
||||
assert_eq!(
|
||||
ChatGptEnvironment::from_chatgpt_base_url("https://chatgpt.com/backend-api/codex")?,
|
||||
ChatGptEnvironment::Production
|
||||
);
|
||||
assert_eq!(
|
||||
ChatGptEnvironment::Production.agent_identity_authapi_base_url(),
|
||||
"https://auth.openai.com/api/accounts"
|
||||
);
|
||||
assert_eq!(
|
||||
ChatGptEnvironment::from_chatgpt_base_url("https://chatgpt-staging.com/backend-api")?,
|
||||
ChatGptEnvironment::Staging
|
||||
);
|
||||
assert_eq!(
|
||||
ChatGptEnvironment::Staging.agent_identity_authapi_base_url(),
|
||||
"https://auth.api.openai.org/api/accounts"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chatgpt_environment_rejects_custom_urls() {
|
||||
assert!(ChatGptEnvironment::from_chatgpt_base_url("http://localhost:8080").is_err(),);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_registration_url_appends_to_authapi_base_url() {
|
||||
assert_eq!(
|
||||
agent_registration_url("https://auth.openai.com/api/accounts"),
|
||||
"https://auth.openai.com/api/accounts/v1/agent/register"
|
||||
);
|
||||
assert_eq!(
|
||||
agent_registration_url("http://localhost:8080"),
|
||||
"http://localhost:8080/v1/agent/register"
|
||||
);
|
||||
assert_eq!(
|
||||
agent_registration_url("http://localhost:8080/backend-api"),
|
||||
"http://localhost:8080/backend-api/v1/agent/register"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_task_registration_url_appends_to_authapi_base_url() {
|
||||
assert_eq!(
|
||||
agent_task_registration_url("https://auth.openai.com/api/accounts", "agent-runtime-id"),
|
||||
"https://auth.openai.com/api/accounts/v1/agent/agent-runtime-id/task/register"
|
||||
);
|
||||
assert_eq!(
|
||||
agent_task_registration_url(
|
||||
"https://auth.openai.com/api/accounts/",
|
||||
"agent-runtime-id"
|
||||
),
|
||||
"https://auth.openai.com/api/accounts/v1/agent/agent-runtime-id/task/register"
|
||||
);
|
||||
assert_eq!(
|
||||
agent_task_registration_url("http://localhost:8080", "agent-runtime-id"),
|
||||
"http://localhost:8080/v1/agent/agent-runtime-id/task/register"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retryable_registration_error_accepts_429_and_5xx() {
|
||||
let too_many_requests = anyhow::Error::new(AgentIdentityRegistrationHttpError::new(
|
||||
"agent registration",
|
||||
reqwest::StatusCode::TOO_MANY_REQUESTS,
|
||||
"rate limited".to_string(),
|
||||
));
|
||||
let unavailable = anyhow::Error::new(AgentIdentityRegistrationHttpError::new(
|
||||
"agent registration",
|
||||
reqwest::StatusCode::SERVICE_UNAVAILABLE,
|
||||
"try later".to_string(),
|
||||
));
|
||||
|
||||
assert!(is_retryable_registration_error(&too_many_requests));
|
||||
assert!(is_retryable_registration_error(&unavailable));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retryable_registration_error_rejects_hard_failures() {
|
||||
let forbidden = anyhow::Error::new(AgentIdentityRegistrationHttpError::new(
|
||||
"agent registration",
|
||||
reqwest::StatusCode::FORBIDDEN,
|
||||
"not allowed".to_string(),
|
||||
));
|
||||
let malformed = anyhow::anyhow!("failed to sign registration request");
|
||||
|
||||
assert!(!is_retryable_registration_error(&forbidden));
|
||||
assert!(!is_retryable_registration_error(&malformed));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_identity_jwks_url_uses_agent_identity_jwt_route() {
|
||||
assert_eq!(
|
||||
agent_identity_jwks_url("https://chatgpt.com/backend-api"),
|
||||
"https://chatgpt.com/backend-api/wham/agent-identities/jwks"
|
||||
@@ -716,7 +956,7 @@ J1bwkqKZTB5dHolX9A58e/xXnfZ5P8f3Z83+Izap3FwqQulk7b1WO1MQcHuVg2NN
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_identity_jwks_url_uses_codex_api_base_url() {
|
||||
fn agent_identity_jwks_url_uses_jwt_issuer_base_url() {
|
||||
assert_eq!(
|
||||
agent_identity_jwks_url("http://localhost:8080/api/codex"),
|
||||
"http://localhost:8080/api/codex/agent-identities/jwks"
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
use crate::events::TrackEventsRequest;
|
||||
use std::fs::File;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
pub(crate) const ANALYTICS_EVENTS_CAPTURE_FILE_ENV_VAR: &str =
|
||||
"CODEX_ANALYTICS_EVENTS_CAPTURE_FILE";
|
||||
|
||||
pub(crate) fn initialize(path: &Path) -> io::Result<()> {
|
||||
open_capture_file(path).map(drop)
|
||||
}
|
||||
|
||||
pub(crate) fn append_payload(path: &Path, payload: &TrackEventsRequest) -> io::Result<()> {
|
||||
let mut line = serde_json::to_vec(payload)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
|
||||
line.push(b'\n');
|
||||
|
||||
let mut file = open_capture_file(path)?;
|
||||
file.write_all(&line)?;
|
||||
file.flush()
|
||||
}
|
||||
|
||||
fn open_capture_file(path: &Path) -> io::Result<File> {
|
||||
let mut options = OpenOptions::new();
|
||||
options.create(true).append(true);
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
options.mode(0o600);
|
||||
}
|
||||
options.open(path)
|
||||
}
|
||||
@@ -9,7 +9,11 @@ use crate::events::CodexCommandExecutionEventParams;
|
||||
use crate::events::CodexCommandExecutionEventRequest;
|
||||
use crate::events::CodexCompactionEventRequest;
|
||||
use crate::events::CodexHookRunEventRequest;
|
||||
use crate::events::CodexOnboardingExternalAgentImportFailureEventRequest;
|
||||
use crate::events::CodexOnboardingExternalAgentImportFailureMetadata;
|
||||
use crate::events::CodexPluginEventRequest;
|
||||
use crate::events::CodexPluginInstallFailedEventRequest;
|
||||
use crate::events::CodexPluginInstallFailedMetadata;
|
||||
use crate::events::CodexPluginUsedEventRequest;
|
||||
use crate::events::CodexReviewEventParams;
|
||||
use crate::events::CodexReviewEventRequest;
|
||||
@@ -43,6 +47,7 @@ use crate::facts::AppInvocation;
|
||||
use crate::facts::AppMentionedInput;
|
||||
use crate::facts::AppUsedInput;
|
||||
use crate::facts::CodexCompactionEvent;
|
||||
use crate::facts::CodexErrKind;
|
||||
use crate::facts::CompactionImplementation;
|
||||
use crate::facts::CompactionPhase;
|
||||
use crate::facts::CompactionReason;
|
||||
@@ -50,10 +55,13 @@ use crate::facts::CompactionStatus;
|
||||
use crate::facts::CompactionStrategy;
|
||||
use crate::facts::CompactionTrigger;
|
||||
use crate::facts::CustomAnalyticsFact;
|
||||
use crate::facts::ExternalAgentConfigImportCompletedInput;
|
||||
use crate::facts::ExternalAgentConfigImportFailureInput;
|
||||
use crate::facts::HookRunFact;
|
||||
use crate::facts::HookRunInput;
|
||||
use crate::facts::InputError;
|
||||
use crate::facts::InvocationType;
|
||||
use crate::facts::PluginInstallFailedInput;
|
||||
use crate::facts::PluginState;
|
||||
use crate::facts::PluginStateChangedInput;
|
||||
use crate::facts::PluginUsedInput;
|
||||
@@ -177,6 +185,7 @@ fn sample_thread_with_metadata(
|
||||
model_provider: "openai".to_string(),
|
||||
created_at: 1,
|
||||
updated_at: 2,
|
||||
recency_at: Some(2),
|
||||
status: AppServerThreadStatus::Idle,
|
||||
path: None,
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
@@ -765,6 +774,7 @@ fn sample_initialize_fact(connection_id: u64) -> AnalyticsFact {
|
||||
experimental_api: false,
|
||||
request_attestation: false,
|
||||
opt_out_notification_methods: None,
|
||||
mcp_server_openai_form_elicitation: false,
|
||||
}),
|
||||
},
|
||||
product_client_id: DEFAULT_ORIGINATOR.to_string(),
|
||||
@@ -822,7 +832,7 @@ fn sample_command_execution_item_with_id(
|
||||
ThreadItem::CommandExecution {
|
||||
id: id.to_string(),
|
||||
command: "echo hi".to_string(),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
cwd: test_path_buf("/tmp").abs().into(),
|
||||
process_id: Some("pid-1".to_string()),
|
||||
source: CommandExecutionSource::Agent,
|
||||
status,
|
||||
@@ -860,6 +870,7 @@ fn sample_command_approval_request(request_id: i64, approval_id: Option<&str>) -
|
||||
item_id: "item-1".to_string(),
|
||||
started_at_ms: 1_000,
|
||||
approval_id: approval_id.map(str::to_string),
|
||||
environment_id: None,
|
||||
reason: None,
|
||||
network_approval_context: None,
|
||||
command: Some("echo hi".to_string()),
|
||||
@@ -1278,11 +1289,13 @@ fn compaction_event_serializes_expected_shape() {
|
||||
phase: CompactionPhase::MidTurn,
|
||||
strategy: CompactionStrategy::Memento,
|
||||
status: CompactionStatus::Completed,
|
||||
error: None,
|
||||
codex_error_kind: None,
|
||||
codex_error_http_status_code: None,
|
||||
active_context_tokens_before: 120_000,
|
||||
active_context_tokens_after: 18_000,
|
||||
retained_image_count: None,
|
||||
compaction_summary_tokens: None,
|
||||
cached_input_tokens: None,
|
||||
started_at: 100,
|
||||
completed_at: 106,
|
||||
duration_ms: Some(6543),
|
||||
@@ -1328,11 +1341,13 @@ fn compaction_event_serializes_expected_shape() {
|
||||
"phase": "mid_turn",
|
||||
"strategy": "memento",
|
||||
"status": "completed",
|
||||
"error": null,
|
||||
"codex_error_kind": null,
|
||||
"codex_error_http_status_code": null,
|
||||
"active_context_tokens_before": 120000,
|
||||
"active_context_tokens_after": 18000,
|
||||
"retained_image_count": null,
|
||||
"compaction_summary_tokens": null,
|
||||
"cached_input_tokens": null,
|
||||
"started_at": 100,
|
||||
"completed_at": 106,
|
||||
"duration_ms": 6543
|
||||
@@ -1655,6 +1670,7 @@ async fn initialize_caches_client_and_thread_lifecycle_publishes_once_initialize
|
||||
experimental_api: false,
|
||||
request_attestation: false,
|
||||
opt_out_notification_methods: None,
|
||||
mcp_server_openai_form_elicitation: false,
|
||||
}),
|
||||
},
|
||||
product_client_id: DEFAULT_ORIGINATOR.to_string(),
|
||||
@@ -1804,6 +1820,7 @@ async fn compaction_event_ingests_custom_fact() {
|
||||
experimental_api: false,
|
||||
request_attestation: false,
|
||||
opt_out_notification_methods: None,
|
||||
mcp_server_openai_form_elicitation: false,
|
||||
}),
|
||||
},
|
||||
product_client_id: DEFAULT_ORIGINATOR.to_string(),
|
||||
@@ -1850,11 +1867,13 @@ async fn compaction_event_ingests_custom_fact() {
|
||||
phase: CompactionPhase::StandaloneTurn,
|
||||
strategy: CompactionStrategy::Memento,
|
||||
status: CompactionStatus::Failed,
|
||||
error: Some("context limit exceeded".to_string()),
|
||||
codex_error_kind: Some(CodexErrKind::ContextWindowExceeded),
|
||||
codex_error_http_status_code: None,
|
||||
active_context_tokens_before: 131_000,
|
||||
active_context_tokens_after: 131_000,
|
||||
retained_image_count: None,
|
||||
compaction_summary_tokens: None,
|
||||
cached_input_tokens: None,
|
||||
started_at: 100,
|
||||
completed_at: 101,
|
||||
duration_ms: Some(1200),
|
||||
@@ -1870,6 +1889,14 @@ async fn compaction_event_ingests_custom_fact() {
|
||||
assert_eq!(payload[0]["event_params"]["session_id"], "session-thread-1");
|
||||
assert_eq!(payload[0]["event_params"]["thread_id"], "thread-1");
|
||||
assert_eq!(payload[0]["event_params"]["turn_id"], "turn-compact");
|
||||
assert_eq!(
|
||||
payload[0]["event_params"]["codex_error_kind"],
|
||||
json!("context_window_exceeded")
|
||||
);
|
||||
assert_eq!(
|
||||
payload[0]["event_params"]["codex_error_http_status_code"],
|
||||
json!(null)
|
||||
);
|
||||
assert_eq!(
|
||||
payload[0]["event_params"]["app_server_client"]["product_client_id"],
|
||||
DEFAULT_ORIGINATOR
|
||||
@@ -1922,6 +1949,7 @@ async fn guardian_review_event_ingests_custom_fact_with_optional_target_item() {
|
||||
experimental_api: false,
|
||||
request_attestation: false,
|
||||
opt_out_notification_methods: None,
|
||||
mcp_server_openai_form_elicitation: false,
|
||||
}),
|
||||
},
|
||||
product_client_id: DEFAULT_ORIGINATOR.to_string(),
|
||||
@@ -1972,6 +2000,11 @@ async fn guardian_review_event_ingests_custom_fact_with_optional_target_item() {
|
||||
guardian_session_kind: None,
|
||||
guardian_model: None,
|
||||
guardian_reasoning_effort: None,
|
||||
guardian_default_review_model_id: Some("codex-auto-review".to_string()),
|
||||
guardian_catalog_contains_auto_review: Some(false),
|
||||
guardian_review_model_overridden: Some(false),
|
||||
guardian_review_model_override: None,
|
||||
guardian_model_provider_id: Some("openai".to_string()),
|
||||
had_prior_review_context: None,
|
||||
review_timeout_ms: 90_000,
|
||||
tool_call_count: None,
|
||||
@@ -2038,6 +2071,26 @@ async fn guardian_review_event_ingests_custom_fact_with_optional_target_item() {
|
||||
assert_eq!(payload[0]["event_params"]["failure_reason"], "timeout");
|
||||
assert_eq!(payload[0]["event_params"]["attempt_count"], 1);
|
||||
assert_eq!(payload[0]["event_params"]["review_timeout_ms"], 90_000);
|
||||
assert_eq!(
|
||||
payload[0]["event_params"]["guardian_default_review_model_id"],
|
||||
"codex-auto-review"
|
||||
);
|
||||
assert_eq!(
|
||||
payload[0]["event_params"]["guardian_catalog_contains_auto_review"],
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
payload[0]["event_params"]["guardian_review_model_overridden"],
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
payload[0]["event_params"]["guardian_review_model_override"],
|
||||
json!(null)
|
||||
);
|
||||
assert_eq!(
|
||||
payload[0]["event_params"]["guardian_model_provider_id"],
|
||||
"openai"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -2776,11 +2829,13 @@ async fn subagent_events_use_inherited_connection_unless_turn_connection_is_expl
|
||||
phase: CompactionPhase::StandaloneTurn,
|
||||
strategy: CompactionStrategy::Memento,
|
||||
status: CompactionStatus::Completed,
|
||||
error: None,
|
||||
codex_error_kind: None,
|
||||
codex_error_http_status_code: None,
|
||||
active_context_tokens_before: 131_000,
|
||||
active_context_tokens_after: 64_000,
|
||||
retained_image_count: None,
|
||||
compaction_summary_tokens: None,
|
||||
cached_input_tokens: None,
|
||||
started_at: 100,
|
||||
completed_at: 101,
|
||||
duration_ms: Some(1200),
|
||||
@@ -3010,6 +3065,36 @@ fn plugin_management_event_serializes_expected_shape() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_install_failed_event_serializes_expected_shape() {
|
||||
let event = TrackEventRequest::PluginInstallFailed(CodexPluginInstallFailedEventRequest {
|
||||
event_type: "codex_plugin_install_failed",
|
||||
event_params: CodexPluginInstallFailedMetadata {
|
||||
plugin: codex_plugin_metadata(sample_plugin_metadata()),
|
||||
error_type: "store_io".to_string(),
|
||||
},
|
||||
});
|
||||
|
||||
let payload = serde_json::to_value(&event).expect("serialize plugin install failed event");
|
||||
|
||||
assert_eq!(
|
||||
payload,
|
||||
json!({
|
||||
"event_type": "codex_plugin_install_failed",
|
||||
"event_params": {
|
||||
"plugin_id": "sample@test",
|
||||
"plugin_name": "sample",
|
||||
"marketplace_name": "test",
|
||||
"has_skills": true,
|
||||
"mcp_server_count": 2,
|
||||
"connector_ids": ["calendar", "drive"],
|
||||
"product_client_id": originator().value,
|
||||
"error_type": "store_io"
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_management_event_can_use_remote_plugin_id_override() {
|
||||
let mut plugin = sample_plugin_metadata();
|
||||
@@ -3377,6 +3462,190 @@ async fn reducer_ingests_plugin_state_changed_fact() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn reducer_ingests_plugin_install_failed_fact() {
|
||||
let mut reducer = AnalyticsReducer::default();
|
||||
let mut events = Vec::new();
|
||||
|
||||
reducer
|
||||
.ingest(
|
||||
AnalyticsFact::Custom(CustomAnalyticsFact::PluginInstallFailed(
|
||||
PluginInstallFailedInput {
|
||||
plugin: sample_plugin_metadata(),
|
||||
error_type: "invalid_plugin".to_string(),
|
||||
},
|
||||
)),
|
||||
&mut events,
|
||||
)
|
||||
.await;
|
||||
|
||||
let payload = serde_json::to_value(&events).expect("serialize events");
|
||||
assert_eq!(
|
||||
payload,
|
||||
json!([{
|
||||
"event_type": "codex_plugin_install_failed",
|
||||
"event_params": {
|
||||
"plugin_id": "sample@test",
|
||||
"plugin_name": "sample",
|
||||
"marketplace_name": "test",
|
||||
"has_skills": true,
|
||||
"mcp_server_count": 2,
|
||||
"connector_ids": ["calendar", "drive"],
|
||||
"product_client_id": originator().value,
|
||||
"error_type": "invalid_plugin"
|
||||
}
|
||||
}])
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn reducer_ingests_plugin_install_failed_fact_without_detail() {
|
||||
let mut reducer = AnalyticsReducer::default();
|
||||
let mut events = Vec::new();
|
||||
let mut plugin = PluginTelemetryMetadata::from_plugin_id(
|
||||
&PluginId::parse("unknown@openai-curated-remote").expect("valid plugin id"),
|
||||
);
|
||||
plugin.remote_plugin_id = Some("plugins~Plugin_00000000000000000000000000000000".to_string());
|
||||
|
||||
reducer
|
||||
.ingest(
|
||||
AnalyticsFact::Custom(CustomAnalyticsFact::PluginInstallFailed(
|
||||
PluginInstallFailedInput {
|
||||
plugin,
|
||||
error_type: "remote_catalog_unexpected_status".to_string(),
|
||||
},
|
||||
)),
|
||||
&mut events,
|
||||
)
|
||||
.await;
|
||||
|
||||
let payload = serde_json::to_value(&events).expect("serialize events");
|
||||
assert_eq!(
|
||||
payload,
|
||||
json!([{
|
||||
"event_type": "codex_plugin_install_failed",
|
||||
"event_params": {
|
||||
"plugin_id": "plugins~Plugin_00000000000000000000000000000000",
|
||||
"plugin_name": "unknown",
|
||||
"marketplace_name": "openai-curated-remote",
|
||||
"has_skills": null,
|
||||
"mcp_server_count": null,
|
||||
"connector_ids": null,
|
||||
"product_client_id": originator().value,
|
||||
"error_type": "remote_catalog_unexpected_status"
|
||||
}
|
||||
}])
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn reducer_ingests_external_agent_config_import_completed_fact() {
|
||||
let mut reducer = AnalyticsReducer::default();
|
||||
let mut events = Vec::new();
|
||||
|
||||
reducer
|
||||
.ingest(
|
||||
AnalyticsFact::Custom(CustomAnalyticsFact::ExternalAgentConfigImportCompleted(
|
||||
ExternalAgentConfigImportCompletedInput {
|
||||
import_id: "import-1".to_string(),
|
||||
source: "app_server".to_string(),
|
||||
item_type: "PLUGINS".to_string(),
|
||||
success_count: 2,
|
||||
failed_count: 1,
|
||||
},
|
||||
)),
|
||||
&mut events,
|
||||
)
|
||||
.await;
|
||||
|
||||
let payload = serde_json::to_value(&events).expect("serialize events");
|
||||
assert_eq!(
|
||||
payload,
|
||||
json!([{
|
||||
"event_type": "codex_onboarding_external_agent_import_complete",
|
||||
"event_params": {
|
||||
"import_id": "import-1",
|
||||
"source": "app_server",
|
||||
"type": "PLUGINS",
|
||||
"success_count": 2,
|
||||
"failed_count": 1,
|
||||
"product_client_id": originator().value,
|
||||
}
|
||||
}])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external_agent_config_import_failure_event_serializes_expected_shape() {
|
||||
let event = TrackEventRequest::ExternalAgentConfigImportFailure(
|
||||
CodexOnboardingExternalAgentImportFailureEventRequest {
|
||||
event_type: "codex_onboarding_external_agent_import_failure",
|
||||
event_params: CodexOnboardingExternalAgentImportFailureMetadata {
|
||||
import_id: "import-1".to_string(),
|
||||
source: "app_server".to_string(),
|
||||
item_type: "SESSIONS".to_string(),
|
||||
failure_stage: "session_missing".to_string(),
|
||||
error_type: "session_missing".to_string(),
|
||||
product_client_id: Some(originator().value),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
let payload = serde_json::to_value(&event).expect("serialize import failure event");
|
||||
|
||||
assert_eq!(
|
||||
payload,
|
||||
json!({
|
||||
"event_type": "codex_onboarding_external_agent_import_failure",
|
||||
"event_params": {
|
||||
"import_id": "import-1",
|
||||
"source": "app_server",
|
||||
"type": "SESSIONS",
|
||||
"failure_stage": "session_missing",
|
||||
"error_type": "session_missing",
|
||||
"product_client_id": originator().value,
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn reducer_ingests_external_agent_config_import_failure_fact() {
|
||||
let mut reducer = AnalyticsReducer::default();
|
||||
let mut events = Vec::new();
|
||||
|
||||
reducer
|
||||
.ingest(
|
||||
AnalyticsFact::Custom(CustomAnalyticsFact::ExternalAgentConfigImportFailure(
|
||||
ExternalAgentConfigImportFailureInput {
|
||||
import_id: "import-1".to_string(),
|
||||
source: "app_server".to_string(),
|
||||
item_type: "SESSIONS".to_string(),
|
||||
failure_stage: "session_missing".to_string(),
|
||||
error_type: "session_missing".to_string(),
|
||||
},
|
||||
)),
|
||||
&mut events,
|
||||
)
|
||||
.await;
|
||||
|
||||
let payload = serde_json::to_value(&events).expect("serialize events");
|
||||
assert_eq!(
|
||||
payload,
|
||||
json!([{
|
||||
"event_type": "codex_onboarding_external_agent_import_failure",
|
||||
"event_params": {
|
||||
"import_id": "import-1",
|
||||
"source": "app_server",
|
||||
"type": "SESSIONS",
|
||||
"failure_stage": "session_missing",
|
||||
"error_type": "session_missing",
|
||||
"product_client_id": originator().value,
|
||||
}
|
||||
}])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn turn_event_serializes_expected_shape() {
|
||||
let event = TrackEventRequest::TurnEvent(Box::new(CodexTurnEventRequest {
|
||||
@@ -3846,6 +4115,32 @@ async fn turn_event_counts_completed_tool_items() {
|
||||
)
|
||||
.await;
|
||||
|
||||
let mcp_tool_call_item = |status, duration_ms| ThreadItem::McpToolCall {
|
||||
id: "mcp-1".to_string(),
|
||||
server: "server".to_string(),
|
||||
tool: "search".to_string(),
|
||||
status,
|
||||
arguments: json!({}),
|
||||
mcp_app_resource_uri: None,
|
||||
plugin_id: Some("sample@test".to_string()),
|
||||
result: None,
|
||||
error: None,
|
||||
duration_ms,
|
||||
};
|
||||
reducer
|
||||
.ingest(
|
||||
AnalyticsFact::Notification(Box::new(ServerNotification::ItemStarted(
|
||||
ItemStartedNotification {
|
||||
thread_id: "thread-2".to_string(),
|
||||
turn_id: "turn-2".to_string(),
|
||||
started_at_ms: 998,
|
||||
item: mcp_tool_call_item(McpToolCallStatus::InProgress, None),
|
||||
},
|
||||
))),
|
||||
&mut out,
|
||||
)
|
||||
.await;
|
||||
|
||||
let completed_tool_items = vec![
|
||||
sample_command_execution_item(CommandExecutionStatus::Completed, Some(0), Some(1)),
|
||||
ThreadItem::FileChange {
|
||||
@@ -3853,18 +4148,7 @@ async fn turn_event_counts_completed_tool_items() {
|
||||
changes: Vec::new(),
|
||||
status: PatchApplyStatus::Completed,
|
||||
},
|
||||
ThreadItem::McpToolCall {
|
||||
id: "mcp-1".to_string(),
|
||||
server: "server".to_string(),
|
||||
tool: "search".to_string(),
|
||||
status: McpToolCallStatus::Completed,
|
||||
arguments: json!({}),
|
||||
mcp_app_resource_uri: None,
|
||||
plugin_id: None,
|
||||
result: None,
|
||||
error: None,
|
||||
duration_ms: Some(2),
|
||||
},
|
||||
mcp_tool_call_item(McpToolCallStatus::Completed, Some(2)),
|
||||
ThreadItem::DynamicToolCall {
|
||||
id: "dynamic-1".to_string(),
|
||||
namespace: None,
|
||||
@@ -3922,6 +4206,13 @@ async fn turn_event_counts_completed_tool_items() {
|
||||
.await;
|
||||
}
|
||||
|
||||
let mcp_tool_call_event = out
|
||||
.iter()
|
||||
.find(|event| matches!(event, TrackEventRequest::McpToolCall(_)))
|
||||
.expect("MCP tool call event should be emitted");
|
||||
let payload = serde_json::to_value(mcp_tool_call_event).expect("serialize MCP tool call event");
|
||||
assert_eq!(payload["event_params"]["plugin_id"], json!("sample@test"));
|
||||
|
||||
reducer
|
||||
.ingest(
|
||||
AnalyticsFact::Notification(Box::new(sample_turn_completed_notification(
|
||||
|
||||
@@ -11,8 +11,11 @@ use crate::facts::AppMentionedInput;
|
||||
use crate::facts::AppUsedInput;
|
||||
use crate::facts::CodexGoalEvent;
|
||||
use crate::facts::CustomAnalyticsFact;
|
||||
use crate::facts::ExternalAgentConfigImportCompletedInput;
|
||||
use crate::facts::ExternalAgentConfigImportFailureInput;
|
||||
use crate::facts::HookRunFact;
|
||||
use crate::facts::HookRunInput;
|
||||
use crate::facts::PluginInstallFailedInput;
|
||||
use crate::facts::PluginState;
|
||||
use crate::facts::PluginStateChangedInput;
|
||||
use crate::facts::SkillInvocation;
|
||||
@@ -38,6 +41,7 @@ use codex_login::default_client::create_client;
|
||||
use codex_plugin::PluginTelemetryMetadata;
|
||||
use codex_protocol::request_permissions::RequestPermissionsResponse;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
@@ -59,15 +63,70 @@ pub struct AnalyticsEventsClient {
|
||||
queue: Option<AnalyticsEventsQueue>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
enum AnalyticsEventsDestination {
|
||||
Http {
|
||||
url: String,
|
||||
},
|
||||
#[cfg(debug_assertions)]
|
||||
CaptureFile {
|
||||
path: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
impl AnalyticsEventsDestination {
|
||||
fn from_base_url(base_url: String) -> Self {
|
||||
let capture_file = analytics_capture_file_from_env();
|
||||
Self::from_base_url_and_capture_file(base_url, capture_file)
|
||||
}
|
||||
|
||||
fn from_base_url_and_capture_file(base_url: String, capture_file: Option<PathBuf>) -> Self {
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(path) = capture_file {
|
||||
if let Err(err) = crate::analytics_capture::initialize(&path) {
|
||||
tracing::error!(
|
||||
path = %path.display(),
|
||||
"failed to initialize analytics event capture; network delivery remains disabled: {err}"
|
||||
);
|
||||
}
|
||||
tracing::warn!(
|
||||
path = %path.display(),
|
||||
"analytics event capture enabled; network delivery is disabled"
|
||||
);
|
||||
return Self::CaptureFile { path };
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
let _ = capture_file;
|
||||
|
||||
let base_url = base_url.trim_end_matches('/');
|
||||
Self::Http {
|
||||
url: format!("{base_url}/codex/analytics-events/events"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn analytics_capture_file_from_env() -> Option<PathBuf> {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
std::env::var_os(crate::analytics_capture::ANALYTICS_EVENTS_CAPTURE_FILE_ENV_VAR)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(PathBuf::from)
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
None
|
||||
}
|
||||
|
||||
impl AnalyticsEventsQueue {
|
||||
pub(crate) fn new(auth_manager: Arc<AuthManager>, base_url: String) -> Self {
|
||||
fn new(auth_manager: Arc<AuthManager>, destination: AnalyticsEventsDestination) -> Self {
|
||||
let (sender, mut receiver) = mpsc::channel(ANALYTICS_EVENTS_QUEUE_SIZE);
|
||||
tokio::spawn(async move {
|
||||
let mut reducer = AnalyticsReducer::default();
|
||||
while let Some(input) = receiver.recv().await {
|
||||
let mut events = Vec::new();
|
||||
reducer.ingest(input, &mut events).await;
|
||||
send_track_events(&auth_manager, &base_url, events).await;
|
||||
send_track_events(&auth_manager, &destination, events).await;
|
||||
}
|
||||
});
|
||||
Self {
|
||||
@@ -124,9 +183,10 @@ impl AnalyticsEventsClient {
|
||||
base_url: String,
|
||||
analytics_enabled: Option<bool>,
|
||||
) -> Self {
|
||||
let destination = AnalyticsEventsDestination::from_base_url(base_url);
|
||||
Self {
|
||||
queue: (analytics_enabled != Some(false))
|
||||
.then(|| AnalyticsEventsQueue::new(Arc::clone(&auth_manager), base_url)),
|
||||
.then(|| AnalyticsEventsQueue::new(Arc::clone(&auth_manager), destination)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,6 +346,33 @@ impl AnalyticsEventsClient {
|
||||
));
|
||||
}
|
||||
|
||||
pub fn track_plugin_install_failed(&self, plugin: PluginTelemetryMetadata, error_type: String) {
|
||||
self.record_fact(AnalyticsFact::Custom(
|
||||
CustomAnalyticsFact::PluginInstallFailed(PluginInstallFailedInput {
|
||||
plugin,
|
||||
error_type,
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn track_external_agent_config_import_completed(
|
||||
&self,
|
||||
input: ExternalAgentConfigImportCompletedInput,
|
||||
) {
|
||||
self.record_fact(AnalyticsFact::Custom(
|
||||
CustomAnalyticsFact::ExternalAgentConfigImportCompleted(input),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn track_external_agent_config_import_failure(
|
||||
&self,
|
||||
input: ExternalAgentConfigImportFailureInput,
|
||||
) {
|
||||
self.record_fact(AnalyticsFact::Custom(
|
||||
CustomAnalyticsFact::ExternalAgentConfigImportFailure(input),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn track_plugin_uninstalled(&self, plugin: PluginTelemetryMetadata) {
|
||||
self.record_fact(AnalyticsFact::Custom(
|
||||
CustomAnalyticsFact::PluginStateChanged(PluginStateChangedInput {
|
||||
@@ -410,7 +497,7 @@ impl AnalyticsEventsClient {
|
||||
|
||||
async fn send_track_events(
|
||||
auth_manager: &AuthManager,
|
||||
base_url: &str,
|
||||
destination: &AnalyticsEventsDestination,
|
||||
events: Vec<TrackEventRequest>,
|
||||
) {
|
||||
if events.is_empty() {
|
||||
@@ -424,10 +511,8 @@ async fn send_track_events(
|
||||
return;
|
||||
}
|
||||
|
||||
let base_url = base_url.trim_end_matches('/');
|
||||
let url = format!("{base_url}/codex/analytics-events/events");
|
||||
for events in track_event_request_batches(events) {
|
||||
send_track_events_request(&auth, &url, events).await;
|
||||
send_track_events_request(&auth, destination, events).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,13 +539,27 @@ fn track_event_request_batches(events: Vec<TrackEventRequest>) -> Vec<Vec<TrackE
|
||||
batches
|
||||
}
|
||||
|
||||
async fn send_track_events_request(auth: &CodexAuth, url: &str, events: Vec<TrackEventRequest>) {
|
||||
async fn send_track_events_request(
|
||||
auth: &CodexAuth,
|
||||
destination: &AnalyticsEventsDestination,
|
||||
events: Vec<TrackEventRequest>,
|
||||
) {
|
||||
if events.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let payload = TrackEventsRequest { events };
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
if capture_track_events_request(destination, &payload) {
|
||||
return;
|
||||
}
|
||||
|
||||
let url = match destination {
|
||||
AnalyticsEventsDestination::Http { url } => url,
|
||||
#[cfg(debug_assertions)]
|
||||
AnalyticsEventsDestination::CaptureFile { .. } => return,
|
||||
};
|
||||
let response = create_client()
|
||||
.post(url)
|
||||
.timeout(ANALYTICS_EVENTS_TIMEOUT)
|
||||
@@ -483,6 +582,24 @@ async fn send_track_events_request(auth: &CodexAuth, url: &str, events: Vec<Trac
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn capture_track_events_request(
|
||||
destination: &AnalyticsEventsDestination,
|
||||
payload: &TrackEventsRequest,
|
||||
) -> bool {
|
||||
let AnalyticsEventsDestination::CaptureFile { path } = destination else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if let Err(err) = crate::analytics_capture::append_payload(path, payload) {
|
||||
tracing::error!(
|
||||
path = %path.display(),
|
||||
"failed to capture analytics events; network delivery remains disabled: {err}"
|
||||
);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "client_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
use super::AnalyticsEventsClient;
|
||||
use super::AnalyticsEventsDestination;
|
||||
use super::AnalyticsEventsQueue;
|
||||
#[cfg(debug_assertions)]
|
||||
use super::capture_track_events_request;
|
||||
#[cfg(debug_assertions)]
|
||||
use super::send_track_events_request;
|
||||
use super::track_event_request_batches;
|
||||
use crate::events::CodexAcceptedLineFingerprintsEventParams;
|
||||
use crate::events::CodexAcceptedLineFingerprintsEventRequest;
|
||||
@@ -31,8 +36,14 @@ use codex_app_server_protocol::TurnSteerResponse;
|
||||
use codex_utils_absolute_path::test_support::PathBufExt;
|
||||
use codex_utils_absolute_path::test_support::test_path_buf;
|
||||
use std::collections::HashSet;
|
||||
#[cfg(debug_assertions)]
|
||||
use std::fs;
|
||||
#[cfg(debug_assertions)]
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
#[cfg(debug_assertions)]
|
||||
use std::time::SystemTime;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::mpsc::error::TryRecvError;
|
||||
|
||||
@@ -74,6 +85,18 @@ fn sample_regular_track_event(thread_id: &str) -> TrackEventRequest {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn unique_capture_path(name: &str) -> PathBuf {
|
||||
let nonce = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.expect("system clock should be after Unix epoch")
|
||||
.as_nanos();
|
||||
std::env::temp_dir().join(format!(
|
||||
"codex-analytics-{name}-{}-{nonce}.jsonl",
|
||||
std::process::id()
|
||||
))
|
||||
}
|
||||
|
||||
fn client_with_receiver() -> (AnalyticsEventsClient, mpsc::Receiver<AnalyticsFact>) {
|
||||
let (sender, receiver) = mpsc::channel(8);
|
||||
let queue = AnalyticsEventsQueue {
|
||||
@@ -84,6 +107,138 @@ fn client_with_receiver() -> (AnalyticsEventsClient, mpsc::Receiver<AnalyticsFac
|
||||
(AnalyticsEventsClient { queue: Some(queue) }, receiver)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(debug_assertions)]
|
||||
fn analytics_destination_uses_explicit_capture_file() {
|
||||
let capture_path = unique_capture_path("destination");
|
||||
let destination = AnalyticsEventsDestination::from_base_url_and_capture_file(
|
||||
"https://chatgpt.com/backend-api/".to_string(),
|
||||
Some(capture_path.clone()),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
destination,
|
||||
AnalyticsEventsDestination::CaptureFile {
|
||||
path: capture_path.clone()
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
fs::read_to_string(&capture_path).expect("read capture file"),
|
||||
""
|
||||
);
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let mode = fs::metadata(&capture_path)
|
||||
.expect("read capture file metadata")
|
||||
.permissions()
|
||||
.mode();
|
||||
assert_eq!(mode & 0o777, 0o600);
|
||||
}
|
||||
fs::remove_file(capture_path).expect("remove capture file");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn analytics_destination_uses_http_without_capture_file() {
|
||||
let destination = AnalyticsEventsDestination::from_base_url_and_capture_file(
|
||||
"https://chatgpt.com/backend-api/".to_string(),
|
||||
/*capture_file*/ None,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
destination,
|
||||
AnalyticsEventsDestination::Http {
|
||||
url: "https://chatgpt.com/backend-api/codex/analytics-events/events".to_string()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn analytics_destination_ignores_capture_file_in_release() {
|
||||
let destination = AnalyticsEventsDestination::from_base_url_and_capture_file(
|
||||
"https://chatgpt.com/backend-api/".to_string(),
|
||||
Some(std::path::PathBuf::from("ignored.jsonl")),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
destination,
|
||||
AnalyticsEventsDestination::Http {
|
||||
url: "https://chatgpt.com/backend-api/codex/analytics-events/events".to_string()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg(debug_assertions)]
|
||||
async fn capture_file_writes_exact_serialized_request() {
|
||||
let capture_path = unique_capture_path("single");
|
||||
let destination = AnalyticsEventsDestination::CaptureFile {
|
||||
path: capture_path.clone(),
|
||||
};
|
||||
let event = sample_regular_track_event("thread-1");
|
||||
let expected_event = serde_json::to_value(&event).expect("serialize expected event");
|
||||
let auth = codex_login::CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
|
||||
send_track_events_request(&auth, &destination, vec![event]).await;
|
||||
|
||||
let contents = fs::read_to_string(&capture_path).expect("read capture file");
|
||||
let lines = contents.lines().collect::<Vec<_>>();
|
||||
assert_eq!(lines.len(), 1);
|
||||
let payload: serde_json::Value =
|
||||
serde_json::from_str(lines[0]).expect("parse captured payload");
|
||||
assert_eq!(payload, serde_json::json!({"events": [expected_event]}));
|
||||
|
||||
fs::remove_file(capture_path).expect("remove capture file");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg(debug_assertions)]
|
||||
async fn capture_file_writes_final_batches_as_separate_lines() {
|
||||
let capture_path = unique_capture_path("batches");
|
||||
let destination = AnalyticsEventsDestination::CaptureFile {
|
||||
path: capture_path.clone(),
|
||||
};
|
||||
let auth = codex_login::CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let events = vec![
|
||||
sample_regular_track_event("thread-1"),
|
||||
sample_accepted_line_fingerprint_event("thread-2"),
|
||||
sample_regular_track_event("thread-3"),
|
||||
];
|
||||
|
||||
for batch in track_event_request_batches(events) {
|
||||
send_track_events_request(&auth, &destination, batch).await;
|
||||
}
|
||||
|
||||
let contents = fs::read_to_string(&capture_path).expect("read capture file");
|
||||
let payloads = contents
|
||||
.lines()
|
||||
.map(|line| serde_json::from_str::<serde_json::Value>(line).expect("parse capture line"))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(payloads.len(), 3);
|
||||
assert_eq!(payloads[0]["events"][0]["skill_id"], "skill-thread-1");
|
||||
assert_eq!(
|
||||
payloads[1]["events"][0]["event_type"],
|
||||
"codex_accepted_line_fingerprints"
|
||||
);
|
||||
assert_eq!(payloads[2]["events"][0]["skill_id"], "skill-thread-3");
|
||||
|
||||
fs::remove_file(capture_path).expect("remove capture file");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(debug_assertions)]
|
||||
fn capture_write_failure_still_consumes_delivery() {
|
||||
let capture_path = unique_capture_path("missing-parent").join("events.jsonl");
|
||||
let destination = AnalyticsEventsDestination::CaptureFile { path: capture_path };
|
||||
let payload = crate::events::TrackEventsRequest {
|
||||
events: vec![sample_regular_track_event("thread-1")],
|
||||
};
|
||||
|
||||
assert!(capture_track_events_request(&destination, &payload));
|
||||
}
|
||||
|
||||
fn sample_turn_start_request() -> ClientRequest {
|
||||
ClientRequest::TurnStart {
|
||||
request_id: RequestId::Integer(1),
|
||||
@@ -130,6 +285,7 @@ fn sample_thread(thread_id: &str) -> Thread {
|
||||
model_provider: "openai".to_string(),
|
||||
created_at: 1,
|
||||
updated_at: 2,
|
||||
recency_at: Some(2),
|
||||
status: AppServerThreadStatus::Idle,
|
||||
path: None,
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
|
||||
@@ -83,6 +83,9 @@ pub(crate) enum TrackEventRequest {
|
||||
PluginUninstalled(CodexPluginEventRequest),
|
||||
PluginEnabled(CodexPluginEventRequest),
|
||||
PluginDisabled(CodexPluginEventRequest),
|
||||
PluginInstallFailed(CodexPluginInstallFailedEventRequest),
|
||||
ExternalAgentConfigImportCompleted(CodexOnboardingExternalAgentImportCompleteEventRequest),
|
||||
ExternalAgentConfigImportFailure(CodexOnboardingExternalAgentImportFailureEventRequest),
|
||||
}
|
||||
|
||||
impl TrackEventRequest {
|
||||
@@ -274,6 +277,11 @@ pub struct GuardianReviewEventParams {
|
||||
pub guardian_session_kind: Option<GuardianReviewSessionKind>,
|
||||
pub guardian_model: Option<String>,
|
||||
pub guardian_reasoning_effort: Option<String>,
|
||||
pub guardian_default_review_model_id: Option<String>,
|
||||
pub guardian_catalog_contains_auto_review: Option<bool>,
|
||||
pub guardian_review_model_overridden: Option<bool>,
|
||||
pub guardian_review_model_override: Option<String>,
|
||||
pub guardian_model_provider_id: Option<String>,
|
||||
pub had_prior_review_context: Option<bool>,
|
||||
pub review_timeout_ms: u64,
|
||||
pub tool_call_count: Option<u64>,
|
||||
@@ -347,6 +355,11 @@ impl GuardianReviewTrackContext {
|
||||
guardian_session_kind: result.guardian_session_kind,
|
||||
guardian_model: result.guardian_model,
|
||||
guardian_reasoning_effort: result.guardian_reasoning_effort,
|
||||
guardian_default_review_model_id: result.guardian_default_review_model_id,
|
||||
guardian_catalog_contains_auto_review: result.guardian_catalog_contains_auto_review,
|
||||
guardian_review_model_overridden: result.guardian_review_model_overridden,
|
||||
guardian_review_model_override: result.guardian_review_model_override,
|
||||
guardian_model_provider_id: result.guardian_model_provider_id,
|
||||
had_prior_review_context: result.had_prior_review_context,
|
||||
review_timeout_ms: self.review_timeout_ms,
|
||||
// TODO(rhan-oai): plumb nested Guardian review session tool-call counts.
|
||||
@@ -383,6 +396,11 @@ pub struct GuardianReviewAnalyticsResult {
|
||||
pub guardian_session_kind: Option<GuardianReviewSessionKind>,
|
||||
pub guardian_model: Option<String>,
|
||||
pub guardian_reasoning_effort: Option<String>,
|
||||
pub guardian_default_review_model_id: Option<String>,
|
||||
pub guardian_catalog_contains_auto_review: Option<bool>,
|
||||
pub guardian_review_model_overridden: Option<bool>,
|
||||
pub guardian_review_model_override: Option<String>,
|
||||
pub guardian_model_provider_id: Option<String>,
|
||||
pub had_prior_review_context: Option<bool>,
|
||||
pub reviewed_action_truncated: bool,
|
||||
pub token_usage: Option<TokenUsage>,
|
||||
@@ -403,6 +421,11 @@ impl GuardianReviewAnalyticsResult {
|
||||
guardian_session_kind: None,
|
||||
guardian_model: None,
|
||||
guardian_reasoning_effort: None,
|
||||
guardian_default_review_model_id: None,
|
||||
guardian_catalog_contains_auto_review: None,
|
||||
guardian_review_model_overridden: None,
|
||||
guardian_review_model_override: None,
|
||||
guardian_model_provider_id: None,
|
||||
had_prior_review_context: None,
|
||||
reviewed_action_truncated: false,
|
||||
token_usage: None,
|
||||
@@ -410,24 +433,38 @@ impl GuardianReviewAnalyticsResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_session(
|
||||
guardian_thread_id: String,
|
||||
guardian_session_kind: GuardianReviewSessionKind,
|
||||
guardian_model: String,
|
||||
guardian_reasoning_effort: Option<String>,
|
||||
had_prior_review_context: bool,
|
||||
) -> Self {
|
||||
pub fn from_session(params: GuardianReviewSessionAnalyticsParams) -> Self {
|
||||
Self {
|
||||
guardian_thread_id: Some(guardian_thread_id),
|
||||
guardian_session_kind: Some(guardian_session_kind),
|
||||
guardian_model: Some(guardian_model),
|
||||
guardian_reasoning_effort,
|
||||
had_prior_review_context: Some(had_prior_review_context),
|
||||
guardian_thread_id: Some(params.guardian_thread_id),
|
||||
guardian_session_kind: Some(params.guardian_session_kind),
|
||||
guardian_model: Some(params.guardian_model),
|
||||
guardian_reasoning_effort: params.guardian_reasoning_effort,
|
||||
guardian_default_review_model_id: Some(params.guardian_default_review_model_id),
|
||||
guardian_catalog_contains_auto_review: Some(
|
||||
params.guardian_catalog_contains_auto_review,
|
||||
),
|
||||
guardian_review_model_overridden: Some(params.guardian_review_model_overridden),
|
||||
guardian_review_model_override: params.guardian_review_model_override,
|
||||
guardian_model_provider_id: Some(params.guardian_model_provider_id),
|
||||
had_prior_review_context: Some(params.had_prior_review_context),
|
||||
..Self::without_session()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GuardianReviewSessionAnalyticsParams {
|
||||
pub guardian_thread_id: String,
|
||||
pub guardian_session_kind: GuardianReviewSessionKind,
|
||||
pub guardian_model: String,
|
||||
pub guardian_reasoning_effort: Option<String>,
|
||||
pub guardian_default_review_model_id: String,
|
||||
pub guardian_catalog_contains_auto_review: bool,
|
||||
pub guardian_review_model_overridden: bool,
|
||||
pub guardian_review_model_override: Option<String>,
|
||||
pub guardian_model_provider_id: String,
|
||||
pub had_prior_review_context: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct GuardianReviewEventPayload {
|
||||
pub(crate) session_id: String,
|
||||
@@ -633,6 +670,7 @@ pub(crate) struct CodexMcpToolCallEventParams {
|
||||
pub(crate) mcp_server_name: String,
|
||||
pub(crate) mcp_tool_name: String,
|
||||
pub(crate) mcp_error_present: bool,
|
||||
pub(crate) plugin_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -762,11 +800,13 @@ pub(crate) struct CodexCompactionEventParams {
|
||||
pub(crate) phase: CompactionPhase,
|
||||
pub(crate) strategy: CompactionStrategy,
|
||||
pub(crate) status: CompactionStatus,
|
||||
pub(crate) error: Option<String>,
|
||||
pub(crate) codex_error_kind: Option<CodexErrKind>,
|
||||
pub(crate) codex_error_http_status_code: Option<u16>,
|
||||
pub(crate) active_context_tokens_before: i64,
|
||||
pub(crate) active_context_tokens_after: i64,
|
||||
pub(crate) retained_image_count: Option<usize>,
|
||||
pub(crate) compaction_summary_tokens: Option<i64>,
|
||||
pub(crate) cached_input_tokens: Option<i64>,
|
||||
pub(crate) started_at: u64,
|
||||
pub(crate) completed_at: u64,
|
||||
pub(crate) duration_ms: Option<u64>,
|
||||
@@ -917,6 +957,53 @@ pub(crate) struct CodexPluginEventRequest {
|
||||
pub(crate) event_params: CodexPluginMetadata,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct CodexPluginInstallFailedMetadata {
|
||||
#[serde(flatten)]
|
||||
pub(crate) plugin: CodexPluginMetadata,
|
||||
pub(crate) error_type: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct CodexPluginInstallFailedEventRequest {
|
||||
pub(crate) event_type: &'static str,
|
||||
pub(crate) event_params: CodexPluginInstallFailedMetadata,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct CodexOnboardingExternalAgentImportCompleteMetadata {
|
||||
pub(crate) import_id: String,
|
||||
pub(crate) source: String,
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) item_type: String,
|
||||
pub(crate) success_count: usize,
|
||||
pub(crate) failed_count: usize,
|
||||
pub(crate) product_client_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct CodexOnboardingExternalAgentImportCompleteEventRequest {
|
||||
pub(crate) event_type: &'static str,
|
||||
pub(crate) event_params: CodexOnboardingExternalAgentImportCompleteMetadata,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct CodexOnboardingExternalAgentImportFailureMetadata {
|
||||
pub(crate) import_id: String,
|
||||
pub(crate) source: String,
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) item_type: String,
|
||||
pub(crate) failure_stage: String,
|
||||
pub(crate) error_type: String,
|
||||
pub(crate) product_client_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct CodexOnboardingExternalAgentImportFailureEventRequest {
|
||||
pub(crate) event_type: &'static str,
|
||||
pub(crate) event_params: CodexOnboardingExternalAgentImportFailureMetadata,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct CodexPluginUsedEventRequest {
|
||||
pub(crate) event_type: &'static str,
|
||||
@@ -999,11 +1086,13 @@ pub(crate) fn codex_compaction_event_params(
|
||||
phase: input.phase,
|
||||
strategy: input.strategy,
|
||||
status: input.status,
|
||||
error: input.error,
|
||||
codex_error_kind: input.codex_error_kind,
|
||||
codex_error_http_status_code: input.codex_error_http_status_code,
|
||||
active_context_tokens_before: input.active_context_tokens_before,
|
||||
active_context_tokens_after: input.active_context_tokens_after,
|
||||
retained_image_count: input.retained_image_count,
|
||||
compaction_summary_tokens: input.compaction_summary_tokens,
|
||||
cached_input_tokens: input.cached_input_tokens,
|
||||
started_at: input.started_at,
|
||||
completed_at: input.completed_at,
|
||||
duration_ms: input.duration_ms,
|
||||
|
||||
@@ -137,7 +137,7 @@ impl TurnCodexErrorFact {
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) enum CodexErrKind {
|
||||
pub enum CodexErrKind {
|
||||
TurnAborted,
|
||||
Stream,
|
||||
ContextWindowExceeded,
|
||||
@@ -364,6 +364,7 @@ pub enum CompactionReason {
|
||||
UserRequested,
|
||||
ContextLimit,
|
||||
ModelDownshift,
|
||||
CompHashChanged,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize)]
|
||||
@@ -407,11 +408,13 @@ pub struct CodexCompactionEvent {
|
||||
pub phase: CompactionPhase,
|
||||
pub strategy: CompactionStrategy,
|
||||
pub status: CompactionStatus,
|
||||
pub error: Option<String>,
|
||||
pub codex_error_kind: Option<CodexErrKind>,
|
||||
pub codex_error_http_status_code: Option<u16>,
|
||||
pub active_context_tokens_before: i64,
|
||||
pub active_context_tokens_after: i64,
|
||||
pub retained_image_count: Option<usize>,
|
||||
pub compaction_summary_tokens: Option<i64>,
|
||||
pub cached_input_tokens: Option<i64>,
|
||||
pub started_at: u64,
|
||||
pub completed_at: u64,
|
||||
pub duration_ms: Option<u64>,
|
||||
@@ -501,6 +504,9 @@ pub(crate) enum CustomAnalyticsFact {
|
||||
HookRun(HookRunInput),
|
||||
PluginUsed(PluginUsedInput),
|
||||
PluginStateChanged(PluginStateChangedInput),
|
||||
PluginInstallFailed(PluginInstallFailedInput),
|
||||
ExternalAgentConfigImportCompleted(ExternalAgentConfigImportCompletedInput),
|
||||
ExternalAgentConfigImportFailure(ExternalAgentConfigImportFailureInput),
|
||||
}
|
||||
|
||||
pub(crate) struct SkillInvokedInput {
|
||||
@@ -539,6 +545,27 @@ pub(crate) struct PluginStateChangedInput {
|
||||
pub state: PluginState,
|
||||
}
|
||||
|
||||
pub(crate) struct PluginInstallFailedInput {
|
||||
pub plugin: PluginTelemetryMetadata,
|
||||
pub error_type: String,
|
||||
}
|
||||
|
||||
pub struct ExternalAgentConfigImportCompletedInput {
|
||||
pub import_id: String,
|
||||
pub source: String,
|
||||
pub item_type: String,
|
||||
pub success_count: usize,
|
||||
pub failed_count: usize,
|
||||
}
|
||||
|
||||
pub struct ExternalAgentConfigImportFailureInput {
|
||||
pub import_id: String,
|
||||
pub source: String,
|
||||
pub item_type: String,
|
||||
pub failure_stage: String,
|
||||
pub error_type: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum PluginState {
|
||||
Installed,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
mod accepted_lines;
|
||||
#[cfg(debug_assertions)]
|
||||
mod analytics_capture;
|
||||
mod client;
|
||||
mod events;
|
||||
mod facts;
|
||||
@@ -16,6 +18,7 @@ pub use events::GuardianReviewAnalyticsResult;
|
||||
pub use events::GuardianReviewDecision;
|
||||
pub use events::GuardianReviewEventParams;
|
||||
pub use events::GuardianReviewFailureReason;
|
||||
pub use events::GuardianReviewSessionAnalyticsParams;
|
||||
pub use events::GuardianReviewSessionKind;
|
||||
pub use events::GuardianReviewTerminalStatus;
|
||||
pub use events::GuardianReviewTrackContext;
|
||||
@@ -24,6 +27,7 @@ pub use facts::AcceptedLineFingerprint;
|
||||
pub use facts::AnalyticsJsonRpcError;
|
||||
pub use facts::AppInvocation;
|
||||
pub use facts::CodexCompactionEvent;
|
||||
pub use facts::CodexErrKind;
|
||||
pub use facts::CodexGoalEvent;
|
||||
pub use facts::CodexTurnSteerEvent;
|
||||
pub use facts::CompactionImplementation;
|
||||
@@ -32,6 +36,8 @@ pub use facts::CompactionReason;
|
||||
pub use facts::CompactionStatus;
|
||||
pub use facts::CompactionStrategy;
|
||||
pub use facts::CompactionTrigger;
|
||||
pub use facts::ExternalAgentConfigImportCompletedInput;
|
||||
pub use facts::ExternalAgentConfigImportFailureInput;
|
||||
pub use facts::GoalEventKind;
|
||||
pub use facts::HookRunFact;
|
||||
pub use facts::InputError;
|
||||
|
||||
@@ -21,7 +21,13 @@ use crate::events::CodexImageGenerationEventParams;
|
||||
use crate::events::CodexImageGenerationEventRequest;
|
||||
use crate::events::CodexMcpToolCallEventParams;
|
||||
use crate::events::CodexMcpToolCallEventRequest;
|
||||
use crate::events::CodexOnboardingExternalAgentImportCompleteEventRequest;
|
||||
use crate::events::CodexOnboardingExternalAgentImportCompleteMetadata;
|
||||
use crate::events::CodexOnboardingExternalAgentImportFailureEventRequest;
|
||||
use crate::events::CodexOnboardingExternalAgentImportFailureMetadata;
|
||||
use crate::events::CodexPluginEventRequest;
|
||||
use crate::events::CodexPluginInstallFailedEventRequest;
|
||||
use crate::events::CodexPluginInstallFailedMetadata;
|
||||
use crate::events::CodexPluginUsedEventRequest;
|
||||
use crate::events::CodexReviewEventParams;
|
||||
use crate::events::CodexReviewEventRequest;
|
||||
@@ -66,7 +72,10 @@ use crate::facts::AppUsedInput;
|
||||
use crate::facts::CodexCompactionEvent;
|
||||
use crate::facts::CodexGoalEvent;
|
||||
use crate::facts::CustomAnalyticsFact;
|
||||
use crate::facts::ExternalAgentConfigImportCompletedInput;
|
||||
use crate::facts::ExternalAgentConfigImportFailureInput;
|
||||
use crate::facts::HookRunInput;
|
||||
use crate::facts::PluginInstallFailedInput;
|
||||
use crate::facts::PluginState;
|
||||
use crate::facts::PluginStateChangedInput;
|
||||
use crate::facts::PluginUsedInput;
|
||||
@@ -384,6 +393,7 @@ impl TurnToolCounts {
|
||||
| ThreadItem::Plan { .. }
|
||||
| ThreadItem::Reasoning { .. }
|
||||
| ThreadItem::ImageView { .. }
|
||||
| ThreadItem::Sleep { .. }
|
||||
| ThreadItem::EnteredReviewMode { .. }
|
||||
| ThreadItem::ExitedReviewMode { .. }
|
||||
| ThreadItem::ContextCompaction { .. } => return,
|
||||
@@ -510,6 +520,15 @@ impl AnalyticsReducer {
|
||||
CustomAnalyticsFact::PluginStateChanged(input) => {
|
||||
self.ingest_plugin_state_changed(input, out);
|
||||
}
|
||||
CustomAnalyticsFact::PluginInstallFailed(input) => {
|
||||
self.ingest_plugin_install_failed(input, out);
|
||||
}
|
||||
CustomAnalyticsFact::ExternalAgentConfigImportCompleted(input) => {
|
||||
self.ingest_external_agent_config_import_completed(input, out);
|
||||
}
|
||||
CustomAnalyticsFact::ExternalAgentConfigImportFailure(input) => {
|
||||
self.ingest_external_agent_config_import_failure(input, out);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -774,6 +793,63 @@ impl AnalyticsReducer {
|
||||
});
|
||||
}
|
||||
|
||||
fn ingest_plugin_install_failed(
|
||||
&mut self,
|
||||
input: PluginInstallFailedInput,
|
||||
out: &mut Vec<TrackEventRequest>,
|
||||
) {
|
||||
let PluginInstallFailedInput { plugin, error_type } = input;
|
||||
out.push(TrackEventRequest::PluginInstallFailed(
|
||||
CodexPluginInstallFailedEventRequest {
|
||||
event_type: "codex_plugin_install_failed",
|
||||
event_params: CodexPluginInstallFailedMetadata {
|
||||
plugin: codex_plugin_metadata(plugin),
|
||||
error_type,
|
||||
},
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
fn ingest_external_agent_config_import_completed(
|
||||
&mut self,
|
||||
input: ExternalAgentConfigImportCompletedInput,
|
||||
out: &mut Vec<TrackEventRequest>,
|
||||
) {
|
||||
out.push(TrackEventRequest::ExternalAgentConfigImportCompleted(
|
||||
CodexOnboardingExternalAgentImportCompleteEventRequest {
|
||||
event_type: "codex_onboarding_external_agent_import_complete",
|
||||
event_params: CodexOnboardingExternalAgentImportCompleteMetadata {
|
||||
import_id: input.import_id,
|
||||
source: input.source,
|
||||
item_type: input.item_type,
|
||||
success_count: input.success_count,
|
||||
failed_count: input.failed_count,
|
||||
product_client_id: Some(originator().value),
|
||||
},
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
fn ingest_external_agent_config_import_failure(
|
||||
&mut self,
|
||||
input: ExternalAgentConfigImportFailureInput,
|
||||
out: &mut Vec<TrackEventRequest>,
|
||||
) {
|
||||
out.push(TrackEventRequest::ExternalAgentConfigImportFailure(
|
||||
CodexOnboardingExternalAgentImportFailureEventRequest {
|
||||
event_type: "codex_onboarding_external_agent_import_failure",
|
||||
event_params: CodexOnboardingExternalAgentImportFailureMetadata {
|
||||
import_id: input.import_id,
|
||||
source: input.source,
|
||||
item_type: input.item_type,
|
||||
failure_stage: input.failure_stage,
|
||||
error_type: input.error_type,
|
||||
product_client_id: Some(originator().value),
|
||||
},
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
async fn ingest_response(
|
||||
&mut self,
|
||||
connection_id: u64,
|
||||
@@ -1620,6 +1696,7 @@ fn tracked_tool_item_id(item: &ThreadItem) -> Option<&str> {
|
||||
| ThreadItem::Reasoning { .. }
|
||||
| ThreadItem::SubAgentActivity { .. }
|
||||
| ThreadItem::ImageView { .. }
|
||||
| ThreadItem::Sleep { .. }
|
||||
| ThreadItem::EnteredReviewMode { .. }
|
||||
| ThreadItem::ExitedReviewMode { .. }
|
||||
| ThreadItem::ContextCompaction { .. } => None,
|
||||
@@ -1751,6 +1828,7 @@ fn tool_item_event(input: ToolItemEventInput<'_>) -> Option<TrackEventRequest> {
|
||||
status,
|
||||
error,
|
||||
duration_ms,
|
||||
plugin_id,
|
||||
..
|
||||
} => {
|
||||
let (terminal_status, failure_kind) = mcp_tool_call_outcome(status)?;
|
||||
@@ -1780,6 +1858,7 @@ fn tool_item_event(input: ToolItemEventInput<'_>) -> Option<TrackEventRequest> {
|
||||
mcp_server_name: server.clone(),
|
||||
mcp_tool_name: tool.clone(),
|
||||
mcp_error_present: error.is_some(),
|
||||
plugin_id: plugin_id.clone(),
|
||||
},
|
||||
},
|
||||
))
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
//! bridging async `mpsc` channels on both sides. Queues are bounded so overload
|
||||
//! surfaces as channel-full errors rather than unbounded memory growth.
|
||||
|
||||
mod path;
|
||||
mod remote;
|
||||
|
||||
use std::error::Error;
|
||||
@@ -22,6 +23,7 @@ use std::fmt;
|
||||
use std::io::Error as IoError;
|
||||
use std::io::ErrorKind;
|
||||
use std::io::Result as IoResult;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -48,11 +50,16 @@ use codex_config::LoaderOverrides;
|
||||
use codex_config::NoopThreadConfigLoader;
|
||||
use codex_config::RemoteThreadConfigLoader;
|
||||
use codex_config::ThreadConfigLoader;
|
||||
use codex_config::config_toml::ConfigToml;
|
||||
use codex_core::config::Config;
|
||||
pub use codex_core::otel_init::build_provider as build_otel_provider;
|
||||
use codex_core::personality_migration::PersonalityMigrationStatus;
|
||||
use codex_core::personality_migration::maybe_migrate_personality;
|
||||
pub use codex_exec_server::EnvironmentManager;
|
||||
pub use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_feedback::CodexFeedback;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use serde::de::DeserializeOwned;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::oneshot;
|
||||
@@ -60,6 +67,7 @@ use tokio::time::timeout;
|
||||
use toml::Value as TomlValue;
|
||||
use tracing::warn;
|
||||
|
||||
pub use crate::path::AppServerPath;
|
||||
pub use crate::remote::RemoteAppServerClient;
|
||||
pub use crate::remote::RemoteAppServerConnectArgs;
|
||||
pub use crate::remote::RemoteAppServerEndpoint;
|
||||
@@ -72,7 +80,6 @@ pub use crate::remote::RemoteAppServerEndpoint;
|
||||
pub mod legacy_core {
|
||||
pub use codex_core::check_execpolicy_for_warnings;
|
||||
pub use codex_core::format_exec_policy_error_with_source;
|
||||
pub use codex_core::grant_read_root_non_elevated;
|
||||
|
||||
pub mod config {
|
||||
pub use codex_core::config::*;
|
||||
@@ -81,26 +88,27 @@ pub mod legacy_core {
|
||||
pub use codex_core::config::edit::*;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod otel_init {
|
||||
pub use codex_core::otel_init::*;
|
||||
}
|
||||
|
||||
pub mod personality_migration {
|
||||
pub use codex_core::personality_migration::*;
|
||||
}
|
||||
|
||||
pub mod test_support {
|
||||
pub use codex_core::test_support::*;
|
||||
}
|
||||
|
||||
pub mod windows_sandbox {
|
||||
pub use codex_core::windows_sandbox::*;
|
||||
}
|
||||
}
|
||||
|
||||
const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
|
||||
/// Runs the embedded app-server personality migration.
|
||||
///
|
||||
/// Returns `true` when the migration changed config and the caller should reload it.
|
||||
pub async fn migrate_personality_if_needed(
|
||||
codex_home: &Path,
|
||||
config_toml: &ConfigToml,
|
||||
state_db: Option<StateDbHandle>,
|
||||
) -> IoResult<bool> {
|
||||
let status = maybe_migrate_personality(codex_home, config_toml, state_db).await?;
|
||||
match status {
|
||||
PersonalityMigrationStatus::Applied => Ok(true),
|
||||
PersonalityMigrationStatus::SkippedMarker
|
||||
| PersonalityMigrationStatus::SkippedExplicitPersonality
|
||||
| PersonalityMigrationStatus::SkippedNoSessions => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
/// Raw app-server request result for typed in-process requests.
|
||||
///
|
||||
/// Even on the in-process path, successful responses still travel back through
|
||||
@@ -342,6 +350,8 @@ pub struct InProcessClientStartArgs {
|
||||
pub client_version: String,
|
||||
/// Whether experimental APIs are requested at initialize time.
|
||||
pub experimental_api: bool,
|
||||
/// Whether MCP servers may send `openai/form` elicitation requests.
|
||||
pub mcp_server_openai_form_elicitation: bool,
|
||||
/// Notification methods this client opts out of receiving.
|
||||
pub opt_out_notification_methods: Vec<String>,
|
||||
/// Queue capacity for command/event channels (clamped to at least 1).
|
||||
@@ -366,6 +376,7 @@ impl InProcessClientStartArgs {
|
||||
} else {
|
||||
Some(self.opt_out_notification_methods.clone())
|
||||
},
|
||||
mcp_server_openai_form_elicitation: self.mcp_server_openai_form_elicitation,
|
||||
};
|
||||
|
||||
InitializeParams {
|
||||
@@ -840,6 +851,15 @@ impl AppServerRequestHandle {
|
||||
}
|
||||
|
||||
impl AppServerClient {
|
||||
pub fn codex_home(&self, local_codex_home: &AbsolutePathBuf) -> Option<AppServerPath> {
|
||||
match self {
|
||||
Self::InProcess(_) => Some(AppServerPath::from_app_server(
|
||||
local_codex_home.display().to_string(),
|
||||
)),
|
||||
Self::Remote(client) => client.codex_home().map(AppServerPath::from_app_server),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn request(&self, request: ClientRequest) -> IoResult<RequestResult> {
|
||||
match self {
|
||||
Self::InProcess(client) => client.request(request).await,
|
||||
@@ -1027,6 +1047,7 @@ mod tests {
|
||||
client_name: "codex-app-server-client-test".to_string(),
|
||||
client_version: "0.0.0-test".to_string(),
|
||||
experimental_api: true,
|
||||
mcp_server_openai_form_elicitation: false,
|
||||
opt_out_notification_methods: Vec::new(),
|
||||
channel_capacity,
|
||||
})
|
||||
@@ -1105,6 +1126,7 @@ mod tests {
|
||||
id: request.id,
|
||||
result: serde_json::json!({
|
||||
"userAgent": "codex_cli_rs/9.8.7-test (Test OS; x86_64) rust",
|
||||
"codexHome": "/server/.codex",
|
||||
}),
|
||||
}),
|
||||
)
|
||||
@@ -1219,11 +1241,25 @@ mod tests {
|
||||
client_name: "codex-app-server-client-test".to_string(),
|
||||
client_version: "0.0.0-test".to_string(),
|
||||
experimental_api: true,
|
||||
mcp_server_openai_form_elicitation: false,
|
||||
opt_out_notification_methods: Vec::new(),
|
||||
channel_capacity: 8,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remote_initialize_params_forward_openai_form_capability() {
|
||||
let mut args = test_remote_connect_args("ws://localhost/rpc".to_string());
|
||||
args.mcp_server_openai_form_elicitation = true;
|
||||
|
||||
assert!(
|
||||
args.initialize_params()
|
||||
.capabilities
|
||||
.expect("initialize capabilities")
|
||||
.mcp_server_openai_form_elicitation
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn typed_request_roundtrip_works() {
|
||||
let client = start_test_client(SessionSource::Exec).await;
|
||||
@@ -1441,6 +1477,7 @@ mod tests {
|
||||
.expect("remote client should connect");
|
||||
|
||||
assert_eq!(client.server_version(), Some("9.8.7-test"));
|
||||
assert_eq!(client.codex_home(), Some("/server/.codex"));
|
||||
let response: GetAccountResponse = client
|
||||
.request_typed(ClientRequest::GetAccount {
|
||||
request_id: RequestId::Integer(1),
|
||||
@@ -1493,6 +1530,7 @@ mod tests {
|
||||
client_name: "codex-app-server-client-test".to_string(),
|
||||
client_version: "0.0.0-test".to_string(),
|
||||
experimental_api: true,
|
||||
mcp_server_openai_form_elicitation: false,
|
||||
opt_out_notification_methods: Vec::new(),
|
||||
channel_capacity: 8,
|
||||
})
|
||||
@@ -1581,6 +1619,7 @@ mod tests {
|
||||
client_name: "codex-app-server-client-test".to_string(),
|
||||
client_version: "0.0.0-test".to_string(),
|
||||
experimental_api: true,
|
||||
mcp_server_openai_form_elicitation: false,
|
||||
opt_out_notification_methods: Vec::new(),
|
||||
channel_capacity: 8,
|
||||
})
|
||||
@@ -1600,6 +1639,7 @@ mod tests {
|
||||
client_name: "codex-app-server-client-test".to_string(),
|
||||
client_version: "0.0.0-test".to_string(),
|
||||
experimental_api: true,
|
||||
mcp_server_openai_form_elicitation: false,
|
||||
opt_out_notification_methods: Vec::new(),
|
||||
channel_capacity: 8,
|
||||
})
|
||||
@@ -1869,6 +1909,7 @@ mod tests {
|
||||
is_secret: false,
|
||||
options: Some(vec![]),
|
||||
}],
|
||||
auto_resolution_ms: None,
|
||||
})
|
||||
.expect("params should serialize"),
|
||||
),
|
||||
@@ -1930,6 +1971,7 @@ mod tests {
|
||||
is_secret: false,
|
||||
options: Some(vec![]),
|
||||
}],
|
||||
auto_resolution_ms: None,
|
||||
})
|
||||
.expect("params should serialize"),
|
||||
),
|
||||
@@ -2143,7 +2185,10 @@ mod tests {
|
||||
assert!(event_requires_delivery(
|
||||
&InProcessServerEvent::ServerNotification(
|
||||
codex_app_server_protocol::ServerNotification::ExternalAgentConfigImportCompleted(
|
||||
codex_app_server_protocol::ExternalAgentConfigImportCompletedNotification {},
|
||||
codex_app_server_protocol::ExternalAgentConfigImportCompletedNotification {
|
||||
import_id: "import".to_string(),
|
||||
item_type_results: Vec::new(),
|
||||
},
|
||||
)
|
||||
)
|
||||
));
|
||||
@@ -2165,7 +2210,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn runtime_start_args_forward_environment_manager() {
|
||||
async fn runtime_start_args_forward_environment_manager_and_openai_form_capability() {
|
||||
let config = Arc::new(build_test_config().await);
|
||||
let environment_manager = Arc::new(
|
||||
EnvironmentManager::create_for_tests(
|
||||
@@ -2198,12 +2243,20 @@ mod tests {
|
||||
client_name: "codex-app-server-client-test".to_string(),
|
||||
client_version: "0.0.0-test".to_string(),
|
||||
experimental_api: true,
|
||||
mcp_server_openai_form_elicitation: true,
|
||||
opt_out_notification_methods: Vec::new(),
|
||||
channel_capacity: DEFAULT_IN_PROCESS_CHANNEL_CAPACITY,
|
||||
}
|
||||
.into_runtime_start_args();
|
||||
|
||||
assert_eq!(runtime_args.config, config);
|
||||
assert!(
|
||||
runtime_args
|
||||
.initialize
|
||||
.capabilities
|
||||
.expect("initialize capabilities")
|
||||
.mcp_server_openai_form_elicitation
|
||||
);
|
||||
assert!(Arc::ptr_eq(
|
||||
&runtime_args.environment_manager,
|
||||
&environment_manager
|
||||
@@ -2239,6 +2292,7 @@ mod tests {
|
||||
client_name: "codex-app-server-client-test".to_string(),
|
||||
client_version: "0.0.0-test".to_string(),
|
||||
experimental_api: true,
|
||||
mcp_server_openai_form_elicitation: false,
|
||||
opt_out_notification_methods: Vec::new(),
|
||||
channel_capacity: DEFAULT_IN_PROCESS_CHANNEL_CAPACITY,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
//! Paths resolved using the app-server host's platform rules.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct AppServerPath(String);
|
||||
|
||||
impl AppServerPath {
|
||||
pub fn from_app_server(path: impl Into<String>) -> Self {
|
||||
Self(path.into())
|
||||
}
|
||||
|
||||
pub fn from_absolute_str(raw: &str) -> Option<Self> {
|
||||
(raw.starts_with('/') || is_windows_absolute_path(raw)).then(|| Self(raw.to_string()))
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn components(&self) -> Vec<&str> {
|
||||
let separators = if is_windows_absolute_path(&self.0) {
|
||||
&['/', '\\'][..]
|
||||
} else {
|
||||
&['/'][..]
|
||||
};
|
||||
self.0
|
||||
.split(separators)
|
||||
.filter(|part| !part.is_empty())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn join(&self, segment: impl AsRef<str>) -> Self {
|
||||
let is_windows = is_windows_absolute_path(&self.0);
|
||||
let (path, separator) = if is_windows {
|
||||
(self.0.trim_end_matches(['/', '\\']), '\\')
|
||||
} else {
|
||||
(self.0.trim_end_matches('/'), '/')
|
||||
};
|
||||
Self(format!("{path}{separator}{}", segment.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AppServerPath {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_windows_absolute_path(path: &str) -> bool {
|
||||
let bytes = path.as_bytes();
|
||||
(bytes.len() >= 3
|
||||
&& bytes[0].is_ascii_alphabetic()
|
||||
&& bytes[1] == b':'
|
||||
&& matches!(bytes[2], b'\\' | b'/'))
|
||||
|| path.starts_with("\\\\")
|
||||
|| path.starts_with("//")
|
||||
}
|
||||
@@ -86,11 +86,12 @@ pub struct RemoteAppServerConnectArgs {
|
||||
pub client_name: String,
|
||||
pub client_version: String,
|
||||
pub experimental_api: bool,
|
||||
pub mcp_server_openai_form_elicitation: bool,
|
||||
pub opt_out_notification_methods: Vec<String>,
|
||||
pub channel_capacity: usize,
|
||||
}
|
||||
impl RemoteAppServerConnectArgs {
|
||||
fn initialize_params(&self) -> InitializeParams {
|
||||
pub(crate) fn initialize_params(&self) -> InitializeParams {
|
||||
let capabilities = InitializeCapabilities {
|
||||
experimental_api: self.experimental_api,
|
||||
request_attestation: false,
|
||||
@@ -99,6 +100,7 @@ impl RemoteAppServerConnectArgs {
|
||||
} else {
|
||||
Some(self.opt_out_notification_methods.clone())
|
||||
},
|
||||
mcp_server_openai_form_elicitation: self.mcp_server_openai_form_elicitation,
|
||||
};
|
||||
|
||||
InitializeParams {
|
||||
@@ -124,7 +126,7 @@ pub(crate) fn websocket_url_supports_auth_token(url: &Url) -> bool {
|
||||
|
||||
enum RemoteClientCommand {
|
||||
Request {
|
||||
request: Box<ClientRequest>,
|
||||
request: Box<JSONRPCRequest>,
|
||||
response_tx: oneshot::Sender<IoResult<RequestResult>>,
|
||||
},
|
||||
Notify {
|
||||
@@ -151,6 +153,7 @@ pub struct RemoteAppServerClient {
|
||||
event_rx: mpsc::UnboundedReceiver<AppServerEvent>,
|
||||
pending_events: VecDeque<AppServerEvent>,
|
||||
server_version: Option<String>,
|
||||
codex_home: Option<String>,
|
||||
worker_handle: tokio::task::JoinHandle<()>,
|
||||
}
|
||||
|
||||
@@ -185,6 +188,10 @@ impl RemoteAppServerClient {
|
||||
self.server_version.as_deref()
|
||||
}
|
||||
|
||||
pub fn codex_home(&self) -> Option<&str> {
|
||||
self.codex_home.as_deref()
|
||||
}
|
||||
|
||||
async fn connect_with_stream<S>(
|
||||
channel_capacity: usize,
|
||||
endpoint: String,
|
||||
@@ -195,7 +202,7 @@ impl RemoteAppServerClient {
|
||||
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
|
||||
{
|
||||
let mut stream = stream;
|
||||
let (pending_events, server_version) = initialize_remote_connection(
|
||||
let (pending_events, server_version, codex_home) = initialize_remote_connection(
|
||||
&mut stream,
|
||||
&endpoint,
|
||||
initialize_params,
|
||||
@@ -218,7 +225,7 @@ impl RemoteAppServerClient {
|
||||
};
|
||||
match command {
|
||||
RemoteClientCommand::Request { request, response_tx } => {
|
||||
let request_id = request_id_from_client_request(&request);
|
||||
let request_id = request.id.clone();
|
||||
if pending_requests.contains_key(&request_id) {
|
||||
let _ = response_tx.send(Err(IoError::new(
|
||||
ErrorKind::InvalidInput,
|
||||
@@ -229,7 +236,7 @@ impl RemoteAppServerClient {
|
||||
pending_requests.insert(request_id.clone(), response_tx);
|
||||
if let Err(err) = write_jsonrpc_message(
|
||||
&mut stream,
|
||||
JSONRPCMessage::Request(jsonrpc_request_from_client_request(*request)),
|
||||
JSONRPCMessage::Request(*request),
|
||||
&endpoint,
|
||||
)
|
||||
.await
|
||||
@@ -472,6 +479,7 @@ impl RemoteAppServerClient {
|
||||
event_rx,
|
||||
pending_events: pending_events.into(),
|
||||
server_version,
|
||||
codex_home,
|
||||
worker_handle,
|
||||
})
|
||||
}
|
||||
@@ -483,25 +491,7 @@ impl RemoteAppServerClient {
|
||||
}
|
||||
|
||||
pub async fn request(&self, request: ClientRequest) -> IoResult<RequestResult> {
|
||||
let (response_tx, response_rx) = oneshot::channel();
|
||||
self.command_tx
|
||||
.send(RemoteClientCommand::Request {
|
||||
request: Box::new(request),
|
||||
response_tx,
|
||||
})
|
||||
.await
|
||||
.map_err(|_| {
|
||||
IoError::new(
|
||||
ErrorKind::BrokenPipe,
|
||||
"remote app-server worker channel is closed",
|
||||
)
|
||||
})?;
|
||||
response_rx.await.map_err(|_| {
|
||||
IoError::new(
|
||||
ErrorKind::BrokenPipe,
|
||||
"remote app-server request channel is closed",
|
||||
)
|
||||
})?
|
||||
self.request_handle().request(request).await
|
||||
}
|
||||
|
||||
pub async fn request_typed<T>(&self, request: ClientRequest) -> Result<T, TypedRequestError>
|
||||
@@ -613,6 +603,7 @@ impl RemoteAppServerClient {
|
||||
event_rx,
|
||||
pending_events: _pending_events,
|
||||
server_version: _server_version,
|
||||
codex_home: _codex_home,
|
||||
worker_handle,
|
||||
} = self;
|
||||
let mut worker_handle = worker_handle;
|
||||
@@ -637,6 +628,11 @@ impl RemoteAppServerClient {
|
||||
|
||||
impl RemoteAppServerRequestHandle {
|
||||
pub async fn request(&self, request: ClientRequest) -> IoResult<RequestResult> {
|
||||
self.request_json_rpc(jsonrpc_request_from_client_request(request))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn request_json_rpc(&self, request: JSONRPCRequest) -> IoResult<RequestResult> {
|
||||
let (response_tx, response_rx) = oneshot::channel();
|
||||
self.command_tx
|
||||
.send(RemoteClientCommand::Request {
|
||||
@@ -800,13 +796,14 @@ async fn initialize_remote_connection<S>(
|
||||
endpoint: &str,
|
||||
params: InitializeParams,
|
||||
initialize_timeout: Duration,
|
||||
) -> IoResult<(Vec<AppServerEvent>, Option<String>)>
|
||||
) -> IoResult<(Vec<AppServerEvent>, Option<String>, Option<String>)>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
let initialize_request_id = RequestId::String("initialize".to_string());
|
||||
let mut pending_events = Vec::new();
|
||||
let mut server_version = None;
|
||||
let mut codex_home = None;
|
||||
write_jsonrpc_message(
|
||||
stream,
|
||||
JSONRPCMessage::Request(jsonrpc_request_from_client_request(
|
||||
@@ -838,6 +835,12 @@ where
|
||||
let (_, rest) = user_agent.split_once('/')?;
|
||||
rest.split_whitespace().next().map(str::to_string)
|
||||
});
|
||||
codex_home = response
|
||||
.result
|
||||
.get("codexHome")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.filter(|codex_home| !codex_home.is_empty())
|
||||
.map(str::to_string);
|
||||
break Ok(());
|
||||
}
|
||||
JSONRPCMessage::Error(error) if error.id == initialize_request_id => {
|
||||
@@ -929,7 +932,7 @@ where
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok((pending_events, server_version))
|
||||
Ok((pending_events, server_version, codex_home))
|
||||
}
|
||||
|
||||
fn app_server_event_from_notification(notification: JSONRPCNotification) -> Option<AppServerEvent> {
|
||||
@@ -951,10 +954,6 @@ fn deliver_event(
|
||||
})
|
||||
}
|
||||
|
||||
fn request_id_from_client_request(request: &ClientRequest) -> RequestId {
|
||||
jsonrpc_request_from_client_request(request.clone()).id
|
||||
}
|
||||
|
||||
fn jsonrpc_request_from_client_request(request: ClientRequest) -> JSONRPCRequest {
|
||||
let value = match serde_json::to_value(request) {
|
||||
Ok(value) => value,
|
||||
@@ -1024,6 +1023,7 @@ mod tests {
|
||||
event_rx,
|
||||
pending_events: VecDeque::new(),
|
||||
server_version: None,
|
||||
codex_home: None,
|
||||
worker_handle,
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ use std::time::Duration;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use anyhow::bail;
|
||||
#[cfg(unix)]
|
||||
use codex_app_server_transport::REMOTE_CONTROL_DISABLED_ENV_VAR;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use tokio::fs;
|
||||
@@ -164,6 +166,9 @@ impl PidBackend {
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::from(stderr_log.into_std().await));
|
||||
if let Some((key, value)) = self.command_env() {
|
||||
command.env(key, value);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
@@ -407,6 +412,19 @@ impl PidBackend {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn command_env(&self) -> Option<(&'static str, &'static str)> {
|
||||
match self.command_kind {
|
||||
PidCommandKind::AppServer {
|
||||
remote_control_enabled: false,
|
||||
} => Some((REMOTE_CONTROL_DISABLED_ENV_VAR, "1")),
|
||||
PidCommandKind::AppServer {
|
||||
remote_control_enabled: true,
|
||||
}
|
||||
| PidCommandKind::UpdateLoop => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn terminate_process(&self, pid: u32) -> Result<()> {
|
||||
match self.command_kind {
|
||||
PidCommandKind::AppServer { .. } => terminate_process(pid),
|
||||
|
||||
@@ -3,6 +3,8 @@ use std::time::Duration;
|
||||
use pretty_assertions::assert_eq;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use codex_app_server_transport::REMOTE_CONTROL_DISABLED_ENV_VAR;
|
||||
|
||||
use super::PidBackend;
|
||||
use super::PidCommandKind;
|
||||
use super::PidFileState;
|
||||
@@ -174,6 +176,24 @@ fn app_server_remote_control_uses_runtime_flag() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_server_disabled_remote_control_uses_compatible_args_and_runtime_env() {
|
||||
let backend = PidBackend::new(
|
||||
"codex".into(),
|
||||
"app-server.pid".into(),
|
||||
/*remote_control_enabled*/ false,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
backend.command_args(),
|
||||
vec!["app-server", "--listen", "unix://"]
|
||||
);
|
||||
assert_eq!(
|
||||
backend.command_env(),
|
||||
Some((REMOTE_CONTROL_DISABLED_ENV_VAR, "1"))
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn read_stderr_log_tail_returns_recent_complete_lines() {
|
||||
let temp_dir = TempDir::new().expect("temp dir");
|
||||
|
||||
@@ -543,6 +543,16 @@ impl Daemon {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if info.is_some() {
|
||||
match mode {
|
||||
RemoteControlMode::Enabled => {
|
||||
remote_control_client::enable_remote_control(&self.socket_path).await?;
|
||||
}
|
||||
RemoteControlMode::Disabled => {
|
||||
remote_control_client::disable_remote_control(&self.socket_path).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(self.remote_control_output(
|
||||
already_remote_control_status(mode),
|
||||
backend.map(|_| BackendKind::Pid),
|
||||
|
||||
@@ -8,9 +8,13 @@ use codex_app_server_protocol::JSONRPCMessage;
|
||||
use codex_app_server_protocol::JSONRPCNotification;
|
||||
use codex_app_server_protocol::JSONRPCRequest;
|
||||
use codex_app_server_protocol::RemoteControlConnectionStatus;
|
||||
use codex_app_server_protocol::RemoteControlDisableParams;
|
||||
use codex_app_server_protocol::RemoteControlDisableResponse;
|
||||
use codex_app_server_protocol::RemoteControlEnableParams;
|
||||
use codex_app_server_protocol::RemoteControlEnableResponse;
|
||||
use codex_app_server_protocol::RemoteControlStatusChangedNotification;
|
||||
use codex_app_server_protocol::RequestId;
|
||||
use serde::de::DeserializeOwned;
|
||||
use tokio::io::AsyncRead;
|
||||
use tokio::io::AsyncWrite;
|
||||
use tokio::time::Instant;
|
||||
@@ -22,13 +26,33 @@ use crate::RemoteControlReadyStatus;
|
||||
use crate::client;
|
||||
|
||||
const REMOTE_CONTROL_READY_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
const REMOTE_CONTROL_ENABLE_REQUEST_ID: RequestId = RequestId::Integer(2);
|
||||
const REMOTE_CONTROL_REQUEST_ID: RequestId = RequestId::Integer(2);
|
||||
const INVALID_PARAMS_ERROR_CODE: i64 = -32602;
|
||||
|
||||
enum RemoteControlRpcResponse<T> {
|
||||
Success(T),
|
||||
InvalidParams,
|
||||
}
|
||||
|
||||
pub(crate) async fn enable_remote_control(socket_path: &Path) -> Result<RemoteControlReadyStatus> {
|
||||
let mut websocket = client::connect(socket_path).await?;
|
||||
enable_remote_control_with_timeout(&mut websocket, REMOTE_CONTROL_READY_TIMEOUT).await
|
||||
}
|
||||
|
||||
pub(crate) async fn disable_remote_control(socket_path: &Path) -> Result<RemoteControlReadyStatus> {
|
||||
let mut websocket = client::connect(socket_path).await?;
|
||||
initialize_client(&mut websocket).await?;
|
||||
let params = serde_json::to_value(RemoteControlDisableParams { ephemeral: true })?;
|
||||
let response: RemoteControlDisableResponse = request_remote_control_with_legacy_fallback(
|
||||
&mut websocket,
|
||||
"remoteControl/disable",
|
||||
params,
|
||||
)
|
||||
.await?;
|
||||
websocket.close(None).await.ok();
|
||||
Ok(RemoteControlReadyStatus::from(response))
|
||||
}
|
||||
|
||||
pub(crate) async fn enable_remote_control_with_connect_retry(
|
||||
socket_path: &Path,
|
||||
connect_timeout: Duration,
|
||||
@@ -43,6 +67,26 @@ async fn enable_remote_control_with_timeout<S>(
|
||||
websocket: &mut WebSocketStream<S>,
|
||||
ready_timeout: Duration,
|
||||
) -> Result<RemoteControlReadyStatus>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
initialize_client(websocket).await?;
|
||||
|
||||
let response: RemoteControlEnableResponse = request_remote_control_with_legacy_fallback(
|
||||
websocket,
|
||||
"remoteControl/enable",
|
||||
serde_json::to_value(RemoteControlEnableParams { ephemeral: true })?,
|
||||
)
|
||||
.await?;
|
||||
let mut latest = RemoteControlReadyStatus::from(response);
|
||||
if latest.status == RemoteControlConnectionStatus::Connecting {
|
||||
latest = wait_for_remote_control_status(websocket, latest, ready_timeout).await?;
|
||||
}
|
||||
websocket.close(None).await.ok();
|
||||
Ok(latest)
|
||||
}
|
||||
|
||||
async fn initialize_client<S>(websocket: &mut WebSocketStream<S>) -> Result<()>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
@@ -53,24 +97,65 @@ where
|
||||
});
|
||||
client::send_message(websocket, &initialized)
|
||||
.await
|
||||
.context("failed to send initialized notification")?;
|
||||
.context("failed to send initialized notification")
|
||||
}
|
||||
|
||||
let enable = JSONRPCMessage::Request(JSONRPCRequest {
|
||||
id: REMOTE_CONTROL_ENABLE_REQUEST_ID,
|
||||
method: "remoteControl/enable".to_string(),
|
||||
params: None,
|
||||
async fn send_remote_control_request<S>(
|
||||
websocket: &mut WebSocketStream<S>,
|
||||
request_id: RequestId,
|
||||
method: &str,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<()>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
let request = JSONRPCMessage::Request(JSONRPCRequest {
|
||||
id: request_id,
|
||||
method: method.to_string(),
|
||||
params,
|
||||
trace: None,
|
||||
});
|
||||
client::send_message(websocket, &enable)
|
||||
client::send_message(websocket, &request)
|
||||
.await
|
||||
.context("failed to send remoteControl/enable request")?;
|
||||
.with_context(|| format!("failed to send {method} request"))
|
||||
}
|
||||
|
||||
let mut latest = read_enable_response(websocket).await?;
|
||||
if latest.status == RemoteControlConnectionStatus::Connecting {
|
||||
latest = wait_for_remote_control_status(websocket, latest, ready_timeout).await?;
|
||||
async fn request_remote_control_with_legacy_fallback<S, T>(
|
||||
websocket: &mut WebSocketStream<S>,
|
||||
method: &str,
|
||||
params: serde_json::Value,
|
||||
) -> Result<T>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
send_remote_control_request(
|
||||
websocket,
|
||||
REMOTE_CONTROL_REQUEST_ID.clone(),
|
||||
method,
|
||||
Some(params),
|
||||
)
|
||||
.await?;
|
||||
match read_remote_control_response(websocket, &REMOTE_CONTROL_REQUEST_ID, method).await? {
|
||||
RemoteControlRpcResponse::Success(response) => Ok(response),
|
||||
RemoteControlRpcResponse::InvalidParams => {
|
||||
send_remote_control_request(
|
||||
websocket,
|
||||
REMOTE_CONTROL_REQUEST_ID.clone(),
|
||||
method,
|
||||
/*params*/ None,
|
||||
)
|
||||
.await?;
|
||||
match read_remote_control_response(websocket, &REMOTE_CONTROL_REQUEST_ID, method)
|
||||
.await?
|
||||
{
|
||||
RemoteControlRpcResponse::Success(response) => Ok(response),
|
||||
RemoteControlRpcResponse::InvalidParams => {
|
||||
Err(anyhow!("{method} rejected legacy params"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
websocket.close(None).await.ok();
|
||||
Ok(latest)
|
||||
}
|
||||
|
||||
async fn connect_with_retry(
|
||||
@@ -97,11 +182,14 @@ async fn connect_with_retry(
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_enable_response<S>(
|
||||
async fn read_remote_control_response<S, T>(
|
||||
websocket: &mut WebSocketStream<S>,
|
||||
) -> Result<RemoteControlReadyStatus>
|
||||
request_id: &RequestId,
|
||||
method: &str,
|
||||
) -> Result<RemoteControlRpcResponse<T>>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
loop {
|
||||
let message = timeout(
|
||||
@@ -109,21 +197,20 @@ where
|
||||
client::read_message(websocket),
|
||||
)
|
||||
.await
|
||||
.context("timed out waiting for remoteControl/enable response")??;
|
||||
.with_context(|| format!("timed out waiting for {method} response"))??;
|
||||
match message {
|
||||
JSONRPCMessage::Response(response)
|
||||
if response.id == REMOTE_CONTROL_ENABLE_REQUEST_ID =>
|
||||
{
|
||||
let response =
|
||||
serde_json::from_value::<RemoteControlEnableResponse>(response.result)
|
||||
.context("failed to parse remoteControl/enable response")?;
|
||||
return Ok(RemoteControlReadyStatus::from(response));
|
||||
JSONRPCMessage::Response(response) if response.id == *request_id => {
|
||||
let response = serde_json::from_value::<T>(response.result)
|
||||
.with_context(|| format!("failed to parse {method} response"))?;
|
||||
return Ok(RemoteControlRpcResponse::Success(response));
|
||||
}
|
||||
JSONRPCMessage::Error(err) if err.id == REMOTE_CONTROL_ENABLE_REQUEST_ID => {
|
||||
return Err(anyhow!(
|
||||
"remoteControl/enable failed: {}",
|
||||
err.error.message
|
||||
));
|
||||
JSONRPCMessage::Error(err)
|
||||
if err.id == *request_id && err.error.code == INVALID_PARAMS_ERROR_CODE =>
|
||||
{
|
||||
return Ok(RemoteControlRpcResponse::InvalidParams);
|
||||
}
|
||||
JSONRPCMessage::Error(err) if err.id == *request_id => {
|
||||
return Err(anyhow!("{method} failed: {}", err.error.message));
|
||||
}
|
||||
JSONRPCMessage::Notification(notification)
|
||||
if remote_control_status_notification(¬ification).is_some() =>
|
||||
@@ -196,6 +283,23 @@ impl From<RemoteControlEnableResponse> for RemoteControlReadyStatus {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RemoteControlDisableResponse> for RemoteControlReadyStatus {
|
||||
fn from(response: RemoteControlDisableResponse) -> Self {
|
||||
let RemoteControlDisableResponse {
|
||||
status,
|
||||
server_name,
|
||||
installation_id: _,
|
||||
environment_id,
|
||||
} = response;
|
||||
Self {
|
||||
status,
|
||||
server_name,
|
||||
environment_id,
|
||||
timed_out: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RemoteControlStatusChangedNotification> for RemoteControlReadyStatus {
|
||||
fn from(notification: RemoteControlStatusChangedNotification) -> Self {
|
||||
let RemoteControlStatusChangedNotification {
|
||||
@@ -216,6 +320,8 @@ impl From<RemoteControlStatusChangedNotification> for RemoteControlReadyStatus {
|
||||
#[cfg(all(test, unix))]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
use codex_app_server_protocol::JSONRPCError;
|
||||
use codex_app_server_protocol::JSONRPCErrorError;
|
||||
use codex_app_server_protocol::JSONRPCResponse;
|
||||
use codex_uds::UnixListener;
|
||||
use pretty_assertions::assert_eq;
|
||||
@@ -243,6 +349,7 @@ mod tests {
|
||||
),
|
||||
after_enable_notification: None,
|
||||
ready_timeout: Duration::from_millis(20),
|
||||
reject_ephemeral_params: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -271,6 +378,7 @@ mod tests {
|
||||
Some("env_test"),
|
||||
)),
|
||||
ready_timeout: Duration::from_secs(1),
|
||||
reject_ephemeral_params: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -296,6 +404,7 @@ mod tests {
|
||||
),
|
||||
after_enable_notification: None,
|
||||
ready_timeout: Duration::from_millis(20),
|
||||
reject_ephemeral_params: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -321,6 +430,7 @@ mod tests {
|
||||
),
|
||||
after_enable_notification: None,
|
||||
ready_timeout: Duration::from_millis(20),
|
||||
reject_ephemeral_params: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -336,11 +446,104 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn enable_remote_control_retries_without_params_for_older_servers() -> Result<()> {
|
||||
let status = run_enable_remote_control_scenario(EnableScenario {
|
||||
initial_notification: None,
|
||||
enable_response: remote_control_status(
|
||||
RemoteControlConnectionStatus::Connected,
|
||||
Some("env_test"),
|
||||
),
|
||||
after_enable_notification: None,
|
||||
ready_timeout: Duration::from_millis(20),
|
||||
reject_ephemeral_params: true,
|
||||
})
|
||||
.await?;
|
||||
|
||||
assert_eq!(
|
||||
status,
|
||||
RemoteControlReadyStatus {
|
||||
status: RemoteControlConnectionStatus::Connected,
|
||||
server_name: TEST_SERVER_NAME.to_string(),
|
||||
environment_id: Some("env_test".to_string()),
|
||||
timed_out: false,
|
||||
}
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn disable_remote_control_retries_without_params_for_older_servers() -> Result<()> {
|
||||
let dir = TempDir::new()?;
|
||||
let socket_path = dir.path().join("app-server.sock");
|
||||
let listener = UnixListener::bind(&socket_path).await?;
|
||||
let server_task = tokio::spawn(async move {
|
||||
let mut websocket = accept_initialized_client(listener).await?;
|
||||
let disable = client::read_message(&mut websocket).await?;
|
||||
let JSONRPCMessage::Request(disable) = disable else {
|
||||
panic!("expected remoteControl/disable request");
|
||||
};
|
||||
assert_eq!(disable.id, REMOTE_CONTROL_REQUEST_ID);
|
||||
assert_eq!(disable.method, "remoteControl/disable");
|
||||
assert_eq!(
|
||||
disable.params,
|
||||
Some(serde_json::json!({ "ephemeral": true }))
|
||||
);
|
||||
client::send_message(
|
||||
&mut websocket,
|
||||
&JSONRPCMessage::Error(JSONRPCError {
|
||||
id: REMOTE_CONTROL_REQUEST_ID,
|
||||
error: JSONRPCErrorError {
|
||||
code: INVALID_PARAMS_ERROR_CODE,
|
||||
message: "Invalid params".to_string(),
|
||||
data: None,
|
||||
},
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
let fallback = client::read_message(&mut websocket).await?;
|
||||
let JSONRPCMessage::Request(fallback) = fallback else {
|
||||
panic!("expected fallback remoteControl/disable request");
|
||||
};
|
||||
assert_eq!(fallback.id, REMOTE_CONTROL_REQUEST_ID);
|
||||
assert_eq!(fallback.method, "remoteControl/disable");
|
||||
assert_eq!(fallback.params, None);
|
||||
client::send_message(
|
||||
&mut websocket,
|
||||
&JSONRPCMessage::Response(JSONRPCResponse {
|
||||
id: REMOTE_CONTROL_REQUEST_ID,
|
||||
result: serde_json::to_value(RemoteControlDisableResponse::from(
|
||||
remote_control_status(
|
||||
RemoteControlConnectionStatus::Disabled,
|
||||
/*environment_id*/ None,
|
||||
),
|
||||
))?,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
Ok::<_, anyhow::Error>(())
|
||||
});
|
||||
|
||||
let status = disable_remote_control(&socket_path).await?;
|
||||
server_task.await??;
|
||||
assert_eq!(
|
||||
status,
|
||||
RemoteControlReadyStatus {
|
||||
status: RemoteControlConnectionStatus::Disabled,
|
||||
server_name: TEST_SERVER_NAME.to_string(),
|
||||
environment_id: None,
|
||||
timed_out: false,
|
||||
}
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct EnableScenario {
|
||||
initial_notification: Option<RemoteControlStatusChangedNotification>,
|
||||
enable_response: RemoteControlStatusChangedNotification,
|
||||
after_enable_notification: Option<RemoteControlStatusChangedNotification>,
|
||||
ready_timeout: Duration,
|
||||
reject_ephemeral_params: bool,
|
||||
}
|
||||
|
||||
async fn run_enable_remote_control_scenario(
|
||||
@@ -359,12 +562,70 @@ mod tests {
|
||||
}
|
||||
|
||||
async fn serve_enable_remote_control_scenario(
|
||||
mut listener: UnixListener,
|
||||
listener: UnixListener,
|
||||
scenario: EnableScenario,
|
||||
) -> Result<()> {
|
||||
let mut websocket = accept_initialized_client(listener).await?;
|
||||
if let Some(status) = scenario.initial_notification {
|
||||
send_remote_control_status(&mut websocket, status).await?;
|
||||
}
|
||||
|
||||
let enable = client::read_message(&mut websocket).await?;
|
||||
let JSONRPCMessage::Request(enable) = enable else {
|
||||
panic!("expected remoteControl/enable request");
|
||||
};
|
||||
assert_eq!(enable.id, REMOTE_CONTROL_REQUEST_ID);
|
||||
assert_eq!(enable.method, "remoteControl/enable");
|
||||
assert_eq!(
|
||||
enable.params,
|
||||
Some(serde_json::json!({ "ephemeral": true }))
|
||||
);
|
||||
if scenario.reject_ephemeral_params {
|
||||
client::send_message(
|
||||
&mut websocket,
|
||||
&JSONRPCMessage::Error(JSONRPCError {
|
||||
id: REMOTE_CONTROL_REQUEST_ID,
|
||||
error: JSONRPCErrorError {
|
||||
code: INVALID_PARAMS_ERROR_CODE,
|
||||
message: "Invalid params".to_string(),
|
||||
data: None,
|
||||
},
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
let fallback = client::read_message(&mut websocket).await?;
|
||||
let JSONRPCMessage::Request(fallback) = fallback else {
|
||||
panic!("expected fallback remoteControl/enable request");
|
||||
};
|
||||
assert_eq!(fallback.id, REMOTE_CONTROL_REQUEST_ID);
|
||||
assert_eq!(fallback.method, "remoteControl/enable");
|
||||
assert_eq!(fallback.params, None);
|
||||
}
|
||||
client::send_message(
|
||||
&mut websocket,
|
||||
&JSONRPCMessage::Response(JSONRPCResponse {
|
||||
id: REMOTE_CONTROL_REQUEST_ID,
|
||||
result: serde_json::to_value(RemoteControlEnableResponse::from(
|
||||
scenario.enable_response,
|
||||
))?,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(status) = scenario.after_enable_notification {
|
||||
send_remote_control_status(&mut websocket, status).await?;
|
||||
} else {
|
||||
tokio::time::sleep(Duration::from_millis(50)).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn accept_initialized_client(
|
||||
mut listener: UnixListener,
|
||||
) -> Result<WebSocketStream<codex_uds::UnixStream>> {
|
||||
let stream = listener.accept().await?;
|
||||
let mut websocket = accept_async(stream).await?;
|
||||
|
||||
let initialize = client::read_message(&mut websocket).await?;
|
||||
let JSONRPCMessage::Request(initialize) = initialize else {
|
||||
panic!("expected initialize request");
|
||||
@@ -397,35 +658,7 @@ mod tests {
|
||||
panic!("expected initialized notification");
|
||||
};
|
||||
assert_eq!(initialized.method, "initialized");
|
||||
|
||||
if let Some(status) = scenario.initial_notification {
|
||||
send_remote_control_status(&mut websocket, status).await?;
|
||||
}
|
||||
|
||||
let enable = client::read_message(&mut websocket).await?;
|
||||
let JSONRPCMessage::Request(enable) = enable else {
|
||||
panic!("expected remoteControl/enable request");
|
||||
};
|
||||
assert_eq!(enable.id, REMOTE_CONTROL_ENABLE_REQUEST_ID);
|
||||
assert_eq!(enable.method, "remoteControl/enable");
|
||||
client::send_message(
|
||||
&mut websocket,
|
||||
&JSONRPCMessage::Response(JSONRPCResponse {
|
||||
id: REMOTE_CONTROL_ENABLE_REQUEST_ID,
|
||||
result: serde_json::to_value(RemoteControlEnableResponse::from(
|
||||
scenario.enable_response,
|
||||
))?,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(status) = scenario.after_enable_notification {
|
||||
send_remote_control_status(&mut websocket, status).await?;
|
||||
} else {
|
||||
tokio::time::sleep(Duration::from_millis(50)).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(websocket)
|
||||
}
|
||||
|
||||
async fn send_remote_control_status<S>(
|
||||
|
||||
@@ -3,5 +3,8 @@ load("//:defs.bzl", "codex_rust_crate")
|
||||
codex_rust_crate(
|
||||
name = "app-server-protocol",
|
||||
crate_name = "codex_app_server_protocol",
|
||||
test_data_extra = glob(["schema/**"], allow_empty = True),
|
||||
test_data_extra = glob(
|
||||
["schema/**"],
|
||||
allow_empty = True,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -19,6 +19,7 @@ codex-experimental-api-macros = { workspace = true }
|
||||
codex-protocol = { workspace = true }
|
||||
codex-shell-command = { workspace = true }
|
||||
codex-utils-absolute-path = { workspace = true }
|
||||
codex-utils-path-uri = { workspace = true }
|
||||
schemars = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
+392
-68
@@ -36,6 +36,26 @@
|
||||
},
|
||||
"AgentMessageInputContent": {
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"input_text"
|
||||
],
|
||||
"title": "InputTextAgentMessageInputContentType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"text",
|
||||
"type"
|
||||
],
|
||||
"title": "InputTextAgentMessageInputContent",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"encrypted_content": {
|
||||
@@ -534,6 +554,18 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ConsumeAccountRateLimitResetCreditParams": {
|
||||
"properties": {
|
||||
"idempotencyKey": {
|
||||
"description": "Identifies one logical reset attempt. A UUID is recommended; reuse the same value when retrying that attempt.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"idempotencyKey"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ContentItem": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -608,31 +640,110 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"DynamicToolSpec": {
|
||||
"properties": {
|
||||
"deferLoading": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"inputSchema": true,
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"namespace": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"description",
|
||||
"inputSchema",
|
||||
"name"
|
||||
"ConversationTextRole": {
|
||||
"enum": [
|
||||
"user",
|
||||
"developer",
|
||||
"assistant"
|
||||
],
|
||||
"type": "object"
|
||||
"type": "string"
|
||||
},
|
||||
"DynamicToolNamespaceTool": {
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"deferLoading": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"inputSchema": true,
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"function"
|
||||
],
|
||||
"title": "FunctionDynamicToolNamespaceToolType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"description",
|
||||
"inputSchema",
|
||||
"name",
|
||||
"type"
|
||||
],
|
||||
"title": "FunctionDynamicToolNamespaceTool",
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"DynamicToolSpec": {
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"deferLoading": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"inputSchema": true,
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"function"
|
||||
],
|
||||
"title": "FunctionDynamicToolSpecType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"description",
|
||||
"inputSchema",
|
||||
"name",
|
||||
"type"
|
||||
],
|
||||
"title": "FunctionDynamicToolSpec",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"tools": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/DynamicToolNamespaceTool"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"namespace"
|
||||
],
|
||||
"title": "NamespaceDynamicToolSpecType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"description",
|
||||
"name",
|
||||
"tools",
|
||||
"type"
|
||||
],
|
||||
"title": "NamespaceDynamicToolSpec",
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ExperimentalFeatureEnablementSetParams": {
|
||||
"properties": {
|
||||
@@ -690,7 +801,7 @@
|
||||
]
|
||||
},
|
||||
"includeHome": {
|
||||
"description": "If true, include detection under the user's home (~/.claude, ~/.codex, etc.).",
|
||||
"description": "If true, include detection under the user's home directory.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
@@ -703,6 +814,13 @@
|
||||
"$ref": "#/definitions/ExternalAgentConfigMigrationItem"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"source": {
|
||||
"description": "Source product that produced the migration items. Missing means unspecified.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -1154,6 +1272,10 @@
|
||||
"description": "Opt into receiving experimental API methods and fields.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"mcpServerOpenaiFormElicitation": {
|
||||
"description": "Allow downstream MCP servers to request OpenAI extended form elicitations.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"optOutNotificationMethods": {
|
||||
"description": "Exact notification method names that should be suppressed for this connection (for example `thread/started`).",
|
||||
"items": {
|
||||
@@ -1193,6 +1315,9 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"ListMcpServerStatusParams": {
|
||||
"properties": {
|
||||
"cursor": {
|
||||
@@ -1735,7 +1860,8 @@
|
||||
"local",
|
||||
"vertical",
|
||||
"workspace-directory",
|
||||
"shared-with-me"
|
||||
"shared-with-me",
|
||||
"created-by-me-remote"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1993,6 +2119,13 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"RealtimeConversationArchitecture": {
|
||||
"enum": [
|
||||
"realtimeapi",
|
||||
"avas"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"RealtimeConversationVersion": {
|
||||
"enum": [
|
||||
"v1",
|
||||
@@ -2124,6 +2257,22 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"RemoteControlDisableParams": {
|
||||
"properties": {
|
||||
"ephemeral": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"RemoteControlEnableParams": {
|
||||
"properties": {
|
||||
"ephemeral": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"RequestId": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -2145,12 +2294,15 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"phase": {
|
||||
"anyOf": [
|
||||
@@ -2192,6 +2344,16 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"recipient": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -2230,6 +2392,16 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ReasoningItemReasoningSummary"
|
||||
@@ -2263,13 +2435,15 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"description": "Legacy id field retained for compatibility with older payloads.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/definitions/LocalShellStatus"
|
||||
@@ -2298,12 +2472,15 @@
|
||||
"call_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
@@ -2343,12 +2520,15 @@
|
||||
"execution": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"type": [
|
||||
@@ -2377,6 +2557,16 @@
|
||||
"call_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"output": {
|
||||
"$ref": "#/definitions/FunctionCallOutputBody"
|
||||
},
|
||||
@@ -2401,16 +2591,19 @@
|
||||
"call_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
},
|
||||
"input": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -2442,6 +2635,16 @@
|
||||
"call_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": [
|
||||
"string",
|
||||
@@ -2478,6 +2681,16 @@
|
||||
"execution": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -2514,12 +2727,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"type": [
|
||||
@@ -2544,7 +2760,21 @@
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
"description": "Existing provider ID retained on serialized history for compatibility.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"result": {
|
||||
"type": "string"
|
||||
@@ -2567,7 +2797,6 @@
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"result",
|
||||
"status",
|
||||
"type"
|
||||
@@ -2580,6 +2809,16 @@
|
||||
"encrypted_content": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"compaction"
|
||||
@@ -2597,6 +2836,16 @@
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"compaction_trigger"
|
||||
@@ -2619,6 +2868,16 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"context_compaction"
|
||||
@@ -2651,6 +2910,23 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ResponseItemMetadata": {
|
||||
"properties": {
|
||||
"source_call_id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"turn_id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ResponsesApiWebSearchAction": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -3860,7 +4136,8 @@
|
||||
"ThreadSortKey": {
|
||||
"enum": [
|
||||
"created_at",
|
||||
"updated_at"
|
||||
"updated_at",
|
||||
"recency_at"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -4036,7 +4313,7 @@
|
||||
"TurnEnvironmentParams": {
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"environmentId": {
|
||||
"type": "string"
|
||||
@@ -6058,6 +6335,30 @@
|
||||
"title": "Account/rateLimits/readRequest",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/definitions/RequestId"
|
||||
},
|
||||
"method": {
|
||||
"enum": [
|
||||
"account/rateLimitResetCredit/consume"
|
||||
],
|
||||
"title": "Account/rateLimitResetCredit/consumeRequestMethod",
|
||||
"type": "string"
|
||||
},
|
||||
"params": {
|
||||
"$ref": "#/definitions/ConsumeAccountRateLimitResetCreditParams"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"method",
|
||||
"params"
|
||||
],
|
||||
"title": "Account/rateLimitResetCredit/consumeRequest",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -6301,6 +6602,29 @@
|
||||
"title": "ExternalAgentConfig/importRequest",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/definitions/RequestId"
|
||||
},
|
||||
"method": {
|
||||
"enum": [
|
||||
"externalAgentConfig/import/readHistories"
|
||||
],
|
||||
"title": "ExternalAgentConfig/import/readHistoriesRequestMethod",
|
||||
"type": "string"
|
||||
},
|
||||
"params": {
|
||||
"type": "null"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"method"
|
||||
],
|
||||
"title": "ExternalAgentConfig/import/readHistoriesRequest",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
+15
-4
@@ -27,7 +27,7 @@
|
||||
"read": {
|
||||
"description": "This will be removed in favor of `entries`.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
@@ -37,7 +37,7 @@
|
||||
"write": {
|
||||
"description": "This will be removed in favor of `entries`.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
@@ -286,7 +286,7 @@
|
||||
{
|
||||
"properties": {
|
||||
"path": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -469,6 +469,9 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"NetworkApprovalContext": {
|
||||
"properties": {
|
||||
"host": {
|
||||
@@ -544,7 +547,7 @@
|
||||
"cwd": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
@@ -552,6 +555,14 @@
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"environmentId": {
|
||||
"default": null,
|
||||
"description": "Environment in which the command will run.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"itemId": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
+21
@@ -557,6 +557,27 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"_meta": true,
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"mode": {
|
||||
"enum": [
|
||||
"openai/form"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"requestedSchema": true
|
||||
},
|
||||
"required": [
|
||||
"message",
|
||||
"mode",
|
||||
"requestedSchema"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"_meta": true,
|
||||
|
||||
+6
-3
@@ -27,7 +27,7 @@
|
||||
"read": {
|
||||
"description": "This will be removed in favor of `entries`.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
@@ -37,7 +37,7 @@
|
||||
"write": {
|
||||
"description": "This will be removed in favor of `entries`.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
@@ -71,7 +71,7 @@
|
||||
{
|
||||
"properties": {
|
||||
"path": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -254,6 +254,9 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"RequestPermissionProfile": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
|
||||
+6
-7
@@ -1,10 +1,6 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": {
|
||||
"AbsolutePathBuf": {
|
||||
"description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
|
||||
"type": "string"
|
||||
},
|
||||
"AdditionalFileSystemPermissions": {
|
||||
"properties": {
|
||||
"entries": {
|
||||
@@ -27,7 +23,7 @@
|
||||
"read": {
|
||||
"description": "This will be removed in favor of `entries`.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
@@ -37,7 +33,7 @@
|
||||
"write": {
|
||||
"description": "This will be removed in favor of `entries`.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
@@ -71,7 +67,7 @@
|
||||
{
|
||||
"properties": {
|
||||
"path": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -279,6 +275,9 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"PermissionGrantScope": {
|
||||
"enum": [
|
||||
"turn",
|
||||
|
||||
+206
-4
@@ -107,7 +107,7 @@
|
||||
"read": {
|
||||
"description": "This will be removed in favor of `entries`.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
@@ -117,7 +117,7 @@
|
||||
"write": {
|
||||
"description": "This will be removed in favor of `entries`.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
@@ -532,6 +532,13 @@
|
||||
"personalAccessToken"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Amazon Bedrock bearer token managed by Codex.",
|
||||
"enum": [
|
||||
"bedrockApiKey"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1132,8 +1139,146 @@
|
||||
"type": "object"
|
||||
},
|
||||
"ExternalAgentConfigImportCompletedNotification": {
|
||||
"properties": {
|
||||
"importId": {
|
||||
"type": "string"
|
||||
},
|
||||
"itemTypeResults": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigImportTypeResult"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"importId",
|
||||
"itemTypeResults"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExternalAgentConfigImportItemTypeFailure": {
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"errorType": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"failureStage": {
|
||||
"type": "string"
|
||||
},
|
||||
"itemType": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"failureStage",
|
||||
"itemType",
|
||||
"message"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExternalAgentConfigImportItemTypeSuccess": {
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"itemType": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
|
||||
},
|
||||
"source": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"target": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"itemType"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExternalAgentConfigImportProgressNotification": {
|
||||
"properties": {
|
||||
"importId": {
|
||||
"type": "string"
|
||||
},
|
||||
"itemTypeResults": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigImportTypeResult"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"importId",
|
||||
"itemTypeResults"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExternalAgentConfigImportTypeResult": {
|
||||
"properties": {
|
||||
"failures": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigImportItemTypeFailure"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"itemType": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
|
||||
},
|
||||
"successes": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigImportItemTypeSuccess"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"failures",
|
||||
"itemType",
|
||||
"successes"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExternalAgentConfigMigrationItemType": {
|
||||
"enum": [
|
||||
"AGENTS_MD",
|
||||
"CONFIG",
|
||||
"SKILLS",
|
||||
"PLUGINS",
|
||||
"MCP_SERVER_CONFIG",
|
||||
"SUBAGENTS",
|
||||
"HOOKS",
|
||||
"COMMANDS",
|
||||
"SESSIONS"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"FileChangeOutputDeltaNotification": {
|
||||
"description": "Deprecated legacy notification for `apply_patch` textual output.\n\nThe server no longer emits this notification.",
|
||||
"properties": {
|
||||
@@ -1197,7 +1342,7 @@
|
||||
{
|
||||
"properties": {
|
||||
"path": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -2191,6 +2336,9 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpServerOauthLoginCompletedNotification": {
|
||||
"properties": {
|
||||
"error": {
|
||||
@@ -3434,6 +3582,14 @@
|
||||
"description": "Usually the first user message in the thread, if available.",
|
||||
"type": "string"
|
||||
},
|
||||
"recencyAt": {
|
||||
"description": "Unix timestamp (in seconds) used for thread recency ordering.",
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"sessionId": {
|
||||
"description": "Session id shared by threads that belong to the same session tree.",
|
||||
"type": "string"
|
||||
@@ -3816,7 +3972,7 @@
|
||||
"cwd": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
@@ -4216,6 +4372,32 @@
|
||||
"title": "ImageViewThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"durationMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"sleep"
|
||||
],
|
||||
"title": "SleepThreadItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"durationMs",
|
||||
"id",
|
||||
"type"
|
||||
],
|
||||
"title": "SleepThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -6183,6 +6365,26 @@
|
||||
"title": "RemoteControl/status/changedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"method": {
|
||||
"enum": [
|
||||
"externalAgentConfig/import/progress"
|
||||
],
|
||||
"title": "ExternalAgentConfig/import/progressNotificationMethod",
|
||||
"type": "string"
|
||||
},
|
||||
"params": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigImportProgressNotification"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"method",
|
||||
"params"
|
||||
],
|
||||
"title": "ExternalAgentConfig/import/progressNotification",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"method": {
|
||||
|
||||
+45
-4
@@ -27,7 +27,7 @@
|
||||
"read": {
|
||||
"description": "This will be removed in favor of `entries`.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
@@ -37,7 +37,7 @@
|
||||
"write": {
|
||||
"description": "This will be removed in favor of `entries`.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
@@ -371,7 +371,7 @@
|
||||
"cwd": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
@@ -379,6 +379,14 @@
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"environmentId": {
|
||||
"default": null,
|
||||
"description": "Environment in which the command will run.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"itemId": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -640,7 +648,7 @@
|
||||
{
|
||||
"properties": {
|
||||
"path": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -823,6 +831,9 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpElicitationArrayType": {
|
||||
"enum": [
|
||||
"array"
|
||||
@@ -1379,6 +1390,27 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"_meta": true,
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"mode": {
|
||||
"enum": [
|
||||
"openai/form"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"requestedSchema": true
|
||||
},
|
||||
"required": [
|
||||
"message",
|
||||
"mode",
|
||||
"requestedSchema"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"_meta": true,
|
||||
@@ -1690,6 +1722,15 @@
|
||||
"ToolRequestUserInputParams": {
|
||||
"description": "EXPERIMENTAL. Params sent with a request_user_input event.",
|
||||
"properties": {
|
||||
"autoResolutionMs": {
|
||||
"default": null,
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"itemId": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -57,6 +57,15 @@
|
||||
},
|
||||
"description": "EXPERIMENTAL. Params sent with a request_user_input event.",
|
||||
"properties": {
|
||||
"autoResolutionMs": {
|
||||
"default": null,
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"itemId": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
+804
-73
File diff suppressed because it is too large
Load Diff
+765
-72
File diff suppressed because it is too large
Load Diff
@@ -30,6 +30,10 @@
|
||||
"description": "Opt into receiving experimental API methods and fields.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"mcpServerOpenaiFormElicitation": {
|
||||
"description": "Allow downstream MCP servers to request OpenAI extended form elicitations.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"optOutNotificationMethods": {
|
||||
"description": "Exact notification method names that should be suppressed for this connection (for example `thread/started`).",
|
||||
"items": {
|
||||
|
||||
@@ -38,6 +38,13 @@
|
||||
"personalAccessToken"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Amazon Bedrock bearer token managed by Codex.",
|
||||
"enum": [
|
||||
"bedrockApiKey"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -133,6 +133,26 @@
|
||||
},
|
||||
"AppsDefaultConfig": {
|
||||
"properties": {
|
||||
"approvals_reviewer": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ApprovalsReviewer"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"default_tools_approval_mode": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AppToolApproval"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"destructive_enabled": {
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
|
||||
+6
@@ -85,6 +85,12 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"allowRemoteControl": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"allowedApprovalPolicies": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/AskForApproval"
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
"idempotencyKey": {
|
||||
"description": "Identifies one logical reset attempt. A UUID is recommended; reuse the same value when retrying that attempt.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"idempotencyKey"
|
||||
],
|
||||
"title": "ConsumeAccountRateLimitResetCreditParams",
|
||||
"type": "object"
|
||||
}
|
||||
Generated
+47
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": {
|
||||
"ConsumeAccountRateLimitResetCreditOutcome": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "A reset credit was consumed and the eligible rate-limit windows were reset.",
|
||||
"enum": [
|
||||
"reset"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "No current rate-limit window is eligible for a reset.",
|
||||
"enum": [
|
||||
"nothingToReset"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "The account has no earned reset credits available.",
|
||||
"enum": [
|
||||
"noCredit"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "The same idempotency key already completed a reset successfully.",
|
||||
"enum": [
|
||||
"alreadyRedeemed"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"outcome": {
|
||||
"$ref": "#/definitions/ConsumeAccountRateLimitResetCreditOutcome"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"outcome"
|
||||
],
|
||||
"title": "ConsumeAccountRateLimitResetCreditResponse",
|
||||
"type": "object"
|
||||
}
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
]
|
||||
},
|
||||
"includeHome": {
|
||||
"description": "If true, include detection under the user's home (~/.claude, ~/.codex, etc.).",
|
||||
"description": "If true, include detection under the user's home directory.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
|
||||
Generated
+122
@@ -1,5 +1,127 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": {
|
||||
"ExternalAgentConfigImportItemTypeFailure": {
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"errorType": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"failureStage": {
|
||||
"type": "string"
|
||||
},
|
||||
"itemType": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"failureStage",
|
||||
"itemType",
|
||||
"message"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExternalAgentConfigImportItemTypeSuccess": {
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"itemType": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
|
||||
},
|
||||
"source": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"target": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"itemType"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExternalAgentConfigImportTypeResult": {
|
||||
"properties": {
|
||||
"failures": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigImportItemTypeFailure"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"itemType": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
|
||||
},
|
||||
"successes": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigImportItemTypeSuccess"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"failures",
|
||||
"itemType",
|
||||
"successes"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExternalAgentConfigMigrationItemType": {
|
||||
"enum": [
|
||||
"AGENTS_MD",
|
||||
"CONFIG",
|
||||
"SKILLS",
|
||||
"PLUGINS",
|
||||
"MCP_SERVER_CONFIG",
|
||||
"SUBAGENTS",
|
||||
"HOOKS",
|
||||
"COMMANDS",
|
||||
"SESSIONS"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"importId": {
|
||||
"type": "string"
|
||||
},
|
||||
"itemTypeResults": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigImportTypeResult"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"importId",
|
||||
"itemTypeResults"
|
||||
],
|
||||
"title": "ExternalAgentConfigImportCompletedNotification",
|
||||
"type": "object"
|
||||
}
|
||||
Generated
+128
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": {
|
||||
"ExternalAgentConfigImportHistory": {
|
||||
"properties": {
|
||||
"completedAtMs": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
},
|
||||
"failures": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigImportItemTypeFailure"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"importId": {
|
||||
"type": "string"
|
||||
},
|
||||
"successes": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigImportItemTypeSuccess"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"completedAtMs",
|
||||
"failures",
|
||||
"importId",
|
||||
"successes"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExternalAgentConfigImportItemTypeFailure": {
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"errorType": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"failureStage": {
|
||||
"type": "string"
|
||||
},
|
||||
"itemType": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"failureStage",
|
||||
"itemType",
|
||||
"message"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExternalAgentConfigImportItemTypeSuccess": {
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"itemType": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
|
||||
},
|
||||
"source": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"target": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"itemType"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExternalAgentConfigMigrationItemType": {
|
||||
"enum": [
|
||||
"AGENTS_MD",
|
||||
"CONFIG",
|
||||
"SKILLS",
|
||||
"PLUGINS",
|
||||
"MCP_SERVER_CONFIG",
|
||||
"SUBAGENTS",
|
||||
"HOOKS",
|
||||
"COMMANDS",
|
||||
"SESSIONS"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"data": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigImportHistory"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data"
|
||||
],
|
||||
"title": "ExternalAgentConfigImportHistoriesReadResponse",
|
||||
"type": "object"
|
||||
}
|
||||
+7
@@ -184,6 +184,13 @@
|
||||
"$ref": "#/definitions/ExternalAgentConfigMigrationItem"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"source": {
|
||||
"description": "Source product that produced the migration items. Missing means unspecified.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
Generated
+127
@@ -0,0 +1,127 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": {
|
||||
"ExternalAgentConfigImportItemTypeFailure": {
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"errorType": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"failureStage": {
|
||||
"type": "string"
|
||||
},
|
||||
"itemType": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"failureStage",
|
||||
"itemType",
|
||||
"message"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExternalAgentConfigImportItemTypeSuccess": {
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"itemType": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
|
||||
},
|
||||
"source": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"target": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"itemType"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExternalAgentConfigImportTypeResult": {
|
||||
"properties": {
|
||||
"failures": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigImportItemTypeFailure"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"itemType": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
|
||||
},
|
||||
"successes": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigImportItemTypeSuccess"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"failures",
|
||||
"itemType",
|
||||
"successes"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExternalAgentConfigMigrationItemType": {
|
||||
"enum": [
|
||||
"AGENTS_MD",
|
||||
"CONFIG",
|
||||
"SKILLS",
|
||||
"PLUGINS",
|
||||
"MCP_SERVER_CONFIG",
|
||||
"SUBAGENTS",
|
||||
"HOOKS",
|
||||
"COMMANDS",
|
||||
"SESSIONS"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"importId": {
|
||||
"type": "string"
|
||||
},
|
||||
"itemTypeResults": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ExternalAgentConfigImportTypeResult"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"importId",
|
||||
"itemTypeResults"
|
||||
],
|
||||
"title": "ExternalAgentConfigImportProgressNotification",
|
||||
"type": "object"
|
||||
}
|
||||
+8
@@ -1,5 +1,13 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
"importId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"importId"
|
||||
],
|
||||
"title": "ExternalAgentConfigImportResponse",
|
||||
"type": "object"
|
||||
}
|
||||
+22
@@ -49,6 +49,18 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"RateLimitResetCreditsSummary": {
|
||||
"properties": {
|
||||
"availableCount": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"availableCount"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"RateLimitSnapshot": {
|
||||
"properties": {
|
||||
"credits": {
|
||||
@@ -179,6 +191,16 @@
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"rateLimitResetCredits": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/RateLimitResetCreditsSummary"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rateLimits": {
|
||||
"allOf": [
|
||||
{
|
||||
|
||||
@@ -45,6 +45,14 @@
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"credentialSource": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AmazonBedrockCredentialSource"
|
||||
}
|
||||
],
|
||||
"default": "awsManaged"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"amazonBedrock"
|
||||
@@ -61,6 +69,13 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"AmazonBedrockCredentialSource": {
|
||||
"enum": [
|
||||
"codexManaged",
|
||||
"awsManaged"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"PlanType": {
|
||||
"enum": [
|
||||
"free",
|
||||
|
||||
+30
-1
@@ -294,6 +294,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpToolCallError": {
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -687,7 +690,7 @@
|
||||
"cwd": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
@@ -1087,6 +1090,32 @@
|
||||
"title": "ImageViewThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"durationMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"sleep"
|
||||
],
|
||||
"title": "SleepThreadItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"durationMs",
|
||||
"id",
|
||||
"type"
|
||||
],
|
||||
"title": "SleepThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
Generated
+6
-3
@@ -27,7 +27,7 @@
|
||||
"read": {
|
||||
"description": "This will be removed in favor of `entries`.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
@@ -37,7 +37,7 @@
|
||||
"write": {
|
||||
"description": "This will be removed in favor of `entries`.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
@@ -78,7 +78,7 @@
|
||||
{
|
||||
"properties": {
|
||||
"path": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -533,6 +533,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"NetworkApprovalProtocol": {
|
||||
"enum": [
|
||||
"http",
|
||||
|
||||
Generated
+6
-3
@@ -27,7 +27,7 @@
|
||||
"read": {
|
||||
"description": "This will be removed in favor of `entries`.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
@@ -37,7 +37,7 @@
|
||||
"write": {
|
||||
"description": "This will be removed in favor of `entries`.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
@@ -71,7 +71,7 @@
|
||||
{
|
||||
"properties": {
|
||||
"path": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -526,6 +526,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"NetworkApprovalProtocol": {
|
||||
"enum": [
|
||||
"http",
|
||||
|
||||
@@ -294,6 +294,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpToolCallError": {
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -687,7 +690,7 @@
|
||||
"cwd": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
@@ -1087,6 +1090,32 @@
|
||||
"title": "ImageViewThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"durationMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"sleep"
|
||||
],
|
||||
"title": "SleepThreadItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"durationMs",
|
||||
"id",
|
||||
"type"
|
||||
],
|
||||
"title": "SleepThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
"AppSummary": {
|
||||
"description": "EXPERIMENTAL - app metadata summary for plugin responses.",
|
||||
"properties": {
|
||||
"category": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"description": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
"local",
|
||||
"vertical",
|
||||
"workspace-directory",
|
||||
"shared-with-me"
|
||||
"shared-with-me",
|
||||
"created-by-me-remote"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
"AppSummary": {
|
||||
"description": "EXPERIMENTAL - app metadata summary for plugin responses.",
|
||||
"properties": {
|
||||
"category": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"description": {
|
||||
"type": [
|
||||
"string",
|
||||
@@ -41,6 +47,12 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"category": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"description": {
|
||||
"type": [
|
||||
"string",
|
||||
@@ -180,6 +192,12 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"shareUrl": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"skills": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/SkillSummary"
|
||||
|
||||
+187
-40
@@ -3,6 +3,26 @@
|
||||
"definitions": {
|
||||
"AgentMessageInputContent": {
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"input_text"
|
||||
],
|
||||
"title": "InputTextAgentMessageInputContentType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"text",
|
||||
"type"
|
||||
],
|
||||
"title": "InputTextAgentMessageInputContent",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"encrypted_content": {
|
||||
@@ -357,12 +377,15 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"phase": {
|
||||
"anyOf": [
|
||||
@@ -404,6 +427,16 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"recipient": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -442,6 +475,16 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ReasoningItemReasoningSummary"
|
||||
@@ -475,13 +518,15 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"description": "Legacy id field retained for compatibility with older payloads.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/definitions/LocalShellStatus"
|
||||
@@ -510,12 +555,15 @@
|
||||
"call_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
@@ -555,12 +603,15 @@
|
||||
"execution": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"type": [
|
||||
@@ -589,6 +640,16 @@
|
||||
"call_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"output": {
|
||||
"$ref": "#/definitions/FunctionCallOutputBody"
|
||||
},
|
||||
@@ -613,16 +674,19 @@
|
||||
"call_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
},
|
||||
"input": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -654,6 +718,16 @@
|
||||
"call_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": [
|
||||
"string",
|
||||
@@ -690,6 +764,16 @@
|
||||
"execution": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -726,12 +810,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"type": [
|
||||
@@ -756,7 +843,21 @@
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
"description": "Existing provider ID retained on serialized history for compatibility.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"result": {
|
||||
"type": "string"
|
||||
@@ -779,7 +880,6 @@
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"result",
|
||||
"status",
|
||||
"type"
|
||||
@@ -792,6 +892,16 @@
|
||||
"encrypted_content": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"compaction"
|
||||
@@ -809,6 +919,16 @@
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"compaction_trigger"
|
||||
@@ -831,6 +951,16 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"context_compaction"
|
||||
@@ -863,6 +993,23 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ResponseItemMetadata": {
|
||||
"properties": {
|
||||
"source_call_id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"turn_id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ResponsesApiWebSearchAction": {
|
||||
"oneOf": [
|
||||
{
|
||||
|
||||
@@ -431,6 +431,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpToolCallError": {
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -831,7 +834,7 @@
|
||||
"cwd": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
@@ -1231,6 +1234,32 @@
|
||||
"title": "ImageViewThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"durationMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"sleep"
|
||||
],
|
||||
"title": "SleepThreadItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"durationMs",
|
||||
"id",
|
||||
"type"
|
||||
],
|
||||
"title": "SleepThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
@@ -536,6 +536,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpToolCallError": {
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -1055,6 +1058,14 @@
|
||||
"description": "Usually the first user message in the thread, if available.",
|
||||
"type": "string"
|
||||
},
|
||||
"recencyAt": {
|
||||
"description": "Unix timestamp (in seconds) used for thread recency ordering.",
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"sessionId": {
|
||||
"description": "Session id shared by threads that belong to the same session tree.",
|
||||
"type": "string"
|
||||
@@ -1315,7 +1326,7 @@
|
||||
"cwd": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
@@ -1715,6 +1726,32 @@
|
||||
"title": "ImageViewThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"durationMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"sleep"
|
||||
],
|
||||
"title": "SleepThreadItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"durationMs",
|
||||
"id",
|
||||
"type"
|
||||
],
|
||||
"title": "SleepThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
"ThreadSortKey": {
|
||||
"enum": [
|
||||
"created_at",
|
||||
"updated_at"
|
||||
"updated_at",
|
||||
"recency_at"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -457,6 +457,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpToolCallError": {
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -870,6 +873,14 @@
|
||||
"description": "Usually the first user message in the thread, if available.",
|
||||
"type": "string"
|
||||
},
|
||||
"recencyAt": {
|
||||
"description": "Unix timestamp (in seconds) used for thread recency ordering.",
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"sessionId": {
|
||||
"description": "Session id shared by threads that belong to the same session tree.",
|
||||
"type": "string"
|
||||
@@ -1130,7 +1141,7 @@
|
||||
"cwd": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
@@ -1530,6 +1541,32 @@
|
||||
"title": "ImageViewThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"durationMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"sleep"
|
||||
],
|
||||
"title": "SleepThreadItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"durationMs",
|
||||
"id",
|
||||
"type"
|
||||
],
|
||||
"title": "SleepThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
+38
-1
@@ -457,6 +457,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpToolCallError": {
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -870,6 +873,14 @@
|
||||
"description": "Usually the first user message in the thread, if available.",
|
||||
"type": "string"
|
||||
},
|
||||
"recencyAt": {
|
||||
"description": "Unix timestamp (in seconds) used for thread recency ordering.",
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"sessionId": {
|
||||
"description": "Session id shared by threads that belong to the same session tree.",
|
||||
"type": "string"
|
||||
@@ -1130,7 +1141,7 @@
|
||||
"cwd": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
@@ -1530,6 +1541,32 @@
|
||||
"title": "ImageViewThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"durationMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"sleep"
|
||||
],
|
||||
"title": "SleepThreadItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"durationMs",
|
||||
"id",
|
||||
"type"
|
||||
],
|
||||
"title": "SleepThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
@@ -457,6 +457,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpToolCallError": {
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -870,6 +873,14 @@
|
||||
"description": "Usually the first user message in the thread, if available.",
|
||||
"type": "string"
|
||||
},
|
||||
"recencyAt": {
|
||||
"description": "Unix timestamp (in seconds) used for thread recency ordering.",
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"sessionId": {
|
||||
"description": "Session id shared by threads that belong to the same session tree.",
|
||||
"type": "string"
|
||||
@@ -1130,7 +1141,7 @@
|
||||
"cwd": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
@@ -1530,6 +1541,32 @@
|
||||
"title": "ImageViewThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"durationMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"sleep"
|
||||
],
|
||||
"title": "SleepThreadItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"durationMs",
|
||||
"id",
|
||||
"type"
|
||||
],
|
||||
"title": "SleepThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
+187
-40
@@ -7,6 +7,26 @@
|
||||
},
|
||||
"AgentMessageInputContent": {
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"input_text"
|
||||
],
|
||||
"title": "InputTextAgentMessageInputContentType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"text",
|
||||
"type"
|
||||
],
|
||||
"title": "InputTextAgentMessageInputContent",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"encrypted_content": {
|
||||
@@ -428,12 +448,15 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"phase": {
|
||||
"anyOf": [
|
||||
@@ -475,6 +498,16 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"recipient": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -513,6 +546,16 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ReasoningItemReasoningSummary"
|
||||
@@ -546,13 +589,15 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"description": "Legacy id field retained for compatibility with older payloads.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/definitions/LocalShellStatus"
|
||||
@@ -581,12 +626,15 @@
|
||||
"call_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
@@ -626,12 +674,15 @@
|
||||
"execution": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"type": [
|
||||
@@ -660,6 +711,16 @@
|
||||
"call_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"output": {
|
||||
"$ref": "#/definitions/FunctionCallOutputBody"
|
||||
},
|
||||
@@ -684,16 +745,19 @@
|
||||
"call_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
},
|
||||
"input": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -725,6 +789,16 @@
|
||||
"call_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": [
|
||||
"string",
|
||||
@@ -761,6 +835,16 @@
|
||||
"execution": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -797,12 +881,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"writeOnly": true
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"type": [
|
||||
@@ -827,7 +914,21 @@
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
"description": "Existing provider ID retained on serialized history for compatibility.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"result": {
|
||||
"type": "string"
|
||||
@@ -850,7 +951,6 @@
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"result",
|
||||
"status",
|
||||
"type"
|
||||
@@ -863,6 +963,16 @@
|
||||
"encrypted_content": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"compaction"
|
||||
@@ -880,6 +990,16 @@
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"compaction_trigger"
|
||||
@@ -902,6 +1022,16 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"context_compaction"
|
||||
@@ -934,6 +1064,23 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ResponseItemMetadata": {
|
||||
"properties": {
|
||||
"source_call_id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"turn_id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ResponsesApiWebSearchAction": {
|
||||
"oneOf": [
|
||||
{
|
||||
|
||||
@@ -536,6 +536,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpToolCallError": {
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -1055,6 +1058,14 @@
|
||||
"description": "Usually the first user message in the thread, if available.",
|
||||
"type": "string"
|
||||
},
|
||||
"recencyAt": {
|
||||
"description": "Unix timestamp (in seconds) used for thread recency ordering.",
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"sessionId": {
|
||||
"description": "Session id shared by threads that belong to the same session tree.",
|
||||
"type": "string"
|
||||
@@ -1315,7 +1326,7 @@
|
||||
"cwd": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
@@ -1715,6 +1726,32 @@
|
||||
"title": "ImageViewThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"durationMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"sleep"
|
||||
],
|
||||
"title": "SleepThreadItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"durationMs",
|
||||
"id",
|
||||
"type"
|
||||
],
|
||||
"title": "SleepThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
@@ -457,6 +457,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpToolCallError": {
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -870,6 +873,14 @@
|
||||
"description": "Usually the first user message in the thread, if available.",
|
||||
"type": "string"
|
||||
},
|
||||
"recencyAt": {
|
||||
"description": "Unix timestamp (in seconds) used for thread recency ordering.",
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"sessionId": {
|
||||
"description": "Session id shared by threads that belong to the same session tree.",
|
||||
"type": "string"
|
||||
@@ -1130,7 +1141,7 @@
|
||||
"cwd": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
@@ -1530,6 +1541,32 @@
|
||||
"title": "ImageViewThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"durationMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"sleep"
|
||||
],
|
||||
"title": "SleepThreadItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"durationMs",
|
||||
"id",
|
||||
"type"
|
||||
],
|
||||
"title": "SleepThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
+99
-25
@@ -94,31 +94,105 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"DynamicToolSpec": {
|
||||
"properties": {
|
||||
"deferLoading": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"inputSchema": true,
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"namespace": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
"DynamicToolNamespaceTool": {
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"deferLoading": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"inputSchema": true,
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"function"
|
||||
],
|
||||
"title": "FunctionDynamicToolNamespaceToolType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"description",
|
||||
"inputSchema",
|
||||
"name",
|
||||
"type"
|
||||
],
|
||||
"title": "FunctionDynamicToolNamespaceTool",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"description",
|
||||
"inputSchema",
|
||||
"name"
|
||||
],
|
||||
"type": "object"
|
||||
]
|
||||
},
|
||||
"DynamicToolSpec": {
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"deferLoading": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"inputSchema": true,
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"function"
|
||||
],
|
||||
"title": "FunctionDynamicToolSpecType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"description",
|
||||
"inputSchema",
|
||||
"name",
|
||||
"type"
|
||||
],
|
||||
"title": "FunctionDynamicToolSpec",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"tools": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/DynamicToolNamespaceTool"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"namespace"
|
||||
],
|
||||
"title": "NamespaceDynamicToolSpecType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"description",
|
||||
"name",
|
||||
"tools",
|
||||
"type"
|
||||
],
|
||||
"title": "NamespaceDynamicToolSpec",
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"Personality": {
|
||||
"enum": [
|
||||
@@ -171,7 +245,7 @@
|
||||
"TurnEnvironmentParams": {
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"environmentId": {
|
||||
"type": "string"
|
||||
|
||||
@@ -536,6 +536,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpToolCallError": {
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -1055,6 +1058,14 @@
|
||||
"description": "Usually the first user message in the thread, if available.",
|
||||
"type": "string"
|
||||
},
|
||||
"recencyAt": {
|
||||
"description": "Unix timestamp (in seconds) used for thread recency ordering.",
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"sessionId": {
|
||||
"description": "Session id shared by threads that belong to the same session tree.",
|
||||
"type": "string"
|
||||
@@ -1315,7 +1326,7 @@
|
||||
"cwd": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
@@ -1715,6 +1726,32 @@
|
||||
"title": "ImageViewThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"durationMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"sleep"
|
||||
],
|
||||
"title": "SleepThreadItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"durationMs",
|
||||
"id",
|
||||
"type"
|
||||
],
|
||||
"title": "SleepThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
+38
-1
@@ -457,6 +457,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpToolCallError": {
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -870,6 +873,14 @@
|
||||
"description": "Usually the first user message in the thread, if available.",
|
||||
"type": "string"
|
||||
},
|
||||
"recencyAt": {
|
||||
"description": "Unix timestamp (in seconds) used for thread recency ordering.",
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"sessionId": {
|
||||
"description": "Session id shared by threads that belong to the same session tree.",
|
||||
"type": "string"
|
||||
@@ -1130,7 +1141,7 @@
|
||||
"cwd": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
@@ -1530,6 +1541,32 @@
|
||||
"title": "ImageViewThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"durationMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"sleep"
|
||||
],
|
||||
"title": "SleepThreadItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"durationMs",
|
||||
"id",
|
||||
"type"
|
||||
],
|
||||
"title": "SleepThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
@@ -457,6 +457,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpToolCallError": {
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -870,6 +873,14 @@
|
||||
"description": "Usually the first user message in the thread, if available.",
|
||||
"type": "string"
|
||||
},
|
||||
"recencyAt": {
|
||||
"description": "Unix timestamp (in seconds) used for thread recency ordering.",
|
||||
"format": "int64",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"sessionId": {
|
||||
"description": "Session id shared by threads that belong to the same session tree.",
|
||||
"type": "string"
|
||||
@@ -1130,7 +1141,7 @@
|
||||
"cwd": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
@@ -1530,6 +1541,32 @@
|
||||
"title": "ImageViewThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"durationMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"sleep"
|
||||
],
|
||||
"title": "SleepThreadItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"durationMs",
|
||||
"id",
|
||||
"type"
|
||||
],
|
||||
"title": "SleepThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
+30
-1
@@ -431,6 +431,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpToolCallError": {
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -831,7 +834,7 @@
|
||||
"cwd": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
@@ -1231,6 +1234,32 @@
|
||||
"title": "ImageViewThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"durationMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"sleep"
|
||||
],
|
||||
"title": "SleepThreadItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"durationMs",
|
||||
"id",
|
||||
"type"
|
||||
],
|
||||
"title": "SleepThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
@@ -130,6 +130,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"ModeKind": {
|
||||
"description": "Initial collaboration mode to use when the TUI starts.",
|
||||
"enum": [
|
||||
@@ -331,7 +334,7 @@
|
||||
"TurnEnvironmentParams": {
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
},
|
||||
"environmentId": {
|
||||
"type": "string"
|
||||
|
||||
@@ -431,6 +431,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpToolCallError": {
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -831,7 +834,7 @@
|
||||
"cwd": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
@@ -1231,6 +1234,32 @@
|
||||
"title": "ImageViewThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"durationMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"sleep"
|
||||
],
|
||||
"title": "SleepThreadItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"durationMs",
|
||||
"id",
|
||||
"type"
|
||||
],
|
||||
"title": "SleepThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
@@ -431,6 +431,9 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"LegacyAppPathString": {
|
||||
"type": "string"
|
||||
},
|
||||
"McpToolCallError": {
|
||||
"properties": {
|
||||
"message": {
|
||||
@@ -831,7 +834,7 @@
|
||||
"cwd": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
"$ref": "#/definitions/LegacyAppPathString"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
@@ -1231,6 +1234,32 @@
|
||||
"title": "ImageViewThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"durationMs": {
|
||||
"format": "uint64",
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"sleep"
|
||||
],
|
||||
"title": "SleepThreadItemType",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"durationMs",
|
||||
"id",
|
||||
"type"
|
||||
],
|
||||
"title": "SleepThreadItem",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"id": {
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type AgentMessageInputContent = { "type": "encrypted_content", encrypted_content: string, };
|
||||
export type AgentMessageInputContent = { "type": "input_text", text: string, } | { "type": "encrypted_content", encrypted_content: string, };
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type AmazonBedrockCredentialSource = "codexManaged" | "awsManaged";
|
||||
+1
-1
@@ -5,4 +5,4 @@
|
||||
/**
|
||||
* Authentication mode for OpenAI-backed providers.
|
||||
*/
|
||||
export type AuthMode = "apikey" | "chatgpt" | "chatgptAuthTokens" | "agentIdentity" | "personalAccessToken";
|
||||
export type AuthMode = "apikey" | "chatgpt" | "chatgptAuthTokens" | "agentIdentity" | "personalAccessToken" | "bedrockApiKey";
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,5 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type ConversationTextRole = "user" | "developer" | "assistant";
|
||||
@@ -14,6 +14,10 @@ experimentalApi: boolean,
|
||||
* Opt into `attestation/generate` requests for upstream `x-oai-attestation`.
|
||||
*/
|
||||
requestAttestation: boolean,
|
||||
/**
|
||||
* Allow downstream MCP servers to request OpenAI extended form elicitations.
|
||||
*/
|
||||
mcpServerOpenaiFormElicitation?: boolean,
|
||||
/**
|
||||
* Exact notification method names that should be suppressed for this
|
||||
* connection (for example `thread/started`).
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* A UTF-8 path for preserving raw path compatibility at the app-server API
|
||||
* boundary while Codex migrates to [`PathUri`].
|
||||
*
|
||||
* Supports storing arbitrary strings read from the API and converting to and
|
||||
* from [`PathUri`] using an explicitly selected native path convention.
|
||||
*
|
||||
* When converting from [`PathUri`], "native" refers to the supplied
|
||||
* [`PathConvention`], which may be foreign to the operating system running
|
||||
* this process. The inner string is private so path-producing code must convert
|
||||
* from [`AbsolutePathBuf`] or use [`Self::from_path_uri`] instead of bypassing
|
||||
* the intended conversion boundary. Non-UTF-8 paths are converted to UTF-8
|
||||
* lossily because this API value is serialized as a JSON string.
|
||||
*
|
||||
* Deserialization accepts any UTF-8 string without interpreting or validating
|
||||
* it. That unrestricted construction path is intentionally available only to
|
||||
* serde: Codex-internal code cannot construct this type directly from a raw
|
||||
* `String` and is instead encouraged to convert through [`PathUri`] or
|
||||
* [`AbsolutePathBuf`]. Relative path text remains valid until an operation
|
||||
* such as [`Self::to_path_uri`] requires an absolute path.
|
||||
*/
|
||||
export type LegacyAppPathString = string;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type RealtimeConversationArchitecture = "realtimeapi" | "avas";
|
||||
@@ -9,10 +9,15 @@ import type { LocalShellStatus } from "./LocalShellStatus";
|
||||
import type { MessagePhase } from "./MessagePhase";
|
||||
import type { ReasoningItemContent } from "./ReasoningItemContent";
|
||||
import type { ReasoningItemReasoningSummary } from "./ReasoningItemReasoningSummary";
|
||||
import type { ResponseItemMetadata } from "./ResponseItemMetadata";
|
||||
import type { WebSearchAction } from "./WebSearchAction";
|
||||
|
||||
export type ResponseItem = { "type": "message", role: string, content: Array<ContentItem>, phase?: MessagePhase, } | { "type": "agent_message", author: string, recipient: string, content: Array<AgentMessageInputContent>, } | { "type": "reasoning", summary: Array<ReasoningItemReasoningSummary>, content?: Array<ReasoningItemContent>, encrypted_content: string | null, } | { "type": "local_shell_call",
|
||||
export type ResponseItem = { "type": "message", role: string, content: Array<ContentItem>, phase?: MessagePhase, metadata?: ResponseItemMetadata, } | { "type": "agent_message", author: string, recipient: string, content: Array<AgentMessageInputContent>, metadata?: ResponseItemMetadata, } | { "type": "reasoning", summary: Array<ReasoningItemReasoningSummary>, content?: Array<ReasoningItemContent>, encrypted_content: string | null, metadata?: ResponseItemMetadata, } | { "type": "local_shell_call",
|
||||
/**
|
||||
* Set when using the Responses API.
|
||||
*/
|
||||
call_id: string | null, status: LocalShellStatus, action: LocalShellAction, } | { "type": "function_call", name: string, namespace?: string, arguments: string, call_id: string, } | { "type": "tool_search_call", call_id: string | null, status?: string, execution: string, arguments: unknown, } | { "type": "function_call_output", call_id: string, output: FunctionCallOutputBody, } | { "type": "custom_tool_call", status?: string, call_id: string, name: string, input: string, } | { "type": "custom_tool_call_output", call_id: string, name?: string, output: FunctionCallOutputBody, } | { "type": "tool_search_output", call_id: string | null, status: string, execution: string, tools: unknown[], } | { "type": "web_search_call", status?: string, action?: WebSearchAction, } | { "type": "image_generation_call", id: string, status: string, revised_prompt?: string, result: string, } | { "type": "compaction", encrypted_content: string, } | { "type": "compaction_trigger" } | { "type": "context_compaction", encrypted_content?: string, } | { "type": "other" };
|
||||
call_id: string | null, status: LocalShellStatus, action: LocalShellAction, metadata?: ResponseItemMetadata, } | { "type": "function_call", name: string, namespace?: string, arguments: string, call_id: string, metadata?: ResponseItemMetadata, } | { "type": "tool_search_call", call_id: string | null, status?: string, execution: string, arguments: unknown, metadata?: ResponseItemMetadata, } | { "type": "function_call_output", call_id: string, output: FunctionCallOutputBody, metadata?: ResponseItemMetadata, } | { "type": "custom_tool_call", status?: string, call_id: string, name: string, input: string, metadata?: ResponseItemMetadata, } | { "type": "custom_tool_call_output", call_id: string, name?: string, output: FunctionCallOutputBody, metadata?: ResponseItemMetadata, } | { "type": "tool_search_output", call_id: string | null, status: string, execution: string, tools: unknown[], metadata?: ResponseItemMetadata, } | { "type": "web_search_call", status?: string, action?: WebSearchAction, metadata?: ResponseItemMetadata, } | { "type": "image_generation_call",
|
||||
/**
|
||||
* Existing provider ID retained on serialized history for compatibility.
|
||||
*/
|
||||
id?: string, status: string, revised_prompt?: string, result: string, metadata?: ResponseItemMetadata, } | { "type": "compaction", encrypted_content: string, metadata?: ResponseItemMetadata, } | { "type": "compaction_trigger", metadata?: ResponseItemMetadata, } | { "type": "context_compaction", encrypted_content?: string, metadata?: ResponseItemMetadata, } | { "type": "other" };
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type ResponseItemMetadata = { turn_id?: string, source_call_id?: string, };
|
||||
File diff suppressed because one or more lines are too long
@@ -16,4 +16,4 @@ import type { ToolRequestUserInputParams } from "./v2/ToolRequestUserInputParams
|
||||
/**
|
||||
* Request initiated from the server and sent to the client.
|
||||
*/
|
||||
export type ServerRequest = { "method": "item/commandExecution/requestApproval", id: RequestId, params: CommandExecutionRequestApprovalParams, } | { "method": "item/fileChange/requestApproval", id: RequestId, params: FileChangeRequestApprovalParams, } | { "method": "item/tool/requestUserInput", id: RequestId, params: ToolRequestUserInputParams, } | { "method": "mcpServer/elicitation/request", id: RequestId, params: McpServerElicitationRequestParams, } | { "method": "item/permissions/requestApproval", id: RequestId, params: PermissionsRequestApprovalParams, } | { "method": "item/tool/call", id: RequestId, params: DynamicToolCallParams, } | { "method": "account/chatgptAuthTokens/refresh", id: RequestId, params: ChatgptAuthTokensRefreshParams, } | { "method": "attestation/generate", id: RequestId, params: AttestationGenerateParams, } | { "method": "applyPatchApproval", id: RequestId, params: ApplyPatchApprovalParams, } | { "method": "execCommandApproval", id: RequestId, params: ExecCommandApprovalParams, };
|
||||
export type ServerRequest ={ "method": "item/commandExecution/requestApproval", id: RequestId, params: CommandExecutionRequestApprovalParams, } | { "method": "item/fileChange/requestApproval", id: RequestId, params: FileChangeRequestApprovalParams, } | { "method": "item/tool/requestUserInput", id: RequestId, params: ToolRequestUserInputParams, } | { "method": "mcpServer/elicitation/request", id: RequestId, params: McpServerElicitationRequestParams, } | { "method": "item/permissions/requestApproval", id: RequestId, params: PermissionsRequestApprovalParams, } | { "method": "item/tool/call", id: RequestId, params: DynamicToolCallParams, } | { "method": "account/chatgptAuthTokens/refresh", id: RequestId, params: ChatgptAuthTokensRefreshParams, } | { "method": "attestation/generate", id: RequestId, params: AttestationGenerateParams, } | { "method": "applyPatchApproval", id: RequestId, params: ApplyPatchApprovalParams, } | { "method": "execCommandApproval", id: RequestId, params: ExecCommandApprovalParams, };
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
export type { AbsolutePathBuf } from "./AbsolutePathBuf";
|
||||
export type { AgentMessageInputContent } from "./AgentMessageInputContent";
|
||||
export type { AgentPath } from "./AgentPath";
|
||||
export type { AmazonBedrockCredentialSource } from "./AmazonBedrockCredentialSource";
|
||||
export type { ApplyPatchApprovalParams } from "./ApplyPatchApprovalParams";
|
||||
export type { ApplyPatchApprovalResponse } from "./ApplyPatchApprovalResponse";
|
||||
export type { AuthMode } from "./AuthMode";
|
||||
@@ -14,6 +15,7 @@ export type { CollaborationMode } from "./CollaborationMode";
|
||||
export type { ContentItem } from "./ContentItem";
|
||||
export type { ConversationGitInfo } from "./ConversationGitInfo";
|
||||
export type { ConversationSummary } from "./ConversationSummary";
|
||||
export type { ConversationTextRole } from "./ConversationTextRole";
|
||||
export type { ExecCommandApprovalParams } from "./ExecCommandApprovalParams";
|
||||
export type { ExecCommandApprovalResponse } from "./ExecCommandApprovalResponse";
|
||||
export type { ExecPolicyAmendment } from "./ExecPolicyAmendment";
|
||||
@@ -40,6 +42,7 @@ export type { InitializeParams } from "./InitializeParams";
|
||||
export type { InitializeResponse } from "./InitializeResponse";
|
||||
export type { InputModality } from "./InputModality";
|
||||
export type { InternalSessionSource } from "./InternalSessionSource";
|
||||
export type { LegacyAppPathString } from "./LegacyAppPathString";
|
||||
export type { LocalShellAction } from "./LocalShellAction";
|
||||
export type { LocalShellExecAction } from "./LocalShellExecAction";
|
||||
export type { LocalShellStatus } from "./LocalShellStatus";
|
||||
@@ -51,6 +54,7 @@ export type { NetworkPolicyRuleAction } from "./NetworkPolicyRuleAction";
|
||||
export type { ParsedCommand } from "./ParsedCommand";
|
||||
export type { Personality } from "./Personality";
|
||||
export type { PlanType } from "./PlanType";
|
||||
export type { RealtimeConversationArchitecture } from "./RealtimeConversationArchitecture";
|
||||
export type { RealtimeConversationVersion } from "./RealtimeConversationVersion";
|
||||
export type { RealtimeOutputModality } from "./RealtimeOutputModality";
|
||||
export type { RealtimeVoice } from "./RealtimeVoice";
|
||||
@@ -64,6 +68,7 @@ export type { Resource } from "./Resource";
|
||||
export type { ResourceContent } from "./ResourceContent";
|
||||
export type { ResourceTemplate } from "./ResourceTemplate";
|
||||
export type { ResponseItem } from "./ResponseItem";
|
||||
export type { ResponseItemMetadata } from "./ResponseItemMetadata";
|
||||
export type { ReviewDecision } from "./ReviewDecision";
|
||||
export type { ServerNotification } from "./ServerNotification";
|
||||
export type { ServerRequest } from "./ServerRequest";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AmazonBedrockCredentialSource } from "../AmazonBedrockCredentialSource";
|
||||
import type { PlanType } from "../PlanType";
|
||||
|
||||
export type Account = { "type": "apiKey", } | { "type": "chatgpt", email: string, planType: PlanType, } | { "type": "amazonBedrock", };
|
||||
export type Account = { "type": "apiKey", } | { "type": "chatgpt", email: string, planType: PlanType, } | { "type": "amazonBedrock", credentialSource: AmazonBedrockCredentialSource, };
|
||||
|
||||
+3
-3
@@ -1,15 +1,15 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
|
||||
import type { LegacyAppPathString } from "../LegacyAppPathString";
|
||||
import type { FileSystemSandboxEntry } from "./FileSystemSandboxEntry";
|
||||
|
||||
export type AdditionalFileSystemPermissions = {
|
||||
/**
|
||||
* This will be removed in favor of `entries`.
|
||||
*/
|
||||
read: Array<AbsolutePathBuf> | null,
|
||||
read: Array<LegacyAppPathString> | null,
|
||||
/**
|
||||
* This will be removed in favor of `entries`.
|
||||
*/
|
||||
write: Array<AbsolutePathBuf> | null, globScanMaxDepth?: number, entries?: Array<FileSystemSandboxEntry>, };
|
||||
write: Array<LegacyAppPathString> | null, globScanMaxDepth?: number, entries?: Array<FileSystemSandboxEntry>, };
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
/**
|
||||
* EXPERIMENTAL - app metadata summary for plugin responses.
|
||||
*/
|
||||
export type AppSummary = { id: string, name: string, description: string | null, installUrl: string | null, };
|
||||
export type AppSummary = { id: string, name: string, description: string | null, installUrl: string | null, category: string | null, };
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AppTemplateUnavailableReason } from "./AppTemplateUnavailableReason";
|
||||
|
||||
export type AppTemplateSummary = { templateId: string, name: string, description: string | null, canonicalConnectorId: string | null, logoUrl: string | null, logoUrlDark: string | null, materializedAppIds: Array<string>, reason: AppTemplateUnavailableReason | null, };
|
||||
export type AppTemplateSummary = { templateId: string, name: string, description: string | null, category: string | null, canonicalConnectorId: string | null, logoUrl: string | null, logoUrlDark: string | null, materializedAppIds: Array<string>, reason: AppTemplateUnavailableReason | null, };
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AppToolApproval } from "./AppToolApproval";
|
||||
import type { ApprovalsReviewer } from "./ApprovalsReviewer";
|
||||
|
||||
export type AppsDefaultConfig = { enabled: boolean, destructive_enabled: boolean, open_world_enabled: boolean, };
|
||||
export type AppsDefaultConfig = { enabled: boolean, approvals_reviewer: ApprovalsReviewer | null, destructive_enabled: boolean, open_world_enabled: boolean, default_tools_approval_mode: AppToolApproval | null, };
|
||||
|
||||
Generated
+5
-2
@@ -1,7 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
|
||||
import type { LegacyAppPathString } from "../LegacyAppPathString";
|
||||
import type { CommandAction } from "./CommandAction";
|
||||
import type { ExecPolicyAmendment } from "./ExecPolicyAmendment";
|
||||
import type { NetworkApprovalContext } from "./NetworkApprovalContext";
|
||||
@@ -20,6 +20,9 @@ startedAtMs: number, /**
|
||||
* (a UUID) used to disambiguate routing.
|
||||
*/
|
||||
approvalId?: string | null, /**
|
||||
* Environment in which the command will run.
|
||||
*/
|
||||
environmentId: string | null, /**
|
||||
* Optional explanatory reason (e.g. request for network access).
|
||||
*/
|
||||
reason?: string | null, /**
|
||||
@@ -31,7 +34,7 @@ networkApprovalContext?: NetworkApprovalContext | null, /**
|
||||
command?: string | null, /**
|
||||
* The command's working directory.
|
||||
*/
|
||||
cwd?: AbsolutePathBuf | null, /**
|
||||
cwd?: LegacyAppPathString | null, /**
|
||||
* Best-effort parsed command actions for friendly display.
|
||||
*/
|
||||
commandActions?: Array<CommandAction> | null, /**
|
||||
|
||||
@@ -8,4 +8,4 @@ import type { ResidencyRequirement } from "./ResidencyRequirement";
|
||||
import type { SandboxMode } from "./SandboxMode";
|
||||
import type { WindowsSandboxSetupMode } from "./WindowsSandboxSetupMode";
|
||||
|
||||
export type ConfigRequirements = {allowedApprovalPolicies: Array<AskForApproval> | null, allowedSandboxModes: Array<SandboxMode> | null, allowedWindowsSandboxImplementations: Array<WindowsSandboxSetupMode> | null, allowedPermissionProfiles: { [key in string]?: boolean } | null, defaultPermissions: string | null, allowedWebSearchModes: Array<WebSearchMode> | null, allowManagedHooksOnly: boolean | null, allowAppshots: boolean | null, computerUse: ComputerUseRequirements | null, featureRequirements: { [key in string]?: boolean } | null, enforceResidency: ResidencyRequirement | null};
|
||||
export type ConfigRequirements = {allowedApprovalPolicies: Array<AskForApproval> | null, allowedSandboxModes: Array<SandboxMode> | null, allowedWindowsSandboxImplementations: Array<WindowsSandboxSetupMode> | null, allowedPermissionProfiles: { [key in string]?: boolean } | null, defaultPermissions: string | null, allowedWebSearchModes: Array<WebSearchMode> | null, allowManagedHooksOnly: boolean | null, allowAppshots: boolean | null, allowRemoteControl: boolean | null, computerUse: ComputerUseRequirements | null, featureRequirements: { [key in string]?: boolean } | null, enforceResidency: ResidencyRequirement | null};
|
||||
|
||||
Generated
+5
@@ -0,0 +1,5 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type ConsumeAccountRateLimitResetCreditOutcome = "reset" | "nothingToReset" | "noCredit" | "alreadyRedeemed";
|
||||
Generated
+10
@@ -0,0 +1,10 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type ConsumeAccountRateLimitResetCreditParams = {
|
||||
/**
|
||||
* Identifies one logical reset attempt. A UUID is recommended; reuse the same value when
|
||||
* retrying that attempt.
|
||||
*/
|
||||
idempotencyKey: string, };
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ConsumeAccountRateLimitResetCreditOutcome } from "./ConsumeAccountRateLimitResetCreditOutcome";
|
||||
|
||||
export type ConsumeAccountRateLimitResetCreditResponse = { outcome: ConsumeAccountRateLimitResetCreditOutcome, };
|
||||
@@ -0,0 +1,6 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { JsonValue } from "../serde_json/JsonValue";
|
||||
|
||||
export type DynamicToolFunctionSpec = { name: string, description: string, inputSchema: JsonValue, deferLoading?: boolean, };
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { DynamicToolNamespaceTool } from "./DynamicToolNamespaceTool";
|
||||
|
||||
export type DynamicToolNamespaceSpec = { name: string, description: string, tools: Array<DynamicToolNamespaceTool>, };
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { DynamicToolFunctionSpec } from "./DynamicToolFunctionSpec";
|
||||
|
||||
export type DynamicToolNamespaceTool = { "type": "function" } & DynamicToolFunctionSpec;
|
||||
@@ -1,6 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { JsonValue } from "../serde_json/JsonValue";
|
||||
import type { DynamicToolFunctionSpec } from "./DynamicToolFunctionSpec";
|
||||
import type { DynamicToolNamespaceSpec } from "./DynamicToolNamespaceSpec";
|
||||
|
||||
export type DynamicToolSpec = { namespace?: string, name: string, description: string, inputSchema: JsonValue, deferLoading?: boolean, };
|
||||
export type DynamicToolSpec = { "type": "function" } & DynamicToolFunctionSpec | { "type": "namespace" } & DynamicToolNamespaceSpec;
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
export type ExternalAgentConfigDetectParams = {
|
||||
/**
|
||||
* If true, include detection under the user's home (~/.claude, ~/.codex, etc.).
|
||||
* If true, include detection under the user's home directory.
|
||||
*/
|
||||
includeHome?: boolean,
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user