mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
89ac3ec27c
## Why
CCA is moving toward a split runtime where the orchestrator may not have
a filesystem, while executors can expose preinstalled plugins and
skills. A thread therefore needs to select capabilities without asking
app-server or core to interpret executor-owned paths through the
orchestrator's filesystem.
The longer-term model is broader than executor skills:
- A plugin is a bundle of skills, MCP servers, connectors/apps, and
hooks.
- A plugin root can be local, executor-owned, or hosted by a backend.
- Components inside one plugin can use different access and execution
mechanisms. A skill may be read from a filesystem or through backend
tools; an HTTP MCP server can run without an executor; a stdio MCP
server or hook needs an execution environment.
- Core should carry generic extension initialization data. The extension
that owns a component should discover it, expose it to the model, and
invoke it through the appropriate runtime.
This PR establishes that architecture through one complete vertical:
selecting a root on an executor, discovering the skills beneath it,
exposing those skills to the model, and reading an explicitly invoked
`SKILL.md` through the same executor.
## Contract
`thread/start` gains an experimental `selectedCapabilityRoots` field:
```json
{
"selectedCapabilityRoots": [
{
"id": "deploy-plugin@1",
"location": {
"type": "environment",
"environmentId": "workspace",
"path": "/opt/codex/plugins/deploy"
}
}
]
}
```
The root is intentionally not classified as a "plugin" or "skill" in the
API. It can point at a standalone skill, a directory containing several
skills, or a plugin containing skills and other components. This PR only
teaches the skills extension how to consume it; later extensions can
resolve MCP, connector, and hook components from the same selection.
The platform-supplied `id` is stable selection identity. The location
says which runtime owns the root and gives that runtime an opaque path.
App-server does not inspect or canonicalize the path.
## What changed
### Generic thread extension initialization
App-server converts selected roots into `ExtensionDataInit`. Core
carries that generic initialization value until the final thread ID is
known, then creates thread-scoped `ExtensionData` before lifecycle
contributors run.
This keeps `Session` and core independent of the capability-selection
contract. The initialization value is consumed during construction; it
is not retained as another long-lived `Session` field.
### Executor-backed skills
The skills extension now owns an `ExecutorSkillProvider` that:
- resolves the selected environment through `EnvironmentManager`
- discovers, canonicalizes, and reads skills through that environment's
`ExecutorFileSystem`
- contributes the bounded selected-skill catalog as stable developer
context
- reads an explicitly invoked skill body through the authority that
listed it
- warns when an environment or root is unavailable
- never falls back to the orchestrator filesystem for an executor-owned
root
Skill catalog and instruction fragments have hard byte bounds, which
also bound them below the 10K-token per-item context limit. If a
selected executor skill has the same name as a legacy local skill, the
executor selection owns that invocation and the local body is not
injected a second time.
Existing local and bundled skill loading remains in place. Omitting
`selectedCapabilityRoots` therefore preserves current local-only
behavior.
## Current semantics
- Only environment-owned locations are represented in this first
contract.
- Roots are resolved by the destination extension, not by app-server or
core.
- An unavailable executor or invalid root produces a warning and no
capabilities from that root; it does not trigger a local-filesystem
fallback.
- Selection applies to a newly started active thread.
- MCP servers, connectors, and hooks beneath a selected plugin root are
not activated yet.
- Selection is not yet persisted or inherited across resume, fork, or
subagent creation. Existing local capabilities continue to behave as
they do today in those flows.
## Planned vertical follow-ups
1. **Hosted HTTP MCP:** add an extension-backed HTTP MCP source that
works without an executor, then replace the special-purpose MCP plugins
loader with that implementation.
2. **Executor MCP:** register and execute stdio MCP servers through the
environment that owns the selected plugin root.
3. **Backend skills:** add a hosted skill source whose catalog and
bodies are accessed through extension tools rather than a filesystem.
4. **Connectors and hooks:** activate those components through their
owning extensions, using the same selected-root boundary and
component-specific runtime.
5. **Durable selection:** define the desired-selection lifecycle,
persist it, and make resume, fork, and subagent inheritance explicit
rather than accidental.
6. **Local convergence:** incrementally route existing local plugin,
skill, and MCP loading through the same extension model while preserving
current local behavior.
Each follow-up remains reviewable as an end-to-end capability. The
platform selects roots, generic thread extension data carries the
selection, and the owning extension resolves and operates its component.
## Verification
Coverage added for:
- app-server end-to-end discovery and explicit invocation of a skill
inside an executor-selected plugin root
- exclusive invocation when a selected executor skill collides with a
local skill name
- executor filesystem authority for discovery, canonicalization, and
reads
- thread extension initialization before lifecycle contributors run
- stable executor catalog context, explicit invocation, context
rebuilding, hidden skills, and preserved host/remote catalog behavior
Targeted protocol, core-skills, skills-extension, core lifecycle, and
app-server executor-skill tests were run during development.
1380 lines
50 KiB
Rust
1380 lines
50 KiB
Rust
use super::ActivePermissionProfile;
|
|
use super::ApprovalsReviewer;
|
|
use super::AskForApproval;
|
|
use super::SandboxMode;
|
|
use super::SandboxPolicy;
|
|
use super::Thread;
|
|
use super::ThreadItem;
|
|
use super::ThreadSource;
|
|
use super::Turn;
|
|
use super::TurnEnvironmentParams;
|
|
use super::TurnItemsView;
|
|
use super::shared::v2_enum_from_core;
|
|
use codex_experimental_api_macros::ExperimentalApi;
|
|
pub use codex_protocol::capabilities::CapabilityRootLocation;
|
|
pub use codex_protocol::capabilities::SelectedCapabilityRoot;
|
|
use codex_protocol::config_types::CollaborationMode;
|
|
use codex_protocol::config_types::Personality;
|
|
use codex_protocol::config_types::ReasoningSummary;
|
|
use codex_protocol::models::ResponseItem;
|
|
use codex_protocol::openai_models::ReasoningEffort;
|
|
use codex_protocol::protocol::ThreadGoalStatus as CoreThreadGoalStatus;
|
|
use codex_protocol::protocol::TokenUsage as CoreTokenUsage;
|
|
use codex_protocol::protocol::TokenUsageInfo as CoreTokenUsageInfo;
|
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
|
use schemars::JsonSchema;
|
|
use serde::Deserialize;
|
|
use serde::Serialize;
|
|
use serde_json::Value as JsonValue;
|
|
use std::collections::HashMap;
|
|
use std::path::PathBuf;
|
|
use ts_rs::TS;
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase", export_to = "v2/")]
|
|
pub enum ThreadStartSource {
|
|
Startup,
|
|
Clear,
|
|
}
|
|
|
|
#[derive(Serialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct DynamicToolSpec {
|
|
#[ts(optional)]
|
|
pub namespace: Option<String>,
|
|
pub name: String,
|
|
pub description: String,
|
|
pub input_schema: JsonValue,
|
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
pub defer_loading: bool,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
struct DynamicToolSpecDe {
|
|
namespace: Option<String>,
|
|
name: String,
|
|
description: String,
|
|
input_schema: JsonValue,
|
|
defer_loading: Option<bool>,
|
|
expose_to_context: Option<bool>,
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for DynamicToolSpec {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
let DynamicToolSpecDe {
|
|
namespace,
|
|
name,
|
|
description,
|
|
input_schema,
|
|
defer_loading,
|
|
expose_to_context,
|
|
} = DynamicToolSpecDe::deserialize(deserializer)?;
|
|
|
|
Ok(Self {
|
|
namespace,
|
|
name,
|
|
description,
|
|
input_schema,
|
|
defer_loading: defer_loading
|
|
.unwrap_or_else(|| expose_to_context.map(|visible| !visible).unwrap_or(false)),
|
|
})
|
|
}
|
|
}
|
|
|
|
// === Threads, Turns, and Items ===
|
|
// Thread APIs
|
|
#[derive(
|
|
Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS, ExperimentalApi,
|
|
)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadStartParams {
|
|
#[ts(optional = nullable)]
|
|
pub model: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub model_provider: Option<String>,
|
|
#[serde(
|
|
default,
|
|
deserialize_with = "crate::protocol::serde_helpers::deserialize_double_option",
|
|
serialize_with = "crate::protocol::serde_helpers::serialize_double_option",
|
|
skip_serializing_if = "Option::is_none"
|
|
)]
|
|
#[ts(optional = nullable)]
|
|
pub service_tier: Option<Option<String>>,
|
|
#[ts(optional = nullable)]
|
|
pub cwd: Option<String>,
|
|
/// Replace the thread's runtime workspace roots. Paths must be absolute.
|
|
#[experimental("thread/start.runtimeWorkspaceRoots")]
|
|
#[ts(optional = nullable)]
|
|
pub runtime_workspace_roots: Option<Vec<AbsolutePathBuf>>,
|
|
#[experimental(nested)]
|
|
#[ts(optional = nullable)]
|
|
pub approval_policy: Option<AskForApproval>,
|
|
/// Override where approval requests are routed for review on this thread
|
|
/// and subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub approvals_reviewer: Option<ApprovalsReviewer>,
|
|
#[ts(optional = nullable)]
|
|
pub sandbox: Option<SandboxMode>,
|
|
/// Named profile id for this thread. Cannot be combined with `sandbox`.
|
|
#[experimental("thread/start.permissions")]
|
|
#[ts(optional = nullable)]
|
|
pub permissions: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub config: Option<HashMap<String, JsonValue>>,
|
|
#[ts(optional = nullable)]
|
|
pub service_name: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub base_instructions: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub developer_instructions: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub personality: Option<Personality>,
|
|
#[ts(optional = nullable)]
|
|
pub ephemeral: Option<bool>,
|
|
#[ts(optional = nullable)]
|
|
pub session_start_source: Option<ThreadStartSource>,
|
|
/// Optional client-supplied analytics source classification for this thread.
|
|
#[ts(optional = nullable)]
|
|
pub thread_source: Option<ThreadSource>,
|
|
/// Optional sticky environments for this thread.
|
|
///
|
|
/// Omitted selects the default environment when environment access is
|
|
/// enabled. Empty disables environment access for turns that do not
|
|
/// provide a turn override. Non-empty selects the first environment as the
|
|
/// current turn environment.
|
|
#[experimental("thread/start.environments")]
|
|
#[ts(optional = nullable)]
|
|
pub environments: Option<Vec<TurnEnvironmentParams>>,
|
|
#[experimental("thread/start.dynamicTools")]
|
|
#[ts(optional = nullable)]
|
|
pub dynamic_tools: Option<Vec<DynamicToolSpec>>,
|
|
/// Capability roots selected for this thread by the hosting platform.
|
|
#[experimental("thread/start.selectedCapabilityRoots")]
|
|
#[ts(optional = nullable)]
|
|
pub selected_capability_roots: Option<Vec<SelectedCapabilityRoot>>,
|
|
/// Test-only experimental field used to validate experimental gating and
|
|
/// schema filtering behavior in a stable way.
|
|
#[experimental("thread/start.mockExperimentalField")]
|
|
#[ts(optional = nullable)]
|
|
pub mock_experimental_field: Option<String>,
|
|
/// If true, opt into emitting raw Responses API items on the event stream.
|
|
/// This is for internal use only (e.g. Codex Cloud).
|
|
#[experimental("thread/start.experimentalRawEvents")]
|
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
pub experimental_raw_events: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct MockExperimentalMethodParams {
|
|
/// Test-only payload field.
|
|
#[ts(optional = nullable)]
|
|
pub value: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct MockExperimentalMethodResponse {
|
|
/// Echoes the input `value`.
|
|
pub echoed: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadStartResponse {
|
|
pub thread: Thread,
|
|
pub model: String,
|
|
pub model_provider: String,
|
|
pub service_tier: Option<String>,
|
|
pub cwd: AbsolutePathBuf,
|
|
/// Thread-scoped runtime workspace roots used to materialize
|
|
/// `:workspace_roots`.
|
|
#[experimental("thread/start.runtimeWorkspaceRoots")]
|
|
#[serde(default)]
|
|
pub runtime_workspace_roots: Vec<AbsolutePathBuf>,
|
|
/// Instruction source files currently loaded for this thread.
|
|
#[serde(default)]
|
|
pub instruction_sources: Vec<AbsolutePathBuf>,
|
|
#[experimental(nested)]
|
|
pub approval_policy: AskForApproval,
|
|
/// Reviewer currently used for approval requests on this thread.
|
|
pub approvals_reviewer: ApprovalsReviewer,
|
|
/// Legacy sandbox policy retained for compatibility. Experimental clients
|
|
/// should prefer `activePermissionProfile` for profile provenance.
|
|
pub sandbox: SandboxPolicy,
|
|
/// Named or implicit built-in profile that produced the active
|
|
/// permissions, when known.
|
|
#[experimental("thread/start.activePermissionProfile")]
|
|
#[serde(default)]
|
|
pub active_permission_profile: Option<ActivePermissionProfile>,
|
|
pub reasoning_effort: Option<ReasoningEffort>,
|
|
}
|
|
|
|
#[derive(
|
|
Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS, ExperimentalApi,
|
|
)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadSettingsUpdateParams {
|
|
pub thread_id: String,
|
|
/// Override the working directory for subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub cwd: Option<PathBuf>,
|
|
/// Override the approval policy for subsequent turns.
|
|
#[experimental(nested)]
|
|
#[ts(optional = nullable)]
|
|
pub approval_policy: Option<AskForApproval>,
|
|
/// Override where approval requests are routed for subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub approvals_reviewer: Option<ApprovalsReviewer>,
|
|
/// Override the sandbox policy for subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub sandbox_policy: Option<SandboxPolicy>,
|
|
/// Select a named permissions profile id for subsequent turns. Cannot be
|
|
/// combined with `sandboxPolicy`.
|
|
#[experimental("thread/settings/update.permissions")]
|
|
#[ts(optional = nullable)]
|
|
pub permissions: Option<String>,
|
|
/// Override the model for subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub model: Option<String>,
|
|
/// Override the service tier for subsequent turns. `null` clears the
|
|
/// current service tier; omission leaves it unchanged.
|
|
#[serde(
|
|
default,
|
|
deserialize_with = "crate::protocol::serde_helpers::deserialize_double_option",
|
|
serialize_with = "crate::protocol::serde_helpers::serialize_double_option",
|
|
skip_serializing_if = "Option::is_none"
|
|
)]
|
|
#[ts(optional = nullable)]
|
|
pub service_tier: Option<Option<String>>,
|
|
/// Override the reasoning effort for subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub effort: Option<ReasoningEffort>,
|
|
/// Override the reasoning summary for subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub summary: Option<ReasoningSummary>,
|
|
/// EXPERIMENTAL - Set a pre-set collaboration mode for subsequent turns.
|
|
///
|
|
/// For `collaboration_mode.settings.developer_instructions`, `null` means
|
|
/// "use the built-in instructions for the selected mode".
|
|
#[experimental("thread/settings/update.collaborationMode")]
|
|
#[ts(optional = nullable)]
|
|
pub collaboration_mode: Option<CollaborationMode>,
|
|
/// Override the personality for subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub personality: Option<Personality>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadSettingsUpdateResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadSettings {
|
|
pub cwd: AbsolutePathBuf,
|
|
pub approval_policy: AskForApproval,
|
|
pub approvals_reviewer: ApprovalsReviewer,
|
|
pub sandbox_policy: SandboxPolicy,
|
|
pub active_permission_profile: Option<ActivePermissionProfile>,
|
|
pub model: String,
|
|
pub model_provider: String,
|
|
pub service_tier: Option<String>,
|
|
pub effort: Option<ReasoningEffort>,
|
|
pub summary: Option<ReasoningSummary>,
|
|
pub collaboration_mode: CollaborationMode,
|
|
pub personality: Option<Personality>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadSettingsUpdatedNotification {
|
|
pub thread_id: String,
|
|
pub thread_settings: ThreadSettings,
|
|
}
|
|
|
|
#[derive(
|
|
Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS, ExperimentalApi,
|
|
)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// There are three ways to resume a thread:
|
|
/// 1. By thread_id: load the thread from disk by thread_id and resume it.
|
|
/// 2. By history: instantiate the thread from memory and resume it.
|
|
/// 3. By path: load the thread from disk by path and resume it.
|
|
///
|
|
/// For non-running threads, the precedence is: history > non-empty path > thread_id.
|
|
/// If using history or a non-empty path for a non-running thread, the thread_id
|
|
/// param will be ignored.
|
|
///
|
|
/// If thread_id identifies a running thread, app-server rejoins that thread and
|
|
/// treats a non-empty path as a consistency check against the active rollout path.
|
|
/// Empty string path values are treated as absent.
|
|
///
|
|
/// Prefer using thread_id whenever possible.
|
|
pub struct ThreadResumeParams {
|
|
pub thread_id: String,
|
|
|
|
/// [UNSTABLE] FOR CODEX CLOUD - DO NOT USE.
|
|
/// If specified, the thread will be resumed with the provided history
|
|
/// instead of loaded from disk.
|
|
#[experimental("thread/resume.history")]
|
|
#[ts(optional = nullable)]
|
|
pub history: Option<Vec<ResponseItem>>,
|
|
|
|
/// [UNSTABLE] Specify the rollout path to resume from.
|
|
/// If specified for a non-running thread, the thread_id param will be ignored.
|
|
/// If thread_id identifies a running thread, the path must match the active
|
|
/// rollout path.
|
|
#[experimental("thread/resume.path")]
|
|
#[serde(
|
|
default,
|
|
deserialize_with = "crate::protocol::serde_helpers::deserialize_empty_path_as_none"
|
|
)]
|
|
#[ts(optional = nullable)]
|
|
pub path: Option<PathBuf>,
|
|
|
|
/// Configuration overrides for the resumed thread, if any.
|
|
#[ts(optional = nullable)]
|
|
pub model: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub model_provider: Option<String>,
|
|
#[serde(
|
|
default,
|
|
deserialize_with = "crate::protocol::serde_helpers::deserialize_double_option",
|
|
serialize_with = "crate::protocol::serde_helpers::serialize_double_option",
|
|
skip_serializing_if = "Option::is_none"
|
|
)]
|
|
#[ts(optional = nullable)]
|
|
pub service_tier: Option<Option<String>>,
|
|
#[ts(optional = nullable)]
|
|
pub cwd: Option<String>,
|
|
/// Replace the thread's runtime workspace roots. Paths must be absolute.
|
|
#[experimental("thread/resume.runtimeWorkspaceRoots")]
|
|
#[ts(optional = nullable)]
|
|
pub runtime_workspace_roots: Option<Vec<AbsolutePathBuf>>,
|
|
#[experimental(nested)]
|
|
#[ts(optional = nullable)]
|
|
pub approval_policy: Option<AskForApproval>,
|
|
/// Override where approval requests are routed for review on this thread
|
|
/// and subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub approvals_reviewer: Option<ApprovalsReviewer>,
|
|
#[ts(optional = nullable)]
|
|
pub sandbox: Option<SandboxMode>,
|
|
/// Named profile id for the resumed thread. Cannot be combined with
|
|
/// `sandbox`.
|
|
#[experimental("thread/resume.permissions")]
|
|
#[ts(optional = nullable)]
|
|
pub permissions: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub config: Option<HashMap<String, serde_json::Value>>,
|
|
#[ts(optional = nullable)]
|
|
pub base_instructions: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub developer_instructions: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub personality: Option<Personality>,
|
|
/// When true, return only thread metadata and live-resume state without
|
|
/// populating `thread.turns`. This is useful when the client plans to call
|
|
/// `thread/turns/list` immediately after resuming.
|
|
#[experimental("thread/resume.excludeTurns")]
|
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
pub exclude_turns: bool,
|
|
/// When present, include a `thread/turns/list` page in the resume response
|
|
/// so clients can bootstrap recent turns without a second request.
|
|
#[experimental("thread/resume.initialTurnsPage")]
|
|
#[ts(optional = nullable)]
|
|
pub initial_turns_page: Option<ThreadResumeInitialTurnsPageParams>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadResumeResponse {
|
|
pub thread: Thread,
|
|
pub model: String,
|
|
pub model_provider: String,
|
|
pub service_tier: Option<String>,
|
|
pub cwd: AbsolutePathBuf,
|
|
/// Thread-scoped runtime workspace roots used to materialize
|
|
/// `:workspace_roots`.
|
|
#[experimental("thread/resume.runtimeWorkspaceRoots")]
|
|
#[serde(default)]
|
|
pub runtime_workspace_roots: Vec<AbsolutePathBuf>,
|
|
/// Instruction source files currently loaded for this thread.
|
|
#[serde(default)]
|
|
pub instruction_sources: Vec<AbsolutePathBuf>,
|
|
#[experimental(nested)]
|
|
pub approval_policy: AskForApproval,
|
|
/// Reviewer currently used for approval requests on this thread.
|
|
pub approvals_reviewer: ApprovalsReviewer,
|
|
/// Legacy sandbox policy retained for compatibility. Experimental clients
|
|
/// should prefer `activePermissionProfile` for profile provenance.
|
|
pub sandbox: SandboxPolicy,
|
|
/// Named or implicit built-in profile that produced the active
|
|
/// permissions, when known.
|
|
#[experimental("thread/resume.activePermissionProfile")]
|
|
#[serde(default)]
|
|
pub active_permission_profile: Option<ActivePermissionProfile>,
|
|
pub reasoning_effort: Option<ReasoningEffort>,
|
|
/// `thread/turns/list` page returned when requested by `initialTurnsPage`.
|
|
#[experimental("thread/resume.initialTurnsPage")]
|
|
#[serde(default)]
|
|
pub initial_turns_page: Option<TurnsPage>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadResumeInitialTurnsPageParams {
|
|
/// Optional turn page size.
|
|
#[ts(optional = nullable)]
|
|
pub limit: Option<u32>,
|
|
/// Optional turn pagination direction; defaults to descending.
|
|
#[ts(optional = nullable)]
|
|
pub sort_direction: Option<SortDirection>,
|
|
/// How much item detail to include for each returned turn; defaults to summary.
|
|
#[ts(optional = nullable)]
|
|
pub items_view: Option<TurnItemsView>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TurnsPage {
|
|
pub data: Vec<Turn>,
|
|
pub next_cursor: Option<String>,
|
|
pub backwards_cursor: Option<String>,
|
|
}
|
|
|
|
impl From<ThreadTurnsListResponse> for TurnsPage {
|
|
fn from(response: ThreadTurnsListResponse) -> Self {
|
|
Self {
|
|
data: response.data,
|
|
next_cursor: response.next_cursor,
|
|
backwards_cursor: response.backwards_cursor,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(
|
|
Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS, ExperimentalApi,
|
|
)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// There are two ways to fork a thread:
|
|
/// 1. By thread_id: load the thread from disk by thread_id and fork it into a new thread.
|
|
/// 2. By path: load the thread from disk by path and fork it into a new thread.
|
|
///
|
|
/// If using a non-empty path, the thread_id param will be ignored.
|
|
/// Empty string path values are treated as absent.
|
|
///
|
|
/// Prefer using thread_id whenever possible.
|
|
pub struct ThreadForkParams {
|
|
pub thread_id: String,
|
|
|
|
/// [UNSTABLE] Specify the rollout path to fork from.
|
|
/// If specified, the thread_id param will be ignored.
|
|
#[experimental("thread/fork.path")]
|
|
#[serde(
|
|
default,
|
|
deserialize_with = "crate::protocol::serde_helpers::deserialize_empty_path_as_none"
|
|
)]
|
|
#[ts(optional = nullable)]
|
|
pub path: Option<PathBuf>,
|
|
|
|
/// Configuration overrides for the forked thread, if any.
|
|
#[ts(optional = nullable)]
|
|
pub model: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub model_provider: Option<String>,
|
|
#[serde(
|
|
default,
|
|
deserialize_with = "crate::protocol::serde_helpers::deserialize_double_option",
|
|
serialize_with = "crate::protocol::serde_helpers::serialize_double_option",
|
|
skip_serializing_if = "Option::is_none"
|
|
)]
|
|
#[ts(optional = nullable)]
|
|
pub service_tier: Option<Option<String>>,
|
|
#[ts(optional = nullable)]
|
|
pub cwd: Option<String>,
|
|
/// Replace the thread's runtime workspace roots. Paths must be absolute.
|
|
#[experimental("thread/fork.runtimeWorkspaceRoots")]
|
|
#[ts(optional = nullable)]
|
|
pub runtime_workspace_roots: Option<Vec<AbsolutePathBuf>>,
|
|
#[experimental(nested)]
|
|
#[ts(optional = nullable)]
|
|
pub approval_policy: Option<AskForApproval>,
|
|
/// Override where approval requests are routed for review on this thread
|
|
/// and subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub approvals_reviewer: Option<ApprovalsReviewer>,
|
|
#[ts(optional = nullable)]
|
|
pub sandbox: Option<SandboxMode>,
|
|
/// Named profile id for the forked thread. Cannot be combined with
|
|
/// `sandbox`.
|
|
#[experimental("thread/fork.permissions")]
|
|
#[ts(optional = nullable)]
|
|
pub permissions: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub config: Option<HashMap<String, serde_json::Value>>,
|
|
#[ts(optional = nullable)]
|
|
pub base_instructions: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub developer_instructions: Option<String>,
|
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
pub ephemeral: bool,
|
|
/// Optional client-supplied analytics source classification for this forked thread.
|
|
#[ts(optional = nullable)]
|
|
pub thread_source: Option<ThreadSource>,
|
|
/// When true, return only thread metadata and live fork state without
|
|
/// populating `thread.turns`. This is useful when the client plans to call
|
|
/// `thread/turns/list` immediately after forking.
|
|
#[experimental("thread/fork.excludeTurns")]
|
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
pub exclude_turns: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadForkResponse {
|
|
pub thread: Thread,
|
|
pub model: String,
|
|
pub model_provider: String,
|
|
pub service_tier: Option<String>,
|
|
pub cwd: AbsolutePathBuf,
|
|
/// Thread-scoped runtime workspace roots used to materialize
|
|
/// `:workspace_roots`.
|
|
#[experimental("thread/fork.runtimeWorkspaceRoots")]
|
|
#[serde(default)]
|
|
pub runtime_workspace_roots: Vec<AbsolutePathBuf>,
|
|
/// Instruction source files currently loaded for this thread.
|
|
#[serde(default)]
|
|
pub instruction_sources: Vec<AbsolutePathBuf>,
|
|
#[experimental(nested)]
|
|
pub approval_policy: AskForApproval,
|
|
/// Reviewer currently used for approval requests on this thread.
|
|
pub approvals_reviewer: ApprovalsReviewer,
|
|
/// Legacy sandbox policy retained for compatibility. Experimental clients
|
|
/// should prefer `activePermissionProfile` for profile provenance.
|
|
pub sandbox: SandboxPolicy,
|
|
/// Named or implicit built-in profile that produced the active
|
|
/// permissions, when known.
|
|
#[experimental("thread/fork.activePermissionProfile")]
|
|
#[serde(default)]
|
|
pub active_permission_profile: Option<ActivePermissionProfile>,
|
|
pub reasoning_effort: Option<ReasoningEffort>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadArchiveParams {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadArchiveResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadUnsubscribeParams {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadUnsubscribeResponse {
|
|
pub status: ThreadUnsubscribeStatus,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ThreadUnsubscribeStatus {
|
|
NotLoaded,
|
|
NotSubscribed,
|
|
Unsubscribed,
|
|
}
|
|
|
|
/// Parameters for `thread/increment_elicitation`.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadIncrementElicitationParams {
|
|
/// Thread whose out-of-band elicitation counter should be incremented.
|
|
pub thread_id: String,
|
|
}
|
|
|
|
/// Response for `thread/increment_elicitation`.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadIncrementElicitationResponse {
|
|
/// Current out-of-band elicitation count after the increment.
|
|
pub count: u64,
|
|
/// Whether timeout accounting is paused after applying the increment.
|
|
pub paused: bool,
|
|
}
|
|
|
|
/// Parameters for `thread/decrement_elicitation`.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadDecrementElicitationParams {
|
|
/// Thread whose out-of-band elicitation counter should be decremented.
|
|
pub thread_id: String,
|
|
}
|
|
|
|
/// Response for `thread/decrement_elicitation`.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadDecrementElicitationResponse {
|
|
/// Current out-of-band elicitation count after the decrement.
|
|
pub count: u64,
|
|
/// Whether timeout accounting remains paused after applying the decrement.
|
|
pub paused: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadSetNameParams {
|
|
pub thread_id: String,
|
|
pub name: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadUnarchiveParams {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadSetNameResponse {}
|
|
|
|
v2_enum_from_core! {
|
|
pub enum ThreadGoalStatus from CoreThreadGoalStatus {
|
|
Active,
|
|
Paused,
|
|
Blocked,
|
|
UsageLimited,
|
|
BudgetLimited,
|
|
Complete,
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadGoal {
|
|
pub thread_id: String,
|
|
pub objective: String,
|
|
pub status: ThreadGoalStatus,
|
|
#[ts(type = "number | null")]
|
|
pub token_budget: Option<i64>,
|
|
#[ts(type = "number")]
|
|
pub tokens_used: i64,
|
|
#[ts(type = "number")]
|
|
pub time_used_seconds: i64,
|
|
#[ts(type = "number")]
|
|
pub created_at: i64,
|
|
#[ts(type = "number")]
|
|
pub updated_at: i64,
|
|
}
|
|
|
|
impl From<codex_protocol::protocol::ThreadGoal> for ThreadGoal {
|
|
fn from(value: codex_protocol::protocol::ThreadGoal) -> Self {
|
|
Self {
|
|
thread_id: value.thread_id.to_string(),
|
|
objective: value.objective,
|
|
status: value.status.into(),
|
|
token_budget: value.token_budget,
|
|
tokens_used: value.tokens_used,
|
|
time_used_seconds: value.time_used_seconds,
|
|
created_at: value.created_at,
|
|
updated_at: value.updated_at,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadGoalSetParams {
|
|
pub thread_id: String,
|
|
#[ts(optional = nullable)]
|
|
pub objective: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub status: Option<ThreadGoalStatus>,
|
|
#[serde(
|
|
default,
|
|
deserialize_with = "crate::protocol::serde_helpers::deserialize_double_option",
|
|
serialize_with = "crate::protocol::serde_helpers::serialize_double_option",
|
|
skip_serializing_if = "Option::is_none"
|
|
)]
|
|
#[ts(optional = nullable, type = "number | null")]
|
|
pub token_budget: Option<Option<i64>>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadGoalSetResponse {
|
|
pub goal: ThreadGoal,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadGoalGetParams {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadGoalGetResponse {
|
|
pub goal: Option<ThreadGoal>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadGoalClearParams {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadGoalClearResponse {
|
|
pub cleared: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadMetadataUpdateParams {
|
|
pub thread_id: String,
|
|
/// Patch the stored Git metadata for this thread.
|
|
/// Omit a field to leave it unchanged, set it to `null` to clear it, or
|
|
/// provide a string to replace the stored value.
|
|
#[ts(optional = nullable)]
|
|
pub git_info: Option<ThreadMetadataGitInfoUpdateParams>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadMetadataGitInfoUpdateParams {
|
|
/// Omit to leave the stored commit unchanged, set to `null` to clear it,
|
|
/// or provide a non-empty string to replace it.
|
|
#[serde(
|
|
default,
|
|
skip_serializing_if = "Option::is_none",
|
|
serialize_with = "crate::protocol::serde_helpers::serialize_double_option",
|
|
deserialize_with = "crate::protocol::serde_helpers::deserialize_double_option"
|
|
)]
|
|
#[ts(optional = nullable, type = "string | null")]
|
|
pub sha: Option<Option<String>>,
|
|
/// Omit to leave the stored branch unchanged, set to `null` to clear it,
|
|
/// or provide a non-empty string to replace it.
|
|
#[serde(
|
|
default,
|
|
skip_serializing_if = "Option::is_none",
|
|
serialize_with = "crate::protocol::serde_helpers::serialize_double_option",
|
|
deserialize_with = "crate::protocol::serde_helpers::deserialize_double_option"
|
|
)]
|
|
#[ts(optional = nullable, type = "string | null")]
|
|
pub branch: Option<Option<String>>,
|
|
/// Omit to leave the stored origin URL unchanged, set to `null` to clear it,
|
|
/// or provide a non-empty string to replace it.
|
|
#[serde(
|
|
default,
|
|
skip_serializing_if = "Option::is_none",
|
|
serialize_with = "crate::protocol::serde_helpers::serialize_double_option",
|
|
deserialize_with = "crate::protocol::serde_helpers::deserialize_double_option"
|
|
)]
|
|
#[ts(optional = nullable, type = "string | null")]
|
|
pub origin_url: Option<Option<String>>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadMetadataUpdateResponse {
|
|
pub thread: Thread,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "lowercase")]
|
|
#[ts(rename_all = "lowercase")]
|
|
pub enum ThreadMemoryMode {
|
|
Enabled,
|
|
Disabled,
|
|
}
|
|
|
|
impl ThreadMemoryMode {
|
|
pub fn as_str(self) -> &'static str {
|
|
match self {
|
|
Self::Enabled => "enabled",
|
|
Self::Disabled => "disabled",
|
|
}
|
|
}
|
|
|
|
pub fn to_core(self) -> codex_protocol::protocol::ThreadMemoryMode {
|
|
match self {
|
|
Self::Enabled => codex_protocol::protocol::ThreadMemoryMode::Enabled,
|
|
Self::Disabled => codex_protocol::protocol::ThreadMemoryMode::Disabled,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadMemoryModeSetParams {
|
|
pub thread_id: String,
|
|
pub mode: ThreadMemoryMode,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadMemoryModeSetResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct MemoryResetResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadUnarchiveResponse {
|
|
pub thread: Thread,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadCompactStartParams {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadCompactStartResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadShellCommandParams {
|
|
pub thread_id: String,
|
|
/// Shell command string evaluated by the thread's configured shell.
|
|
/// Unlike `command/exec`, this intentionally preserves shell syntax
|
|
/// such as pipes, redirects, and quoting. This runs unsandboxed with full
|
|
/// access rather than inheriting the thread sandbox policy.
|
|
pub command: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadShellCommandResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadApproveGuardianDeniedActionParams {
|
|
pub thread_id: String,
|
|
/// Serialized `codex_protocol::protocol::GuardianAssessmentEvent`.
|
|
pub event: JsonValue,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadApproveGuardianDeniedActionResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadBackgroundTerminalsCleanParams {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadBackgroundTerminalsCleanResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRollbackParams {
|
|
pub thread_id: String,
|
|
/// The number of turns to drop from the end of the thread. Must be >= 1.
|
|
///
|
|
/// This only modifies the thread's history and does not revert local file changes
|
|
/// that have been made by the agent. Clients are responsible for reverting these changes.
|
|
pub num_turns: u32,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRollbackResponse {
|
|
/// The updated thread after applying the rollback, with `turns` populated.
|
|
///
|
|
/// The ThreadItems stored in each Turn are lossy since we explicitly do not
|
|
/// persist all agent interactions, such as command executions. This is the same
|
|
/// behavior as `thread/resume`.
|
|
pub thread: Thread,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadListParams {
|
|
/// Opaque pagination cursor returned by a previous call.
|
|
#[ts(optional = nullable)]
|
|
pub cursor: Option<String>,
|
|
/// Optional page size; defaults to a reasonable server-side value.
|
|
#[ts(optional = nullable)]
|
|
pub limit: Option<u32>,
|
|
/// Optional sort key; defaults to created_at.
|
|
#[ts(optional = nullable)]
|
|
pub sort_key: Option<ThreadSortKey>,
|
|
/// Optional sort direction; defaults to descending (newest first).
|
|
#[ts(optional = nullable)]
|
|
pub sort_direction: Option<SortDirection>,
|
|
/// Optional provider filter; when set, only sessions recorded under these
|
|
/// providers are returned. When present but empty, includes all providers.
|
|
#[ts(optional = nullable)]
|
|
pub model_providers: Option<Vec<String>>,
|
|
/// Optional source filter; when set, only sessions from these source kinds
|
|
/// are returned. When omitted or empty, defaults to interactive sources.
|
|
#[ts(optional = nullable)]
|
|
pub source_kinds: Option<Vec<ThreadSourceKind>>,
|
|
/// Optional archived filter; when set to true, only archived threads are returned.
|
|
/// If false or null, only non-archived threads are returned.
|
|
#[ts(optional = nullable)]
|
|
pub archived: Option<bool>,
|
|
/// Optional cwd filter or filters; when set, only threads whose session cwd
|
|
/// exactly matches one of these paths are returned.
|
|
#[ts(optional = nullable, type = "string | Array<string> | null")]
|
|
pub cwd: Option<ThreadListCwdFilter>,
|
|
/// If true, return from the state DB without scanning JSONL rollouts to
|
|
/// repair thread metadata. Omitted or false preserves scan-and-repair
|
|
/// behavior.
|
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
pub use_state_db_only: bool,
|
|
/// Optional substring filter for the extracted thread title.
|
|
#[ts(optional = nullable)]
|
|
pub search_term: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadSearchParams {
|
|
/// Opaque pagination cursor returned by a previous call.
|
|
#[ts(optional = nullable)]
|
|
pub cursor: Option<String>,
|
|
/// Optional page size; defaults to a reasonable server-side value.
|
|
#[ts(optional = nullable)]
|
|
pub limit: Option<u32>,
|
|
/// Optional sort key; defaults to created_at.
|
|
#[ts(optional = nullable)]
|
|
pub sort_key: Option<ThreadSortKey>,
|
|
/// Optional sort direction; defaults to descending (newest first).
|
|
#[ts(optional = nullable)]
|
|
pub sort_direction: Option<SortDirection>,
|
|
/// Optional source filter; when set, only sessions from these source kinds
|
|
/// are returned. When omitted or empty, defaults to interactive sources.
|
|
#[ts(optional = nullable)]
|
|
pub source_kinds: Option<Vec<ThreadSourceKind>>,
|
|
/// Optional archived filter; when set to true, only archived threads are returned.
|
|
/// If false or null, only non-archived threads are returned.
|
|
#[ts(optional = nullable)]
|
|
pub archived: Option<bool>,
|
|
/// Required substring/full-text query for thread search.
|
|
pub search_term: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema)]
|
|
#[serde(untagged)]
|
|
pub enum ThreadListCwdFilter {
|
|
One(String),
|
|
Many(Vec<String>),
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase", export_to = "v2/")]
|
|
pub enum ThreadSourceKind {
|
|
Cli,
|
|
#[serde(rename = "vscode")]
|
|
#[ts(rename = "vscode")]
|
|
VsCode,
|
|
Exec,
|
|
AppServer,
|
|
SubAgent,
|
|
SubAgentReview,
|
|
SubAgentCompact,
|
|
SubAgentThreadSpawn,
|
|
SubAgentOther,
|
|
Unknown,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ThreadSortKey {
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum SortDirection {
|
|
Asc,
|
|
Desc,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadListResponse {
|
|
pub data: Vec<Thread>,
|
|
/// Opaque cursor to pass to the next call to continue after the last item.
|
|
/// if None, there are no more items to return.
|
|
pub next_cursor: Option<String>,
|
|
/// Opaque cursor to pass as `cursor` when reversing `sortDirection`.
|
|
/// This is only populated when the page contains at least one thread.
|
|
/// Use it with the opposite `sortDirection`; for timestamp sorts it anchors
|
|
/// at the start of the page timestamp so same-second updates are not skipped.
|
|
pub backwards_cursor: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadSearchResult {
|
|
pub thread: Thread,
|
|
pub snippet: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadSearchResponse {
|
|
pub data: Vec<ThreadSearchResult>,
|
|
/// Opaque cursor to pass to the next call to continue after the last item.
|
|
/// if None, there are no more items to return.
|
|
pub next_cursor: Option<String>,
|
|
/// Opaque cursor to pass as `cursor` when reversing `sortDirection`.
|
|
/// This is only populated when the page contains at least one thread.
|
|
/// Use it with the opposite `sortDirection`; for timestamp sorts it anchors
|
|
/// at the start of the page timestamp so same-second updates are not skipped.
|
|
pub backwards_cursor: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadLoadedListParams {
|
|
/// Opaque pagination cursor returned by a previous call.
|
|
#[ts(optional = nullable)]
|
|
pub cursor: Option<String>,
|
|
/// Optional page size; defaults to no limit.
|
|
#[ts(optional = nullable)]
|
|
pub limit: Option<u32>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadLoadedListResponse {
|
|
/// Thread ids for sessions currently loaded in memory.
|
|
pub data: Vec<String>,
|
|
/// Opaque cursor to pass to the next call to continue after the last item.
|
|
/// if None, there are no more items to return.
|
|
pub next_cursor: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ThreadStatus {
|
|
NotLoaded,
|
|
Idle,
|
|
SystemError,
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
Active {
|
|
active_flags: Vec<ThreadActiveFlag>,
|
|
},
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ThreadActiveFlag {
|
|
WaitingOnApproval,
|
|
WaitingOnUserInput,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadReadParams {
|
|
pub thread_id: String,
|
|
/// When true, include turns and their items from rollout history.
|
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
pub include_turns: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadReadResponse {
|
|
pub thread: Thread,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadInjectItemsParams {
|
|
pub thread_id: String,
|
|
/// Raw Responses API items to append to the thread's model-visible history.
|
|
pub items: Vec<JsonValue>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadInjectItemsResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadTurnsListParams {
|
|
pub thread_id: String,
|
|
/// Opaque cursor to pass to the next call to continue after the last turn.
|
|
#[ts(optional = nullable)]
|
|
pub cursor: Option<String>,
|
|
/// Optional turn page size.
|
|
#[ts(optional = nullable)]
|
|
pub limit: Option<u32>,
|
|
/// Optional turn pagination direction; defaults to descending.
|
|
#[ts(optional = nullable)]
|
|
pub sort_direction: Option<SortDirection>,
|
|
/// How much item detail to include for each returned turn; defaults to summary.
|
|
#[ts(optional = nullable)]
|
|
pub items_view: Option<TurnItemsView>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadTurnsListResponse {
|
|
pub data: Vec<Turn>,
|
|
/// Opaque cursor to pass to the next call to continue after the last turn.
|
|
/// if None, there are no more turns to return.
|
|
pub next_cursor: Option<String>,
|
|
/// Opaque cursor to pass as `cursor` when reversing `sortDirection`.
|
|
/// This is only populated when the page contains at least one turn.
|
|
/// Use it with the opposite `sortDirection` to include the anchor turn again
|
|
/// and catch updates to that turn.
|
|
pub backwards_cursor: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadTurnsItemsListParams {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
/// Opaque cursor to pass to the next call to continue after the last item.
|
|
#[ts(optional = nullable)]
|
|
pub cursor: Option<String>,
|
|
/// Optional item page size.
|
|
#[ts(optional = nullable)]
|
|
pub limit: Option<u32>,
|
|
/// Optional item pagination direction; defaults to ascending.
|
|
#[ts(optional = nullable)]
|
|
pub sort_direction: Option<SortDirection>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadTurnsItemsListResponse {
|
|
pub data: Vec<ThreadItem>,
|
|
/// Opaque cursor to pass to the next call to continue after the last item.
|
|
/// if None, there are no more items to return.
|
|
pub next_cursor: Option<String>,
|
|
/// Opaque cursor to pass as `cursor` when reversing `sortDirection`.
|
|
/// This is only populated when the page contains at least one item.
|
|
pub backwards_cursor: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadTokenUsageUpdatedNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub token_usage: ThreadTokenUsage,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadTokenUsage {
|
|
pub total: TokenUsageBreakdown,
|
|
pub last: TokenUsageBreakdown,
|
|
// TODO(aibrahim): make this not optional
|
|
#[ts(type = "number | null")]
|
|
pub model_context_window: Option<i64>,
|
|
}
|
|
|
|
impl From<CoreTokenUsageInfo> for ThreadTokenUsage {
|
|
fn from(value: CoreTokenUsageInfo) -> Self {
|
|
Self {
|
|
total: value.total_token_usage.into(),
|
|
last: value.last_token_usage.into(),
|
|
model_context_window: value.model_context_window,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TokenUsageBreakdown {
|
|
#[ts(type = "number")]
|
|
pub total_tokens: i64,
|
|
#[ts(type = "number")]
|
|
pub input_tokens: i64,
|
|
#[ts(type = "number")]
|
|
pub cached_input_tokens: i64,
|
|
#[ts(type = "number")]
|
|
pub output_tokens: i64,
|
|
#[ts(type = "number")]
|
|
pub reasoning_output_tokens: i64,
|
|
}
|
|
|
|
impl From<CoreTokenUsage> for TokenUsageBreakdown {
|
|
fn from(value: CoreTokenUsage) -> Self {
|
|
Self {
|
|
total_tokens: value.total_tokens,
|
|
input_tokens: value.input_tokens,
|
|
cached_input_tokens: value.cached_input_tokens,
|
|
output_tokens: value.output_tokens,
|
|
reasoning_output_tokens: value.reasoning_output_tokens,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Thread/Turn lifecycle notifications and item progress events
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadStartedNotification {
|
|
pub thread: Thread,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadStatusChangedNotification {
|
|
pub thread_id: String,
|
|
pub status: ThreadStatus,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadArchivedNotification {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadUnarchivedNotification {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadClosedNotification {
|
|
pub thread_id: String,
|
|
}
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadNameUpdatedNotification {
|
|
pub thread_id: String,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional)]
|
|
pub thread_name: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadGoalUpdatedNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: Option<String>,
|
|
pub goal: ThreadGoal,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadGoalClearedNotification {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
/// Deprecated: Use `ContextCompaction` item type instead.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ContextCompactedNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
}
|