Files
codex/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json
Michael Bolin c25d905f61 permissions: support workspace roots in profiles (#22610)
## Why

This is the configuration/model half of the alternative permissions
migration we discussed as a comparison point for
[#22401](https://github.com/openai/codex/pull/22401) and
[#22402](https://github.com/openai/codex/pull/22402).

The old `workspace-write` model mixes three concerns that we want to
keep separate:
- reusable profile rules that should stay immutable once selected
- user/runtime workspace roots from `cwd`, `--add-dir`, and legacy
workspace-write config
- internal Codex writable roots such as memories, which should not be
shown as user workspace roots

This PR gives permission profiles first-class `workspace_roots` so users
can opt multiple repositories into the same `:workspace_roots` rules
without using broad absolute-path write grants. It also starts
separating the raw selected profile from the effective runtime profile
by making `Permissions` expose explicit accessors instead of public
mutable fields.

A representative `config.toml` looks like this:

```toml
default_permissions = "dev"

[permissions.dev.workspace_roots]
"~/code/openai" = true
"~/code/developers-website" = true

[permissions.dev.filesystem.":workspace_roots"]
"." = "write"
".codex" = "read"
".git" = "read"
".vscode" = "read"
```

If Codex starts in `~/code/codex` with that profile selected, the
effective workspace-root set becomes:
- `~/code/codex` from the runtime `cwd`
- `~/code/openai` from the profile
- `~/code/developers-website` from the profile

The `:workspace_roots` rules are materialized across each root, so
`.git`, `.codex`, and `.vscode` stay scoped the same way everywhere.
Runtime additions such as `--add-dir` can still layer on later stack
entries without mutating the selected profile.

## Stack Shape

This PR intentionally stops before the profile-identity cleanup in
[#22683](https://github.com/openai/codex/pull/22683) so the base review
stays focused on config loading, workspace-root materialization, and
compatibility with legacy `workspace-write`.

The representation in this PR is therefore transitional: `Permissions`
carries enough state to distinguish the raw constrained profile from the
effective runtime profile, and there are still call sites that must keep
the active profile identity and constrained profile value in sync. The
follow-up PR replaces that with a single resolved profile state
(`ResolvedPermissionProfile` / `PermissionProfileState`) that keeps the
profile id, immutable `PermissionProfile`, and profile-declared
workspace roots together. That follow-up removes APIs such as
`set_constrained_permission_profile_with_active_profile()` where
separate arguments could drift out of sync.

Downstream PRs then build on this base to switch app-server turn updates
to profile ids plus runtime workspace roots and to finish the
user-visible summary behavior. Reviewers should judge this PR as the
workspace-roots foundation, not as the final in-memory shape of selected
permission profiles.

## Review Guide

Suggested review order:

1. Start with `codex-rs/core/src/config/mod.rs`.
This is the main shape change in the base slice. `Permissions` now
stores a private raw `Constrained<PermissionProfile>` plus runtime
`workspace_roots`. Callers use `permission_profile()` when they need the
raw constrained value and `effective_permission_profile()` when they
need a materialized runtime profile. As noted above,
[#22683](https://github.com/openai/codex/pull/22683) replaces this
transitional shape with a resolved profile state that keeps identity and
profile data together.

2. Review `codex-rs/config/src/permissions_toml.rs` and
`codex-rs/core/src/config/permissions.rs`.
These add `[permissions.<id>.workspace_roots]`, resolve enabled entries
relative to the policy cwd, and keep `:workspace_roots` deny-read glob
patterns symbolic until the actual roots are known.

3. Review `codex-rs/protocol/src/permissions.rs` and
`codex-rs/protocol/src/models.rs`.
These add the policy/profile materialization helpers that expand exact
`:workspace_roots` entries and scoped deny-read globs over every
workspace root. This is also where `ActivePermissionProfileModification`
is removed from the core model.

4. Review the legacy bridge in
`Config::load_from_base_config_with_overrides` and
`Config::set_legacy_sandbox_policy`.
This is where legacy `workspace-write` roots become runtime workspace
roots, while Codex internal writable roots stay internal and do not
appear as user-facing workspace roots.

5. Then skim downstream call sites.
The interesting pattern is raw-vs-effective access: state/proxy/bwrap
paths keep the raw constrained profile, while execution, summaries, and
user-visible status use the effective profile and workspace-root list.

## What Changed

- added `[permissions.<id>.workspace_roots]` to the config model and
schema
- added runtime `workspace_roots` state to `Config`/`Permissions` and
`ConfigOverrides`
- made `Permissions` profile fields private and replaced direct mutation
with accessors/setters
- added `PermissionProfile` and `FileSystemSandboxPolicy` helpers for
materializing `:workspace_roots` exact paths and deny-read globs across
all roots
- moved legacy additional writable roots into runtime workspace-root
state instead of active profile modifications
- removed `ActivePermissionProfileModification` and its app-server
protocol/schema export
- updated sandbox/status summary paths so internal writable roots are
not reported as user workspace roots

## Verification Strategy

The targeted tests cover the behavior at the layers where regressions
are most likely:
- `codex-rs/core/src/config/config_tests.rs` verifies config loading,
legacy workspace-root seeding, effective profile materialization, and
memory-root handling.
- `codex-rs/core/src/config/permissions_tests.rs` verifies profile
`workspace_roots` parsing and `:workspace_roots` scoped/glob
compilation.
- `codex-rs/protocol/src/permissions.rs` unit tests verify exact and
glob materialization over multiple workspace roots.
- `codex-rs/tui/src/status/tests.rs` and
`codex-rs/utils/sandbox-summary/src/sandbox_summary.rs` verify the
user-facing summaries show effective workspace roots and hide internal
writes.

I also ran `cargo check --tests` locally after the latest stack refresh
to catch cross-crate API breakage from the private-field/accessor
changes.







---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22610).
* #22612
* #22611
* #22683
* __->__ #22610
2026-05-14 18:25:23 -07:00

2598 lines
63 KiB
JSON
Generated

{
"$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"
},
"ActivePermissionProfile": {
"properties": {
"extends": {
"default": null,
"description": "Parent profile identifier once permissions profiles support inheritance. This is currently always `null`.",
"type": [
"string",
"null"
]
},
"id": {
"description": "Identifier from `default_permissions` or the implicit built-in default, such as `:workspace` or a user-defined `[permissions.<id>]` profile.",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
},
"AgentPath": {
"type": "string"
},
"ApprovalsReviewer": {
"description": "Configures who approval requests are routed to for review. Examples include sandbox escapes, blocked network access, MCP approval prompts, and ARC escalations. Defaults to `user`. `auto_review` uses a carefully prompted subagent to gather relevant context and apply a risk-based decision framework before approving or denying the request. The legacy value `guardian_subagent` is accepted for compatibility.",
"enum": [
"user",
"auto_review",
"guardian_subagent"
],
"type": "string"
},
"AskForApproval": {
"oneOf": [
{
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"granular": {
"properties": {
"mcp_elicitations": {
"type": "boolean"
},
"request_permissions": {
"default": false,
"type": "boolean"
},
"rules": {
"type": "boolean"
},
"sandbox_approval": {
"type": "boolean"
},
"skill_approval": {
"default": false,
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"granular"
],
"title": "GranularAskForApproval",
"type": "object"
}
]
},
"ByteRange": {
"properties": {
"end": {
"format": "uint",
"minimum": 0.0,
"type": "integer"
},
"start": {
"format": "uint",
"minimum": 0.0,
"type": "integer"
}
},
"required": [
"end",
"start"
],
"type": "object"
},
"CodexErrorInfo": {
"description": "This translation layer make sure that we expose codex error code in camel case.\n\nWhen an upstream HTTP status is available (for example, from the Responses API or a provider), it is forwarded in `httpStatusCode` on the relevant `codexErrorInfo` variant.",
"oneOf": [
{
"enum": [
"contextWindowExceeded",
"usageLimitExceeded",
"serverOverloaded",
"cyberPolicy",
"internalServerError",
"unauthorized",
"badRequest",
"threadRollbackFailed",
"sandboxError",
"other"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"httpConnectionFailed": {
"properties": {
"httpStatusCode": {
"format": "uint16",
"minimum": 0.0,
"type": [
"integer",
"null"
]
}
},
"type": "object"
}
},
"required": [
"httpConnectionFailed"
],
"title": "HttpConnectionFailedCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Failed to connect to the response SSE stream.",
"properties": {
"responseStreamConnectionFailed": {
"properties": {
"httpStatusCode": {
"format": "uint16",
"minimum": 0.0,
"type": [
"integer",
"null"
]
}
},
"type": "object"
}
},
"required": [
"responseStreamConnectionFailed"
],
"title": "ResponseStreamConnectionFailedCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "The response SSE stream disconnected in the middle of a turn before completion.",
"properties": {
"responseStreamDisconnected": {
"properties": {
"httpStatusCode": {
"format": "uint16",
"minimum": 0.0,
"type": [
"integer",
"null"
]
}
},
"type": "object"
}
},
"required": [
"responseStreamDisconnected"
],
"title": "ResponseStreamDisconnectedCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Reached the retry limit for responses.",
"properties": {
"responseTooManyFailedAttempts": {
"properties": {
"httpStatusCode": {
"format": "uint16",
"minimum": 0.0,
"type": [
"integer",
"null"
]
}
},
"type": "object"
}
},
"required": [
"responseTooManyFailedAttempts"
],
"title": "ResponseTooManyFailedAttemptsCodexErrorInfo",
"type": "object"
},
{
"additionalProperties": false,
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
"properties": {
"activeTurnNotSteerable": {
"properties": {
"turnKind": {
"$ref": "#/definitions/NonSteerableTurnKind"
}
},
"required": [
"turnKind"
],
"type": "object"
}
},
"required": [
"activeTurnNotSteerable"
],
"title": "ActiveTurnNotSteerableCodexErrorInfo",
"type": "object"
}
]
},
"CollabAgentState": {
"properties": {
"message": {
"type": [
"string",
"null"
]
},
"status": {
"$ref": "#/definitions/CollabAgentStatus"
}
},
"required": [
"status"
],
"type": "object"
},
"CollabAgentStatus": {
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
"notFound"
],
"type": "string"
},
"CollabAgentTool": {
"enum": [
"spawnAgent",
"sendInput",
"resumeAgent",
"wait",
"closeAgent"
],
"type": "string"
},
"CollabAgentToolCallStatus": {
"enum": [
"inProgress",
"completed",
"failed"
],
"type": "string"
},
"CommandAction": {
"oneOf": [
{
"properties": {
"command": {
"type": "string"
},
"name": {
"type": "string"
},
"path": {
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
"read"
],
"title": "ReadCommandActionType",
"type": "string"
}
},
"required": [
"command",
"name",
"path",
"type"
],
"title": "ReadCommandAction",
"type": "object"
},
{
"properties": {
"command": {
"type": "string"
},
"path": {
"type": [
"string",
"null"
]
},
"type": {
"enum": [
"listFiles"
],
"title": "ListFilesCommandActionType",
"type": "string"
}
},
"required": [
"command",
"type"
],
"title": "ListFilesCommandAction",
"type": "object"
},
{
"properties": {
"command": {
"type": "string"
},
"path": {
"type": [
"string",
"null"
]
},
"query": {
"type": [
"string",
"null"
]
},
"type": {
"enum": [
"search"
],
"title": "SearchCommandActionType",
"type": "string"
}
},
"required": [
"command",
"type"
],
"title": "SearchCommandAction",
"type": "object"
},
{
"properties": {
"command": {
"type": "string"
},
"type": {
"enum": [
"unknown"
],
"title": "UnknownCommandActionType",
"type": "string"
}
},
"required": [
"command",
"type"
],
"title": "UnknownCommandAction",
"type": "object"
}
]
},
"CommandExecutionSource": {
"enum": [
"agent",
"userShell",
"unifiedExecStartup",
"unifiedExecInteraction"
],
"type": "string"
},
"CommandExecutionStatus": {
"enum": [
"inProgress",
"completed",
"failed",
"declined"
],
"type": "string"
},
"DynamicToolCallOutputContentItem": {
"oneOf": [
{
"properties": {
"text": {
"type": "string"
},
"type": {
"enum": [
"inputText"
],
"title": "InputTextDynamicToolCallOutputContentItemType",
"type": "string"
}
},
"required": [
"text",
"type"
],
"title": "InputTextDynamicToolCallOutputContentItem",
"type": "object"
},
{
"properties": {
"imageUrl": {
"type": "string"
},
"type": {
"enum": [
"inputImage"
],
"title": "InputImageDynamicToolCallOutputContentItemType",
"type": "string"
}
},
"required": [
"imageUrl",
"type"
],
"title": "InputImageDynamicToolCallOutputContentItem",
"type": "object"
}
]
},
"DynamicToolCallStatus": {
"enum": [
"inProgress",
"completed",
"failed"
],
"type": "string"
},
"FileSystemAccessMode": {
"enum": [
"read",
"write",
"none"
],
"type": "string"
},
"FileSystemPath": {
"oneOf": [
{
"properties": {
"path": {
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
"path"
],
"title": "PathFileSystemPathType",
"type": "string"
}
},
"required": [
"path",
"type"
],
"title": "PathFileSystemPath",
"type": "object"
},
{
"properties": {
"pattern": {
"type": "string"
},
"type": {
"enum": [
"glob_pattern"
],
"title": "GlobPatternFileSystemPathType",
"type": "string"
}
},
"required": [
"pattern",
"type"
],
"title": "GlobPatternFileSystemPath",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"special"
],
"title": "SpecialFileSystemPathType",
"type": "string"
},
"value": {
"$ref": "#/definitions/FileSystemSpecialPath"
}
},
"required": [
"type",
"value"
],
"title": "SpecialFileSystemPath",
"type": "object"
}
]
},
"FileSystemSandboxEntry": {
"properties": {
"access": {
"$ref": "#/definitions/FileSystemAccessMode"
},
"path": {
"$ref": "#/definitions/FileSystemPath"
}
},
"required": [
"access",
"path"
],
"type": "object"
},
"FileSystemSpecialPath": {
"oneOf": [
{
"properties": {
"kind": {
"enum": [
"root"
],
"type": "string"
}
},
"required": [
"kind"
],
"title": "RootFileSystemSpecialPath",
"type": "object"
},
{
"properties": {
"kind": {
"enum": [
"minimal"
],
"type": "string"
}
},
"required": [
"kind"
],
"title": "MinimalFileSystemSpecialPath",
"type": "object"
},
{
"properties": {
"kind": {
"enum": [
"project_roots"
],
"type": "string"
},
"subpath": {
"type": [
"string",
"null"
]
}
},
"required": [
"kind"
],
"title": "KindFileSystemSpecialPath",
"type": "object"
},
{
"properties": {
"kind": {
"enum": [
"tmpdir"
],
"type": "string"
}
},
"required": [
"kind"
],
"title": "TmpdirFileSystemSpecialPath",
"type": "object"
},
{
"properties": {
"kind": {
"enum": [
"slash_tmp"
],
"type": "string"
}
},
"required": [
"kind"
],
"title": "SlashTmpFileSystemSpecialPath",
"type": "object"
},
{
"properties": {
"kind": {
"enum": [
"unknown"
],
"type": "string"
},
"path": {
"type": "string"
},
"subpath": {
"type": [
"string",
"null"
]
}
},
"required": [
"kind",
"path"
],
"type": "object"
}
]
},
"FileUpdateChange": {
"properties": {
"diff": {
"type": "string"
},
"kind": {
"$ref": "#/definitions/PatchChangeKind"
},
"path": {
"type": "string"
}
},
"required": [
"diff",
"kind",
"path"
],
"type": "object"
},
"GitInfo": {
"properties": {
"branch": {
"type": [
"string",
"null"
]
},
"originUrl": {
"type": [
"string",
"null"
]
},
"sha": {
"type": [
"string",
"null"
]
}
},
"type": "object"
},
"HookPromptFragment": {
"properties": {
"hookRunId": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"hookRunId",
"text"
],
"type": "object"
},
"McpToolCallError": {
"properties": {
"message": {
"type": "string"
}
},
"required": [
"message"
],
"type": "object"
},
"McpToolCallResult": {
"properties": {
"_meta": true,
"content": {
"items": true,
"type": "array"
},
"structuredContent": true
},
"required": [
"content"
],
"type": "object"
},
"McpToolCallStatus": {
"enum": [
"inProgress",
"completed",
"failed"
],
"type": "string"
},
"MemoryCitation": {
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/MemoryCitationEntry"
},
"type": "array"
},
"threadIds": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"entries",
"threadIds"
],
"type": "object"
},
"MemoryCitationEntry": {
"properties": {
"lineEnd": {
"format": "uint32",
"minimum": 0.0,
"type": "integer"
},
"lineStart": {
"format": "uint32",
"minimum": 0.0,
"type": "integer"
},
"note": {
"type": "string"
},
"path": {
"type": "string"
}
},
"required": [
"lineEnd",
"lineStart",
"note",
"path"
],
"type": "object"
},
"MessagePhase": {
"description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.",
"oneOf": [
{
"description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.",
"enum": [
"commentary"
],
"type": "string"
},
{
"description": "The assistant's terminal answer text for the current turn.",
"enum": [
"final_answer"
],
"type": "string"
}
]
},
"NetworkAccess": {
"enum": [
"restricted",
"enabled"
],
"type": "string"
},
"NonSteerableTurnKind": {
"enum": [
"review",
"compact"
],
"type": "string"
},
"PatchApplyStatus": {
"enum": [
"inProgress",
"completed",
"failed",
"declined"
],
"type": "string"
},
"PatchChangeKind": {
"oneOf": [
{
"properties": {
"type": {
"enum": [
"add"
],
"title": "AddPatchChangeKindType",
"type": "string"
}
},
"required": [
"type"
],
"title": "AddPatchChangeKind",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"delete"
],
"title": "DeletePatchChangeKindType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DeletePatchChangeKind",
"type": "object"
},
{
"properties": {
"move_path": {
"type": [
"string",
"null"
]
},
"type": {
"enum": [
"update"
],
"title": "UpdatePatchChangeKindType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UpdatePatchChangeKind",
"type": "object"
}
]
},
"PermissionProfile": {
"oneOf": [
{
"description": "Codex owns sandbox construction for this profile.",
"properties": {
"fileSystem": {
"$ref": "#/definitions/PermissionProfileFileSystemPermissions"
},
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
"type": {
"enum": [
"managed"
],
"title": "ManagedPermissionProfileType",
"type": "string"
}
},
"required": [
"fileSystem",
"network",
"type"
],
"title": "ManagedPermissionProfile",
"type": "object"
},
{
"description": "Do not apply an outer sandbox.",
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledPermissionProfileType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledPermissionProfile",
"type": "object"
},
{
"description": "Filesystem isolation is enforced by an external caller.",
"properties": {
"network": {
"$ref": "#/definitions/PermissionProfileNetworkPermissions"
},
"type": {
"enum": [
"external"
],
"title": "ExternalPermissionProfileType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalPermissionProfile",
"type": "object"
}
]
},
"PermissionProfileFileSystemPermissions": {
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
},
"type": "array"
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"required": [
"entries",
"type"
],
"title": "RestrictedPermissionProfileFileSystemPermissions",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissionsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedPermissionProfileFileSystemPermissions",
"type": "object"
}
]
},
"PermissionProfileNetworkPermissions": {
"properties": {
"enabled": {
"type": "boolean"
}
},
"required": [
"enabled"
],
"type": "object"
},
"ReasoningEffort": {
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
"enum": [
"none",
"minimal",
"low",
"medium",
"high",
"xhigh"
],
"type": "string"
},
"SandboxPolicy": {
"oneOf": [
{
"properties": {
"type": {
"enum": [
"dangerFullAccess"
],
"title": "DangerFullAccessSandboxPolicyType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DangerFullAccessSandboxPolicy",
"type": "object"
},
{
"properties": {
"networkAccess": {
"default": false,
"type": "boolean"
},
"type": {
"enum": [
"readOnly"
],
"title": "ReadOnlySandboxPolicyType",
"type": "string"
}
},
"required": [
"type"
],
"title": "ReadOnlySandboxPolicy",
"type": "object"
},
{
"properties": {
"networkAccess": {
"allOf": [
{
"$ref": "#/definitions/NetworkAccess"
}
],
"default": "restricted"
},
"type": {
"enum": [
"externalSandbox"
],
"title": "ExternalSandboxSandboxPolicyType",
"type": "string"
}
},
"required": [
"type"
],
"title": "ExternalSandboxSandboxPolicy",
"type": "object"
},
{
"properties": {
"excludeSlashTmp": {
"default": false,
"type": "boolean"
},
"excludeTmpdirEnvVar": {
"default": false,
"type": "boolean"
},
"networkAccess": {
"default": false,
"type": "boolean"
},
"type": {
"enum": [
"workspaceWrite"
],
"title": "WorkspaceWriteSandboxPolicyType",
"type": "string"
},
"writableRoots": {
"default": [],
"items": {
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": "array"
}
},
"required": [
"type"
],
"title": "WorkspaceWriteSandboxPolicy",
"type": "object"
}
]
},
"SessionSource": {
"oneOf": [
{
"enum": [
"cli",
"vscode",
"exec",
"appServer",
"unknown"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"custom": {
"type": "string"
}
},
"required": [
"custom"
],
"title": "CustomSessionSource",
"type": "object"
},
{
"additionalProperties": false,
"properties": {
"subAgent": {
"$ref": "#/definitions/SubAgentSource"
}
},
"required": [
"subAgent"
],
"title": "SubAgentSessionSource",
"type": "object"
}
]
},
"SubAgentSource": {
"oneOf": [
{
"enum": [
"review",
"compact",
"memory_consolidation"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"thread_spawn": {
"properties": {
"agent_nickname": {
"default": null,
"type": [
"string",
"null"
]
},
"agent_path": {
"anyOf": [
{
"$ref": "#/definitions/AgentPath"
},
{
"type": "null"
}
],
"default": null
},
"agent_role": {
"default": null,
"type": [
"string",
"null"
]
},
"depth": {
"format": "int32",
"type": "integer"
},
"parent_thread_id": {
"$ref": "#/definitions/ThreadId"
}
},
"required": [
"depth",
"parent_thread_id"
],
"type": "object"
}
},
"required": [
"thread_spawn"
],
"title": "ThreadSpawnSubAgentSource",
"type": "object"
},
{
"additionalProperties": false,
"properties": {
"other": {
"type": "string"
}
},
"required": [
"other"
],
"title": "OtherSubAgentSource",
"type": "object"
}
]
},
"TextElement": {
"properties": {
"byteRange": {
"allOf": [
{
"$ref": "#/definitions/ByteRange"
}
],
"description": "Byte range in the parent `text` buffer that this element occupies."
},
"placeholder": {
"description": "Optional human-readable placeholder for the element, displayed in the UI.",
"type": [
"string",
"null"
]
}
},
"required": [
"byteRange"
],
"type": "object"
},
"Thread": {
"properties": {
"agentNickname": {
"description": "Optional random unique nickname assigned to an AgentControl-spawned sub-agent.",
"type": [
"string",
"null"
]
},
"agentRole": {
"description": "Optional role (agent_role) assigned to an AgentControl-spawned sub-agent.",
"type": [
"string",
"null"
]
},
"cliVersion": {
"description": "Version of the CLI that created the thread.",
"type": "string"
},
"createdAt": {
"description": "Unix timestamp (in seconds) when the thread was created.",
"format": "int64",
"type": "integer"
},
"cwd": {
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "Working directory captured for the thread."
},
"ephemeral": {
"description": "Whether the thread is ephemeral and should not be materialized on disk.",
"type": "boolean"
},
"forkedFromId": {
"description": "Source thread id when this thread was created by forking another thread.",
"type": [
"string",
"null"
]
},
"gitInfo": {
"anyOf": [
{
"$ref": "#/definitions/GitInfo"
},
{
"type": "null"
}
],
"description": "Optional Git metadata captured when the thread was created."
},
"id": {
"type": "string"
},
"modelProvider": {
"description": "Model provider used for this thread (for example, 'openai').",
"type": "string"
},
"name": {
"description": "Optional user-facing thread title.",
"type": [
"string",
"null"
]
},
"path": {
"description": "[UNSTABLE] Path to the thread on disk.",
"type": [
"string",
"null"
]
},
"preview": {
"description": "Usually the first user message in the thread, if available.",
"type": "string"
},
"sessionId": {
"description": "Session id shared by threads that belong to the same session tree.",
"type": "string"
},
"source": {
"allOf": [
{
"$ref": "#/definitions/SessionSource"
}
],
"description": "Origin of the thread (CLI, VSCode, codex exec, codex app-server, etc.)."
},
"status": {
"allOf": [
{
"$ref": "#/definitions/ThreadStatus"
}
],
"description": "Current runtime status for the thread."
},
"threadSource": {
"anyOf": [
{
"$ref": "#/definitions/ThreadSource"
},
{
"type": "null"
}
],
"description": "Optional analytics source classification for this thread."
},
"turns": {
"description": "Only populated on `thread/resume`, `thread/rollback`, `thread/fork`, and `thread/read` (when `includeTurns` is true) responses. For all other responses and notifications returning a Thread, the turns field will be an empty list.",
"items": {
"$ref": "#/definitions/Turn"
},
"type": "array"
},
"updatedAt": {
"description": "Unix timestamp (in seconds) when the thread was last updated.",
"format": "int64",
"type": "integer"
}
},
"required": [
"cliVersion",
"createdAt",
"cwd",
"ephemeral",
"id",
"modelProvider",
"preview",
"sessionId",
"source",
"status",
"turns",
"updatedAt"
],
"type": "object"
},
"ThreadActiveFlag": {
"enum": [
"waitingOnApproval",
"waitingOnUserInput"
],
"type": "string"
},
"ThreadId": {
"type": "string"
},
"ThreadItem": {
"oneOf": [
{
"properties": {
"content": {
"items": {
"$ref": "#/definitions/UserInput"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"userMessage"
],
"title": "UserMessageThreadItemType",
"type": "string"
}
},
"required": [
"content",
"id",
"type"
],
"title": "UserMessageThreadItem",
"type": "object"
},
{
"properties": {
"fragments": {
"items": {
"$ref": "#/definitions/HookPromptFragment"
},
"type": "array"
},
"id": {
"type": "string"
},
"type": {
"enum": [
"hookPrompt"
],
"title": "HookPromptThreadItemType",
"type": "string"
}
},
"required": [
"fragments",
"id",
"type"
],
"title": "HookPromptThreadItem",
"type": "object"
},
{
"properties": {
"id": {
"type": "string"
},
"memoryCitation": {
"anyOf": [
{
"$ref": "#/definitions/MemoryCitation"
},
{
"type": "null"
}
],
"default": null
},
"phase": {
"anyOf": [
{
"$ref": "#/definitions/MessagePhase"
},
{
"type": "null"
}
],
"default": null
},
"text": {
"type": "string"
},
"type": {
"enum": [
"agentMessage"
],
"title": "AgentMessageThreadItemType",
"type": "string"
}
},
"required": [
"id",
"text",
"type"
],
"title": "AgentMessageThreadItem",
"type": "object"
},
{
"description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.",
"properties": {
"id": {
"type": "string"
},
"text": {
"type": "string"
},
"type": {
"enum": [
"plan"
],
"title": "PlanThreadItemType",
"type": "string"
}
},
"required": [
"id",
"text",
"type"
],
"title": "PlanThreadItem",
"type": "object"
},
{
"properties": {
"content": {
"default": [],
"items": {
"type": "string"
},
"type": "array"
},
"id": {
"type": "string"
},
"summary": {
"default": [],
"items": {
"type": "string"
},
"type": "array"
},
"type": {
"enum": [
"reasoning"
],
"title": "ReasoningThreadItemType",
"type": "string"
}
},
"required": [
"id",
"type"
],
"title": "ReasoningThreadItem",
"type": "object"
},
{
"properties": {
"aggregatedOutput": {
"description": "The command's output, aggregated from stdout and stderr.",
"type": [
"string",
"null"
]
},
"command": {
"description": "The command to be executed.",
"type": "string"
},
"commandActions": {
"description": "A best-effort parsing of the command to understand the action(s) it will perform. This returns a list of CommandAction objects because a single shell command may be composed of many commands piped together.",
"items": {
"$ref": "#/definitions/CommandAction"
},
"type": "array"
},
"cwd": {
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
"format": "int64",
"type": [
"integer",
"null"
]
},
"exitCode": {
"description": "The command's exit code.",
"format": "int32",
"type": [
"integer",
"null"
]
},
"id": {
"type": "string"
},
"processId": {
"description": "Identifier for the underlying PTY process (when available).",
"type": [
"string",
"null"
]
},
"source": {
"allOf": [
{
"$ref": "#/definitions/CommandExecutionSource"
}
],
"default": "agent"
},
"status": {
"$ref": "#/definitions/CommandExecutionStatus"
},
"type": {
"enum": [
"commandExecution"
],
"title": "CommandExecutionThreadItemType",
"type": "string"
}
},
"required": [
"command",
"commandActions",
"cwd",
"id",
"status",
"type"
],
"title": "CommandExecutionThreadItem",
"type": "object"
},
{
"properties": {
"changes": {
"items": {
"$ref": "#/definitions/FileUpdateChange"
},
"type": "array"
},
"id": {
"type": "string"
},
"status": {
"$ref": "#/definitions/PatchApplyStatus"
},
"type": {
"enum": [
"fileChange"
],
"title": "FileChangeThreadItemType",
"type": "string"
}
},
"required": [
"changes",
"id",
"status",
"type"
],
"title": "FileChangeThreadItem",
"type": "object"
},
{
"properties": {
"arguments": true,
"durationMs": {
"description": "The duration of the MCP tool call in milliseconds.",
"format": "int64",
"type": [
"integer",
"null"
]
},
"error": {
"anyOf": [
{
"$ref": "#/definitions/McpToolCallError"
},
{
"type": "null"
}
]
},
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
"$ref": "#/definitions/McpToolCallResult"
},
{
"type": "null"
}
]
},
"server": {
"type": "string"
},
"status": {
"$ref": "#/definitions/McpToolCallStatus"
},
"tool": {
"type": "string"
},
"type": {
"enum": [
"mcpToolCall"
],
"title": "McpToolCallThreadItemType",
"type": "string"
}
},
"required": [
"arguments",
"id",
"server",
"status",
"tool",
"type"
],
"title": "McpToolCallThreadItem",
"type": "object"
},
{
"properties": {
"arguments": true,
"contentItems": {
"items": {
"$ref": "#/definitions/DynamicToolCallOutputContentItem"
},
"type": [
"array",
"null"
]
},
"durationMs": {
"description": "The duration of the dynamic tool call in milliseconds.",
"format": "int64",
"type": [
"integer",
"null"
]
},
"id": {
"type": "string"
},
"namespace": {
"type": [
"string",
"null"
]
},
"status": {
"$ref": "#/definitions/DynamicToolCallStatus"
},
"success": {
"type": [
"boolean",
"null"
]
},
"tool": {
"type": "string"
},
"type": {
"enum": [
"dynamicToolCall"
],
"title": "DynamicToolCallThreadItemType",
"type": "string"
}
},
"required": [
"arguments",
"id",
"status",
"tool",
"type"
],
"title": "DynamicToolCallThreadItem",
"type": "object"
},
{
"properties": {
"agentsStates": {
"additionalProperties": {
"$ref": "#/definitions/CollabAgentState"
},
"description": "Last known status of the target agents, when available.",
"type": "object"
},
"id": {
"description": "Unique identifier for this collab tool call.",
"type": "string"
},
"model": {
"description": "Model requested for the spawned agent, when applicable.",
"type": [
"string",
"null"
]
},
"prompt": {
"description": "Prompt text sent as part of the collab tool call, when available.",
"type": [
"string",
"null"
]
},
"reasoningEffort": {
"anyOf": [
{
"$ref": "#/definitions/ReasoningEffort"
},
{
"type": "null"
}
],
"description": "Reasoning effort requested for the spawned agent, when applicable."
},
"receiverThreadIds": {
"description": "Thread ID of the receiving agent, when applicable. In case of spawn operation, this corresponds to the newly spawned agent.",
"items": {
"type": "string"
},
"type": "array"
},
"senderThreadId": {
"description": "Thread ID of the agent issuing the collab request.",
"type": "string"
},
"status": {
"allOf": [
{
"$ref": "#/definitions/CollabAgentToolCallStatus"
}
],
"description": "Current status of the collab tool call."
},
"tool": {
"allOf": [
{
"$ref": "#/definitions/CollabAgentTool"
}
],
"description": "Name of the collab tool that was invoked."
},
"type": {
"enum": [
"collabAgentToolCall"
],
"title": "CollabAgentToolCallThreadItemType",
"type": "string"
}
},
"required": [
"agentsStates",
"id",
"receiverThreadIds",
"senderThreadId",
"status",
"tool",
"type"
],
"title": "CollabAgentToolCallThreadItem",
"type": "object"
},
{
"properties": {
"action": {
"anyOf": [
{
"$ref": "#/definitions/WebSearchAction"
},
{
"type": "null"
}
]
},
"id": {
"type": "string"
},
"query": {
"type": "string"
},
"type": {
"enum": [
"webSearch"
],
"title": "WebSearchThreadItemType",
"type": "string"
}
},
"required": [
"id",
"query",
"type"
],
"title": "WebSearchThreadItem",
"type": "object"
},
{
"properties": {
"id": {
"type": "string"
},
"path": {
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
"imageView"
],
"title": "ImageViewThreadItemType",
"type": "string"
}
},
"required": [
"id",
"path",
"type"
],
"title": "ImageViewThreadItem",
"type": "object"
},
{
"properties": {
"id": {
"type": "string"
},
"result": {
"type": "string"
},
"revisedPrompt": {
"type": [
"string",
"null"
]
},
"savedPath": {
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {
"type": "string"
},
"type": {
"enum": [
"imageGeneration"
],
"title": "ImageGenerationThreadItemType",
"type": "string"
}
},
"required": [
"id",
"result",
"status",
"type"
],
"title": "ImageGenerationThreadItem",
"type": "object"
},
{
"properties": {
"id": {
"type": "string"
},
"review": {
"type": "string"
},
"type": {
"enum": [
"enteredReviewMode"
],
"title": "EnteredReviewModeThreadItemType",
"type": "string"
}
},
"required": [
"id",
"review",
"type"
],
"title": "EnteredReviewModeThreadItem",
"type": "object"
},
{
"properties": {
"id": {
"type": "string"
},
"review": {
"type": "string"
},
"type": {
"enum": [
"exitedReviewMode"
],
"title": "ExitedReviewModeThreadItemType",
"type": "string"
}
},
"required": [
"id",
"review",
"type"
],
"title": "ExitedReviewModeThreadItem",
"type": "object"
},
{
"properties": {
"id": {
"type": "string"
},
"type": {
"enum": [
"contextCompaction"
],
"title": "ContextCompactionThreadItemType",
"type": "string"
}
},
"required": [
"id",
"type"
],
"title": "ContextCompactionThreadItem",
"type": "object"
}
]
},
"ThreadSource": {
"enum": [
"user",
"subagent",
"memory_consolidation"
],
"type": "string"
},
"ThreadStatus": {
"oneOf": [
{
"properties": {
"type": {
"enum": [
"notLoaded"
],
"title": "NotLoadedThreadStatusType",
"type": "string"
}
},
"required": [
"type"
],
"title": "NotLoadedThreadStatus",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"idle"
],
"title": "IdleThreadStatusType",
"type": "string"
}
},
"required": [
"type"
],
"title": "IdleThreadStatus",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"systemError"
],
"title": "SystemErrorThreadStatusType",
"type": "string"
}
},
"required": [
"type"
],
"title": "SystemErrorThreadStatus",
"type": "object"
},
{
"properties": {
"activeFlags": {
"items": {
"$ref": "#/definitions/ThreadActiveFlag"
},
"type": "array"
},
"type": {
"enum": [
"active"
],
"title": "ActiveThreadStatusType",
"type": "string"
}
},
"required": [
"activeFlags",
"type"
],
"title": "ActiveThreadStatus",
"type": "object"
}
]
},
"Turn": {
"properties": {
"completedAt": {
"description": "Unix timestamp (in seconds) when the turn completed.",
"format": "int64",
"type": [
"integer",
"null"
]
},
"durationMs": {
"description": "Duration between turn start and completion in milliseconds, if known.",
"format": "int64",
"type": [
"integer",
"null"
]
},
"error": {
"anyOf": [
{
"$ref": "#/definitions/TurnError"
},
{
"type": "null"
}
],
"description": "Only populated when the Turn's status is failed."
},
"id": {
"type": "string"
},
"items": {
"description": "Thread items currently included in this turn payload.",
"items": {
"$ref": "#/definitions/ThreadItem"
},
"type": "array"
},
"itemsView": {
"allOf": [
{
"$ref": "#/definitions/TurnItemsView"
}
],
"default": "full",
"description": "Describes how much of `items` has been loaded for this turn."
},
"startedAt": {
"description": "Unix timestamp (in seconds) when the turn started.",
"format": "int64",
"type": [
"integer",
"null"
]
},
"status": {
"$ref": "#/definitions/TurnStatus"
}
},
"required": [
"id",
"items",
"status"
],
"type": "object"
},
"TurnError": {
"properties": {
"additionalDetails": {
"default": null,
"type": [
"string",
"null"
]
},
"codexErrorInfo": {
"anyOf": [
{
"$ref": "#/definitions/CodexErrorInfo"
},
{
"type": "null"
}
]
},
"message": {
"type": "string"
}
},
"required": [
"message"
],
"type": "object"
},
"TurnItemsView": {
"oneOf": [
{
"description": "`items` was not loaded for this turn. The field is intentionally empty.",
"enum": [
"notLoaded"
],
"type": "string"
},
{
"description": "`items` contains only a display summary for this turn.",
"enum": [
"summary"
],
"type": "string"
},
{
"description": "`items` contains every ThreadItem available from persisted app-server history for this turn.",
"enum": [
"full"
],
"type": "string"
}
]
},
"TurnStatus": {
"enum": [
"completed",
"interrupted",
"failed",
"inProgress"
],
"type": "string"
},
"UserInput": {
"oneOf": [
{
"properties": {
"text": {
"type": "string"
},
"text_elements": {
"default": [],
"description": "UI-defined spans within `text` used to render or persist special elements.",
"items": {
"$ref": "#/definitions/TextElement"
},
"type": "array"
},
"type": {
"enum": [
"text"
],
"title": "TextUserInputType",
"type": "string"
}
},
"required": [
"text",
"type"
],
"title": "TextUserInput",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"image"
],
"title": "ImageUserInputType",
"type": "string"
},
"url": {
"type": "string"
}
},
"required": [
"type",
"url"
],
"title": "ImageUserInput",
"type": "object"
},
{
"properties": {
"path": {
"type": "string"
},
"type": {
"enum": [
"localImage"
],
"title": "LocalImageUserInputType",
"type": "string"
}
},
"required": [
"path",
"type"
],
"title": "LocalImageUserInput",
"type": "object"
},
{
"properties": {
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"type": {
"enum": [
"skill"
],
"title": "SkillUserInputType",
"type": "string"
}
},
"required": [
"name",
"path",
"type"
],
"title": "SkillUserInput",
"type": "object"
},
{
"properties": {
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"type": {
"enum": [
"mention"
],
"title": "MentionUserInputType",
"type": "string"
}
},
"required": [
"name",
"path",
"type"
],
"title": "MentionUserInput",
"type": "object"
}
]
},
"WebSearchAction": {
"oneOf": [
{
"properties": {
"queries": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"query": {
"type": [
"string",
"null"
]
},
"type": {
"enum": [
"search"
],
"title": "SearchWebSearchActionType",
"type": "string"
}
},
"required": [
"type"
],
"title": "SearchWebSearchAction",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"openPage"
],
"title": "OpenPageWebSearchActionType",
"type": "string"
},
"url": {
"type": [
"string",
"null"
]
}
},
"required": [
"type"
],
"title": "OpenPageWebSearchAction",
"type": "object"
},
{
"properties": {
"pattern": {
"type": [
"string",
"null"
]
},
"type": {
"enum": [
"findInPage"
],
"title": "FindInPageWebSearchActionType",
"type": "string"
},
"url": {
"type": [
"string",
"null"
]
}
},
"required": [
"type"
],
"title": "FindInPageWebSearchAction",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"other"
],
"title": "OtherWebSearchActionType",
"type": "string"
}
},
"required": [
"type"
],
"title": "OtherWebSearchAction",
"type": "object"
}
]
}
},
"properties": {
"approvalPolicy": {
"$ref": "#/definitions/AskForApproval"
},
"approvalsReviewer": {
"allOf": [
{
"$ref": "#/definitions/ApprovalsReviewer"
}
],
"description": "Reviewer currently used for approval requests on this thread."
},
"cwd": {
"$ref": "#/definitions/AbsolutePathBuf"
},
"instructionSources": {
"default": [],
"description": "Instruction source files currently loaded for this thread.",
"items": {
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": "array"
},
"model": {
"type": "string"
},
"modelProvider": {
"type": "string"
},
"reasoningEffort": {
"anyOf": [
{
"$ref": "#/definitions/ReasoningEffort"
},
{
"type": "null"
}
]
},
"sandbox": {
"allOf": [
{
"$ref": "#/definitions/SandboxPolicy"
}
],
"description": "Legacy sandbox policy retained for compatibility. Experimental clients should prefer `permissionProfile` when they need exact runtime permissions."
},
"serviceTier": {
"type": [
"string",
"null"
]
},
"thread": {
"$ref": "#/definitions/Thread"
}
},
"required": [
"approvalPolicy",
"approvalsReviewer",
"cwd",
"model",
"modelProvider",
"sandbox",
"thread"
],
"title": "ThreadStartResponse",
"type": "object"
}