mirror of
https://github.com/openai/codex.git
synced 2026-10-28 18:58:46 +00:00
Merge d7f9d5cbce into sapling-pr-archive-bolinfest
This commit is contained in:
Generated
+14
@@ -1375,6 +1375,7 @@ dependencies = [
|
||||
"codex-login",
|
||||
"codex-plugin",
|
||||
"codex-protocol",
|
||||
"codex-utils-absolute-path",
|
||||
"os_info",
|
||||
"pretty_assertions",
|
||||
"serde",
|
||||
@@ -2236,6 +2237,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"codex-config",
|
||||
"codex-protocol",
|
||||
"codex-utils-absolute-path",
|
||||
"futures",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
@@ -2385,6 +2387,7 @@ dependencies = [
|
||||
"codex-models-manager",
|
||||
"codex-protocol",
|
||||
"codex-shell-command",
|
||||
"codex-utils-absolute-path",
|
||||
"codex-utils-cli",
|
||||
"codex-utils-json-to-toml",
|
||||
"core_test_support",
|
||||
@@ -2824,6 +2827,17 @@ dependencies = [
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codex-thread-store"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"chrono",
|
||||
"codex-protocol",
|
||||
"serde",
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codex-tools"
|
||||
version = "0.0.0"
|
||||
|
||||
@@ -87,6 +87,7 @@ members = [
|
||||
"state",
|
||||
"terminal-detection",
|
||||
"test-binary-support",
|
||||
"thread-store",
|
||||
"codex-experimental-api-macros",
|
||||
"plugin",
|
||||
]
|
||||
|
||||
@@ -29,4 +29,5 @@ tokio = { workspace = true, features = [
|
||||
tracing = { workspace = true, features = ["log"] }
|
||||
|
||||
[dev-dependencies]
|
||||
codex-utils-absolute-path = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
|
||||
@@ -85,6 +85,8 @@ use codex_protocol::protocol::SandboxPolicy;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use codex_protocol::protocol::SubAgentSource;
|
||||
use codex_protocol::protocol::TokenUsage;
|
||||
use codex_utils_absolute_path::test_support::PathBufExt;
|
||||
use codex_utils_absolute_path::test_support::test_path_buf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use std::collections::HashSet;
|
||||
@@ -112,7 +114,7 @@ fn sample_thread_with_source(
|
||||
updated_at: 2,
|
||||
status: AppServerThreadStatus::Idle,
|
||||
path: None,
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
cli_version: "0.0.0".to_string(),
|
||||
source,
|
||||
agent_nickname: None,
|
||||
@@ -131,7 +133,7 @@ fn sample_thread_start_response(thread_id: &str, ephemeral: bool, model: &str) -
|
||||
model: model.to_string(),
|
||||
model_provider: "openai".to_string(),
|
||||
service_tier: None,
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
instruction_sources: Vec::new(),
|
||||
approval_policy: AppServerAskForApproval::OnFailure,
|
||||
approvals_reviewer: AppServerApprovalsReviewer::User,
|
||||
@@ -182,7 +184,7 @@ fn sample_thread_resume_response_with_source(
|
||||
model: model.to_string(),
|
||||
model_provider: "openai".to_string(),
|
||||
service_tier: None,
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
instruction_sources: Vec::new(),
|
||||
approval_policy: AppServerAskForApproval::OnFailure,
|
||||
approvals_reviewer: AppServerApprovalsReviewer::User,
|
||||
|
||||
+10
-6
@@ -75,7 +75,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -326,11 +326,15 @@
|
||||
]
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"itemId": {
|
||||
"type": "string"
|
||||
|
||||
@@ -608,7 +608,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1180,7 +1180,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"source": {
|
||||
"$ref": "#/definitions/GuardianCommandSource"
|
||||
@@ -1211,7 +1211,7 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"program": {
|
||||
"type": "string"
|
||||
@@ -1240,11 +1240,11 @@
|
||||
{
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"files": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
@@ -1518,7 +1518,7 @@
|
||||
"$ref": "#/definitions/HookScope"
|
||||
},
|
||||
"sourcePath": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"startedAt": {
|
||||
"format": "int64",
|
||||
@@ -2461,8 +2461,12 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "Working directory captured for the thread.",
|
||||
"type": "string"
|
||||
"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.",
|
||||
@@ -2769,8 +2773,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -3099,7 +3107,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -3132,9 +3140,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -346,11 +346,15 @@
|
||||
]
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"itemId": {
|
||||
"type": "string"
|
||||
|
||||
@@ -1841,11 +1841,15 @@
|
||||
]
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"itemId": {
|
||||
"type": "string"
|
||||
@@ -5950,7 +5954,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -8243,7 +8247,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
"source": {
|
||||
"$ref": "#/definitions/v2/GuardianCommandSource"
|
||||
@@ -8274,7 +8278,7 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
"program": {
|
||||
"type": "string"
|
||||
@@ -8303,11 +8307,11 @@
|
||||
{
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
"files": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
@@ -8583,7 +8587,7 @@
|
||||
"$ref": "#/definitions/v2/HookScope"
|
||||
},
|
||||
"sourcePath": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
"startedAt": {
|
||||
"format": "int64",
|
||||
@@ -12158,15 +12162,23 @@
|
||||
]
|
||||
},
|
||||
"iconLarge": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"iconSmall": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"shortDescription": {
|
||||
@@ -12647,8 +12659,12 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "Working directory captured for the thread.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "Working directory captured for the thread."
|
||||
},
|
||||
"ephemeral": {
|
||||
"description": "Whether the thread is ephemeral and should not be materialized on disk.",
|
||||
@@ -12928,13 +12944,13 @@
|
||||
"description": "Reviewer currently used for approval requests on this thread."
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
"instructionSources": {
|
||||
"default": [],
|
||||
"description": "Instruction source files currently loaded for this thread.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
@@ -13192,8 +13208,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -13522,7 +13542,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -13555,9 +13575,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
@@ -14280,13 +14304,13 @@
|
||||
"description": "Reviewer currently used for approval requests on this thread."
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
"instructionSources": {
|
||||
"default": [],
|
||||
"description": "Instruction source files currently loaded for this thread.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
@@ -14579,13 +14603,13 @@
|
||||
"description": "Reviewer currently used for approval requests on this thread."
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
"instructionSources": {
|
||||
"default": [],
|
||||
"description": "Instruction source files currently loaded for this thread.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
|
||||
+46
-26
@@ -2571,7 +2571,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -4975,7 +4975,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"source": {
|
||||
"$ref": "#/definitions/GuardianCommandSource"
|
||||
@@ -5006,7 +5006,7 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"program": {
|
||||
"type": "string"
|
||||
@@ -5035,11 +5035,11 @@
|
||||
{
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"files": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
@@ -5315,7 +5315,7 @@
|
||||
"$ref": "#/definitions/HookScope"
|
||||
},
|
||||
"sourcePath": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"startedAt": {
|
||||
"format": "int64",
|
||||
@@ -10006,15 +10006,23 @@
|
||||
]
|
||||
},
|
||||
"iconLarge": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"iconSmall": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"shortDescription": {
|
||||
@@ -10495,8 +10503,12 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "Working directory captured for the thread.",
|
||||
"type": "string"
|
||||
"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.",
|
||||
@@ -10776,13 +10788,13 @@
|
||||
"description": "Reviewer currently used for approval requests on this thread."
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"instructionSources": {
|
||||
"default": [],
|
||||
"description": "Instruction source files currently loaded for this thread.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
@@ -11040,8 +11052,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -11370,7 +11386,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -11403,9 +11419,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
@@ -12128,13 +12148,13 @@
|
||||
"description": "Reviewer currently used for approval requests on this thread."
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"instructionSources": {
|
||||
"default": [],
|
||||
"description": "Instruction source files currently loaded for this thread.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
@@ -12427,13 +12447,13 @@
|
||||
"description": "Reviewer currently used for approval requests on this thread."
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"instructionSources": {
|
||||
"default": [],
|
||||
"description": "Instruction source files currently loaded for this thread.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"HookEventName": {
|
||||
"enum": [
|
||||
"preToolUse",
|
||||
@@ -103,7 +107,7 @@
|
||||
"$ref": "#/definitions/HookScope"
|
||||
},
|
||||
"sourcePath": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"startedAt": {
|
||||
"format": "int64",
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"HookEventName": {
|
||||
"enum": [
|
||||
"preToolUse",
|
||||
@@ -103,7 +107,7 @@
|
||||
"$ref": "#/definitions/HookScope"
|
||||
},
|
||||
"sourcePath": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"startedAt": {
|
||||
"format": "int64",
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"ByteRange": {
|
||||
"properties": {
|
||||
"end": {
|
||||
@@ -78,7 +82,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -665,8 +669,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -995,7 +1003,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1028,9 +1036,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
|
||||
+8
-4
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"AutoReviewDecisionSource": {
|
||||
"description": "[UNSTABLE] Source that produced a terminal guardian approval review decision.",
|
||||
"enum": [
|
||||
@@ -54,7 +58,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"source": {
|
||||
"$ref": "#/definitions/GuardianCommandSource"
|
||||
@@ -85,7 +89,7 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"program": {
|
||||
"type": "string"
|
||||
@@ -114,11 +118,11 @@
|
||||
{
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"files": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
|
||||
+8
-4
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"GuardianApprovalReview": {
|
||||
"description": "[UNSTABLE] Temporary guardian approval review payload used by `item/autoApprovalReview/*` notifications. This shape is expected to change soon.",
|
||||
"properties": {
|
||||
@@ -47,7 +51,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"source": {
|
||||
"$ref": "#/definitions/GuardianCommandSource"
|
||||
@@ -78,7 +82,7 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"program": {
|
||||
"type": "string"
|
||||
@@ -107,11 +111,11 @@
|
||||
{
|
||||
"properties": {
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"files": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"ByteRange": {
|
||||
"properties": {
|
||||
"end": {
|
||||
@@ -78,7 +82,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -665,8 +669,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -995,7 +1003,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1028,9 +1036,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
|
||||
@@ -293,15 +293,23 @@
|
||||
]
|
||||
},
|
||||
"iconLarge": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"iconSmall": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"shortDescription": {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"ByteRange": {
|
||||
"properties": {
|
||||
"end": {
|
||||
@@ -214,7 +218,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -808,8 +812,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -1138,7 +1146,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1171,9 +1179,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
|
||||
@@ -55,15 +55,23 @@
|
||||
]
|
||||
},
|
||||
"iconLarge": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"iconSmall": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"shortDescription": {
|
||||
|
||||
@@ -279,7 +279,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1036,8 +1036,12 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "Working directory captured for the thread.",
|
||||
"type": "string"
|
||||
"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.",
|
||||
@@ -1322,8 +1326,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -1652,7 +1660,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1685,9 +1693,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
@@ -2185,13 +2197,13 @@
|
||||
"description": "Reviewer currently used for approval requests on this thread."
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"instructionSources": {
|
||||
"default": [],
|
||||
"description": "Instruction source files currently loaded for this thread.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"AgentPath": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -217,7 +221,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -794,8 +798,12 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "Working directory captured for the thread.",
|
||||
"type": "string"
|
||||
"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.",
|
||||
@@ -1080,8 +1088,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -1410,7 +1422,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1443,9 +1455,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"AgentPath": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -217,7 +221,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -794,8 +798,12 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "Working directory captured for the thread.",
|
||||
"type": "string"
|
||||
"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.",
|
||||
@@ -1080,8 +1088,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -1410,7 +1422,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1443,9 +1455,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"AgentPath": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -217,7 +221,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -794,8 +798,12 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "Working directory captured for the thread.",
|
||||
"type": "string"
|
||||
"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.",
|
||||
@@ -1080,8 +1088,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -1410,7 +1422,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1443,9 +1455,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
|
||||
@@ -279,7 +279,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1036,8 +1036,12 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "Working directory captured for the thread.",
|
||||
"type": "string"
|
||||
"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.",
|
||||
@@ -1322,8 +1326,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -1652,7 +1660,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1685,9 +1693,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
@@ -2185,13 +2197,13 @@
|
||||
"description": "Reviewer currently used for approval requests on this thread."
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"instructionSources": {
|
||||
"default": [],
|
||||
"description": "Instruction source files currently loaded for this thread.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"AgentPath": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -217,7 +221,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -794,8 +798,12 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "Working directory captured for the thread.",
|
||||
"type": "string"
|
||||
"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.",
|
||||
@@ -1080,8 +1088,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -1410,7 +1422,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1443,9 +1455,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
|
||||
@@ -279,7 +279,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1036,8 +1036,12 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "Working directory captured for the thread.",
|
||||
"type": "string"
|
||||
"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.",
|
||||
@@ -1322,8 +1326,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -1652,7 +1660,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1685,9 +1693,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
@@ -2185,13 +2197,13 @@
|
||||
"description": "Reviewer currently used for approval requests on this thread."
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"instructionSources": {
|
||||
"default": [],
|
||||
"description": "Instruction source files currently loaded for this thread.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"AgentPath": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -217,7 +221,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -794,8 +798,12 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "Working directory captured for the thread.",
|
||||
"type": "string"
|
||||
"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.",
|
||||
@@ -1080,8 +1088,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -1410,7 +1422,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1443,9 +1455,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"AgentPath": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -217,7 +221,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -794,8 +798,12 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "Working directory captured for the thread.",
|
||||
"type": "string"
|
||||
"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.",
|
||||
@@ -1080,8 +1088,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -1410,7 +1422,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1443,9 +1455,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"ByteRange": {
|
||||
"properties": {
|
||||
"end": {
|
||||
@@ -214,7 +218,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -808,8 +812,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -1138,7 +1146,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1171,9 +1179,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"ByteRange": {
|
||||
"properties": {
|
||||
"end": {
|
||||
@@ -214,7 +218,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -808,8 +812,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -1138,7 +1146,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1171,9 +1179,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"ByteRange": {
|
||||
"properties": {
|
||||
"end": {
|
||||
@@ -214,7 +218,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -808,8 +812,12 @@
|
||||
"type": "array"
|
||||
},
|
||||
"cwd": {
|
||||
"description": "The command's working directory.",
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "The command's working directory."
|
||||
},
|
||||
"durationMs": {
|
||||
"description": "The duration of the command execution in milliseconds.",
|
||||
@@ -1138,7 +1146,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
@@ -1171,9 +1179,13 @@
|
||||
]
|
||||
},
|
||||
"savedPath": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
|
||||
|
||||
export type CommandAction = { "type": "read", command: string, name: string, path: string, } | { "type": "listFiles", command: string, path: string | null, } | { "type": "search", command: string, query: string | null, path: string | null, } | { "type": "unknown", command: string, };
|
||||
export type CommandAction = { "type": "read", command: string, name: string, path: AbsolutePathBuf, } | { "type": "listFiles", command: string, path: string | null, } | { "type": "search", command: string, query: string | null, path: string | null, } | { "type": "unknown", command: string, };
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
|
||||
import type { AdditionalPermissionProfile } from "./AdditionalPermissionProfile";
|
||||
import type { CommandAction } from "./CommandAction";
|
||||
import type { CommandExecutionApprovalDecision } from "./CommandExecutionApprovalDecision";
|
||||
@@ -34,7 +35,7 @@ command?: string | null,
|
||||
/**
|
||||
* The command's working directory.
|
||||
*/
|
||||
cwd?: string | null,
|
||||
cwd?: AbsolutePathBuf | null,
|
||||
/**
|
||||
* Best-effort parsed command actions for friendly display.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
|
||||
import type { GuardianCommandSource } from "./GuardianCommandSource";
|
||||
import type { NetworkApprovalProtocol } from "./NetworkApprovalProtocol";
|
||||
|
||||
export type GuardianApprovalReviewAction = { "type": "command", source: GuardianCommandSource, command: string, cwd: string, } | { "type": "execve", source: GuardianCommandSource, program: string, argv: Array<string>, cwd: string, } | { "type": "applyPatch", cwd: string, files: Array<string>, } | { "type": "networkAccess", target: string, host: string, protocol: NetworkApprovalProtocol, port: number, } | { "type": "mcpToolCall", server: string, toolName: string, connectorId: string | null, connectorName: string | null, toolTitle: string | null, };
|
||||
export type GuardianApprovalReviewAction = { "type": "command", source: GuardianCommandSource, command: string, cwd: AbsolutePathBuf, } | { "type": "execve", source: GuardianCommandSource, program: string, argv: Array<string>, cwd: AbsolutePathBuf, } | { "type": "applyPatch", cwd: AbsolutePathBuf, files: Array<AbsolutePathBuf>, } | { "type": "networkAccess", target: string, host: string, protocol: NetworkApprovalProtocol, port: number, } | { "type": "mcpToolCall", server: string, toolName: string, connectorId: string | null, connectorName: string | null, toolTitle: string | null, };
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
|
||||
import type { HookEventName } from "./HookEventName";
|
||||
import type { HookExecutionMode } from "./HookExecutionMode";
|
||||
import type { HookHandlerType } from "./HookHandlerType";
|
||||
@@ -8,4 +9,4 @@ import type { HookOutputEntry } from "./HookOutputEntry";
|
||||
import type { HookRunStatus } from "./HookRunStatus";
|
||||
import type { HookScope } from "./HookScope";
|
||||
|
||||
export type HookRunSummary = { id: string, eventName: HookEventName, handlerType: HookHandlerType, executionMode: HookExecutionMode, scope: HookScope, sourcePath: string, displayOrder: bigint, status: HookRunStatus, statusMessage: string | null, startedAt: bigint, completedAt: bigint | null, durationMs: bigint | null, entries: Array<HookOutputEntry>, };
|
||||
export type HookRunSummary = { id: string, eventName: HookEventName, handlerType: HookHandlerType, executionMode: HookExecutionMode, scope: HookScope, sourcePath: AbsolutePathBuf, displayOrder: bigint, status: HookRunStatus, statusMessage: string | null, startedAt: bigint, completedAt: bigint | null, durationMs: bigint | null, entries: Array<HookOutputEntry>, };
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
|
||||
|
||||
export type SkillInterface = { displayName?: string, shortDescription?: string, iconSmall?: string, iconLarge?: string, brandColor?: string, defaultPrompt?: string, };
|
||||
export type SkillInterface = { displayName?: string, shortDescription?: string, iconSmall?: AbsolutePathBuf, iconLarge?: AbsolutePathBuf, brandColor?: string, defaultPrompt?: string, };
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
|
||||
import type { GitInfo } from "./GitInfo";
|
||||
import type { SessionSource } from "./SessionSource";
|
||||
import type { ThreadStatus } from "./ThreadStatus";
|
||||
@@ -42,7 +43,7 @@ path: string | null,
|
||||
/**
|
||||
* Working directory captured for the thread.
|
||||
*/
|
||||
cwd: string,
|
||||
cwd: AbsolutePathBuf,
|
||||
/**
|
||||
* Version of the CLI that created the thread.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
|
||||
import type { ReasoningEffort } from "../ReasoningEffort";
|
||||
import type { ServiceTier } from "../ServiceTier";
|
||||
import type { ApprovalsReviewer } from "./ApprovalsReviewer";
|
||||
@@ -8,11 +9,11 @@ import type { AskForApproval } from "./AskForApproval";
|
||||
import type { SandboxPolicy } from "./SandboxPolicy";
|
||||
import type { Thread } from "./Thread";
|
||||
|
||||
export type ThreadForkResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: string,
|
||||
export type ThreadForkResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: AbsolutePathBuf,
|
||||
/**
|
||||
* Instruction source files currently loaded for this thread.
|
||||
*/
|
||||
instructionSources: Array<string>, approvalPolicy: AskForApproval,
|
||||
instructionSources: Array<AbsolutePathBuf>, approvalPolicy: AskForApproval,
|
||||
/**
|
||||
* Reviewer currently used for approval requests on this thread.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
|
||||
import type { MessagePhase } from "../MessagePhase";
|
||||
import type { ReasoningEffort } from "../ReasoningEffort";
|
||||
import type { JsonValue } from "../serde_json/JsonValue";
|
||||
@@ -30,7 +31,7 @@ command: string,
|
||||
/**
|
||||
* The command's working directory.
|
||||
*/
|
||||
cwd: string,
|
||||
cwd: AbsolutePathBuf,
|
||||
/**
|
||||
* Identifier for the underlying PTY process (when available).
|
||||
*/
|
||||
@@ -97,4 +98,4 @@ reasoningEffort: ReasoningEffort | null,
|
||||
/**
|
||||
* Last known status of the target agents, when available.
|
||||
*/
|
||||
agentsStates: { [key in string]?: CollabAgentState }, } | { "type": "webSearch", id: string, query: string, action: WebSearchAction | null, } | { "type": "imageView", id: string, path: string, } | { "type": "imageGeneration", id: string, status: string, revisedPrompt: string | null, result: string, savedPath?: string, } | { "type": "enteredReviewMode", id: string, review: string, } | { "type": "exitedReviewMode", id: string, review: string, } | { "type": "contextCompaction", id: string, };
|
||||
agentsStates: { [key in string]?: CollabAgentState }, } | { "type": "webSearch", id: string, query: string, action: WebSearchAction | null, } | { "type": "imageView", id: string, path: AbsolutePathBuf, } | { "type": "imageGeneration", id: string, status: string, revisedPrompt: string | null, result: string, savedPath?: AbsolutePathBuf, } | { "type": "enteredReviewMode", id: string, review: string, } | { "type": "exitedReviewMode", id: string, review: string, } | { "type": "contextCompaction", id: string, };
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
|
||||
import type { ReasoningEffort } from "../ReasoningEffort";
|
||||
import type { ServiceTier } from "../ServiceTier";
|
||||
import type { ApprovalsReviewer } from "./ApprovalsReviewer";
|
||||
@@ -8,11 +9,11 @@ import type { AskForApproval } from "./AskForApproval";
|
||||
import type { SandboxPolicy } from "./SandboxPolicy";
|
||||
import type { Thread } from "./Thread";
|
||||
|
||||
export type ThreadResumeResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: string,
|
||||
export type ThreadResumeResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: AbsolutePathBuf,
|
||||
/**
|
||||
* Instruction source files currently loaded for this thread.
|
||||
*/
|
||||
instructionSources: Array<string>, approvalPolicy: AskForApproval,
|
||||
instructionSources: Array<AbsolutePathBuf>, approvalPolicy: AskForApproval,
|
||||
/**
|
||||
* Reviewer currently used for approval requests on this thread.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
|
||||
import type { ReasoningEffort } from "../ReasoningEffort";
|
||||
import type { ServiceTier } from "../ServiceTier";
|
||||
import type { ApprovalsReviewer } from "./ApprovalsReviewer";
|
||||
@@ -8,11 +9,11 @@ import type { AskForApproval } from "./AskForApproval";
|
||||
import type { SandboxPolicy } from "./SandboxPolicy";
|
||||
import type { Thread } from "./Thread";
|
||||
|
||||
export type ThreadStartResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: string,
|
||||
export type ThreadStartResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: AbsolutePathBuf,
|
||||
/**
|
||||
* Instruction source files currently loaded for this thread.
|
||||
*/
|
||||
instructionSources: Array<string>, approvalPolicy: AskForApproval,
|
||||
instructionSources: Array<AbsolutePathBuf>, approvalPolicy: AskForApproval,
|
||||
/**
|
||||
* Reviewer currently used for approval requests on this thread.
|
||||
*/
|
||||
|
||||
@@ -1065,21 +1065,20 @@ mod tests {
|
||||
use codex_protocol::protocol::RealtimeOutputModality;
|
||||
use codex_protocol::protocol::RealtimeVoice;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_absolute_path::test_support::PathBufExt;
|
||||
use codex_utils_absolute_path::test_support::test_path_buf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn absolute_path_string(path: &str) -> String {
|
||||
let trimmed = path.trim_start_matches('/');
|
||||
if cfg!(windows) {
|
||||
format!(r"C:\{}", trimmed.replace('/', "\\"))
|
||||
} else {
|
||||
format!("/{trimmed}")
|
||||
}
|
||||
let path = format!("/{}", path.trim_start_matches('/'));
|
||||
test_path_buf(&path).display().to_string()
|
||||
}
|
||||
|
||||
fn absolute_path(path: &str) -> AbsolutePathBuf {
|
||||
AbsolutePathBuf::from_absolute_path(absolute_path_string(path)).expect("absolute path")
|
||||
let path = format!("/{}", path.trim_start_matches('/'));
|
||||
test_path_buf(&path).abs()
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1410,7 +1409,7 @@ mod tests {
|
||||
updated_at: 2,
|
||||
status: v2::ThreadStatus::Idle,
|
||||
path: None,
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: absolute_path("/tmp"),
|
||||
cli_version: "0.0.0".to_string(),
|
||||
source: v2::SessionSource::Exec,
|
||||
agent_nickname: None,
|
||||
@@ -1422,8 +1421,8 @@ mod tests {
|
||||
model: "gpt-5".to_string(),
|
||||
model_provider: "openai".to_string(),
|
||||
service_tier: None,
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
instruction_sources: vec![PathBuf::from("/tmp/AGENTS.md")],
|
||||
cwd: absolute_path("/tmp"),
|
||||
instruction_sources: vec![absolute_path("/tmp/AGENTS.md")],
|
||||
approval_policy: v2::AskForApproval::OnFailure,
|
||||
approvals_reviewer: v2::ApprovalsReviewer::User,
|
||||
sandbox: v2::SandboxPolicy::DangerFullAccess,
|
||||
@@ -1450,7 +1449,7 @@ mod tests {
|
||||
"type": "idle"
|
||||
},
|
||||
"path": null,
|
||||
"cwd": "/tmp",
|
||||
"cwd": absolute_path_string("tmp"),
|
||||
"cliVersion": "0.0.0",
|
||||
"source": "exec",
|
||||
"agentNickname": null,
|
||||
@@ -1462,8 +1461,8 @@ mod tests {
|
||||
"model": "gpt-5",
|
||||
"modelProvider": "openai",
|
||||
"serviceTier": null,
|
||||
"cwd": "/tmp",
|
||||
"instructionSources": ["/tmp/AGENTS.md"],
|
||||
"cwd": absolute_path_string("tmp"),
|
||||
"instructionSources": [absolute_path_string("tmp/AGENTS.md")],
|
||||
"approvalPolicy": "on-failure",
|
||||
"approvalsReviewer": "user",
|
||||
"sandbox": {
|
||||
|
||||
@@ -78,7 +78,7 @@ pub fn build_command_execution_approval_request_item(
|
||||
.parsed_cmd
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(CommandAction::from)
|
||||
.map(|parsed| CommandAction::from_core_with_cwd(parsed, &payload.cwd))
|
||||
.collect(),
|
||||
aggregated_output: None,
|
||||
exit_code: None,
|
||||
@@ -98,7 +98,7 @@ pub fn build_command_execution_begin_item(payload: &ExecCommandBeginEvent) -> Th
|
||||
.parsed_cmd
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(CommandAction::from)
|
||||
.map(|parsed| CommandAction::from_core_with_cwd(parsed, &payload.cwd))
|
||||
.collect(),
|
||||
aggregated_output: None,
|
||||
exit_code: None,
|
||||
@@ -125,7 +125,7 @@ pub fn build_command_execution_end_item(payload: &ExecCommandEndEvent) -> Thread
|
||||
.parsed_cmd
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(CommandAction::from)
|
||||
.map(|parsed| CommandAction::from_core_with_cwd(parsed, &payload.cwd))
|
||||
.collect(),
|
||||
aggregated_output,
|
||||
exit_code: Some(payload.exit_code),
|
||||
@@ -179,7 +179,10 @@ pub fn build_item_from_guardian_event(
|
||||
command: command.clone(),
|
||||
}]
|
||||
} else {
|
||||
parsed_cmd.into_iter().map(CommandAction::from).collect()
|
||||
parsed_cmd
|
||||
.into_iter()
|
||||
.map(|parsed| CommandAction::from_core_with_cwd(parsed, cwd))
|
||||
.collect()
|
||||
};
|
||||
Some(ThreadItem::CommandExecution {
|
||||
id: id.clone(),
|
||||
|
||||
@@ -552,7 +552,7 @@ impl ThreadHistoryBuilder {
|
||||
fn handle_view_image_tool_call(&mut self, payload: &ViewImageToolCallEvent) {
|
||||
let item = ThreadItem::ImageView {
|
||||
id: payload.call_id.clone(),
|
||||
path: payload.path.to_string_lossy().into_owned(),
|
||||
path: payload.path.clone(),
|
||||
};
|
||||
self.upsert_item_in_current_turn(item);
|
||||
}
|
||||
@@ -1193,6 +1193,8 @@ mod tests {
|
||||
use codex_protocol::protocol::TurnStartedEvent;
|
||||
use codex_protocol::protocol::UserMessageEvent;
|
||||
use codex_protocol::protocol::WebSearchEndEvent;
|
||||
use codex_utils_absolute_path::test_support::PathBufExt;
|
||||
use codex_utils_absolute_path::test_support::test_path_buf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
@@ -1397,7 +1399,7 @@ mod tests {
|
||||
status: "completed".into(),
|
||||
revised_prompt: Some("final prompt".into()),
|
||||
result: "Zm9v".into(),
|
||||
saved_path: Some("/tmp/ig_123.png".into()),
|
||||
saved_path: Some(test_path_buf("/tmp/ig_123.png").abs()),
|
||||
})),
|
||||
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
|
||||
turn_id: "turn-image".into(),
|
||||
@@ -1431,7 +1433,7 @@ mod tests {
|
||||
status: "completed".into(),
|
||||
revised_prompt: Some("final prompt".into()),
|
||||
result: "Zm9v".into(),
|
||||
saved_path: Some("/tmp/ig_123.png".into()),
|
||||
saved_path: Some(test_path_buf("/tmp/ig_123.png").abs()),
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1786,7 +1788,7 @@ mod tests {
|
||||
process_id: Some("pid-1".into()),
|
||||
turn_id: "turn-1".into(),
|
||||
command: vec!["echo".into(), "hello world".into()],
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
parsed_cmd: vec![ParsedCommand::Unknown {
|
||||
cmd: "echo hello world".into(),
|
||||
}],
|
||||
@@ -1835,7 +1837,7 @@ mod tests {
|
||||
ThreadItem::CommandExecution {
|
||||
id: "exec-1".into(),
|
||||
command: "echo 'hello world'".into(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
process_id: Some("pid-1".into()),
|
||||
source: CommandExecutionSource::Agent,
|
||||
status: CommandExecutionStatus::Completed,
|
||||
@@ -2005,7 +2007,7 @@ mod tests {
|
||||
process_id: Some("pid-2".into()),
|
||||
turn_id: "turn-1".into(),
|
||||
command: vec!["ls".into()],
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
parsed_cmd: vec![ParsedCommand::Unknown { cmd: "ls".into() }],
|
||||
source: ExecCommandSource::Agent,
|
||||
interaction_input: None,
|
||||
@@ -2047,7 +2049,7 @@ mod tests {
|
||||
ThreadItem::CommandExecution {
|
||||
id: "exec-declined".into(),
|
||||
command: "ls".into(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
process_id: Some("pid-2".into()),
|
||||
source: CommandExecutionSource::Agent,
|
||||
status: CommandExecutionStatus::Declined,
|
||||
@@ -2101,7 +2103,7 @@ mod tests {
|
||||
"type": "command",
|
||||
"source": "shell",
|
||||
"command": "rm -rf /tmp/guardian",
|
||||
"cwd": "/tmp",
|
||||
"cwd": test_path_buf("/tmp"),
|
||||
}))
|
||||
.expect("guardian action"),
|
||||
}),
|
||||
@@ -2120,7 +2122,7 @@ mod tests {
|
||||
"type": "command",
|
||||
"source": "shell",
|
||||
"command": "rm -rf /tmp/guardian",
|
||||
"cwd": "/tmp",
|
||||
"cwd": test_path_buf("/tmp"),
|
||||
}))
|
||||
.expect("guardian action"),
|
||||
}),
|
||||
@@ -2138,7 +2140,7 @@ mod tests {
|
||||
ThreadItem::CommandExecution {
|
||||
id: "guardian-exec".into(),
|
||||
command: "rm -rf /tmp/guardian".into(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
process_id: None,
|
||||
source: CommandExecutionSource::Agent,
|
||||
status: CommandExecutionStatus::Declined,
|
||||
@@ -2181,7 +2183,7 @@ mod tests {
|
||||
"source": "shell",
|
||||
"program": "/bin/rm",
|
||||
"argv": ["/usr/bin/rm", "-f", "/tmp/file.sqlite"],
|
||||
"cwd": "/tmp",
|
||||
"cwd": test_path_buf("/tmp"),
|
||||
}))
|
||||
.expect("guardian action"),
|
||||
}),
|
||||
@@ -2199,7 +2201,7 @@ mod tests {
|
||||
ThreadItem::CommandExecution {
|
||||
id: "guardian-execve".into(),
|
||||
command: "/bin/rm -f /tmp/file.sqlite".into(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
process_id: None,
|
||||
source: CommandExecutionSource::Agent,
|
||||
status: CommandExecutionStatus::InProgress,
|
||||
@@ -2251,7 +2253,7 @@ mod tests {
|
||||
process_id: Some("pid-42".into()),
|
||||
turn_id: "turn-a".into(),
|
||||
command: vec!["echo".into(), "done".into()],
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
parsed_cmd: vec![ParsedCommand::Unknown {
|
||||
cmd: "echo done".into(),
|
||||
}],
|
||||
@@ -2288,7 +2290,7 @@ mod tests {
|
||||
ThreadItem::CommandExecution {
|
||||
id: "exec-late".into(),
|
||||
command: "echo done".into(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
process_id: Some("pid-42".into()),
|
||||
source: CommandExecutionSource::Agent,
|
||||
status: CommandExecutionStatus::Completed,
|
||||
@@ -2340,7 +2342,7 @@ mod tests {
|
||||
process_id: Some("pid-42".into()),
|
||||
turn_id: "turn-missing".into(),
|
||||
command: vec!["echo".into(), "done".into()],
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
parsed_cmd: vec![ParsedCommand::Unknown {
|
||||
cmd: "echo done".into(),
|
||||
}],
|
||||
|
||||
@@ -80,7 +80,6 @@ use codex_protocol::protocol::RealtimeVoicesList;
|
||||
use codex_protocol::protocol::ReviewDecision as CoreReviewDecision;
|
||||
use codex_protocol::protocol::SessionSource as CoreSessionSource;
|
||||
use codex_protocol::protocol::SkillDependencies as CoreSkillDependencies;
|
||||
use codex_protocol::protocol::SkillErrorInfo as CoreSkillErrorInfo;
|
||||
use codex_protocol::protocol::SkillInterface as CoreSkillInterface;
|
||||
use codex_protocol::protocol::SkillMetadata as CoreSkillMetadata;
|
||||
use codex_protocol::protocol::SkillScope as CoreSkillScope;
|
||||
@@ -449,7 +448,7 @@ pub struct HookRunSummary {
|
||||
pub handler_type: HookHandlerType,
|
||||
pub execution_mode: HookExecutionMode,
|
||||
pub scope: HookScope,
|
||||
pub source_path: PathBuf,
|
||||
pub source_path: AbsolutePathBuf,
|
||||
pub display_order: i64,
|
||||
pub status: HookRunStatus,
|
||||
pub status_message: Option<String>,
|
||||
@@ -1467,7 +1466,7 @@ pub enum CommandAction {
|
||||
Read {
|
||||
command: String,
|
||||
name: String,
|
||||
path: PathBuf,
|
||||
path: AbsolutePathBuf,
|
||||
},
|
||||
ListFiles {
|
||||
command: String,
|
||||
@@ -1545,7 +1544,11 @@ impl CommandAction {
|
||||
command: cmd,
|
||||
name,
|
||||
path,
|
||||
} => CoreParsedCommand::Read { cmd, name, path },
|
||||
} => CoreParsedCommand::Read {
|
||||
cmd,
|
||||
name,
|
||||
path: path.into_path_buf(),
|
||||
},
|
||||
CommandAction::ListFiles { command: cmd, path } => {
|
||||
CoreParsedCommand::ListFiles { cmd, path }
|
||||
}
|
||||
@@ -1559,13 +1562,13 @@ impl CommandAction {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CoreParsedCommand> for CommandAction {
|
||||
fn from(value: CoreParsedCommand) -> Self {
|
||||
impl CommandAction {
|
||||
pub fn from_core_with_cwd(value: CoreParsedCommand, cwd: &AbsolutePathBuf) -> Self {
|
||||
match value {
|
||||
CoreParsedCommand::Read { cmd, name, path } => CommandAction::Read {
|
||||
command: cmd,
|
||||
name,
|
||||
path,
|
||||
path: cwd.join(path),
|
||||
},
|
||||
CoreParsedCommand::ListFiles { cmd, path } => {
|
||||
CommandAction::ListFiles { command: cmd, path }
|
||||
@@ -2720,10 +2723,10 @@ pub struct ThreadStartResponse {
|
||||
pub model: String,
|
||||
pub model_provider: String,
|
||||
pub service_tier: Option<ServiceTier>,
|
||||
pub cwd: PathBuf,
|
||||
pub cwd: AbsolutePathBuf,
|
||||
/// Instruction source files currently loaded for this thread.
|
||||
#[serde(default)]
|
||||
pub instruction_sources: Vec<PathBuf>,
|
||||
pub instruction_sources: Vec<AbsolutePathBuf>,
|
||||
#[experimental(nested)]
|
||||
pub approval_policy: AskForApproval,
|
||||
/// Reviewer currently used for approval requests on this thread.
|
||||
@@ -2809,10 +2812,10 @@ pub struct ThreadResumeResponse {
|
||||
pub model: String,
|
||||
pub model_provider: String,
|
||||
pub service_tier: Option<ServiceTier>,
|
||||
pub cwd: PathBuf,
|
||||
pub cwd: AbsolutePathBuf,
|
||||
/// Instruction source files currently loaded for this thread.
|
||||
#[serde(default)]
|
||||
pub instruction_sources: Vec<PathBuf>,
|
||||
pub instruction_sources: Vec<AbsolutePathBuf>,
|
||||
#[experimental(nested)]
|
||||
pub approval_policy: AskForApproval,
|
||||
/// Reviewer currently used for approval requests on this thread.
|
||||
@@ -2889,10 +2892,10 @@ pub struct ThreadForkResponse {
|
||||
pub model: String,
|
||||
pub model_provider: String,
|
||||
pub service_tier: Option<ServiceTier>,
|
||||
pub cwd: PathBuf,
|
||||
pub cwd: AbsolutePathBuf,
|
||||
/// Instruction source files currently loaded for this thread.
|
||||
#[serde(default)]
|
||||
pub instruction_sources: Vec<PathBuf>,
|
||||
pub instruction_sources: Vec<AbsolutePathBuf>,
|
||||
#[experimental(nested)]
|
||||
pub approval_policy: AskForApproval,
|
||||
/// Reviewer currently used for approval requests on this thread.
|
||||
@@ -3436,9 +3439,9 @@ pub struct SkillInterface {
|
||||
#[ts(optional)]
|
||||
pub short_description: Option<String>,
|
||||
#[ts(optional)]
|
||||
pub icon_small: Option<PathBuf>,
|
||||
pub icon_small: Option<AbsolutePathBuf>,
|
||||
#[ts(optional)]
|
||||
pub icon_large: Option<PathBuf>,
|
||||
pub icon_large: Option<AbsolutePathBuf>,
|
||||
#[ts(optional)]
|
||||
pub brand_color: Option<String>,
|
||||
#[ts(optional)]
|
||||
@@ -3722,15 +3725,6 @@ impl From<CoreSkillScope> for SkillScope {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CoreSkillErrorInfo> for SkillErrorInfo {
|
||||
fn from(value: CoreSkillErrorInfo) -> Self {
|
||||
Self {
|
||||
path: value.path,
|
||||
message: value.message,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
@@ -3755,7 +3749,7 @@ pub struct Thread {
|
||||
/// [UNSTABLE] Path to the thread on disk.
|
||||
pub path: Option<PathBuf>,
|
||||
/// Working directory captured for the thread.
|
||||
pub cwd: PathBuf,
|
||||
pub cwd: AbsolutePathBuf,
|
||||
/// Version of the CLI that created the thread.
|
||||
pub cli_version: String,
|
||||
/// Origin of the thread (CLI, VSCode, codex exec, codex app-server, etc.).
|
||||
@@ -4530,7 +4524,7 @@ pub enum ThreadItem {
|
||||
/// The command to be executed.
|
||||
command: String,
|
||||
/// The command's working directory.
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
/// Identifier for the underlying PTY process (when available).
|
||||
process_id: Option<String>,
|
||||
#[serde(default)]
|
||||
@@ -4614,7 +4608,7 @@ pub enum ThreadItem {
|
||||
},
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(rename_all = "camelCase")]
|
||||
ImageView { id: String, path: String },
|
||||
ImageView { id: String, path: AbsolutePathBuf },
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(rename_all = "camelCase")]
|
||||
ImageGeneration {
|
||||
@@ -4624,7 +4618,7 @@ pub enum ThreadItem {
|
||||
result: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
saved_path: Option<String>,
|
||||
saved_path: Option<AbsolutePathBuf>,
|
||||
},
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(rename_all = "camelCase")]
|
||||
@@ -4786,7 +4780,7 @@ impl From<GuardianCommandSource> for CoreGuardianCommandSource {
|
||||
pub struct GuardianCommandReviewAction {
|
||||
pub source: GuardianCommandSource,
|
||||
pub command: String,
|
||||
pub cwd: PathBuf,
|
||||
pub cwd: AbsolutePathBuf,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
@@ -4796,15 +4790,15 @@ pub struct GuardianExecveReviewAction {
|
||||
pub source: GuardianCommandSource,
|
||||
pub program: String,
|
||||
pub argv: Vec<String>,
|
||||
pub cwd: PathBuf,
|
||||
pub cwd: AbsolutePathBuf,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct GuardianApplyPatchReviewAction {
|
||||
pub cwd: PathBuf,
|
||||
pub files: Vec<PathBuf>,
|
||||
pub cwd: AbsolutePathBuf,
|
||||
pub files: Vec<AbsolutePathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
@@ -4838,7 +4832,7 @@ pub enum GuardianApprovalReviewAction {
|
||||
Command {
|
||||
source: GuardianCommandSource,
|
||||
command: String,
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
},
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(rename_all = "camelCase")]
|
||||
@@ -4846,11 +4840,14 @@ pub enum GuardianApprovalReviewAction {
|
||||
source: GuardianCommandSource,
|
||||
program: String,
|
||||
argv: Vec<String>,
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
},
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(rename_all = "camelCase")]
|
||||
ApplyPatch { cwd: PathBuf, files: Vec<PathBuf> },
|
||||
ApplyPatch {
|
||||
cwd: AbsolutePathBuf,
|
||||
files: Vec<AbsolutePathBuf>,
|
||||
},
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(rename_all = "camelCase")]
|
||||
NetworkAccess {
|
||||
@@ -5759,7 +5756,7 @@ pub struct CommandExecutionRequestApprovalParams {
|
||||
/// The command's working directory.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional = nullable)]
|
||||
pub cwd: Option<PathBuf>,
|
||||
pub cwd: Option<AbsolutePathBuf>,
|
||||
/// Best-effort parsed command actions for friendly display.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional = nullable)]
|
||||
@@ -6564,22 +6561,20 @@ mod tests {
|
||||
use codex_protocol::protocol::NetworkAccess as CoreNetworkAccess;
|
||||
use codex_protocol::protocol::ReadOnlyAccess as CoreReadOnlyAccess;
|
||||
use codex_protocol::user_input::UserInput as CoreUserInput;
|
||||
use codex_utils_absolute_path::test_support::PathBufExt;
|
||||
use codex_utils_absolute_path::test_support::test_path_buf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn absolute_path_string(path: &str) -> String {
|
||||
let trimmed = path.trim_start_matches('/');
|
||||
if cfg!(windows) {
|
||||
format!(r"C:\{}", trimmed.replace('/', "\\"))
|
||||
} else {
|
||||
format!("/{trimmed}")
|
||||
}
|
||||
let path = format!("/{}", path.trim_start_matches('/'));
|
||||
test_path_buf(&path).display().to_string()
|
||||
}
|
||||
|
||||
fn absolute_path(path: &str) -> AbsolutePathBuf {
|
||||
AbsolutePathBuf::from_absolute_path(absolute_path_string(path))
|
||||
.expect("path must be absolute")
|
||||
let path = format!("/{}", path.trim_start_matches('/'));
|
||||
test_path_buf(&path).abs()
|
||||
}
|
||||
|
||||
fn test_absolute_path() -> AbsolutePathBuf {
|
||||
@@ -6604,7 +6599,7 @@ mod tests {
|
||||
"turnId": "turn_123",
|
||||
"itemId": "call_123",
|
||||
"command": "cat file",
|
||||
"cwd": "/tmp",
|
||||
"cwd": absolute_path_string("tmp"),
|
||||
"commandActions": null,
|
||||
"reason": null,
|
||||
"networkApprovalContext": null,
|
||||
@@ -8063,7 +8058,7 @@ mod tests {
|
||||
"type": "command",
|
||||
"source": "shell",
|
||||
"command": "rm -rf /tmp/example.sqlite",
|
||||
"cwd": "/tmp",
|
||||
"cwd": absolute_path_string("tmp"),
|
||||
});
|
||||
let action: GuardianApprovalReviewAction =
|
||||
serde_json::from_value(value.clone()).expect("guardian review action");
|
||||
@@ -8073,7 +8068,7 @@ mod tests {
|
||||
GuardianApprovalReviewAction::Command {
|
||||
source: GuardianCommandSource::Shell,
|
||||
command: "rm -rf /tmp/example.sqlite".to_string(),
|
||||
cwd: "/tmp".into(),
|
||||
cwd: absolute_path("tmp"),
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -8647,7 +8642,7 @@ mod tests {
|
||||
"updatedAt": 1,
|
||||
"status": { "type": "idle" },
|
||||
"path": null,
|
||||
"cwd": "/tmp",
|
||||
"cwd": absolute_path_string("tmp"),
|
||||
"cliVersion": "0.0.0",
|
||||
"source": "exec",
|
||||
"agentNickname": null,
|
||||
@@ -8659,7 +8654,7 @@ mod tests {
|
||||
"model": "gpt-5",
|
||||
"modelProvider": "openai",
|
||||
"serviceTier": null,
|
||||
"cwd": "/tmp",
|
||||
"cwd": absolute_path_string("tmp"),
|
||||
"approvalPolicy": "on-failure",
|
||||
"approvalsReviewer": "user",
|
||||
"sandbox": { "type": "dangerFullAccess" },
|
||||
@@ -8673,9 +8668,9 @@ mod tests {
|
||||
let fork: ThreadForkResponse =
|
||||
serde_json::from_value(response).expect("thread/fork response");
|
||||
|
||||
assert_eq!(start.instruction_sources, Vec::<PathBuf>::new());
|
||||
assert_eq!(resume.instruction_sources, Vec::<PathBuf>::new());
|
||||
assert_eq!(fork.instruction_sources, Vec::<PathBuf>::new());
|
||||
assert_eq!(start.instruction_sources, Vec::<AbsolutePathBuf>::new());
|
||||
assert_eq!(resume.instruction_sources, Vec::<AbsolutePathBuf>::new());
|
||||
assert_eq!(fork.instruction_sources, Vec::<AbsolutePathBuf>::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -140,9 +140,9 @@ use codex_protocol::request_user_input::RequestUserInputAnswer as CoreRequestUse
|
||||
use codex_protocol::request_user_input::RequestUserInputResponse as CoreRequestUserInputResponse;
|
||||
use codex_sandboxing::policy_transforms::intersect_permission_profiles;
|
||||
use codex_shell_command::parse_command::shlex_join;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::sync::oneshot;
|
||||
@@ -159,7 +159,7 @@ enum CommandExecutionApprovalPresentation {
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct CommandExecutionCompletionItem {
|
||||
command: String,
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
command_actions: Vec<V2ParsedCommand>,
|
||||
}
|
||||
|
||||
@@ -644,7 +644,7 @@ pub(crate) async fn apply_bespoke_event_handling(
|
||||
call_id: call_id.clone(),
|
||||
approval_id,
|
||||
command,
|
||||
cwd,
|
||||
cwd: cwd.to_path_buf(),
|
||||
reason,
|
||||
parsed_cmd,
|
||||
};
|
||||
@@ -666,7 +666,7 @@ pub(crate) async fn apply_bespoke_event_handling(
|
||||
let command_actions = parsed_cmd
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(V2ParsedCommand::from)
|
||||
.map(|parsed| V2ParsedCommand::from_core_with_cwd(parsed, &cwd))
|
||||
.collect::<Vec<_>>();
|
||||
let presentation = if let Some(network_approval_context) =
|
||||
network_approval_context.map(V2NetworkApprovalContext::from)
|
||||
@@ -1463,7 +1463,7 @@ pub(crate) async fn apply_bespoke_event_handling(
|
||||
EventMsg::ViewImageToolCall(view_image_event) => {
|
||||
let item = ThreadItem::ImageView {
|
||||
id: view_image_event.call_id.clone(),
|
||||
path: view_image_event.path.to_string_lossy().into_owned(),
|
||||
path: view_image_event.path.clone(),
|
||||
};
|
||||
let started = ItemStartedNotification {
|
||||
thread_id: conversation_id.to_string(),
|
||||
@@ -1648,13 +1648,13 @@ pub(crate) async fn apply_bespoke_event_handling(
|
||||
return;
|
||||
}
|
||||
let item_id = exec_command_begin_event.call_id.clone();
|
||||
let cwd = exec_command_begin_event.cwd.clone();
|
||||
let command_actions = exec_command_begin_event
|
||||
.parsed_cmd
|
||||
.into_iter()
|
||||
.map(V2ParsedCommand::from)
|
||||
.map(|parsed| V2ParsedCommand::from_core_with_cwd(parsed, &cwd))
|
||||
.collect::<Vec<_>>();
|
||||
let command = shlex_join(&exec_command_begin_event.command);
|
||||
let cwd = exec_command_begin_event.cwd;
|
||||
let process_id = exec_command_begin_event.process_id;
|
||||
let first_start = {
|
||||
let mut state = thread_state.lock().await;
|
||||
@@ -1834,7 +1834,8 @@ pub(crate) async fn apply_bespoke_event_handling(
|
||||
.await
|
||||
{
|
||||
Ok(summary) => {
|
||||
let mut thread = summary_to_thread(summary);
|
||||
let fallback_cwd = conversation.config_snapshot().await.cwd;
|
||||
let mut thread = summary_to_thread(summary, &fallback_cwd);
|
||||
match read_rollout_items_from_rollout(rollout_path.as_path()).await {
|
||||
Ok(items) => {
|
||||
thread.turns = build_turns_from_rollout_items(&items);
|
||||
@@ -2035,7 +2036,7 @@ async fn start_command_execution_item(
|
||||
turn_id: String,
|
||||
item_id: String,
|
||||
command: String,
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
command_actions: Vec<V2ParsedCommand>,
|
||||
source: CommandExecutionSource,
|
||||
outgoing: &ThreadScopedOutgoingMessageSender,
|
||||
@@ -2078,7 +2079,7 @@ async fn complete_command_execution_item(
|
||||
turn_id: String,
|
||||
item_id: String,
|
||||
command: String,
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
process_id: Option<String>,
|
||||
source: CommandExecutionSource,
|
||||
command_actions: Vec<V2ParsedCommand>,
|
||||
@@ -3002,6 +3003,8 @@ mod tests {
|
||||
use codex_protocol::protocol::TokenUsage;
|
||||
use codex_protocol::protocol::TokenUsageInfo;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_absolute_path::test_support::PathBufExt;
|
||||
use codex_utils_absolute_path::test_support::test_path_buf;
|
||||
use core_test_support::load_default_config_for_test;
|
||||
use pretty_assertions::assert_eq;
|
||||
use rmcp::model::Content;
|
||||
@@ -3054,7 +3057,7 @@ mod tests {
|
||||
fn command_execution_completion_item(command: &str) -> CommandExecutionCompletionItem {
|
||||
CommandExecutionCompletionItem {
|
||||
command: command.to_string(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
command_actions: vec![V2ParsedCommand::Unknown {
|
||||
command: command.to_string(),
|
||||
}],
|
||||
@@ -3100,7 +3103,7 @@ mod tests {
|
||||
"type": "command",
|
||||
"source": "shell",
|
||||
"command": format!("rm -f /tmp/{id}.sqlite"),
|
||||
"cwd": "/tmp",
|
||||
"cwd": test_path_buf("/tmp"),
|
||||
}))
|
||||
.expect("guardian action"),
|
||||
}
|
||||
@@ -3146,7 +3149,7 @@ mod tests {
|
||||
let action = codex_protocol::protocol::GuardianAssessmentAction::Command {
|
||||
source: codex_protocol::protocol::GuardianCommandSource::Shell,
|
||||
command: "rm -rf /tmp/example.sqlite".to_string(),
|
||||
cwd: "/tmp".into(),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
};
|
||||
let notification = guardian_auto_approval_review_notification(
|
||||
&conversation_id,
|
||||
@@ -3189,7 +3192,7 @@ mod tests {
|
||||
let action = codex_protocol::protocol::GuardianAssessmentAction::Command {
|
||||
source: codex_protocol::protocol::GuardianCommandSource::Shell,
|
||||
command: "rm -rf /tmp/example.sqlite".to_string(),
|
||||
cwd: "/tmp".into(),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
};
|
||||
let notification = guardian_auto_approval_review_notification(
|
||||
&conversation_id,
|
||||
|
||||
@@ -611,15 +611,12 @@ pub(crate) struct CodexMessageProcessorArgs {
|
||||
}
|
||||
|
||||
impl CodexMessageProcessor {
|
||||
async fn instruction_sources_from_config(config: &Config) -> Vec<PathBuf> {
|
||||
let mut paths: Vec<PathBuf> = config.user_instructions_path.iter().cloned().collect();
|
||||
async fn instruction_sources_from_config(config: &Config) -> Vec<AbsolutePathBuf> {
|
||||
let mut paths: Vec<AbsolutePathBuf> =
|
||||
config.user_instructions_path.iter().cloned().collect();
|
||||
match codex_core::discover_project_doc_paths(config, LOCAL_FS.as_ref()).await {
|
||||
Ok(project_doc_paths) => {
|
||||
paths.extend(
|
||||
project_doc_paths
|
||||
.into_iter()
|
||||
.map(|path| path.as_path().to_path_buf()),
|
||||
);
|
||||
paths.extend(project_doc_paths);
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!(error = %err, "failed to discover project docs for thread response");
|
||||
@@ -2162,7 +2159,7 @@ impl CodexMessageProcessor {
|
||||
&effective_policy,
|
||||
&effective_file_system_sandbox_policy,
|
||||
effective_network_sandbox_policy,
|
||||
sandbox_cwd.as_path(),
|
||||
&sandbox_cwd,
|
||||
&codex_linux_sandbox_exe,
|
||||
use_legacy_landlock,
|
||||
) {
|
||||
@@ -3201,7 +3198,7 @@ impl CodexMessageProcessor {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut thread = summary_to_thread(summary);
|
||||
let mut thread = summary_to_thread(summary, &self.config.cwd);
|
||||
self.attach_thread_name(thread_uuid, &mut thread).await;
|
||||
thread.status = resolve_thread_status(
|
||||
self.thread_watch_manager
|
||||
@@ -3284,7 +3281,7 @@ impl CodexMessageProcessor {
|
||||
config_snapshot.session_source.clone(),
|
||||
);
|
||||
builder.model_provider = Some(model_provider.clone());
|
||||
builder.cwd = config_snapshot.cwd.clone();
|
||||
builder.cwd = config_snapshot.cwd.to_path_buf();
|
||||
builder.cli_version = Some(env!("CARGO_PKG_VERSION").to_string());
|
||||
builder.sandbox_policy = config_snapshot.sandbox_policy.clone();
|
||||
builder.approval_mode = config_snapshot.approval_policy;
|
||||
@@ -3509,7 +3506,7 @@ impl CodexMessageProcessor {
|
||||
message: format!("failed to read unarchived thread: {err}"),
|
||||
data: None,
|
||||
})?;
|
||||
Ok(summary_to_thread(summary))
|
||||
Ok(summary_to_thread(summary, &self.config.cwd))
|
||||
}
|
||||
.await;
|
||||
|
||||
@@ -3770,7 +3767,7 @@ impl CodexMessageProcessor {
|
||||
let conversation_id = summary.conversation_id;
|
||||
thread_ids.insert(conversation_id);
|
||||
|
||||
let thread = summary_to_thread(summary);
|
||||
let thread = summary_to_thread(summary, &self.config.cwd);
|
||||
status_ids.push(thread.id.clone());
|
||||
threads.push((conversation_id, thread));
|
||||
}
|
||||
@@ -3914,11 +3911,11 @@ impl CodexMessageProcessor {
|
||||
}
|
||||
|
||||
let mut thread = if let Some(summary) = db_summary {
|
||||
summary_to_thread(summary)
|
||||
summary_to_thread(summary, &self.config.cwd)
|
||||
} else if let Some(rollout_path) = rollout_path.as_ref() {
|
||||
let fallback_provider = self.config.model_provider_id.as_str();
|
||||
match read_summary_from_rollout(rollout_path, fallback_provider).await {
|
||||
Ok(summary) => summary_to_thread(summary),
|
||||
Ok(summary) => summary_to_thread(summary, &self.config.cwd),
|
||||
Err(err) => {
|
||||
self.send_internal_error(
|
||||
request_id,
|
||||
@@ -4424,9 +4421,7 @@ impl CodexMessageProcessor {
|
||||
);
|
||||
}
|
||||
let mut config_for_instruction_sources = self.config.as_ref().clone();
|
||||
if let Ok(cwd) = AbsolutePathBuf::try_from(config_snapshot.cwd.clone()) {
|
||||
config_for_instruction_sources.cwd = cwd;
|
||||
}
|
||||
config_for_instruction_sources.cwd = config_snapshot.cwd.clone();
|
||||
let instruction_sources =
|
||||
Self::instruction_sources_from_config(&config_for_instruction_sources).await;
|
||||
let thread_summary = match load_thread_summary_for_rollout(
|
||||
@@ -4809,7 +4804,7 @@ impl CodexMessageProcessor {
|
||||
.await
|
||||
{
|
||||
Ok(summary) => {
|
||||
let mut thread = summary_to_thread(summary);
|
||||
let mut thread = summary_to_thread(summary, &self.config.cwd);
|
||||
thread.forked_from_id =
|
||||
forked_from_id_from_rollout(fork_rollout_path.as_path()).await;
|
||||
thread
|
||||
@@ -6348,7 +6343,7 @@ impl CodexMessageProcessor {
|
||||
)
|
||||
.await;
|
||||
let skills_input = codex_core::skills::SkillsLoadInput::new(
|
||||
cwd_abs,
|
||||
cwd_abs.clone(),
|
||||
effective_skill_roots,
|
||||
config_layer_stack,
|
||||
config.bundled_skills_enabled(),
|
||||
@@ -7615,7 +7610,7 @@ impl CodexMessageProcessor {
|
||||
if let Some(rollout_path) = review_thread.rollout_path() {
|
||||
match read_summary_from_rollout(rollout_path.as_path(), fallback_provider).await {
|
||||
Ok(summary) => {
|
||||
let mut thread = summary_to_thread(summary);
|
||||
let mut thread = summary_to_thread(summary, &self.config.cwd);
|
||||
self.thread_watch_manager
|
||||
.upsert_thread_silently(thread.clone())
|
||||
.await;
|
||||
@@ -8817,7 +8812,7 @@ fn collect_resume_override_mismatches(
|
||||
}
|
||||
if let Some(requested_cwd) = request.cwd.as_deref() {
|
||||
let requested_cwd_path = std::path::PathBuf::from(requested_cwd);
|
||||
if requested_cwd_path != config_snapshot.cwd {
|
||||
if requested_cwd_path != config_snapshot.cwd.as_path() {
|
||||
mismatch_details.push(format!(
|
||||
"cwd requested={} active={}",
|
||||
requested_cwd_path.display(),
|
||||
@@ -9601,7 +9596,7 @@ async fn load_thread_summary_for_rollout(
|
||||
) -> std::result::Result<Thread, String> {
|
||||
let mut thread = read_summary_from_rollout(rollout_path, fallback_provider)
|
||||
.await
|
||||
.map(summary_to_thread)
|
||||
.map(|summary| summary_to_thread(summary, &config.cwd))
|
||||
.map_err(|err| {
|
||||
format!(
|
||||
"failed to load rollout `{}` for thread {thread_id}: {err}",
|
||||
@@ -9612,10 +9607,13 @@ async fn load_thread_summary_for_rollout(
|
||||
if let Some(persisted_metadata) = persisted_metadata {
|
||||
merge_mutable_thread_metadata(
|
||||
&mut thread,
|
||||
summary_to_thread(summary_from_thread_metadata(persisted_metadata)),
|
||||
summary_to_thread(
|
||||
summary_from_thread_metadata(persisted_metadata),
|
||||
&config.cwd,
|
||||
),
|
||||
);
|
||||
} else if let Some(summary) = read_summary_from_state_db_by_thread_id(config, thread_id).await {
|
||||
merge_mutable_thread_metadata(&mut thread, summary_to_thread(summary));
|
||||
merge_mutable_thread_metadata(&mut thread, summary_to_thread(summary, &config.cwd));
|
||||
}
|
||||
let title = if let Some(metadata) = persisted_metadata {
|
||||
non_empty_title(metadata)
|
||||
@@ -9735,7 +9733,10 @@ fn build_thread_from_snapshot(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn summary_to_thread(summary: ConversationSummary) -> Thread {
|
||||
pub(crate) fn summary_to_thread(
|
||||
summary: ConversationSummary,
|
||||
fallback_cwd: &AbsolutePathBuf,
|
||||
) -> Thread {
|
||||
let ConversationSummary {
|
||||
conversation_id,
|
||||
path,
|
||||
@@ -9756,6 +9757,15 @@ pub(crate) fn summary_to_thread(summary: ConversationSummary) -> Thread {
|
||||
branch: info.branch,
|
||||
origin_url: info.origin_url,
|
||||
});
|
||||
let cwd =
|
||||
AbsolutePathBuf::relative_to_current_dir(path_utils::normalize_for_native_workdir(cwd))
|
||||
.unwrap_or_else(|err| {
|
||||
warn!(
|
||||
path = %path.display(),
|
||||
"failed to normalize thread cwd while summarizing thread: {err}"
|
||||
);
|
||||
fallback_cwd.clone()
|
||||
});
|
||||
|
||||
Thread {
|
||||
id: conversation_id.to_string(),
|
||||
@@ -9789,6 +9799,8 @@ mod tests {
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use codex_protocol::protocol::SubAgentSource;
|
||||
use codex_utils_absolute_path::test_support::PathBufExt;
|
||||
use codex_utils_absolute_path::test_support::test_path_buf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use std::path::PathBuf;
|
||||
@@ -9923,7 +9935,7 @@ mod tests {
|
||||
approval_policy: codex_protocol::protocol::AskForApproval::OnRequest,
|
||||
approvals_reviewer: codex_protocol::config_types::ApprovalsReviewer::User,
|
||||
sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess,
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
ephemeral: false,
|
||||
reasoning_effort: None,
|
||||
personality: None,
|
||||
@@ -10283,7 +10295,8 @@ mod tests {
|
||||
fs::write(&path, format!("{}\n", serde_json::to_string(&line)?))?;
|
||||
|
||||
let summary = read_summary_from_rollout(path.as_path(), "fallback").await?;
|
||||
let thread = summary_to_thread(summary);
|
||||
let fallback_cwd = AbsolutePathBuf::from_absolute_path("/")?;
|
||||
let thread = summary_to_thread(summary, &fallback_cwd);
|
||||
|
||||
assert_eq!(thread.agent_nickname, Some("atlas".to_string()));
|
||||
assert_eq!(thread.agent_role, Some("explorer".to_string()));
|
||||
@@ -10417,7 +10430,8 @@ mod tests {
|
||||
/*git_origin_url*/ None,
|
||||
);
|
||||
|
||||
let thread = summary_to_thread(summary);
|
||||
let fallback_cwd = AbsolutePathBuf::from_absolute_path("/")?;
|
||||
let thread = summary_to_thread(summary, &fallback_cwd);
|
||||
|
||||
assert_eq!(thread.agent_nickname, Some("atlas".to_string()));
|
||||
assert_eq!(thread.agent_role, Some("explorer".to_string()));
|
||||
|
||||
@@ -14,7 +14,6 @@ use codex_core::file_watcher::FileWatcherSubscriber;
|
||||
use codex_core::file_watcher::Receiver;
|
||||
use codex_core::file_watcher::WatchPath;
|
||||
use codex_core::file_watcher::WatchRegistration;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::collections::hash_map::Entry;
|
||||
@@ -128,7 +127,7 @@ impl FsWatchManager {
|
||||
};
|
||||
let outgoing = self.outgoing.clone();
|
||||
let (subscriber, rx) = self.file_watcher.add_subscriber();
|
||||
let watch_root = params.path.to_path_buf().clone();
|
||||
let watch_root = params.path.clone();
|
||||
let registration = subscriber.register_paths(vec![WatchPath {
|
||||
path: params.path.to_path_buf(),
|
||||
recursive: false,
|
||||
@@ -166,7 +165,7 @@ impl FsWatchManager {
|
||||
let mut changed_paths = event
|
||||
.paths
|
||||
.into_iter()
|
||||
.map(|path| AbsolutePathBuf::resolve_path_against_base(&path, &watch_root))
|
||||
.map(|path| watch_root.join(path))
|
||||
.collect::<Vec<_>>();
|
||||
changed_paths.sort_by(|left, right| left.as_path().cmp(right.as_path()));
|
||||
if !changed_paths.is_empty() {
|
||||
|
||||
@@ -44,10 +44,9 @@ fn main() -> anyhow::Result<()> {
|
||||
let loader_overrides = if disable_managed_config_from_debug_env() {
|
||||
LoaderOverrides::without_managed_config_for_tests()
|
||||
} else {
|
||||
LoaderOverrides {
|
||||
managed_config_path: managed_config_path_from_debug_env(),
|
||||
..Default::default()
|
||||
}
|
||||
managed_config_path_from_debug_env()
|
||||
.map(LoaderOverrides::with_managed_config_path_for_tests)
|
||||
.unwrap_or_default()
|
||||
};
|
||||
let transport = args.listen;
|
||||
let session_source = args.session_source;
|
||||
|
||||
@@ -725,6 +725,8 @@ impl MessageProcessor {
|
||||
session: Arc<ConnectionSessionState>,
|
||||
request_context: RequestContext,
|
||||
) {
|
||||
let connection_id = connection_request_id.connection_id;
|
||||
|
||||
if !session.initialized() {
|
||||
let error = JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
|
||||
@@ -8,6 +8,7 @@ use codex_core::CodexThread;
|
||||
use codex_core::ThreadConfigSnapshot;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
@@ -28,7 +29,7 @@ pub(crate) struct PendingThreadResumeRequest {
|
||||
pub(crate) request_id: ConnectionRequestId,
|
||||
pub(crate) rollout_path: PathBuf,
|
||||
pub(crate) config_snapshot: ThreadConfigSnapshot,
|
||||
pub(crate) instruction_sources: Vec<PathBuf>,
|
||||
pub(crate) instruction_sources: Vec<AbsolutePathBuf>,
|
||||
pub(crate) thread_summary: codex_app_server_protocol::Thread,
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,6 @@ use codex_app_server_protocol::ThreadStatus;
|
||||
use codex_app_server_protocol::ThreadStatusChangedNotification;
|
||||
use codex_protocol::ThreadId;
|
||||
use std::collections::HashMap;
|
||||
#[cfg(test)]
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
#[cfg(test)]
|
||||
@@ -455,6 +453,8 @@ fn loaded_thread_status(runtime: &RuntimeFacts) -> ThreadStatus {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use codex_utils_absolute_path::test_support::PathBufExt;
|
||||
use codex_utils_absolute_path::test_support::test_path_buf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use tokio::time::Duration;
|
||||
use tokio::time::timeout;
|
||||
@@ -895,7 +895,7 @@ mod tests {
|
||||
updated_at: 0,
|
||||
status: ThreadStatus::NotLoaded,
|
||||
path: None,
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
cli_version: "test".to_string(),
|
||||
agent_nickname: None,
|
||||
agent_role: None,
|
||||
|
||||
@@ -402,7 +402,6 @@ mod tests {
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use std::path::PathBuf;
|
||||
use tokio::time::Duration;
|
||||
use tokio::time::timeout;
|
||||
|
||||
@@ -772,7 +771,7 @@ mod tests {
|
||||
reason: Some("Need extra read access".to_string()),
|
||||
network_approval_context: None,
|
||||
command: Some("cat file".to_string()),
|
||||
cwd: Some(PathBuf::from("/tmp")),
|
||||
cwd: Some(absolute_path("/tmp")),
|
||||
command_actions: None,
|
||||
additional_permissions: Some(
|
||||
codex_app_server_protocol::AdditionalPermissionProfile {
|
||||
@@ -834,7 +833,7 @@ mod tests {
|
||||
reason: Some("Need extra read access".to_string()),
|
||||
network_approval_context: None,
|
||||
command: Some("cat file".to_string()),
|
||||
cwd: Some(PathBuf::from("/tmp")),
|
||||
cwd: Some(absolute_path("/tmp")),
|
||||
command_actions: None,
|
||||
additional_permissions: Some(
|
||||
codex_app_server_protocol::AdditionalPermissionProfile {
|
||||
|
||||
@@ -15,10 +15,12 @@ pub use auth_fixtures::write_chatgpt_auth;
|
||||
use codex_app_server_protocol::JSONRPCResponse;
|
||||
pub use config::write_mock_responses_config_toml;
|
||||
pub use config::write_mock_responses_config_toml_with_chatgpt_base_url;
|
||||
pub use core_test_support::PathBufExt;
|
||||
pub use core_test_support::format_with_current_shell;
|
||||
pub use core_test_support::format_with_current_shell_display;
|
||||
pub use core_test_support::format_with_current_shell_display_non_login;
|
||||
pub use core_test_support::format_with_current_shell_non_login;
|
||||
pub use core_test_support::test_absolute_path;
|
||||
pub use core_test_support::test_path_buf_with_windows;
|
||||
pub use core_test_support::test_tmp_path;
|
||||
pub use core_test_support::test_tmp_path_buf;
|
||||
|
||||
@@ -144,6 +144,11 @@ impl McpProcess {
|
||||
cmd.current_dir(codex_home);
|
||||
cmd.env("CODEX_HOME", codex_home);
|
||||
cmd.env("RUST_LOG", "info");
|
||||
// Keep integration tests isolated from host managed configuration.
|
||||
cmd.env(
|
||||
"CODEX_APP_SERVER_MANAGED_CONFIG_PATH",
|
||||
codex_home.join("managed_config.toml"),
|
||||
);
|
||||
cmd.env_remove(CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR);
|
||||
cmd.args(args);
|
||||
|
||||
|
||||
@@ -114,6 +114,11 @@ impl Match for RealtimeCallRequestCapture {
|
||||
}
|
||||
}
|
||||
|
||||
fn normalized_json_string(raw: &str) -> Result<String> {
|
||||
let value: Value = serde_json::from_str(raw).context("expected JSON fixture to parse")?;
|
||||
serde_json::to_string(&value).context("expected JSON fixture to serialize")
|
||||
}
|
||||
|
||||
struct GatedSseResponse {
|
||||
gate_rx: Mutex<Option<mpsc::Receiver<()>>>,
|
||||
response: String,
|
||||
@@ -1135,6 +1140,7 @@ async fn realtime_webrtc_start_emits_sdp_notification() -> Result<()> {
|
||||
);
|
||||
let body = String::from_utf8(request.body).context("multipart body should be utf-8")?;
|
||||
let session = r#"{"tool_choice":"auto","type":"realtime","model":"gpt-realtime-1.5","instructions":"backend prompt\n\nstartup context","output_modalities":["audio"],"audio":{"input":{"format":{"type":"audio/pcm","rate":24000},"noise_reduction":{"type":"near_field"},"turn_detection":{"type":"server_vad","interrupt_response":true,"create_response":true}},"output":{"format":{"type":"audio/pcm","rate":24000},"voice":"marin"}},"tools":[{"type":"function","name":"background_agent","description":"Send a user request to the background agent. Use this as the default action. Do not rephrase the user's ask or rewrite it in your own words; pass along the user's own words. If the background agent is idle, this starts a new task and returns the final result to the user. If the background agent is already working on a task, this sends the request as guidance to steer that previous task. If the user asks to do something next, later, after this, or once current work finishes, call this tool so the work is actually queued instead of merely promising to do it later.","parameters":{"type":"object","properties":{"prompt":{"type":"string","description":"The user request to delegate to the background agent."}},"required":["prompt"],"additionalProperties":false}}]}"#;
|
||||
let session = normalized_json_string(session)?;
|
||||
assert_eq!(
|
||||
body,
|
||||
format!(
|
||||
@@ -2181,6 +2187,7 @@ fn assert_call_create_multipart(
|
||||
Some("multipart/form-data; boundary=codex-realtime-call-boundary")
|
||||
);
|
||||
let body = String::from_utf8(request.body).context("multipart body should be utf-8")?;
|
||||
let session = normalized_json_string(session)?;
|
||||
assert_eq!(
|
||||
body,
|
||||
format!(
|
||||
|
||||
@@ -54,7 +54,7 @@ async fn skills_list_includes_skills_from_per_cwd_extra_user_roots() -> Result<(
|
||||
.await??;
|
||||
let SkillsListResponse { data } = to_response(response)?;
|
||||
assert_eq!(data.len(), 1);
|
||||
assert_eq!(data[0].cwd, cwd.path().to_path_buf());
|
||||
assert_eq!(data[0].cwd.as_path(), cwd.path());
|
||||
assert!(
|
||||
data[0]
|
||||
.skills
|
||||
@@ -156,7 +156,7 @@ async fn skills_list_ignores_per_cwd_extra_roots_for_unknown_cwd() -> Result<()>
|
||||
.await??;
|
||||
let SkillsListResponse { data } = to_response(response)?;
|
||||
assert_eq!(data.len(), 1);
|
||||
assert_eq!(data[0].cwd, requested_cwd.path().to_path_buf());
|
||||
assert_eq!(data[0].cwd.as_path(), requested_cwd.path());
|
||||
assert!(
|
||||
data[0]
|
||||
.skills
|
||||
|
||||
@@ -117,9 +117,9 @@ async fn thread_fork_creates_new_thread_and_emits_started() -> Result<()> {
|
||||
assert_eq!(thread.model_provider, "mock_provider");
|
||||
assert_eq!(thread.status, ThreadStatus::Idle);
|
||||
let thread_path = thread.path.clone().expect("thread path");
|
||||
assert!(thread_path.is_absolute());
|
||||
assert_ne!(thread_path, original_path);
|
||||
assert!(thread.cwd.is_absolute());
|
||||
assert!(thread_path.as_path().is_absolute());
|
||||
assert_ne!(thread_path.as_path(), original_path);
|
||||
assert!(thread.cwd.as_path().is_absolute());
|
||||
assert_eq!(thread.source, SessionSource::VsCode);
|
||||
assert_eq!(thread.name, None);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ use app_test_support::create_fake_rollout_with_source;
|
||||
use app_test_support::create_final_assistant_message_sse_response;
|
||||
use app_test_support::create_mock_responses_server_sequence;
|
||||
use app_test_support::rollout_path;
|
||||
use app_test_support::test_absolute_path;
|
||||
use app_test_support::to_response;
|
||||
use chrono::DateTime;
|
||||
use chrono::Utc;
|
||||
@@ -37,7 +38,6 @@ use std::fs;
|
||||
use std::fs::FileTimes;
|
||||
use std::fs::OpenOptions;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use tempfile::TempDir;
|
||||
use tokio::time::timeout;
|
||||
use uuid::Uuid;
|
||||
@@ -372,7 +372,7 @@ async fn thread_list_pagination_next_cursor_none_on_last_page() -> Result<()> {
|
||||
assert_eq!(thread.model_provider, "mock_provider");
|
||||
assert!(thread.created_at > 0);
|
||||
assert_eq!(thread.updated_at, thread.created_at);
|
||||
assert_eq!(thread.cwd, PathBuf::from("/"));
|
||||
assert_eq!(thread.cwd, test_absolute_path("/"));
|
||||
assert_eq!(thread.cli_version, "0.0.0");
|
||||
assert_eq!(thread.source, SessionSource::Cli);
|
||||
assert_eq!(thread.git_info, None);
|
||||
@@ -399,7 +399,7 @@ async fn thread_list_pagination_next_cursor_none_on_last_page() -> Result<()> {
|
||||
assert_eq!(thread.model_provider, "mock_provider");
|
||||
assert!(thread.created_at > 0);
|
||||
assert_eq!(thread.updated_at, thread.created_at);
|
||||
assert_eq!(thread.cwd, PathBuf::from("/"));
|
||||
assert_eq!(thread.cwd, test_absolute_path("/"));
|
||||
assert_eq!(thread.cli_version, "0.0.0");
|
||||
assert_eq!(thread.source, SessionSource::Cli);
|
||||
assert_eq!(thread.git_info, None);
|
||||
@@ -455,7 +455,7 @@ async fn thread_list_respects_provider_filter() -> Result<()> {
|
||||
let expected_ts = chrono::DateTime::parse_from_rfc3339("2025-01-02T11:00:00Z")?.timestamp();
|
||||
assert_eq!(thread.created_at, expected_ts);
|
||||
assert_eq!(thread.updated_at, expected_ts);
|
||||
assert_eq!(thread.cwd, PathBuf::from("/"));
|
||||
assert_eq!(thread.cwd, test_absolute_path("/"));
|
||||
assert_eq!(thread.cli_version, "0.0.0");
|
||||
assert_eq!(thread.source, SessionSource::Cli);
|
||||
assert_eq!(thread.git_info, None);
|
||||
@@ -518,7 +518,7 @@ async fn thread_list_respects_cwd_filter() -> Result<()> {
|
||||
assert_eq!(data.len(), 1);
|
||||
assert_eq!(data[0].id, filtered_id);
|
||||
assert_ne!(data[0].id, unfiltered_id);
|
||||
assert_eq!(data[0].cwd, target_cwd);
|
||||
assert_eq!(data[0].cwd.as_path(), target_cwd.as_path());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1032,7 +1032,7 @@ async fn thread_list_includes_git_info() -> Result<()> {
|
||||
};
|
||||
assert_eq!(thread.git_info, Some(expected_git));
|
||||
assert_eq!(thread.source, SessionSource::Cli);
|
||||
assert_eq!(thread.cwd, PathBuf::from("/"));
|
||||
assert_eq!(thread.cwd, test_absolute_path("/"));
|
||||
assert_eq!(thread.cli_version, "0.0.0");
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -2,6 +2,7 @@ use anyhow::Result;
|
||||
use app_test_support::McpProcess;
|
||||
use app_test_support::create_fake_rollout_with_text_elements;
|
||||
use app_test_support::create_mock_responses_server_repeating_assistant;
|
||||
use app_test_support::test_absolute_path;
|
||||
use app_test_support::to_response;
|
||||
use codex_app_server_protocol::JSONRPCError;
|
||||
use codex_app_server_protocol::JSONRPCResponse;
|
||||
@@ -32,7 +33,6 @@ use core_test_support::responses;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::Value;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use tempfile::TempDir;
|
||||
use tokio::time::timeout;
|
||||
|
||||
@@ -83,7 +83,7 @@ async fn thread_read_returns_summary_without_turns() -> Result<()> {
|
||||
assert_eq!(thread.model_provider, "mock_provider");
|
||||
assert!(!thread.ephemeral, "stored rollouts should not be ephemeral");
|
||||
assert!(thread.path.as_ref().expect("thread path").is_absolute());
|
||||
assert_eq!(thread.cwd, PathBuf::from("/"));
|
||||
assert_eq!(thread.cwd, test_absolute_path("/"));
|
||||
assert_eq!(thread.cli_version, "0.0.0");
|
||||
assert_eq!(thread.source, SessionSource::Cli);
|
||||
assert_eq!(thread.git_info, None);
|
||||
|
||||
@@ -8,6 +8,7 @@ use app_test_support::create_mock_responses_server_repeating_assistant;
|
||||
use app_test_support::create_mock_responses_server_sequence_unchecked;
|
||||
use app_test_support::create_shell_command_sse_response;
|
||||
use app_test_support::rollout_path;
|
||||
use app_test_support::test_absolute_path;
|
||||
use app_test_support::to_response;
|
||||
use app_test_support::write_chatgpt_auth;
|
||||
use chrono::Utc;
|
||||
@@ -244,7 +245,7 @@ async fn thread_resume_returns_rollout_history() -> Result<()> {
|
||||
assert_eq!(thread.preview, preview);
|
||||
assert_eq!(thread.model_provider, "mock_provider");
|
||||
assert!(thread.path.as_ref().expect("thread path").is_absolute());
|
||||
assert_eq!(thread.cwd, PathBuf::from("/"));
|
||||
assert_eq!(thread.cwd, test_absolute_path("/"));
|
||||
assert_eq!(thread.cli_version, "0.0.0");
|
||||
assert_eq!(thread.source, SessionSource::Cli);
|
||||
assert_eq!(thread.git_info, None);
|
||||
@@ -1613,7 +1614,7 @@ async fn thread_resume_prefers_path_over_thread_id() -> Result<()> {
|
||||
let resume_id = mcp
|
||||
.send_thread_resume_request(ThreadResumeParams {
|
||||
thread_id: "not-a-valid-thread-id".to_string(),
|
||||
path: Some(thread_path),
|
||||
path: Some(thread_path.to_path_buf()),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
@@ -1742,7 +1743,7 @@ async fn start_materialized_thread_and_restart(
|
||||
Ok(RestartedThreadFixture {
|
||||
mcp: second_mcp,
|
||||
thread_id,
|
||||
rollout_file_path,
|
||||
rollout_file_path: rollout_file_path.to_path_buf(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ use wiremock::matchers::path;
|
||||
use super::analytics::assert_basic_thread_initialized_event;
|
||||
use super::analytics::mount_analytics_capture;
|
||||
use super::analytics::thread_initialized_event;
|
||||
use super::analytics::wait_for_analytics_event;
|
||||
use super::analytics::wait_for_analytics_payload;
|
||||
|
||||
const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
|
||||
@@ -212,14 +211,15 @@ async fn thread_start_response_includes_loaded_instruction_sources() -> Result<(
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn normalize_path_for_comparison(path: PathBuf) -> PathBuf {
|
||||
fn normalize_path_for_comparison(path: impl AsRef<Path>) -> PathBuf {
|
||||
let path = path.as_ref();
|
||||
let path = path.display().to_string();
|
||||
PathBuf::from(path.strip_prefix(r"\\?\").unwrap_or(&path))
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn normalize_path_for_comparison(path: PathBuf) -> PathBuf {
|
||||
path
|
||||
fn normalize_path_for_comparison(path: impl AsRef<Path>) -> PathBuf {
|
||||
path.as_ref().to_path_buf()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -281,16 +281,25 @@ async fn thread_start_does_not_track_thread_initialized_analytics_without_featur
|
||||
.await??;
|
||||
let _ = to_response::<ThreadStartResponse>(resp)?;
|
||||
|
||||
let payload = wait_for_analytics_event(
|
||||
&server,
|
||||
Duration::from_millis(250),
|
||||
"codex_thread_initialized",
|
||||
)
|
||||
.await;
|
||||
assert!(
|
||||
payload.is_err(),
|
||||
"thread analytics should be gated off when general_analytics is disabled"
|
||||
);
|
||||
assert_no_thread_initialized_analytics(&server, Duration::from_millis(250)).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn assert_no_thread_initialized_analytics(
|
||||
server: &MockServer,
|
||||
wait_duration: Duration,
|
||||
) -> Result<()> {
|
||||
tokio::time::sleep(wait_duration).await;
|
||||
let requests = server.received_requests().await.unwrap_or_default();
|
||||
for request in requests.iter().filter(|request| {
|
||||
request.method == "POST" && request.url.path() == "/codex/analytics-events/events"
|
||||
}) {
|
||||
let payload: Value = serde_json::from_slice(&request.body)?;
|
||||
assert!(
|
||||
thread_initialized_event(&payload).is_err(),
|
||||
"thread analytics should be gated off when general_analytics is disabled; payload={payload}"
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1716,7 +1716,7 @@ async fn turn_start_updates_sandbox_and_cwd_between_turns_v2() -> Result<()> {
|
||||
else {
|
||||
unreachable!("loop ensures we break on command execution items");
|
||||
};
|
||||
assert_eq!(cwd, second_cwd);
|
||||
assert_eq!(cwd.as_path(), second_cwd.as_path());
|
||||
let expected_command = format_with_current_shell_display("echo second turn");
|
||||
assert_eq!(command, expected_command);
|
||||
assert_eq!(status, CommandExecutionStatus::InProgress);
|
||||
|
||||
@@ -166,7 +166,7 @@ async fn turn_start_shell_zsh_fork_executes_command_v2() -> Result<()> {
|
||||
assert!(command.contains("/bin/sh -c"));
|
||||
assert!(command.contains("sleep 0.01"));
|
||||
assert!(command.contains(&release_marker.display().to_string()));
|
||||
assert_eq!(cwd, workspace);
|
||||
assert_eq!(cwd.as_path(), workspace.as_path());
|
||||
|
||||
mcp.interrupt_turn_and_wait_for_aborted(thread.id, turn.id, DEFAULT_READ_TIMEOUT)
|
||||
.await?;
|
||||
|
||||
@@ -35,9 +35,9 @@ pub const APPLY_PATCH_TOOL_INSTRUCTIONS: &str = include_str!("../apply_patch_too
|
||||
/// internal `apply_patch` path.
|
||||
///
|
||||
/// Although this constant lives in `codex-apply-patch` (to avoid forcing
|
||||
/// `codex-arg0` to depend on `codex-core`), it is part of the "codex core"
|
||||
/// process-invocation contract between the apply-patch runtime and the arg0
|
||||
/// dispatcher.
|
||||
/// `codex-arg0` to depend on `codex-core`), it remains part of the "codex core"
|
||||
/// process-invocation contract for the standalone `apply_patch` command
|
||||
/// surface.
|
||||
pub const CODEX_CORE_APPLY_PATCH_ARG1: &str = "--codex-run-as-apply-patch";
|
||||
|
||||
#[derive(Debug, Error, PartialEq)]
|
||||
@@ -134,8 +134,8 @@ pub enum MaybeApplyPatchVerified {
|
||||
pub struct ApplyPatchAction {
|
||||
changes: HashMap<PathBuf, ApplyPatchFileChange>,
|
||||
|
||||
/// The raw patch argument that can be used with `apply_patch` as an exec
|
||||
/// call. i.e., if the original arg was parsed in "lenient" mode with a
|
||||
/// The raw patch argument that can be used to apply the patch. i.e., if the
|
||||
/// original arg was parsed in "lenient" mode with a
|
||||
/// heredoc, this should be the value without the heredoc wrapper.
|
||||
pub patch: String,
|
||||
|
||||
@@ -274,23 +274,13 @@ async fn apply_hunks_to_files(
|
||||
let path_abs = hunk.resolve_path(cwd);
|
||||
match hunk {
|
||||
Hunk::AddFile { contents, .. } => {
|
||||
if let Some(parent_abs) = path_abs.parent() {
|
||||
fs.create_directory(
|
||||
&parent_abs,
|
||||
CreateDirectoryOptions { recursive: true },
|
||||
sandbox,
|
||||
)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Failed to create parent directories for {}",
|
||||
path_abs.display()
|
||||
)
|
||||
})?;
|
||||
}
|
||||
fs.write_file(&path_abs, contents.clone().into_bytes(), sandbox)
|
||||
.await
|
||||
.with_context(|| format!("Failed to write file {}", path_abs.display()))?;
|
||||
write_file_with_missing_parent_retry(
|
||||
fs,
|
||||
&path_abs,
|
||||
contents.clone().into_bytes(),
|
||||
sandbox,
|
||||
)
|
||||
.await?;
|
||||
added.push(affected_path);
|
||||
}
|
||||
Hunk::DeleteFile { .. } => {
|
||||
@@ -323,23 +313,13 @@ async fn apply_hunks_to_files(
|
||||
derive_new_contents_from_chunks(&path_abs, chunks, fs, sandbox).await?;
|
||||
if let Some(dest) = move_path {
|
||||
let dest_abs = AbsolutePathBuf::resolve_path_against_base(dest, cwd);
|
||||
if let Some(parent_abs) = dest_abs.parent() {
|
||||
fs.create_directory(
|
||||
&parent_abs,
|
||||
CreateDirectoryOptions { recursive: true },
|
||||
sandbox,
|
||||
)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Failed to create parent directories for {}",
|
||||
dest_abs.display()
|
||||
)
|
||||
})?;
|
||||
}
|
||||
fs.write_file(&dest_abs, new_contents.into_bytes(), sandbox)
|
||||
.await
|
||||
.with_context(|| format!("Failed to write file {}", dest_abs.display()))?;
|
||||
write_file_with_missing_parent_retry(
|
||||
fs,
|
||||
&dest_abs,
|
||||
new_contents.into_bytes(),
|
||||
sandbox,
|
||||
)
|
||||
.await?;
|
||||
let result: io::Result<()> = async {
|
||||
let metadata = fs.get_metadata(&path_abs, sandbox).await?;
|
||||
if metadata.is_directory {
|
||||
@@ -379,6 +359,40 @@ async fn apply_hunks_to_files(
|
||||
})
|
||||
}
|
||||
|
||||
async fn write_file_with_missing_parent_retry(
|
||||
fs: &dyn ExecutorFileSystem,
|
||||
path_abs: &AbsolutePathBuf,
|
||||
contents: Vec<u8>,
|
||||
sandbox: Option<&FileSystemSandboxContext>,
|
||||
) -> anyhow::Result<()> {
|
||||
match fs.write_file(path_abs, contents.clone(), sandbox).await {
|
||||
Ok(()) => Ok(()),
|
||||
Err(err) if err.kind() == io::ErrorKind::NotFound => {
|
||||
if let Some(parent_abs) = path_abs.parent() {
|
||||
fs.create_directory(
|
||||
&parent_abs,
|
||||
CreateDirectoryOptions { recursive: true },
|
||||
sandbox,
|
||||
)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Failed to create parent directories for {}",
|
||||
path_abs.display()
|
||||
)
|
||||
})?;
|
||||
}
|
||||
fs.write_file(path_abs, contents, sandbox)
|
||||
.await
|
||||
.with_context(|| format!("Failed to write file {}", path_abs.display()))?;
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
Err(err).with_context(|| format!("Failed to write file {}", path_abs.display()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AppliedPatch {
|
||||
original_contents: String,
|
||||
new_contents: String,
|
||||
|
||||
@@ -254,7 +254,7 @@ where
|
||||
///
|
||||
/// - UNIX: `apply_patch` symlink to the current executable
|
||||
/// - WINDOWS: `apply_patch.bat` batch script to invoke the current executable
|
||||
/// with the "secret" --codex-run-as-apply-patch flag.
|
||||
/// with the hidden `--codex-run-as-apply-patch` flag.
|
||||
///
|
||||
/// This temporary directory is prepended to the PATH environment variable so
|
||||
/// that `apply_patch` can be on the PATH without requiring the user to
|
||||
|
||||
@@ -511,8 +511,11 @@ fn discover_skills_under_root(
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_skill_file(path: &Path, scope: SkillScope) -> Result<SkillMetadata, SkillParseError> {
|
||||
let contents = fs::read_to_string(path).map_err(SkillParseError::Read)?;
|
||||
fn parse_skill_file(
|
||||
path: &AbsolutePathBuf,
|
||||
scope: SkillScope,
|
||||
) -> Result<SkillMetadata, SkillParseError> {
|
||||
let contents = fs::read_to_string(path.as_path()).map_err(SkillParseError::Read)?;
|
||||
|
||||
let frontmatter = extract_frontmatter(&contents).ok_or(SkillParseError::MissingFrontmatter)?;
|
||||
|
||||
@@ -524,8 +527,8 @@ fn parse_skill_file(path: &Path, scope: SkillScope) -> Result<SkillMetadata, Ski
|
||||
.as_deref()
|
||||
.map(sanitize_single_line)
|
||||
.filter(|value| !value.is_empty())
|
||||
.unwrap_or_else(|| default_skill_name(path));
|
||||
let name = namespaced_skill_name(path, &base_name);
|
||||
.unwrap_or_else(|| default_skill_name(path.as_path()));
|
||||
let name = namespaced_skill_name(path.as_path(), &base_name);
|
||||
let description = parsed
|
||||
.description
|
||||
.as_deref()
|
||||
@@ -553,9 +556,7 @@ fn parse_skill_file(path: &Path, scope: SkillScope) -> Result<SkillMetadata, Ski
|
||||
)?;
|
||||
}
|
||||
|
||||
let resolved_path = AbsolutePathBuf::from_absolute_path_checked(path)
|
||||
.and_then(|path| path.canonicalize())
|
||||
.map_err(SkillParseError::Read)?;
|
||||
let resolved_path = path.canonicalize().map_err(SkillParseError::Read)?;
|
||||
|
||||
Ok(SkillMetadata {
|
||||
name,
|
||||
@@ -584,7 +585,7 @@ fn namespaced_skill_name(path: &Path, base_name: &str) -> String {
|
||||
.unwrap_or_else(|| base_name.to_string())
|
||||
}
|
||||
|
||||
fn load_skill_metadata(skill_path: &Path) -> LoadedSkillMetadata {
|
||||
fn load_skill_metadata(skill_path: &AbsolutePathBuf) -> LoadedSkillMetadata {
|
||||
// Fail open: optional metadata should not block loading SKILL.md.
|
||||
let Some(skill_dir) = skill_path.parent() else {
|
||||
return LoadedSkillMetadata::default();
|
||||
@@ -592,11 +593,11 @@ fn load_skill_metadata(skill_path: &Path) -> LoadedSkillMetadata {
|
||||
let metadata_path = skill_dir
|
||||
.join(SKILLS_METADATA_DIR)
|
||||
.join(SKILLS_METADATA_FILENAME);
|
||||
if !metadata_path.exists() {
|
||||
if !metadata_path.as_path().exists() {
|
||||
return LoadedSkillMetadata::default();
|
||||
}
|
||||
|
||||
let contents = match fs::read_to_string(&metadata_path) {
|
||||
let contents = match fs::read_to_string(metadata_path.as_path()) {
|
||||
Ok(contents) => contents,
|
||||
Err(error) => {
|
||||
tracing::warn!(
|
||||
@@ -609,7 +610,7 @@ fn load_skill_metadata(skill_path: &Path) -> LoadedSkillMetadata {
|
||||
};
|
||||
|
||||
let parsed: SkillMetadataFile = {
|
||||
let _guard = AbsolutePathBufGuard::new(skill_dir);
|
||||
let _guard = AbsolutePathBufGuard::new(skill_dir.as_path());
|
||||
match serde_yaml::from_str(&contents) {
|
||||
Ok(parsed) => parsed,
|
||||
Err(error) => {
|
||||
@@ -629,13 +630,16 @@ fn load_skill_metadata(skill_path: &Path) -> LoadedSkillMetadata {
|
||||
policy,
|
||||
} = parsed;
|
||||
LoadedSkillMetadata {
|
||||
interface: resolve_interface(interface, skill_dir),
|
||||
interface: resolve_interface(interface, &skill_dir),
|
||||
dependencies: resolve_dependencies(dependencies),
|
||||
policy: resolve_policy(policy),
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_interface(interface: Option<Interface>, skill_dir: &Path) -> Option<SkillInterface> {
|
||||
fn resolve_interface(
|
||||
interface: Option<Interface>,
|
||||
skill_dir: &AbsolutePathBuf,
|
||||
) -> Option<SkillInterface> {
|
||||
let interface = interface?;
|
||||
let interface = SkillInterface {
|
||||
display_name: resolve_str(
|
||||
@@ -726,10 +730,10 @@ fn resolve_dependency_tool(tool: DependencyTool) -> Option<SkillToolDependency>
|
||||
}
|
||||
|
||||
fn resolve_asset_path(
|
||||
skill_dir: &Path,
|
||||
skill_dir: &AbsolutePathBuf,
|
||||
field: &'static str,
|
||||
path: Option<PathBuf>,
|
||||
) -> Option<PathBuf> {
|
||||
) -> Option<AbsolutePathBuf> {
|
||||
// Icons must be relative paths under the skill's assets/ directory; otherwise return None.
|
||||
let path = path?;
|
||||
if path.as_os_str().is_empty() {
|
||||
|
||||
@@ -495,16 +495,8 @@ interface:
|
||||
interface: Some(SkillInterface {
|
||||
display_name: Some("UI Skill".to_string()),
|
||||
short_description: Some("short desc".to_string()),
|
||||
icon_small: Some(
|
||||
normalized_skill_dir
|
||||
.join("assets/small-400px.png")
|
||||
.to_path_buf()
|
||||
),
|
||||
icon_large: Some(
|
||||
normalized_skill_dir
|
||||
.join("assets/large-logo.svg")
|
||||
.to_path_buf()
|
||||
),
|
||||
icon_small: Some(normalized_skill_dir.join("assets/small-400px.png")),
|
||||
icon_large: Some(normalized_skill_dir.join("assets/large-logo.svg")),
|
||||
brand_color: Some("#3B82F6".to_string()),
|
||||
default_prompt: Some("default prompt".to_string()),
|
||||
}),
|
||||
@@ -656,8 +648,8 @@ async fn accepts_icon_paths_under_assets_dir() {
|
||||
interface: Some(SkillInterface {
|
||||
display_name: Some("UI Skill".to_string()),
|
||||
short_description: None,
|
||||
icon_small: Some(normalized_skill_dir.join("assets/icon.png").to_path_buf()),
|
||||
icon_large: Some(normalized_skill_dir.join("assets/logo.svg").to_path_buf()),
|
||||
icon_small: Some(normalized_skill_dir.join("assets/icon.png")),
|
||||
icon_large: Some(normalized_skill_dir.join("assets/logo.svg")),
|
||||
brand_color: None,
|
||||
default_prompt: None,
|
||||
}),
|
||||
@@ -749,11 +741,7 @@ async fn ignores_default_prompt_over_max_length() {
|
||||
interface: Some(SkillInterface {
|
||||
display_name: Some("UI Skill".to_string()),
|
||||
short_description: None,
|
||||
icon_small: Some(
|
||||
normalized_skill_dir
|
||||
.join("assets/small-400px.png")
|
||||
.to_path_buf()
|
||||
),
|
||||
icon_small: Some(normalized_skill_dir.join("assets/small-400px.png")),
|
||||
icon_large: None,
|
||||
brand_color: None,
|
||||
default_prompt: None,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_protocol::protocol::Product;
|
||||
@@ -56,8 +55,8 @@ pub struct SkillPolicy {
|
||||
pub struct SkillInterface {
|
||||
pub display_name: Option<String>,
|
||||
pub short_description: Option<String>,
|
||||
pub icon_small: Option<PathBuf>,
|
||||
pub icon_large: Option<PathBuf>,
|
||||
pub icon_small: Option<AbsolutePathBuf>,
|
||||
pub icon_large: Option<AbsolutePathBuf>,
|
||||
pub brand_color: Option<String>,
|
||||
pub default_prompt: Option<String>,
|
||||
}
|
||||
|
||||
@@ -84,4 +84,6 @@ instead of running with weaker enforcement.
|
||||
|
||||
### All Platforms
|
||||
|
||||
Expects the binary containing `codex-core` to simulate the virtual `apply_patch` CLI when `arg1` is `--codex-run-as-apply-patch`. See the `codex-arg0` crate for details.
|
||||
Expects the binary containing `codex-core` to simulate the virtual
|
||||
`apply_patch` CLI when `arg1` is `--codex-run-as-apply-patch`. See the
|
||||
`codex-arg0` crate for details.
|
||||
|
||||
@@ -18,16 +18,13 @@ pub(crate) enum InternalApplyPatchInvocation {
|
||||
|
||||
/// The `apply_patch` call was approved, either automatically because it
|
||||
/// appears that it should be allowed based on the user's sandbox policy
|
||||
/// *or* because the user explicitly approved it. In either case, we use
|
||||
/// exec with [`codex_apply_patch::CODEX_CORE_APPLY_PATCH_ARG1`] to realize
|
||||
/// the `apply_patch` call,
|
||||
/// but [`ApplyPatchExec::auto_approved`] is used to determine the sandbox
|
||||
/// used with the `exec()`.
|
||||
DelegateToExec(ApplyPatchExec),
|
||||
/// *or* because the user explicitly approved it. The runtime realizes the
|
||||
/// patch through the selected environment filesystem.
|
||||
DelegateToRuntime(ApplyPatchRuntimeInvocation),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ApplyPatchExec {
|
||||
pub(crate) struct ApplyPatchRuntimeInvocation {
|
||||
pub(crate) action: ApplyPatchAction,
|
||||
pub(crate) auto_approved: bool,
|
||||
pub(crate) exec_approval_requirement: ExecApprovalRequirement,
|
||||
@@ -49,7 +46,7 @@ pub(crate) async fn apply_patch(
|
||||
SafetyCheck::AutoApprove {
|
||||
user_explicitly_approved,
|
||||
..
|
||||
} => InternalApplyPatchInvocation::DelegateToExec(ApplyPatchExec {
|
||||
} => InternalApplyPatchInvocation::DelegateToRuntime(ApplyPatchRuntimeInvocation {
|
||||
action,
|
||||
auto_approved: !user_explicitly_approved,
|
||||
exec_approval_requirement: ExecApprovalRequirement::Skip {
|
||||
@@ -61,7 +58,7 @@ pub(crate) async fn apply_patch(
|
||||
// Delegate the approval prompt (including cached approvals) to the
|
||||
// tool runtime, consistent with how shell/unified_exec approvals
|
||||
// are orchestrator-driven.
|
||||
InternalApplyPatchInvocation::DelegateToExec(ApplyPatchExec {
|
||||
InternalApplyPatchInvocation::DelegateToRuntime(ApplyPatchRuntimeInvocation {
|
||||
action,
|
||||
auto_approved: false,
|
||||
exec_approval_requirement: ExecApprovalRequirement::NeedsApproval {
|
||||
|
||||
+34
-42
@@ -1,7 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Debug;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicU64;
|
||||
@@ -650,7 +649,7 @@ impl Codex {
|
||||
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
codex_home: config.codex_home.to_path_buf(),
|
||||
codex_home: config.codex_home.clone(),
|
||||
thread_name: None,
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
metrics_service_name,
|
||||
@@ -1132,7 +1131,7 @@ fn local_time_context() -> (String, String) {
|
||||
|
||||
async fn thread_title_from_state_db(
|
||||
state_db: Option<&state_db::StateDbHandle>,
|
||||
codex_home: &Path,
|
||||
codex_home: &AbsolutePathBuf,
|
||||
conversation_id: ThreadId,
|
||||
) -> Option<String> {
|
||||
if let Some(metadata) = state_db
|
||||
@@ -1189,7 +1188,7 @@ pub(crate) struct SessionConfiguration {
|
||||
/// the process-wide current working directory.
|
||||
cwd: AbsolutePathBuf,
|
||||
/// Directory containing all Codex state for this session.
|
||||
codex_home: PathBuf,
|
||||
codex_home: AbsolutePathBuf,
|
||||
/// Optional user-facing name for the thread, updated during the session.
|
||||
thread_name: Option<String>,
|
||||
|
||||
@@ -1208,7 +1207,7 @@ pub(crate) struct SessionConfiguration {
|
||||
}
|
||||
|
||||
impl SessionConfiguration {
|
||||
pub(crate) fn codex_home(&self) -> &PathBuf {
|
||||
pub(crate) fn codex_home(&self) -> &AbsolutePathBuf {
|
||||
&self.codex_home
|
||||
}
|
||||
|
||||
@@ -1220,7 +1219,7 @@ impl SessionConfiguration {
|
||||
approval_policy: self.approval_policy.value(),
|
||||
approvals_reviewer: self.approvals_reviewer,
|
||||
sandbox_policy: self.sandbox_policy.get().clone(),
|
||||
cwd: self.cwd.to_path_buf(),
|
||||
cwd: self.cwd.clone(),
|
||||
ephemeral: self.original_config_do_not_use.ephemeral,
|
||||
reasoning_effort: self.collaboration_mode.reasoning_effort(),
|
||||
personality: self.personality,
|
||||
@@ -1471,7 +1470,7 @@ impl Session {
|
||||
per_turn_config
|
||||
}
|
||||
|
||||
pub(crate) async fn codex_home(&self) -> PathBuf {
|
||||
pub(crate) async fn codex_home(&self) -> AbsolutePathBuf {
|
||||
let state = self.state.lock().await;
|
||||
state.session_configuration.codex_home().clone()
|
||||
}
|
||||
@@ -1572,7 +1571,7 @@ impl Session {
|
||||
conversation_id.to_string(),
|
||||
&session_source,
|
||||
sub_id.clone(),
|
||||
cwd.to_path_buf(),
|
||||
cwd.clone(),
|
||||
session_configuration.sandbox_policy.get(),
|
||||
session_configuration.windows_sandbox_level,
|
||||
));
|
||||
@@ -1927,9 +1926,9 @@ impl Session {
|
||||
tx
|
||||
} else {
|
||||
ShellSnapshot::start_snapshotting(
|
||||
config.codex_home.to_path_buf(),
|
||||
config.codex_home.clone(),
|
||||
conversation_id,
|
||||
session_configuration.cwd.to_path_buf(),
|
||||
session_configuration.cwd.clone(),
|
||||
&mut default_shell,
|
||||
session_telemetry.clone(),
|
||||
)
|
||||
@@ -2133,7 +2132,7 @@ impl Session {
|
||||
approval_policy: session_configuration.approval_policy.value(),
|
||||
approvals_reviewer: session_configuration.approvals_reviewer,
|
||||
sandbox_policy: session_configuration.sandbox_policy.get().clone(),
|
||||
cwd: session_configuration.cwd.to_path_buf(),
|
||||
cwd: session_configuration.cwd.clone(),
|
||||
reasoning_effort: session_configuration.collaboration_mode.reasoning_effort(),
|
||||
history_log_id,
|
||||
history_entry_count,
|
||||
@@ -2492,9 +2491,9 @@ impl Session {
|
||||
|
||||
fn maybe_refresh_shell_snapshot_for_cwd(
|
||||
&self,
|
||||
previous_cwd: &Path,
|
||||
next_cwd: &Path,
|
||||
codex_home: &Path,
|
||||
previous_cwd: &AbsolutePathBuf,
|
||||
next_cwd: &AbsolutePathBuf,
|
||||
codex_home: &AbsolutePathBuf,
|
||||
session_source: &SessionSource,
|
||||
) {
|
||||
if previous_cwd == next_cwd {
|
||||
@@ -2513,9 +2512,9 @@ impl Session {
|
||||
}
|
||||
|
||||
ShellSnapshot::refresh_snapshot(
|
||||
codex_home.to_path_buf(),
|
||||
codex_home.clone(),
|
||||
self.conversation_id,
|
||||
next_cwd.to_path_buf(),
|
||||
next_cwd.clone(),
|
||||
self.services.user_shell.as_ref().clone(),
|
||||
self.services.shell_snapshot_tx.clone(),
|
||||
self.services.session_telemetry.clone(),
|
||||
@@ -2779,14 +2778,6 @@ impl Session {
|
||||
}
|
||||
};
|
||||
|
||||
let config_toml_path = match AbsolutePathBuf::try_from(config_toml_path) {
|
||||
Ok(path) => path,
|
||||
Err(err) => {
|
||||
warn!("failed to resolve user config path while reloading layer: {err}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut state = self.state.lock().await;
|
||||
let mut config = (*state.session_configuration.original_config_do_not_use).clone();
|
||||
config.config_layer_stack = config
|
||||
@@ -3184,7 +3175,7 @@ impl Session {
|
||||
call_id: String,
|
||||
approval_id: Option<String>,
|
||||
command: Vec<String>,
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
reason: Option<String>,
|
||||
network_approval_context: Option<NetworkApprovalContext>,
|
||||
proposed_execpolicy_amendment: Option<ExecPolicyAmendment>,
|
||||
@@ -5395,9 +5386,12 @@ mod handlers {
|
||||
cwds: Vec<PathBuf>,
|
||||
force_reload: bool,
|
||||
) {
|
||||
let cwds = if cwds.is_empty() {
|
||||
let default_cwd = {
|
||||
let state = sess.state.lock().await;
|
||||
vec![state.session_configuration.cwd.to_path_buf()]
|
||||
state.session_configuration.cwd.to_path_buf()
|
||||
};
|
||||
let cwds = if cwds.is_empty() {
|
||||
vec![default_cwd]
|
||||
} else {
|
||||
cwds
|
||||
};
|
||||
@@ -5412,14 +5406,13 @@ mod handlers {
|
||||
let cwd_abs = match AbsolutePathBuf::relative_to_current_dir(cwd.as_path()) {
|
||||
Ok(path) => path,
|
||||
Err(err) => {
|
||||
let message = err.to_string();
|
||||
let cwd_for_entry = cwd.clone();
|
||||
let error_path = cwd.clone();
|
||||
skills.push(SkillsListEntry {
|
||||
cwd: cwd_for_entry.clone(),
|
||||
cwd,
|
||||
skills: Vec::new(),
|
||||
errors: vec![SkillErrorInfo {
|
||||
path: cwd_for_entry,
|
||||
message,
|
||||
path: error_path,
|
||||
message: err.to_string(),
|
||||
}],
|
||||
});
|
||||
continue;
|
||||
@@ -5436,14 +5429,13 @@ mod handlers {
|
||||
{
|
||||
Ok(config_layer_stack) => config_layer_stack,
|
||||
Err(err) => {
|
||||
let message = err.to_string();
|
||||
let cwd_for_entry = cwd.clone();
|
||||
let error_path = cwd.clone();
|
||||
skills.push(SkillsListEntry {
|
||||
cwd: cwd_for_entry.clone(),
|
||||
cwd,
|
||||
skills: Vec::new(),
|
||||
errors: vec![SkillErrorInfo {
|
||||
path: cwd_for_entry,
|
||||
message,
|
||||
path: error_path,
|
||||
message: err.to_string(),
|
||||
}],
|
||||
});
|
||||
continue;
|
||||
@@ -5456,7 +5448,7 @@ mod handlers {
|
||||
)
|
||||
.await;
|
||||
let skills_input = crate::SkillsLoadInput::new(
|
||||
cwd_abs,
|
||||
cwd_abs.clone(),
|
||||
effective_skill_roots,
|
||||
config_layer_stack,
|
||||
config.bundled_skills_enabled(),
|
||||
@@ -5870,7 +5862,7 @@ mod handlers {
|
||||
sess.maybe_emit_unknown_model_warning_for_turn(turn_context.as_ref())
|
||||
.await;
|
||||
sess.refresh_mcp_servers_if_requested(&turn_context).await;
|
||||
match resolve_review_request(review_request, turn_context.cwd.as_path()) {
|
||||
match resolve_review_request(review_request, &turn_context.cwd) {
|
||||
Ok(resolved) => {
|
||||
spawn_review_thread(
|
||||
Arc::clone(sess),
|
||||
@@ -5986,7 +5978,7 @@ async fn spawn_review_thread(
|
||||
sess.conversation_id.to_string(),
|
||||
&session_source,
|
||||
review_turn_id.clone(),
|
||||
parent_turn_context.cwd.to_path_buf(),
|
||||
parent_turn_context.cwd.clone(),
|
||||
parent_turn_context.sandbox_policy.get(),
|
||||
parent_turn_context.windows_sandbox_level,
|
||||
));
|
||||
@@ -6501,7 +6493,7 @@ pub(crate) async fn run_turn(
|
||||
let stop_request = codex_hooks::StopRequest {
|
||||
session_id: sess.conversation_id,
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
cwd: turn_context.cwd.to_path_buf(),
|
||||
cwd: turn_context.cwd.clone(),
|
||||
transcript_path: sess.hook_transcript_path().await,
|
||||
model: turn_context.model_info.slug.clone(),
|
||||
permission_mode: stop_hook_permission_mode,
|
||||
@@ -6551,7 +6543,7 @@ pub(crate) async fn run_turn(
|
||||
.hooks()
|
||||
.dispatch(HookPayload {
|
||||
session_id: sess.conversation_id,
|
||||
cwd: turn_context.cwd.to_path_buf(),
|
||||
cwd: turn_context.cwd.clone(),
|
||||
client: turn_context.app_server_client_name.clone(),
|
||||
triggered_at: chrono::Utc::now(),
|
||||
hook_event: HookEvent::AfterAgent {
|
||||
|
||||
@@ -571,7 +571,7 @@ async fn handle_patch_approval(
|
||||
new_guardian_review_id(),
|
||||
GuardianApprovalRequest::ApplyPatch {
|
||||
id: approval_id.clone(),
|
||||
cwd: parent_ctx.cwd.to_path_buf(),
|
||||
cwd: parent_ctx.cwd.clone(),
|
||||
files,
|
||||
patch,
|
||||
},
|
||||
|
||||
@@ -23,9 +23,10 @@ use codex_protocol::request_permissions::RequestPermissionsResponse;
|
||||
use codex_protocol::request_user_input::RequestUserInputAnswer;
|
||||
use codex_protocol::request_user_input::RequestUserInputEvent;
|
||||
use codex_protocol::request_user_input::RequestUserInputQuestion;
|
||||
use core_test_support::PathBufExt;
|
||||
use core_test_support::test_path_buf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::sync::watch;
|
||||
@@ -282,7 +283,7 @@ async fn handle_exec_approval_uses_call_id_for_guardian_review_and_approval_id_f
|
||||
approval_id: Some("callback-approval-1".to_string()),
|
||||
turn_id: "child-turn-1".to_string(),
|
||||
command: vec!["rm".to_string(), "-rf".to_string(), "tmp".to_string()],
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
reason: Some("unsafe subcommand".to_string()),
|
||||
network_approval_context: None,
|
||||
proposed_execpolicy_amendment: None,
|
||||
@@ -313,7 +314,7 @@ async fn handle_exec_approval_uses_call_id_for_guardian_review_and_approval_id_f
|
||||
let expected_action = GuardianAssessmentAction::Command {
|
||||
source: GuardianCommandSource::Shell,
|
||||
command: "rm -rf tmp".to_string(),
|
||||
cwd: "/tmp".into(),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
};
|
||||
assert!(!assessment_event.id.is_empty());
|
||||
assert_eq!(
|
||||
|
||||
@@ -1919,7 +1919,7 @@ async fn set_rate_limits_retains_previous_credits() {
|
||||
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
codex_home: config.codex_home.to_path_buf(),
|
||||
codex_home: config.codex_home.clone(),
|
||||
thread_name: None,
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
metrics_service_name: None,
|
||||
@@ -2021,7 +2021,7 @@ async fn set_rate_limits_updates_plan_type_when_present() {
|
||||
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
codex_home: config.codex_home.to_path_buf(),
|
||||
codex_home: config.codex_home.clone(),
|
||||
thread_name: None,
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
metrics_service_name: None,
|
||||
@@ -2373,7 +2373,7 @@ pub(crate) async fn make_session_configuration_for_tests() -> SessionConfigurati
|
||||
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
codex_home: config.codex_home.to_path_buf(),
|
||||
codex_home: config.codex_home.clone(),
|
||||
thread_name: None,
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
metrics_service_name: None,
|
||||
@@ -2636,7 +2636,7 @@ async fn session_new_fails_when_zsh_fork_enabled_without_zsh_path() {
|
||||
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
codex_home: config.codex_home.to_path_buf(),
|
||||
codex_home: config.codex_home.clone(),
|
||||
thread_name: None,
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
metrics_service_name: None,
|
||||
@@ -2740,7 +2740,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
||||
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
codex_home: config.codex_home.to_path_buf(),
|
||||
codex_home: config.codex_home.clone(),
|
||||
thread_name: None,
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
metrics_service_name: None,
|
||||
@@ -3586,7 +3586,7 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx(
|
||||
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
codex_home: config.codex_home.to_path_buf(),
|
||||
codex_home: config.codex_home.clone(),
|
||||
thread_name: None,
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
metrics_service_name: None,
|
||||
@@ -4121,7 +4121,7 @@ async fn handle_output_item_done_records_image_save_history_message() {
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let call_id = "ig_history_records_message";
|
||||
let expected_saved_path = crate::stream_events_utils::image_generation_artifact_path(
|
||||
turn_context.config.codex_home.as_path(),
|
||||
&turn_context.config.codex_home,
|
||||
&session.conversation_id.to_string(),
|
||||
call_id,
|
||||
);
|
||||
@@ -4145,7 +4145,7 @@ async fn handle_output_item_done_records_image_save_history_message() {
|
||||
|
||||
let history = session.clone_history().await;
|
||||
let image_output_path = crate::stream_events_utils::image_generation_artifact_path(
|
||||
turn_context.config.codex_home.as_path(),
|
||||
&turn_context.config.codex_home,
|
||||
&session.conversation_id.to_string(),
|
||||
"<image_id>",
|
||||
);
|
||||
@@ -4173,7 +4173,7 @@ async fn handle_output_item_done_skips_image_save_message_when_save_fails() {
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let call_id = "ig_history_no_message";
|
||||
let expected_saved_path = crate::stream_events_utils::image_generation_artifact_path(
|
||||
turn_context.config.codex_home.as_path(),
|
||||
&turn_context.config.codex_home,
|
||||
&session.conversation_id.to_string(),
|
||||
call_id,
|
||||
);
|
||||
|
||||
@@ -24,6 +24,7 @@ use codex_protocol::protocol::ThreadMemoryMode;
|
||||
use codex_protocol::protocol::TokenUsage;
|
||||
use codex_protocol::protocol::W3cTraceContext;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use rmcp::model::ReadResourceRequestParams;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
@@ -40,7 +41,7 @@ pub struct ThreadConfigSnapshot {
|
||||
pub approval_policy: AskForApproval,
|
||||
pub approvals_reviewer: ApprovalsReviewer,
|
||||
pub sandbox_policy: SandboxPolicy,
|
||||
pub cwd: PathBuf,
|
||||
pub cwd: AbsolutePathBuf,
|
||||
pub ephemeral: bool,
|
||||
pub reasoning_effort: Option<ReasoningEffort>,
|
||||
pub personality: Option<Personality>,
|
||||
|
||||
@@ -272,7 +272,7 @@ pub struct Config {
|
||||
pub user_instructions: Option<String>,
|
||||
|
||||
/// Path to the global AGENTS file loaded into `user_instructions`.
|
||||
pub user_instructions_path: Option<PathBuf>,
|
||||
pub user_instructions_path: Option<AbsolutePathBuf>,
|
||||
|
||||
/// Base instructions override.
|
||||
pub base_instructions: Option<String>,
|
||||
@@ -1578,7 +1578,6 @@ impl Config {
|
||||
};
|
||||
let memories_root = memory_root(&codex_home);
|
||||
std::fs::create_dir_all(&memories_root)?;
|
||||
let memories_root = AbsolutePathBuf::from_absolute_path(&memories_root)?;
|
||||
if !additional_writable_roots
|
||||
.iter()
|
||||
.any(|existing| existing == &memories_root)
|
||||
@@ -2210,11 +2209,10 @@ impl Config {
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn load_instructions(codex_dir: Option<&Path>) -> Option<LoadedUserInstructions> {
|
||||
fn load_instructions(codex_dir: Option<&AbsolutePathBuf>) -> Option<LoadedUserInstructions> {
|
||||
let base = codex_dir?;
|
||||
for candidate in [LOCAL_PROJECT_DOC_FILENAME, DEFAULT_PROJECT_DOC_FILENAME] {
|
||||
let mut path = base.to_path_buf();
|
||||
path.push(candidate);
|
||||
let path = base.join(candidate);
|
||||
if let Ok(contents) = std::fs::read_to_string(&path) {
|
||||
let trimmed = contents.trim();
|
||||
if !trimmed.is_empty() {
|
||||
@@ -2297,7 +2295,7 @@ impl Config {
|
||||
|
||||
struct LoadedUserInstructions {
|
||||
contents: String,
|
||||
path: PathBuf,
|
||||
path: AbsolutePathBuf,
|
||||
}
|
||||
|
||||
pub(crate) fn uses_deprecated_instructions_file(config_layer_stack: &ConfigLayerStack) -> bool {
|
||||
|
||||
@@ -221,7 +221,7 @@ pub async fn process_exec_tool_call(
|
||||
sandbox_policy: &SandboxPolicy,
|
||||
file_system_sandbox_policy: &FileSystemSandboxPolicy,
|
||||
network_sandbox_policy: NetworkSandboxPolicy,
|
||||
sandbox_cwd: &Path,
|
||||
sandbox_cwd: &AbsolutePathBuf,
|
||||
codex_linux_sandbox_exe: &Option<PathBuf>,
|
||||
use_legacy_landlock: bool,
|
||||
stdout_stream: Option<StdoutStream>,
|
||||
@@ -247,7 +247,7 @@ pub fn build_exec_request(
|
||||
sandbox_policy: &SandboxPolicy,
|
||||
file_system_sandbox_policy: &FileSystemSandboxPolicy,
|
||||
network_sandbox_policy: NetworkSandboxPolicy,
|
||||
sandbox_cwd: &Path,
|
||||
sandbox_cwd: &AbsolutePathBuf,
|
||||
codex_linux_sandbox_exe: &Option<PathBuf>,
|
||||
use_legacy_landlock: bool,
|
||||
) -> Result<ExecRequest> {
|
||||
@@ -845,7 +845,7 @@ async fn exec(
|
||||
program: PathBuf::from(program),
|
||||
args: args.into(),
|
||||
arg0: arg0_ref,
|
||||
cwd: cwd.to_path_buf(),
|
||||
cwd,
|
||||
network_sandbox_policy,
|
||||
// The environment already has attempt-scoped proxy settings from
|
||||
// apply_to_env_for_attempt above. Passing network here would reapply
|
||||
@@ -881,7 +881,7 @@ pub(crate) fn unsupported_windows_restricted_token_sandbox_reason(
|
||||
sandbox_policy: &SandboxPolicy,
|
||||
file_system_sandbox_policy: &FileSystemSandboxPolicy,
|
||||
network_sandbox_policy: NetworkSandboxPolicy,
|
||||
sandbox_policy_cwd: &Path,
|
||||
sandbox_policy_cwd: &AbsolutePathBuf,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
) -> Option<String> {
|
||||
if windows_sandbox_level == WindowsSandboxLevel::Elevated {
|
||||
@@ -912,7 +912,7 @@ pub(crate) fn resolve_windows_restricted_token_filesystem_overrides(
|
||||
sandbox_policy: &SandboxPolicy,
|
||||
file_system_sandbox_policy: &FileSystemSandboxPolicy,
|
||||
network_sandbox_policy: NetworkSandboxPolicy,
|
||||
sandbox_policy_cwd: &Path,
|
||||
sandbox_policy_cwd: &AbsolutePathBuf,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
) -> std::result::Result<Option<WindowsSandboxFilesystemOverrides>, String> {
|
||||
if sandbox != SandboxType::WindowsRestrictedToken
|
||||
@@ -1048,7 +1048,7 @@ pub(crate) fn resolve_windows_elevated_filesystem_overrides(
|
||||
sandbox_policy: &SandboxPolicy,
|
||||
file_system_sandbox_policy: &FileSystemSandboxPolicy,
|
||||
network_sandbox_policy: NetworkSandboxPolicy,
|
||||
sandbox_policy_cwd: &Path,
|
||||
sandbox_policy_cwd: &AbsolutePathBuf,
|
||||
use_windows_elevated_backend: bool,
|
||||
) -> std::result::Result<Option<WindowsSandboxFilesystemOverrides>, String> {
|
||||
if sandbox != SandboxType::WindowsRestrictedToken || !use_windows_elevated_backend {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use super::*;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_sandboxing::SandboxType;
|
||||
use core_test_support::PathBufExt;
|
||||
use core_test_support::PathExt;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
@@ -369,7 +371,7 @@ async fn process_exec_tool_call_preserves_full_buffer_capture_policy() -> Result
|
||||
&sandbox_policy,
|
||||
&FileSystemSandboxPolicy::from(&sandbox_policy),
|
||||
NetworkSandboxPolicy::Enabled,
|
||||
cwd.as_path(),
|
||||
&cwd,
|
||||
&None,
|
||||
/*use_legacy_landlock*/ false,
|
||||
/*stdout_stream*/ None,
|
||||
@@ -436,7 +438,7 @@ fn windows_restricted_token_rejects_network_only_restrictions() {
|
||||
network_access: codex_protocol::protocol::NetworkAccess::Restricted,
|
||||
};
|
||||
let file_system_policy = FileSystemSandboxPolicy::unrestricted();
|
||||
let sandbox_policy_cwd = std::env::current_dir().expect("cwd");
|
||||
let sandbox_policy_cwd = AbsolutePathBuf::current_dir().expect("cwd");
|
||||
|
||||
assert_eq!(
|
||||
unsupported_windows_restricted_token_sandbox_reason(
|
||||
@@ -457,7 +459,7 @@ fn windows_restricted_token_rejects_network_only_restrictions() {
|
||||
fn windows_restricted_token_allows_legacy_restricted_policies() {
|
||||
let policy = SandboxPolicy::new_read_only_policy();
|
||||
let file_system_policy = FileSystemSandboxPolicy::from(&policy);
|
||||
let sandbox_policy_cwd = std::env::current_dir().expect("cwd");
|
||||
let sandbox_policy_cwd = AbsolutePathBuf::current_dir().expect("cwd");
|
||||
|
||||
assert_eq!(
|
||||
unsupported_windows_restricted_token_sandbox_reason(
|
||||
@@ -482,7 +484,7 @@ fn windows_restricted_token_allows_legacy_workspace_write_policies() {
|
||||
exclude_slash_tmp: true,
|
||||
};
|
||||
let file_system_policy = FileSystemSandboxPolicy::from(&policy);
|
||||
let sandbox_policy_cwd = std::env::current_dir().expect("cwd");
|
||||
let sandbox_policy_cwd = AbsolutePathBuf::current_dir().expect("cwd");
|
||||
|
||||
assert_eq!(
|
||||
unsupported_windows_restricted_token_sandbox_reason(
|
||||
@@ -520,7 +522,7 @@ fn windows_elevated_allows_legacy_restricted_read_policies() {
|
||||
&policy,
|
||||
&file_system_policy,
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
temp_dir.path(),
|
||||
&temp_dir.path().abs(),
|
||||
WindowsSandboxLevel::Elevated,
|
||||
),
|
||||
None
|
||||
@@ -561,7 +563,7 @@ fn windows_restricted_token_rejects_split_only_filesystem_policies() {
|
||||
&policy,
|
||||
&file_system_policy,
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
temp_dir.path(),
|
||||
&temp_dir.path().abs(),
|
||||
WindowsSandboxLevel::RestrictedToken,
|
||||
),
|
||||
Some(
|
||||
@@ -605,7 +607,7 @@ fn windows_restricted_token_rejects_root_write_read_only_carveouts() {
|
||||
&policy,
|
||||
&file_system_policy,
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
temp_dir.path(),
|
||||
&temp_dir.path().abs(),
|
||||
WindowsSandboxLevel::RestrictedToken,
|
||||
),
|
||||
Some(
|
||||
@@ -618,9 +620,11 @@ fn windows_restricted_token_rejects_root_write_read_only_carveouts() {
|
||||
#[test]
|
||||
fn windows_restricted_token_supports_full_read_split_write_read_carveouts() {
|
||||
let temp_dir = tempfile::TempDir::new().expect("tempdir");
|
||||
let cwd = dunce::canonicalize(temp_dir.path()).expect("canonicalize temp dir");
|
||||
let cwd = dunce::canonicalize(temp_dir.path())
|
||||
.expect("canonicalize temp dir")
|
||||
.abs();
|
||||
let docs = cwd.join("docs");
|
||||
std::fs::create_dir_all(&docs).expect("create docs");
|
||||
std::fs::create_dir_all(docs.as_path()).expect("create docs");
|
||||
let policy = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![],
|
||||
read_only_access: codex_protocol::protocol::ReadOnlyAccess::FullAccess,
|
||||
@@ -642,20 +646,14 @@ fn windows_restricted_token_supports_full_read_split_write_read_carveouts() {
|
||||
access: codex_protocol::permissions::FileSystemAccessMode::Write,
|
||||
},
|
||||
codex_protocol::permissions::FileSystemSandboxEntry {
|
||||
path: codex_protocol::permissions::FileSystemPath::Path {
|
||||
path: codex_utils_absolute_path::AbsolutePathBuf::from_absolute_path(&docs)
|
||||
.expect("absolute docs"),
|
||||
},
|
||||
path: codex_protocol::permissions::FileSystemPath::Path { path: docs.clone() },
|
||||
access: codex_protocol::permissions::FileSystemAccessMode::Read,
|
||||
},
|
||||
]);
|
||||
|
||||
// The legacy workspace-write root already protects top-level `.codex`, so
|
||||
// the restricted-token overlay only needs the extra read-only docs carveout.
|
||||
let expected_deny_write_paths = vec![
|
||||
codex_utils_absolute_path::AbsolutePathBuf::from_absolute_path(&docs)
|
||||
.expect("absolute docs"),
|
||||
];
|
||||
let expected_deny_write_paths = vec![docs];
|
||||
|
||||
assert_eq!(
|
||||
resolve_windows_restricted_token_filesystem_overrides(
|
||||
@@ -700,7 +698,7 @@ fn windows_elevated_supports_split_restricted_read_roots() {
|
||||
&policy,
|
||||
&file_system_policy,
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
temp_dir.path(),
|
||||
&temp_dir.path().abs(),
|
||||
/*use_windows_elevated_backend*/ true,
|
||||
),
|
||||
Ok(Some(WindowsSandboxFilesystemOverrides {
|
||||
@@ -752,7 +750,7 @@ fn windows_elevated_supports_split_write_read_carveouts() {
|
||||
&policy,
|
||||
&file_system_policy,
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
temp_dir.path(),
|
||||
&temp_dir.path().abs(),
|
||||
/*use_windows_elevated_backend*/ true,
|
||||
),
|
||||
Ok(Some(WindowsSandboxFilesystemOverrides {
|
||||
@@ -806,7 +804,7 @@ fn windows_elevated_rejects_unreadable_split_carveouts() {
|
||||
&policy,
|
||||
&file_system_policy,
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
temp_dir.path(),
|
||||
&temp_dir.path().abs(),
|
||||
WindowsSandboxLevel::Elevated,
|
||||
),
|
||||
Some(
|
||||
@@ -864,7 +862,7 @@ fn windows_elevated_rejects_reopened_writable_descendants() {
|
||||
&policy,
|
||||
&file_system_policy,
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
temp_dir.path(),
|
||||
&temp_dir.path().abs(),
|
||||
WindowsSandboxLevel::Elevated,
|
||||
),
|
||||
Some(
|
||||
@@ -998,7 +996,7 @@ async fn process_exec_tool_call_respects_cancellation_token() -> Result<()> {
|
||||
&SandboxPolicy::DangerFullAccess,
|
||||
&FileSystemSandboxPolicy::from(&SandboxPolicy::DangerFullAccess),
|
||||
NetworkSandboxPolicy::Enabled,
|
||||
cwd.as_path(),
|
||||
&cwd,
|
||||
&None,
|
||||
/*use_legacy_landlock*/ false,
|
||||
/*stdout_stream*/ None,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use codex_protocol::approvals::GuardianAssessmentAction;
|
||||
use codex_protocol::approvals::GuardianCommandSource;
|
||||
@@ -17,7 +16,7 @@ pub(crate) enum GuardianApprovalRequest {
|
||||
Shell {
|
||||
id: String,
|
||||
command: Vec<String>,
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
sandbox_permissions: crate::sandboxing::SandboxPermissions,
|
||||
additional_permissions: Option<PermissionProfile>,
|
||||
justification: Option<String>,
|
||||
@@ -25,7 +24,7 @@ pub(crate) enum GuardianApprovalRequest {
|
||||
ExecCommand {
|
||||
id: String,
|
||||
command: Vec<String>,
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
sandbox_permissions: crate::sandboxing::SandboxPermissions,
|
||||
additional_permissions: Option<PermissionProfile>,
|
||||
justification: Option<String>,
|
||||
@@ -37,12 +36,12 @@ pub(crate) enum GuardianApprovalRequest {
|
||||
source: GuardianCommandSource,
|
||||
program: String,
|
||||
argv: Vec<String>,
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
additional_permissions: Option<PermissionProfile>,
|
||||
},
|
||||
ApplyPatch {
|
||||
id: String,
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
files: Vec<AbsolutePathBuf>,
|
||||
patch: String,
|
||||
},
|
||||
@@ -151,12 +150,12 @@ fn serialize_command_guardian_action(
|
||||
fn command_assessment_action(
|
||||
source: GuardianCommandSource,
|
||||
command: &[String],
|
||||
cwd: &Path,
|
||||
cwd: &AbsolutePathBuf,
|
||||
) -> GuardianAssessmentAction {
|
||||
GuardianAssessmentAction::Command {
|
||||
source,
|
||||
command: codex_shell_command::parse_command::shlex_join(command),
|
||||
cwd: cwd.to_path_buf(),
|
||||
cwd: cwd.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,10 +322,7 @@ pub(crate) fn guardian_assessment_action(
|
||||
GuardianApprovalRequest::ApplyPatch { cwd, files, .. } => {
|
||||
GuardianAssessmentAction::ApplyPatch {
|
||||
cwd: cwd.clone(),
|
||||
files: files
|
||||
.iter()
|
||||
.map(codex_utils_absolute_path::AbsolutePathBuf::to_path_buf)
|
||||
.collect(),
|
||||
files: files.clone(),
|
||||
}
|
||||
}
|
||||
GuardianApprovalRequest::NetworkAccess {
|
||||
|
||||
@@ -35,6 +35,7 @@ use crate::rollout::recorder::RolloutRecorder;
|
||||
use codex_config::types::McpServerConfig;
|
||||
use codex_features::Feature;
|
||||
use codex_model_provider_info::ModelProviderInfo;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
|
||||
use super::GUARDIAN_REVIEW_TIMEOUT;
|
||||
use super::GUARDIAN_REVIEWER_NAME;
|
||||
@@ -129,7 +130,7 @@ struct GuardianReviewSessionReuseKey {
|
||||
base_instructions: Option<String>,
|
||||
user_instructions: Option<String>,
|
||||
compact_prompt: Option<String>,
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
mcp_servers: Constrained<HashMap<String, McpServerConfig>>,
|
||||
codex_linux_sandbox_exe: Option<PathBuf>,
|
||||
main_execve_wrapper_exe: Option<PathBuf>,
|
||||
@@ -156,7 +157,7 @@ impl GuardianReviewSessionReuseKey {
|
||||
base_instructions: spawn_config.base_instructions.clone(),
|
||||
user_instructions: spawn_config.user_instructions.clone(),
|
||||
compact_prompt: spawn_config.compact_prompt.clone(),
|
||||
cwd: spawn_config.cwd.to_path_buf(),
|
||||
cwd: spawn_config.cwd.clone(),
|
||||
mcp_servers: spawn_config.mcp_servers.clone(),
|
||||
codex_linux_sandbox_exe: spawn_config.codex_linux_sandbox_exe.clone(),
|
||||
main_execve_wrapper_exe: spawn_config.main_execve_wrapper_exe.clone(),
|
||||
|
||||
@@ -50,7 +50,6 @@ use insta::Settings;
|
||||
use insta::assert_snapshot;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tempfile::TempDir;
|
||||
@@ -155,6 +154,20 @@ fn guardian_snapshot_options() -> ContextSnapshotOptions {
|
||||
.strip_agents_md_user_context()
|
||||
}
|
||||
|
||||
fn normalize_guardian_snapshot_paths(text: String) -> String {
|
||||
let platform_path = test_path_buf("/repo/codex-rs/core").display().to_string();
|
||||
if platform_path == "/repo/codex-rs/core" {
|
||||
return text;
|
||||
}
|
||||
|
||||
let escaped_platform_path = serde_json::to_string(&platform_path)
|
||||
.expect("test path should serialize")
|
||||
.trim_matches('"')
|
||||
.to_string();
|
||||
text.replace(&escaped_platform_path, "/repo/codex-rs/core")
|
||||
.replace(&platform_path, "/repo/codex-rs/core")
|
||||
}
|
||||
|
||||
fn guardian_prompt_text(items: &[codex_protocol::user_input::UserInput]) -> String {
|
||||
items
|
||||
.iter()
|
||||
@@ -220,7 +233,7 @@ async fn build_guardian_prompt_full_mode_preserves_initial_review_format() -> an
|
||||
GuardianApprovalRequest::Shell {
|
||||
id: "shell-1".to_string(),
|
||||
command: vec!["git".to_string(), "push".to_string()],
|
||||
cwd: PathBuf::from("/repo/codex-rs/core"),
|
||||
cwd: test_path_buf("/repo/codex-rs/core").abs(),
|
||||
sandbox_permissions: crate::sandboxing::SandboxPermissions::UseDefault,
|
||||
additional_permissions: None,
|
||||
justification: Some("Need to push the reviewed docs fix.".to_string()),
|
||||
@@ -276,7 +289,7 @@ async fn build_guardian_prompt_delta_mode_preserves_original_numbering() -> anyh
|
||||
GuardianApprovalRequest::Shell {
|
||||
id: "shell-2".to_string(),
|
||||
command: vec!["git".to_string(), "push".to_string()],
|
||||
cwd: PathBuf::from("/repo/codex-rs/core"),
|
||||
cwd: test_path_buf("/repo/codex-rs/core").abs(),
|
||||
sandbox_permissions: crate::sandboxing::SandboxPermissions::UseDefault,
|
||||
additional_permissions: None,
|
||||
justification: Some("Need to push the second docs fix.".to_string()),
|
||||
@@ -314,7 +327,7 @@ async fn build_guardian_prompt_delta_mode_handles_empty_delta() -> anyhow::Resul
|
||||
GuardianApprovalRequest::Shell {
|
||||
id: "shell-2".to_string(),
|
||||
command: vec!["git".to_string(), "push".to_string()],
|
||||
cwd: PathBuf::from("/repo/codex-rs/core"),
|
||||
cwd: test_path_buf("/repo/codex-rs/core").abs(),
|
||||
sandbox_permissions: crate::sandboxing::SandboxPermissions::UseDefault,
|
||||
additional_permissions: None,
|
||||
justification: Some("Need to push the second docs fix.".to_string()),
|
||||
@@ -349,7 +362,7 @@ async fn build_guardian_prompt_stale_delta_cursor_falls_back_to_full_prompt() ->
|
||||
GuardianApprovalRequest::Shell {
|
||||
id: "shell-3".to_string(),
|
||||
command: vec!["git".to_string(), "push".to_string()],
|
||||
cwd: PathBuf::from("/repo/codex-rs/core"),
|
||||
cwd: test_path_buf("/repo/codex-rs/core").abs(),
|
||||
sandbox_permissions: crate::sandboxing::SandboxPermissions::UseDefault,
|
||||
additional_permissions: None,
|
||||
justification: Some("Need to push the docs fix.".to_string()),
|
||||
@@ -434,7 +447,7 @@ async fn build_guardian_prompt_stale_delta_version_falls_back_to_full_prompt() -
|
||||
GuardianApprovalRequest::Shell {
|
||||
id: "shell-4".to_string(),
|
||||
command: vec!["git".to_string(), "push".to_string()],
|
||||
cwd: PathBuf::from("/repo/codex-rs/core"),
|
||||
cwd: test_path_buf("/repo/codex-rs/core").abs(),
|
||||
sandbox_permissions: crate::sandboxing::SandboxPermissions::UseDefault,
|
||||
additional_permissions: None,
|
||||
justification: Some("Need to push after the compaction.".to_string()),
|
||||
@@ -566,7 +579,7 @@ fn format_guardian_action_pretty_truncates_large_string_fields() -> serde_json::
|
||||
let patch = "line\n".repeat(100_000);
|
||||
let action = GuardianApprovalRequest::ApplyPatch {
|
||||
id: "patch-1".to_string(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
files: Vec::new(),
|
||||
patch: patch.clone(),
|
||||
};
|
||||
@@ -622,7 +635,7 @@ fn guardian_approval_request_to_json_renders_mcp_tool_call_shape() -> serde_json
|
||||
|
||||
#[test]
|
||||
fn guardian_assessment_action_redacts_apply_patch_patch_text() {
|
||||
let cwd = test_path_buf("/tmp");
|
||||
let cwd = test_path_buf("/tmp").abs();
|
||||
let file = test_path_buf("/tmp/guardian.txt").abs();
|
||||
let action = GuardianApprovalRequest::ApplyPatch {
|
||||
id: "patch-1".to_string(),
|
||||
@@ -654,7 +667,7 @@ fn guardian_request_turn_id_prefers_network_access_owner_turn() {
|
||||
};
|
||||
let apply_patch = GuardianApprovalRequest::ApplyPatch {
|
||||
id: "patch-1".to_string(),
|
||||
cwd: test_path_buf("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
files: vec![test_path_buf("/tmp/guardian.txt").abs()],
|
||||
patch: "*** Begin Patch\n*** Update File: guardian.txt\n@@\n+hello\n*** End Patch"
|
||||
.to_string(),
|
||||
@@ -682,7 +695,7 @@ async fn cancelled_guardian_review_emits_terminal_abort_without_warning() {
|
||||
"review-cancelled-guardian".to_string(),
|
||||
GuardianApprovalRequest::ApplyPatch {
|
||||
id: "patch-1".to_string(),
|
||||
cwd: test_path_buf("/tmp"),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
files: vec![test_path_buf("/tmp/guardian.txt").abs()],
|
||||
patch: "*** Begin Patch\n*** Update File: guardian.txt\n@@\n+hello\n*** End Patch"
|
||||
.to_string(),
|
||||
@@ -888,7 +901,7 @@ async fn guardian_review_request_layout_matches_model_visible_request_snapshot()
|
||||
"origin".to_string(),
|
||||
"guardian-approval-mvp".to_string(),
|
||||
],
|
||||
cwd: PathBuf::from("/repo/codex-rs/core"),
|
||||
cwd: test_path_buf("/repo/codex-rs/core").abs(),
|
||||
sandbox_permissions: crate::sandboxing::SandboxPermissions::UseDefault,
|
||||
additional_permissions: None,
|
||||
justification: Some("Need to push the reviewed docs fix to the repo remote.".to_string()),
|
||||
@@ -915,11 +928,11 @@ async fn guardian_review_request_layout_matches_model_visible_request_snapshot()
|
||||
settings.bind(|| {
|
||||
assert_snapshot!(
|
||||
"codex_core__guardian__tests__guardian_review_request_layout",
|
||||
context_snapshot::format_labeled_requests_snapshot(
|
||||
normalize_guardian_snapshot_paths(context_snapshot::format_labeled_requests_snapshot(
|
||||
"Guardian review request layout",
|
||||
&[("Guardian Review Request", &request)],
|
||||
&guardian_snapshot_options(),
|
||||
)
|
||||
))
|
||||
);
|
||||
});
|
||||
|
||||
@@ -935,7 +948,7 @@ async fn build_guardian_prompt_items_includes_parent_session_id() -> anyhow::Res
|
||||
GuardianApprovalRequest::Shell {
|
||||
id: "shell-1".to_string(),
|
||||
command: vec!["git".to_string(), "status".to_string()],
|
||||
cwd: PathBuf::from("/repo"),
|
||||
cwd: test_path_buf("/repo").abs(),
|
||||
sandbox_permissions: crate::sandboxing::SandboxPermissions::UseDefault,
|
||||
additional_permissions: None,
|
||||
justification: None,
|
||||
@@ -1009,7 +1022,7 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
||||
let first_request = GuardianApprovalRequest::Shell {
|
||||
id: "shell-1".to_string(),
|
||||
command: vec!["git".to_string(), "push".to_string()],
|
||||
cwd: PathBuf::from("/repo/codex-rs/core"),
|
||||
cwd: test_path_buf("/repo/codex-rs/core").abs(),
|
||||
sandbox_permissions: crate::sandboxing::SandboxPermissions::UseDefault,
|
||||
additional_permissions: None,
|
||||
justification: Some("Need to push the first docs fix.".to_string()),
|
||||
@@ -1055,7 +1068,7 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
||||
"push".to_string(),
|
||||
"--force-with-lease".to_string(),
|
||||
],
|
||||
cwd: PathBuf::from("/repo/codex-rs/core"),
|
||||
cwd: test_path_buf("/repo/codex-rs/core").abs(),
|
||||
sandbox_permissions: crate::sandboxing::SandboxPermissions::UseDefault,
|
||||
additional_permissions: None,
|
||||
justification: Some("Need to push the second docs fix.".to_string()),
|
||||
@@ -1097,7 +1110,7 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
||||
let third_request = GuardianApprovalRequest::Shell {
|
||||
id: "shell-3".to_string(),
|
||||
command: vec!["git".to_string(), "push".to_string()],
|
||||
cwd: PathBuf::from("/repo/codex-rs/core"),
|
||||
cwd: test_path_buf("/repo/codex-rs/core").abs(),
|
||||
sandbox_permissions: crate::sandboxing::SandboxPermissions::UseDefault,
|
||||
additional_permissions: None,
|
||||
justification: Some("Need to push the third docs fix.".to_string()),
|
||||
@@ -1193,13 +1206,15 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
||||
"codex_core__guardian__tests__guardian_followup_review_request_layout",
|
||||
format!(
|
||||
"{}\n\nshared_prompt_cache_key: {}\nfollowup_contains_first_rationale: {}",
|
||||
context_snapshot::format_labeled_requests_snapshot(
|
||||
"Guardian follow-up review request layout",
|
||||
&[
|
||||
("Initial Guardian Review Request", &requests[0]),
|
||||
("Follow-up Guardian Review Request", &requests[1]),
|
||||
],
|
||||
&guardian_snapshot_options(),
|
||||
normalize_guardian_snapshot_paths(
|
||||
context_snapshot::format_labeled_requests_snapshot(
|
||||
"Guardian follow-up review request layout",
|
||||
&[
|
||||
("Initial Guardian Review Request", &requests[0]),
|
||||
("Follow-up Guardian Review Request", &requests[1]),
|
||||
],
|
||||
&guardian_snapshot_options(),
|
||||
)
|
||||
),
|
||||
first_body["prompt_cache_key"] == second_body["prompt_cache_key"],
|
||||
second_body.to_string().contains(first_rationale),
|
||||
@@ -1257,7 +1272,7 @@ async fn guardian_review_surfaces_responses_api_errors_in_rejection_reason() ->
|
||||
GuardianApprovalRequest::Shell {
|
||||
id: "shell-guardian-error".to_string(),
|
||||
command: vec!["git".to_string(), "push".to_string()],
|
||||
cwd: PathBuf::from("/repo/codex-rs/core"),
|
||||
cwd: test_path_buf("/repo/codex-rs/core").abs(),
|
||||
sandbox_permissions: crate::sandboxing::SandboxPermissions::UseDefault,
|
||||
additional_permissions: None,
|
||||
justification: Some("Need to push the reviewed docs fix.".to_string()),
|
||||
@@ -1380,7 +1395,7 @@ async fn guardian_parallel_reviews_fork_from_last_committed_trunk_history() -> a
|
||||
let initial_request = GuardianApprovalRequest::Shell {
|
||||
id: "shell-guardian-1".to_string(),
|
||||
command: vec!["git".to_string(), "status".to_string()],
|
||||
cwd: PathBuf::from("/repo/codex-rs/core"),
|
||||
cwd: test_path_buf("/repo/codex-rs/core").abs(),
|
||||
sandbox_permissions: crate::sandboxing::SandboxPermissions::UseDefault,
|
||||
additional_permissions: None,
|
||||
justification: Some("Inspect repo state before proceeding.".to_string()),
|
||||
@@ -1425,7 +1440,7 @@ async fn guardian_parallel_reviews_fork_from_last_committed_trunk_history() -> a
|
||||
let second_request = GuardianApprovalRequest::Shell {
|
||||
id: "shell-guardian-2".to_string(),
|
||||
command: vec!["git".to_string(), "diff".to_string()],
|
||||
cwd: PathBuf::from("/repo/codex-rs/core"),
|
||||
cwd: test_path_buf("/repo/codex-rs/core").abs(),
|
||||
sandbox_permissions: crate::sandboxing::SandboxPermissions::UseDefault,
|
||||
additional_permissions: None,
|
||||
justification: Some("Inspect pending changes before proceeding.".to_string()),
|
||||
@@ -1433,7 +1448,7 @@ async fn guardian_parallel_reviews_fork_from_last_committed_trunk_history() -> a
|
||||
let third_request = GuardianApprovalRequest::Shell {
|
||||
id: "shell-guardian-3".to_string(),
|
||||
command: vec!["git".to_string(), "push".to_string()],
|
||||
cwd: PathBuf::from("/repo/codex-rs/core"),
|
||||
cwd: test_path_buf("/repo/codex-rs/core").abs(),
|
||||
sandbox_permissions: crate::sandboxing::SandboxPermissions::UseDefault,
|
||||
additional_permissions: None,
|
||||
justification: Some("Inspect whether pushing is safe before proceeding.".to_string()),
|
||||
|
||||
@@ -96,7 +96,7 @@ pub(crate) async fn run_pending_session_start_hooks(
|
||||
|
||||
let request = codex_hooks::SessionStartRequest {
|
||||
session_id: sess.conversation_id,
|
||||
cwd: turn_context.cwd.to_path_buf(),
|
||||
cwd: turn_context.cwd.clone(),
|
||||
transcript_path: sess.hook_transcript_path().await,
|
||||
model: turn_context.model_info.slug.clone(),
|
||||
permission_mode: hook_permission_mode(turn_context),
|
||||
@@ -124,7 +124,7 @@ pub(crate) async fn run_pre_tool_use_hooks(
|
||||
let request = PreToolUseRequest {
|
||||
session_id: sess.conversation_id,
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
cwd: turn_context.cwd.to_path_buf(),
|
||||
cwd: turn_context.cwd.clone(),
|
||||
transcript_path: sess.hook_transcript_path().await,
|
||||
model: turn_context.model_info.slug.clone(),
|
||||
permission_mode: hook_permission_mode(turn_context),
|
||||
@@ -155,7 +155,7 @@ pub(crate) async fn run_post_tool_use_hooks(
|
||||
let request = PostToolUseRequest {
|
||||
session_id: sess.conversation_id,
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
cwd: turn_context.cwd.to_path_buf(),
|
||||
cwd: turn_context.cwd.clone(),
|
||||
transcript_path: sess.hook_transcript_path().await,
|
||||
model: turn_context.model_info.slug.clone(),
|
||||
permission_mode: hook_permission_mode(turn_context),
|
||||
@@ -180,7 +180,7 @@ pub(crate) async fn run_user_prompt_submit_hooks(
|
||||
let request = UserPromptSubmitRequest {
|
||||
session_id: sess.conversation_id,
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
cwd: turn_context.cwd.to_path_buf(),
|
||||
cwd: turn_context.cwd.clone(),
|
||||
transcript_path: sess.hook_transcript_path().await,
|
||||
model: turn_context.model_info.slug.clone(),
|
||||
permission_mode: hook_permission_mode(turn_context),
|
||||
|
||||
@@ -4,19 +4,19 @@ use std::io::Result;
|
||||
use std::io::Seek;
|
||||
use std::io::SeekFrom;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use tokio::fs;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub(crate) const INSTALLATION_ID_FILENAME: &str = "installation_id";
|
||||
|
||||
pub(crate) async fn resolve_installation_id(codex_home: &Path) -> Result<String> {
|
||||
pub(crate) async fn resolve_installation_id(codex_home: &AbsolutePathBuf) -> Result<String> {
|
||||
let path = codex_home.join(INSTALLATION_ID_FILENAME);
|
||||
fs::create_dir_all(codex_home).await?;
|
||||
tokio::task::spawn_blocking(move || {
|
||||
@@ -67,6 +67,7 @@ pub(crate) async fn resolve_installation_id(codex_home: &Path) -> Result<String>
|
||||
mod tests {
|
||||
use super::INSTALLATION_ID_FILENAME;
|
||||
use super::resolve_installation_id;
|
||||
use core_test_support::PathExt;
|
||||
use pretty_assertions::assert_eq;
|
||||
use tempfile::TempDir;
|
||||
use uuid::Uuid;
|
||||
@@ -77,9 +78,10 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn resolve_installation_id_generates_and_persists_uuid() {
|
||||
let codex_home = TempDir::new().expect("create temp dir");
|
||||
let codex_home_abs = codex_home.path().abs();
|
||||
let persisted_path = codex_home.path().join(INSTALLATION_ID_FILENAME);
|
||||
|
||||
let installation_id = resolve_installation_id(codex_home.path())
|
||||
let installation_id = resolve_installation_id(&codex_home_abs)
|
||||
.await
|
||||
.expect("resolve installation id");
|
||||
|
||||
@@ -103,6 +105,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn resolve_installation_id_reuses_existing_uuid() {
|
||||
let codex_home = TempDir::new().expect("create temp dir");
|
||||
let codex_home_abs = codex_home.path().abs();
|
||||
let existing = Uuid::new_v4().to_string().to_uppercase();
|
||||
std::fs::write(
|
||||
codex_home.path().join(INSTALLATION_ID_FILENAME),
|
||||
@@ -110,7 +113,7 @@ mod tests {
|
||||
)
|
||||
.expect("write installation id");
|
||||
|
||||
let resolved = resolve_installation_id(codex_home.path())
|
||||
let resolved = resolve_installation_id(&codex_home_abs)
|
||||
.await
|
||||
.expect("resolve installation id");
|
||||
|
||||
@@ -125,13 +128,14 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn resolve_installation_id_rewrites_invalid_file_contents() {
|
||||
let codex_home = TempDir::new().expect("create temp dir");
|
||||
let codex_home_abs = codex_home.path().abs();
|
||||
std::fs::write(
|
||||
codex_home.path().join(INSTALLATION_ID_FILENAME),
|
||||
"not-a-uuid",
|
||||
)
|
||||
.expect("write invalid installation id");
|
||||
|
||||
let resolved = resolve_installation_id(codex_home.path())
|
||||
let resolved = resolve_installation_id(&codex_home_abs)
|
||||
.await
|
||||
.expect("resolve installation id");
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ use codex_protocol::protocol::SandboxPolicy;
|
||||
use codex_sandboxing::landlock::CODEX_LINUX_SANDBOX_ARG0;
|
||||
use codex_sandboxing::landlock::allow_network_for_proxy;
|
||||
use codex_sandboxing::landlock::create_linux_sandbox_command_args_for_policies;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use tokio::process::Child;
|
||||
|
||||
/// Spawn a shell tool command under the Linux sandbox helper
|
||||
@@ -25,9 +25,9 @@ use tokio::process::Child;
|
||||
pub async fn spawn_command_under_linux_sandbox<P>(
|
||||
codex_linux_sandbox_exe: P,
|
||||
command: Vec<String>,
|
||||
command_cwd: PathBuf,
|
||||
command_cwd: AbsolutePathBuf,
|
||||
sandbox_policy: &SandboxPolicy,
|
||||
sandbox_policy_cwd: &Path,
|
||||
sandbox_policy_cwd: &AbsolutePathBuf,
|
||||
use_legacy_landlock: bool,
|
||||
stdio_policy: StdioPolicy,
|
||||
network: Option<&NetworkProxy>,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
|
||||
@@ -55,10 +54,10 @@ use codex_protocol::request_user_input::RequestUserInputResponse;
|
||||
use codex_rmcp_client::ElicitationAction;
|
||||
use codex_rmcp_client::ElicitationResponse;
|
||||
use codex_rollout::state_db;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use rmcp::model::ToolAnnotations;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use toml_edit::value;
|
||||
use tracing::Instrument;
|
||||
@@ -1512,7 +1511,7 @@ async fn maybe_persist_mcp_tool_approval(
|
||||
}
|
||||
|
||||
async fn persist_codex_app_tool_approval(
|
||||
codex_home: &Path,
|
||||
codex_home: &AbsolutePathBuf,
|
||||
connector_id: &str,
|
||||
tool_name: &str,
|
||||
) -> anyhow::Result<()> {
|
||||
@@ -1545,7 +1544,7 @@ async fn persist_custom_mcp_tool_approval(
|
||||
if !servers.contains_key(server) {
|
||||
anyhow::bail!("MCP server `{server}` is not configured in config.toml");
|
||||
}
|
||||
config.codex_home.to_path_buf()
|
||||
config.codex_home.clone()
|
||||
};
|
||||
|
||||
ConfigEditsBuilder::new(&config_folder)
|
||||
@@ -1563,7 +1562,10 @@ async fn persist_custom_mcp_tool_approval(
|
||||
.await
|
||||
}
|
||||
|
||||
fn project_mcp_tool_approval_config_folder(config: &Config, server: &str) -> Option<PathBuf> {
|
||||
fn project_mcp_tool_approval_config_folder(
|
||||
config: &Config,
|
||||
server: &str,
|
||||
) -> Option<AbsolutePathBuf> {
|
||||
config
|
||||
.config_layer_stack
|
||||
.layers_high_to_low()
|
||||
@@ -1582,9 +1584,7 @@ fn project_mcp_tool_approval_config_folder(config: &Config, server: &str) -> Opt
|
||||
HashMap::<String, codex_config::types::McpServerConfig>::deserialize(value).ok()
|
||||
})?;
|
||||
if servers.contains_key(server) {
|
||||
layer
|
||||
.config_folder()
|
||||
.map(|folder| folder.as_path().to_path_buf())
|
||||
layer.config_folder()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use codex_config::types::McpServerConfig;
|
||||
use codex_config::types::McpServerToolConfig;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
use core_test_support::PathExt;
|
||||
use core_test_support::responses::ev_assistant_message;
|
||||
use core_test_support::responses::ev_completed;
|
||||
use core_test_support::responses::ev_response_created;
|
||||
@@ -1043,7 +1044,7 @@ fn accepted_elicitation_without_content_defaults_to_accept() {
|
||||
async fn persist_codex_app_tool_approval_writes_tool_override() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
|
||||
persist_codex_app_tool_approval(tmp.path(), "calendar", "calendar/list_events")
|
||||
persist_codex_app_tool_approval(&tmp.path().abs(), "calendar", "calendar/list_events")
|
||||
.await
|
||||
.expect("persist approval");
|
||||
|
||||
@@ -1216,7 +1217,7 @@ async fn maybe_persist_mcp_tool_approval_writes_project_config_for_project_serve
|
||||
.await
|
||||
.expect("trust project");
|
||||
let config = ConfigBuilder::default()
|
||||
.codex_home(codex_home)
|
||||
.codex_home(codex_home.to_path_buf())
|
||||
.fallback_cwd(Some(project_dir.path().to_path_buf()))
|
||||
.build()
|
||||
.await
|
||||
|
||||
@@ -96,10 +96,11 @@ mod metrics {
|
||||
pub(super) const MEMORY_PHASE_TWO_TOKEN_USAGE: &str = "codex.memory.phase2.token_usage";
|
||||
}
|
||||
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn memory_root(codex_home: &Path) -> PathBuf {
|
||||
pub fn memory_root(codex_home: &AbsolutePathBuf) -> AbsolutePathBuf {
|
||||
codex_home.join("memories")
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ use codex_protocol::protocol::TokenUsage;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
use codex_state::Stage1Output;
|
||||
use codex_state::StateRuntime;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@@ -288,16 +287,7 @@ mod agent {
|
||||
let root = memory_root(&config.codex_home);
|
||||
let mut agent_config = config.as_ref().clone();
|
||||
|
||||
match AbsolutePathBuf::from_absolute_path(root) {
|
||||
Ok(root) => agent_config.cwd = root,
|
||||
Err(err) => {
|
||||
warn!(
|
||||
"memory phase-2 consolidation could not set cwd from codex_home {}: {err}",
|
||||
agent_config.codex_home.display()
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
agent_config.cwd = root;
|
||||
// Consolidation threads must never feed back into phase-1 memory generation.
|
||||
agent_config.memories.generate_memories = false;
|
||||
// Approval policy
|
||||
@@ -308,14 +298,7 @@ mod agent {
|
||||
let _ = agent_config.features.disable(Feature::MemoryTool);
|
||||
|
||||
// Sandbox policy
|
||||
let mut writable_roots = Vec::new();
|
||||
match AbsolutePathBuf::from_absolute_path(agent_config.codex_home.clone()) {
|
||||
Ok(codex_home) => writable_roots.push(codex_home),
|
||||
Err(err) => warn!(
|
||||
"memory phase-2 consolidation could not add codex_home writable root {}: {err}",
|
||||
agent_config.codex_home.display()
|
||||
),
|
||||
}
|
||||
let writable_roots = vec![agent_config.codex_home.clone()];
|
||||
// The consolidation agent only needs local codex_home write access and no network.
|
||||
let consolidation_sandbox_policy = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots,
|
||||
|
||||
@@ -6,6 +6,7 @@ use codex_protocol::openai_models::ModelInfo;
|
||||
use codex_state::Phase2InputSelection;
|
||||
use codex_state::Stage1Output;
|
||||
use codex_state::Stage1OutputRef;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_output_truncation::TruncationPolicy;
|
||||
use codex_utils_output_truncation::truncate_text;
|
||||
use codex_utils_template::Template;
|
||||
@@ -231,7 +232,9 @@ pub(super) fn build_stage_one_input_message(
|
||||
/// Build prompt used for read path. This prompt must be added to the developer instructions. In
|
||||
/// case of large memory files, the `memory_summary.md` is truncated at
|
||||
/// [phase_one::MEMORY_TOOL_DEVELOPER_INSTRUCTIONS_SUMMARY_TOKEN_LIMIT].
|
||||
pub(crate) async fn build_memory_tool_developer_instructions(codex_home: &Path) -> Option<String> {
|
||||
pub(crate) async fn build_memory_tool_developer_instructions(
|
||||
codex_home: &AbsolutePathBuf,
|
||||
) -> Option<String> {
|
||||
let base_path = memory_root(codex_home);
|
||||
let memory_summary_path = base_path.join("memory_summary.md");
|
||||
let memory_summary = fs::read_to_string(&memory_summary_path)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::*;
|
||||
use codex_models_manager::model_info::model_info_from_slug;
|
||||
use core_test_support::PathExt;
|
||||
use pretty_assertions::assert_eq;
|
||||
use tempfile::tempdir;
|
||||
use tokio::fs as tokio_fs;
|
||||
@@ -56,7 +57,7 @@ fn build_stage_one_input_message_uses_default_limit_when_model_context_window_mi
|
||||
#[tokio::test]
|
||||
async fn build_memory_tool_developer_instructions_renders_embedded_template() {
|
||||
let temp = tempdir().unwrap();
|
||||
let codex_home = temp.path();
|
||||
let codex_home = temp.path().abs();
|
||||
let memories_dir = codex_home.join("memories");
|
||||
tokio_fs::create_dir_all(&memories_dir).await.unwrap();
|
||||
tokio_fs::write(
|
||||
@@ -66,7 +67,7 @@ async fn build_memory_tool_developer_instructions_renders_embedded_template() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let instructions = build_memory_tool_developer_instructions(codex_home)
|
||||
let instructions = build_memory_tool_developer_instructions(&codex_home)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ use chrono::Utc;
|
||||
use codex_config::types::DEFAULT_MEMORIES_MAX_RAW_MEMORIES_FOR_CONSOLIDATION;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_state::Stage1Output;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::Value;
|
||||
use std::path::PathBuf;
|
||||
@@ -17,8 +18,7 @@ use tempfile::tempdir;
|
||||
|
||||
#[test]
|
||||
fn memory_root_uses_shared_global_path() {
|
||||
let dir = tempdir().expect("tempdir");
|
||||
let codex_home = dir.path().join("codex");
|
||||
let codex_home = AbsolutePathBuf::current_dir().expect("cwd").join("codex");
|
||||
assert_eq!(memory_root(&codex_home), codex_home.join("memories"));
|
||||
}
|
||||
|
||||
@@ -678,7 +678,10 @@ mod phase2 {
|
||||
.expect("get consolidation thread");
|
||||
let config_snapshot = subagent.config_snapshot().await;
|
||||
pretty_assertions::assert_eq!(config_snapshot.approval_policy, AskForApproval::Never);
|
||||
pretty_assertions::assert_eq!(config_snapshot.cwd, memory_root(&harness.config.codex_home));
|
||||
pretty_assertions::assert_eq!(
|
||||
config_snapshot.cwd.as_path(),
|
||||
memory_root(&harness.config.codex_home).as_path()
|
||||
);
|
||||
match config_snapshot.sandbox_policy {
|
||||
SandboxPolicy::WorkspaceWrite { writable_roots, .. } => {
|
||||
assert!(
|
||||
|
||||
@@ -26,7 +26,6 @@ use std::io::Seek;
|
||||
use std::io::SeekFrom;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
@@ -37,6 +36,7 @@ use tokio::io::AsyncReadExt;
|
||||
|
||||
use crate::config::Config;
|
||||
use codex_config::types::HistoryPersistence;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
|
||||
use codex_protocol::ThreadId;
|
||||
#[cfg(unix)]
|
||||
@@ -60,8 +60,8 @@ pub struct HistoryEntry {
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
fn history_filepath(config: &Config) -> PathBuf {
|
||||
config.codex_home.join(HISTORY_FILENAME).to_path_buf()
|
||||
fn history_filepath(config: &Config) -> AbsolutePathBuf {
|
||||
config.codex_home.join(HISTORY_FILENAME)
|
||||
}
|
||||
|
||||
/// Append a `text` entry associated with `conversation_id` to the history file.
|
||||
|
||||
@@ -25,8 +25,8 @@ use codex_network_proxy::NetworkProxyState;
|
||||
use codex_network_proxy::build_config_state;
|
||||
use codex_network_proxy::normalize_host;
|
||||
use codex_network_proxy::validate_policy_against_constraints;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use serde::Deserialize;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
@@ -86,17 +86,12 @@ fn collect_layer_mtimes(stack: &ConfigLayerStack) -> Vec<LayerMtime> {
|
||||
.iter()
|
||||
.filter_map(|layer| {
|
||||
let path = match &layer.name {
|
||||
ConfigLayerSource::System { file } => Some(file.as_path().to_path_buf()),
|
||||
ConfigLayerSource::User { file } => Some(file.as_path().to_path_buf()),
|
||||
ConfigLayerSource::Project { dot_codex_folder } => Some(
|
||||
dot_codex_folder
|
||||
.join(CONFIG_TOML_FILE)
|
||||
.as_path()
|
||||
.to_path_buf(),
|
||||
),
|
||||
ConfigLayerSource::LegacyManagedConfigTomlFromFile { file } => {
|
||||
Some(file.as_path().to_path_buf())
|
||||
ConfigLayerSource::System { file } => Some(file.clone()),
|
||||
ConfigLayerSource::User { file } => Some(file.clone()),
|
||||
ConfigLayerSource::Project { dot_codex_folder } => {
|
||||
Some(dot_codex_folder.join(CONFIG_TOML_FILE))
|
||||
}
|
||||
ConfigLayerSource::LegacyManagedConfigTomlFromFile { file } => Some(file.clone()),
|
||||
_ => None,
|
||||
};
|
||||
path.map(LayerMtime::new)
|
||||
@@ -265,12 +260,12 @@ fn is_user_controlled_layer(layer: &ConfigLayerSource) -> bool {
|
||||
|
||||
#[derive(Clone)]
|
||||
struct LayerMtime {
|
||||
path: PathBuf,
|
||||
path: AbsolutePathBuf,
|
||||
mtime: Option<std::time::SystemTime>,
|
||||
}
|
||||
|
||||
impl LayerMtime {
|
||||
fn new(path: PathBuf) -> Self {
|
||||
fn new(path: AbsolutePathBuf) -> Self {
|
||||
let mtime = path.metadata().and_then(|m| m.modified()).ok();
|
||||
Self { path, mtime }
|
||||
}
|
||||
|
||||
@@ -199,7 +199,9 @@ async fn list_tool_suggest_discoverable_plugins_does_not_reload_marketplace_per_
|
||||
assert_eq!(discoverable_plugins.len(), 1);
|
||||
assert_eq!(discoverable_plugins[0].id, "slack@openai-curated");
|
||||
|
||||
let logs = String::from_utf8(buffer.lock().expect("buffer lock").clone()).expect("utf8 logs");
|
||||
let logs = String::from_utf8(buffer.lock().expect("buffer lock").clone())
|
||||
.expect("utf8 logs")
|
||||
.replace('\\', "/");
|
||||
assert_eq!(logs.matches("ignoring interface.defaultPrompt").count(), 2);
|
||||
let normalized_logs = logs.replace('\\', "/");
|
||||
assert_eq!(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use codex_git_utils::merge_base_with_head;
|
||||
use codex_protocol::protocol::ReviewRequest;
|
||||
use codex_protocol::protocol::ReviewTarget;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_template::Template;
|
||||
use std::path::Path;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
@@ -38,7 +38,7 @@ static COMMIT_PROMPT_TEMPLATE: LazyLock<Template> = LazyLock::new(|| {
|
||||
|
||||
pub fn resolve_review_request(
|
||||
request: ReviewRequest,
|
||||
cwd: &Path,
|
||||
cwd: &AbsolutePathBuf,
|
||||
) -> anyhow::Result<ResolvedReviewRequest> {
|
||||
let target = request.target;
|
||||
let prompt = review_prompt(&target, cwd)?;
|
||||
@@ -53,7 +53,7 @@ pub fn resolve_review_request(
|
||||
})
|
||||
}
|
||||
|
||||
pub fn review_prompt(target: &ReviewTarget, cwd: &Path) -> anyhow::Result<String> {
|
||||
pub fn review_prompt(target: &ReviewTarget, cwd: &AbsolutePathBuf) -> anyhow::Result<String> {
|
||||
match target {
|
||||
ReviewTarget::UncommittedChanges => Ok(UNCOMMITTED_PROMPT.to_string()),
|
||||
ReviewTarget::BaseBranch { branch } => {
|
||||
@@ -161,7 +161,7 @@ mod tests {
|
||||
sha: "deadbeef".to_string(),
|
||||
title: None,
|
||||
},
|
||||
Path::new("."),
|
||||
&AbsolutePathBuf::current_dir().expect("cwd"),
|
||||
)
|
||||
.expect("commit prompt should render"),
|
||||
"Review the code changes introduced by commit deadbeef. Provide prioritized, actionable findings."
|
||||
@@ -176,7 +176,7 @@ mod tests {
|
||||
sha: "deadbeef".to_string(),
|
||||
title: Some("Fix bug".to_string()),
|
||||
},
|
||||
Path::new("."),
|
||||
&AbsolutePathBuf::current_dir().expect("cwd"),
|
||||
)
|
||||
.expect("commit prompt should render"),
|
||||
"Review the code changes introduced by commit deadbeef (\"Fix bug\"). Provide prioritized, actionable findings."
|
||||
|
||||
@@ -11,6 +11,7 @@ use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
use codex_sandboxing::SandboxType;
|
||||
use codex_sandboxing::get_platform_sandbox;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
|
||||
const PATCH_REJECTED_OUTSIDE_PROJECT_REASON: &str =
|
||||
"writing outside of the project; rejected by user approval settings";
|
||||
@@ -34,7 +35,7 @@ pub fn assess_patch_safety(
|
||||
policy: AskForApproval,
|
||||
sandbox_policy: &SandboxPolicy,
|
||||
file_system_sandbox_policy: &FileSystemSandboxPolicy,
|
||||
cwd: &Path,
|
||||
cwd: &AbsolutePathBuf,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
) -> SafetyCheck {
|
||||
if action.is_empty() {
|
||||
@@ -119,7 +120,7 @@ fn patch_rejection_reason(sandbox_policy: &SandboxPolicy) -> &'static str {
|
||||
fn is_write_patch_constrained_to_writable_paths(
|
||||
action: &ApplyPatchAction,
|
||||
file_system_sandbox_policy: &FileSystemSandboxPolicy,
|
||||
cwd: &Path,
|
||||
cwd: &AbsolutePathBuf,
|
||||
) -> bool {
|
||||
// Normalize a path by removing `.` and resolving `..` without touching the
|
||||
// filesystem (works even if the file does not exist).
|
||||
|
||||
@@ -5,7 +5,7 @@ use codex_protocol::protocol::FileSystemSandboxEntry;
|
||||
use codex_protocol::protocol::FileSystemSpecialPath;
|
||||
use codex_protocol::protocol::GranularApprovalConfig;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use core_test_support::PathBufExt;
|
||||
use core_test_support::PathExt;
|
||||
use pretty_assertions::assert_eq;
|
||||
use tempfile::TempDir;
|
||||
|
||||
@@ -14,14 +14,12 @@ fn test_writable_roots_constraint() {
|
||||
// Use a temporary directory as our workspace to avoid touching
|
||||
// the real current working directory.
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let cwd = tmp.path().to_path_buf();
|
||||
let parent = cwd.parent().unwrap().to_path_buf();
|
||||
let cwd = tmp.path().abs();
|
||||
let parent = cwd.parent().unwrap();
|
||||
|
||||
// Helper to build a single‑entry patch that adds a file at `p`.
|
||||
let make_add_change = |p: PathBuf| {
|
||||
let p = p.abs();
|
||||
ApplyPatchAction::new_add_for_test(&p, "".to_string())
|
||||
};
|
||||
let make_add_change =
|
||||
|p: AbsolutePathBuf| ApplyPatchAction::new_add_for_test(&p, "".to_string());
|
||||
|
||||
let add_inside = make_add_change(cwd.join("inner.txt"));
|
||||
let add_outside = make_add_change(parent.join("outside.txt"));
|
||||
@@ -51,7 +49,7 @@ fn test_writable_roots_constraint() {
|
||||
// With the parent dir explicitly added as a writable root, the
|
||||
// outside write should be permitted.
|
||||
let policy_with_parent = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![AbsolutePathBuf::try_from(parent).unwrap()],
|
||||
writable_roots: vec![parent],
|
||||
read_only_access: Default::default(),
|
||||
network_access: false,
|
||||
exclude_tmpdir_env_var: true,
|
||||
@@ -67,8 +65,8 @@ fn test_writable_roots_constraint() {
|
||||
#[test]
|
||||
fn external_sandbox_auto_approves_in_on_request() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let cwd = tmp.path().to_path_buf();
|
||||
let add_inside_path = cwd.join("inner.txt").abs();
|
||||
let cwd = tmp.path().abs();
|
||||
let add_inside_path = cwd.join("inner.txt");
|
||||
let add_inside = ApplyPatchAction::new_add_for_test(&add_inside_path, "".to_string());
|
||||
|
||||
let policy = SandboxPolicy::ExternalSandbox {
|
||||
@@ -94,9 +92,9 @@ fn external_sandbox_auto_approves_in_on_request() {
|
||||
#[test]
|
||||
fn granular_with_all_flags_true_matches_on_request_for_out_of_root_patch() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let cwd = tmp.path().to_path_buf();
|
||||
let parent = cwd.parent().unwrap().to_path_buf();
|
||||
let outside_path = parent.join("outside.txt").abs();
|
||||
let cwd = tmp.path().abs();
|
||||
let parent = cwd.parent().unwrap();
|
||||
let outside_path = parent.join("outside.txt");
|
||||
let add_outside = ApplyPatchAction::new_add_for_test(&outside_path, "".to_string());
|
||||
let policy_workspace_only = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![],
|
||||
@@ -139,9 +137,9 @@ fn granular_with_all_flags_true_matches_on_request_for_out_of_root_patch() {
|
||||
#[test]
|
||||
fn granular_sandbox_approval_false_rejects_out_of_root_patch() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let cwd = tmp.path().to_path_buf();
|
||||
let parent = cwd.parent().unwrap().to_path_buf();
|
||||
let outside_path = parent.join("outside.txt").abs();
|
||||
let cwd = tmp.path().abs();
|
||||
let parent = cwd.parent().unwrap();
|
||||
let outside_path = parent.join("outside.txt");
|
||||
let add_outside = ApplyPatchAction::new_add_for_test(&outside_path, "".to_string());
|
||||
let policy_workspace_only = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![],
|
||||
@@ -175,8 +173,8 @@ fn granular_sandbox_approval_false_rejects_out_of_root_patch() {
|
||||
#[test]
|
||||
fn read_only_policy_rejects_patch_with_read_only_reason() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let cwd = tmp.path().to_path_buf();
|
||||
let inside_path = cwd.join("inside.txt").abs();
|
||||
let cwd = tmp.path().abs();
|
||||
let inside_path = cwd.join("inside.txt");
|
||||
let action = ApplyPatchAction::new_add_for_test(&inside_path, "".to_string());
|
||||
let sandbox_policy = SandboxPolicy::new_read_only_policy();
|
||||
let file_system_sandbox_policy =
|
||||
@@ -204,9 +202,9 @@ fn read_only_policy_rejects_patch_with_read_only_reason() {
|
||||
#[test]
|
||||
fn explicit_unreadable_paths_prevent_auto_approval_for_external_sandbox() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let cwd = tmp.path().to_path_buf();
|
||||
let cwd = tmp.path().abs();
|
||||
let blocked_path = cwd.join("blocked.txt");
|
||||
let blocked_absolute = blocked_path.abs();
|
||||
let blocked_absolute = blocked_path;
|
||||
let action = ApplyPatchAction::new_add_for_test(&blocked_absolute, "".to_string());
|
||||
let sandbox_policy = SandboxPolicy::ExternalSandbox {
|
||||
network_access: codex_protocol::protocol::NetworkAccess::Restricted,
|
||||
@@ -247,9 +245,9 @@ fn explicit_unreadable_paths_prevent_auto_approval_for_external_sandbox() {
|
||||
#[test]
|
||||
fn explicit_read_only_subpaths_prevent_auto_approval_for_external_sandbox() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let cwd = tmp.path().to_path_buf();
|
||||
let cwd = tmp.path().abs();
|
||||
let blocked_path = cwd.join("docs").join("blocked.txt");
|
||||
let blocked_absolute = blocked_path.abs();
|
||||
let blocked_absolute = blocked_path;
|
||||
let docs_absolute = AbsolutePathBuf::resolve_path_against_base("docs", &cwd);
|
||||
let action = ApplyPatchAction::new_add_for_test(&blocked_absolute, "".to_string());
|
||||
let sandbox_policy = SandboxPolicy::ExternalSandbox {
|
||||
@@ -291,8 +289,8 @@ fn explicit_read_only_subpaths_prevent_auto_approval_for_external_sandbox() {
|
||||
#[test]
|
||||
fn missing_project_dot_codex_config_requires_approval() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let cwd = tmp.path().to_path_buf();
|
||||
let config_path = cwd.join(".codex").join("config.toml").abs();
|
||||
let cwd = tmp.path().abs();
|
||||
let config_path = cwd.join(".codex").join("config.toml");
|
||||
let action = ApplyPatchAction::new_add_for_test(&config_path, "".to_string());
|
||||
let sandbox_policy = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![],
|
||||
|
||||
@@ -10,16 +10,16 @@ use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
use codex_sandboxing::seatbelt::MACOS_PATH_TO_SEATBELT_EXECUTABLE;
|
||||
use codex_sandboxing::seatbelt::create_seatbelt_command_args_for_policies;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use tokio::process::Child;
|
||||
|
||||
pub async fn spawn_command_under_seatbelt(
|
||||
command: Vec<String>,
|
||||
command_cwd: PathBuf,
|
||||
command_cwd: AbsolutePathBuf,
|
||||
sandbox_policy: &SandboxPolicy,
|
||||
sandbox_policy_cwd: &Path,
|
||||
sandbox_policy_cwd: &AbsolutePathBuf,
|
||||
stdio_policy: StdioPolicy,
|
||||
network: Option<&NetworkProxy>,
|
||||
mut env: HashMap<String, String>,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::io::ErrorKind;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Stdio;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@@ -16,6 +15,7 @@ use anyhow::anyhow;
|
||||
use anyhow::bail;
|
||||
use codex_otel::SessionTelemetry;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use tokio::fs;
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::watch;
|
||||
@@ -25,8 +25,8 @@ use tracing::info_span;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ShellSnapshot {
|
||||
pub path: PathBuf,
|
||||
pub cwd: PathBuf,
|
||||
pub path: AbsolutePathBuf,
|
||||
pub cwd: AbsolutePathBuf,
|
||||
}
|
||||
|
||||
const SNAPSHOT_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
@@ -36,9 +36,9 @@ const EXCLUDED_EXPORT_VARS: &[&str] = &["PWD", "OLDPWD"];
|
||||
|
||||
impl ShellSnapshot {
|
||||
pub fn start_snapshotting(
|
||||
codex_home: PathBuf,
|
||||
codex_home: AbsolutePathBuf,
|
||||
session_id: ThreadId,
|
||||
session_cwd: PathBuf,
|
||||
session_cwd: AbsolutePathBuf,
|
||||
shell: &mut Shell,
|
||||
session_telemetry: SessionTelemetry,
|
||||
) -> watch::Sender<Option<Arc<ShellSnapshot>>> {
|
||||
@@ -58,9 +58,9 @@ impl ShellSnapshot {
|
||||
}
|
||||
|
||||
pub fn refresh_snapshot(
|
||||
codex_home: PathBuf,
|
||||
codex_home: AbsolutePathBuf,
|
||||
session_id: ThreadId,
|
||||
session_cwd: PathBuf,
|
||||
session_cwd: AbsolutePathBuf,
|
||||
shell: Shell,
|
||||
shell_snapshot_tx: watch::Sender<Option<Arc<ShellSnapshot>>>,
|
||||
session_telemetry: SessionTelemetry,
|
||||
@@ -76,9 +76,9 @@ impl ShellSnapshot {
|
||||
}
|
||||
|
||||
fn spawn_snapshot_task(
|
||||
codex_home: PathBuf,
|
||||
codex_home: AbsolutePathBuf,
|
||||
session_id: ThreadId,
|
||||
session_cwd: PathBuf,
|
||||
session_cwd: AbsolutePathBuf,
|
||||
snapshot_shell: Shell,
|
||||
shell_snapshot_tx: watch::Sender<Option<Arc<ShellSnapshot>>>,
|
||||
session_telemetry: SessionTelemetry,
|
||||
@@ -87,14 +87,10 @@ impl ShellSnapshot {
|
||||
tokio::spawn(
|
||||
async move {
|
||||
let timer = session_telemetry.start_timer("codex.shell_snapshot.duration_ms", &[]);
|
||||
let snapshot = ShellSnapshot::try_new(
|
||||
&codex_home,
|
||||
session_id,
|
||||
session_cwd.as_path(),
|
||||
&snapshot_shell,
|
||||
)
|
||||
.await
|
||||
.map(Arc::new);
|
||||
let snapshot =
|
||||
ShellSnapshot::try_new(&codex_home, session_id, &session_cwd, &snapshot_shell)
|
||||
.await
|
||||
.map(Arc::new);
|
||||
let success = snapshot.is_ok();
|
||||
let success_tag = if success { "true" } else { "false" };
|
||||
let _ = timer.map(|timer| timer.record(&[("success", success_tag)]));
|
||||
@@ -110,9 +106,9 @@ impl ShellSnapshot {
|
||||
}
|
||||
|
||||
async fn try_new(
|
||||
codex_home: &Path,
|
||||
codex_home: &AbsolutePathBuf,
|
||||
session_id: ThreadId,
|
||||
session_cwd: &Path,
|
||||
session_cwd: &AbsolutePathBuf,
|
||||
shell: &Shell,
|
||||
) -> std::result::Result<Self, &'static str> {
|
||||
// File to store the snapshot
|
||||
@@ -132,7 +128,7 @@ impl ShellSnapshot {
|
||||
.join(format!("{session_id}.tmp-{nonce}"));
|
||||
|
||||
// Clean the (unlikely) leaked snapshot files.
|
||||
let codex_home = codex_home.to_path_buf();
|
||||
let codex_home = codex_home.clone();
|
||||
let cleanup_session_id = session_id;
|
||||
tokio::spawn(async move {
|
||||
if let Err(err) = cleanup_stale_snapshots(&codex_home, cleanup_session_id).await {
|
||||
@@ -141,24 +137,23 @@ impl ShellSnapshot {
|
||||
});
|
||||
|
||||
// Make the new snapshot.
|
||||
let temp_path =
|
||||
match write_shell_snapshot(shell.shell_type.clone(), &temp_path, session_cwd).await {
|
||||
Ok(path) => {
|
||||
tracing::info!("Shell snapshot successfully created: {}", path.display());
|
||||
path
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!(
|
||||
"Failed to create shell snapshot for {}: {err:?}",
|
||||
shell.name()
|
||||
);
|
||||
return Err("write_failed");
|
||||
}
|
||||
};
|
||||
if let Err(err) =
|
||||
write_shell_snapshot(shell.shell_type.clone(), &temp_path, session_cwd).await
|
||||
{
|
||||
tracing::warn!(
|
||||
"Failed to create shell snapshot for {}: {err:?}",
|
||||
shell.name()
|
||||
);
|
||||
return Err("write_failed");
|
||||
}
|
||||
tracing::info!(
|
||||
"Shell snapshot successfully created: {}",
|
||||
temp_path.display()
|
||||
);
|
||||
|
||||
let temp_snapshot = Self {
|
||||
path: temp_path.clone(),
|
||||
cwd: session_cwd.to_path_buf(),
|
||||
cwd: session_cwd.clone(),
|
||||
};
|
||||
|
||||
if let Err(err) = validate_snapshot(shell, &temp_snapshot.path, session_cwd).await {
|
||||
@@ -175,7 +170,7 @@ impl ShellSnapshot {
|
||||
|
||||
Ok(Self {
|
||||
path,
|
||||
cwd: session_cwd.to_path_buf(),
|
||||
cwd: session_cwd.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -193,9 +188,9 @@ impl Drop for ShellSnapshot {
|
||||
|
||||
async fn write_shell_snapshot(
|
||||
shell_type: ShellType,
|
||||
output_path: &Path,
|
||||
cwd: &Path,
|
||||
) -> Result<PathBuf> {
|
||||
output_path: &AbsolutePathBuf,
|
||||
cwd: &AbsolutePathBuf,
|
||||
) -> Result<()> {
|
||||
if shell_type == ShellType::PowerShell || shell_type == ShellType::Cmd {
|
||||
bail!("Shell snapshot not supported yet for {shell_type:?}");
|
||||
}
|
||||
@@ -207,7 +202,7 @@ async fn write_shell_snapshot(
|
||||
|
||||
if let Some(parent) = output_path.parent() {
|
||||
let parent_display = parent.display();
|
||||
fs::create_dir_all(parent)
|
||||
fs::create_dir_all(&parent)
|
||||
.await
|
||||
.with_context(|| format!("Failed to create snapshot parent {parent_display}"))?;
|
||||
}
|
||||
@@ -217,10 +212,10 @@ async fn write_shell_snapshot(
|
||||
.await
|
||||
.with_context(|| format!("Failed to write snapshot to {snapshot_path}"))?;
|
||||
|
||||
Ok(output_path.to_path_buf())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn capture_snapshot(shell: &Shell, cwd: &Path) -> Result<String> {
|
||||
async fn capture_snapshot(shell: &Shell, cwd: &AbsolutePathBuf) -> Result<String> {
|
||||
let shell_type = shell.shell_type.clone();
|
||||
match shell_type {
|
||||
ShellType::Zsh => run_shell_script(shell, &zsh_snapshot_script(), cwd).await,
|
||||
@@ -240,7 +235,11 @@ fn strip_snapshot_preamble(snapshot: &str) -> Result<String> {
|
||||
Ok(snapshot[start..].to_string())
|
||||
}
|
||||
|
||||
async fn validate_snapshot(shell: &Shell, snapshot_path: &Path, cwd: &Path) -> Result<()> {
|
||||
async fn validate_snapshot(
|
||||
shell: &Shell,
|
||||
snapshot_path: &AbsolutePathBuf,
|
||||
cwd: &AbsolutePathBuf,
|
||||
) -> Result<()> {
|
||||
let snapshot_path_display = snapshot_path.display();
|
||||
let script = format!("set -e; . \"{snapshot_path_display}\"");
|
||||
run_script_with_timeout(
|
||||
@@ -254,7 +253,7 @@ async fn validate_snapshot(shell: &Shell, snapshot_path: &Path, cwd: &Path) -> R
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
async fn run_shell_script(shell: &Shell, script: &str, cwd: &Path) -> Result<String> {
|
||||
async fn run_shell_script(shell: &Shell, script: &str, cwd: &AbsolutePathBuf) -> Result<String> {
|
||||
run_script_with_timeout(
|
||||
shell,
|
||||
script,
|
||||
@@ -270,7 +269,7 @@ async fn run_script_with_timeout(
|
||||
script: &str,
|
||||
snapshot_timeout: Duration,
|
||||
use_login_shell: bool,
|
||||
cwd: &Path,
|
||||
cwd: &AbsolutePathBuf,
|
||||
) -> Result<String> {
|
||||
let args = shell.derive_exec_args(script, use_login_shell);
|
||||
let shell_name = shell.name();
|
||||
@@ -489,7 +488,10 @@ $envVars | ForEach-Object {
|
||||
/// Removes shell snapshots that either lack a matching session rollout file or
|
||||
/// whose rollouts have not been updated within the retention window.
|
||||
/// The active session id is exempt from cleanup.
|
||||
pub async fn cleanup_stale_snapshots(codex_home: &Path, active_session_id: ThreadId) -> Result<()> {
|
||||
pub async fn cleanup_stale_snapshots(
|
||||
codex_home: &AbsolutePathBuf,
|
||||
active_session_id: ThreadId,
|
||||
) -> Result<()> {
|
||||
let snapshot_dir = codex_home.join(SNAPSHOT_DIR);
|
||||
|
||||
let mut entries = match fs::read_dir(&snapshot_dir).await {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use super::*;
|
||||
use core_test_support::PathBufExt;
|
||||
use core_test_support::PathExt;
|
||||
use pretty_assertions::assert_eq;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::path::PathBuf;
|
||||
#[cfg(unix)]
|
||||
use std::process::Command;
|
||||
#[cfg(target_os = "linux")]
|
||||
@@ -80,7 +83,7 @@ fn assert_posix_snapshot_sections(snapshot: &str) {
|
||||
async fn get_snapshot(shell_type: ShellType) -> Result<String> {
|
||||
let dir = tempdir()?;
|
||||
let path = dir.path().join("snapshot.sh");
|
||||
write_shell_snapshot(shell_type, &path, dir.path()).await?;
|
||||
write_shell_snapshot(shell_type, &path.abs(), &dir.path().abs()).await?;
|
||||
let content = fs::read_to_string(&path).await?;
|
||||
Ok(content)
|
||||
}
|
||||
@@ -194,12 +197,17 @@ async fn try_new_creates_and_deletes_snapshot_file() -> Result<()> {
|
||||
shell_snapshot: crate::shell::empty_shell_snapshot_receiver(),
|
||||
};
|
||||
|
||||
let snapshot = ShellSnapshot::try_new(dir.path(), ThreadId::new(), dir.path(), &shell)
|
||||
.await
|
||||
.expect("snapshot should be created");
|
||||
let snapshot = ShellSnapshot::try_new(
|
||||
&dir.path().abs(),
|
||||
ThreadId::new(),
|
||||
&dir.path().abs(),
|
||||
&shell,
|
||||
)
|
||||
.await
|
||||
.expect("snapshot should be created");
|
||||
let path = snapshot.path.clone();
|
||||
assert!(path.exists());
|
||||
assert_eq!(snapshot.cwd, dir.path().to_path_buf());
|
||||
assert_eq!(snapshot.cwd, dir.path().abs());
|
||||
|
||||
drop(snapshot);
|
||||
|
||||
@@ -219,12 +227,14 @@ async fn try_new_uses_distinct_generation_paths() -> Result<()> {
|
||||
shell_snapshot: crate::shell::empty_shell_snapshot_receiver(),
|
||||
};
|
||||
|
||||
let initial_snapshot = ShellSnapshot::try_new(dir.path(), session_id, dir.path(), &shell)
|
||||
.await
|
||||
.expect("initial snapshot should be created");
|
||||
let refreshed_snapshot = ShellSnapshot::try_new(dir.path(), session_id, dir.path(), &shell)
|
||||
.await
|
||||
.expect("refreshed snapshot should be created");
|
||||
let initial_snapshot =
|
||||
ShellSnapshot::try_new(&dir.path().abs(), session_id, &dir.path().abs(), &shell)
|
||||
.await
|
||||
.expect("initial snapshot should be created");
|
||||
let refreshed_snapshot =
|
||||
ShellSnapshot::try_new(&dir.path().abs(), session_id, &dir.path().abs(), &shell)
|
||||
.await
|
||||
.expect("refreshed snapshot should be created");
|
||||
let initial_path = initial_snapshot.path.clone();
|
||||
let refreshed_path = refreshed_snapshot.path.clone();
|
||||
|
||||
@@ -250,7 +260,7 @@ async fn snapshot_shell_does_not_inherit_stdin() -> Result<()> {
|
||||
let _stdin_guard = BlockingStdinPipe::install()?;
|
||||
|
||||
let dir = tempdir()?;
|
||||
let home = dir.path();
|
||||
let home = dir.path().abs();
|
||||
let read_status_path = home.join("stdin-read-status");
|
||||
let read_status_display = read_status_path.display();
|
||||
// Persist the startup `read` exit status so the test can assert whether
|
||||
@@ -274,7 +284,7 @@ async fn snapshot_shell_does_not_inherit_stdin() -> Result<()> {
|
||||
&script,
|
||||
Duration::from_secs(2),
|
||||
/*use_login_shell*/ true,
|
||||
home,
|
||||
&home,
|
||||
)
|
||||
.await
|
||||
.context("run snapshot command")?;
|
||||
@@ -318,7 +328,7 @@ async fn timed_out_snapshot_shell_is_terminated() -> Result<()> {
|
||||
&script,
|
||||
Duration::from_secs(1),
|
||||
/*use_login_shell*/ true,
|
||||
dir.path(),
|
||||
&dir.path().abs(),
|
||||
)
|
||||
.await
|
||||
.expect_err("snapshot shell should time out");
|
||||
@@ -403,7 +413,7 @@ async fn write_rollout_stub(codex_home: &Path, session_id: ThreadId) -> Result<P
|
||||
#[tokio::test]
|
||||
async fn cleanup_stale_snapshots_removes_orphans_and_keeps_live() -> Result<()> {
|
||||
let dir = tempdir()?;
|
||||
let codex_home = dir.path();
|
||||
let codex_home = dir.path().abs();
|
||||
let snapshot_dir = codex_home.join(SNAPSHOT_DIR);
|
||||
fs::create_dir_all(&snapshot_dir).await?;
|
||||
|
||||
@@ -413,12 +423,12 @@ async fn cleanup_stale_snapshots_removes_orphans_and_keeps_live() -> Result<()>
|
||||
let orphan_snapshot = snapshot_dir.join(format!("{orphan_session}.456.sh"));
|
||||
let invalid_snapshot = snapshot_dir.join("not-a-snapshot.txt");
|
||||
|
||||
write_rollout_stub(codex_home, live_session).await?;
|
||||
write_rollout_stub(&codex_home, live_session).await?;
|
||||
fs::write(&live_snapshot, "live").await?;
|
||||
fs::write(&orphan_snapshot, "orphan").await?;
|
||||
fs::write(&invalid_snapshot, "invalid").await?;
|
||||
|
||||
cleanup_stale_snapshots(codex_home, ThreadId::new()).await?;
|
||||
cleanup_stale_snapshots(&codex_home, ThreadId::new()).await?;
|
||||
|
||||
assert_eq!(live_snapshot.exists(), true);
|
||||
assert_eq!(orphan_snapshot.exists(), false);
|
||||
@@ -430,18 +440,18 @@ async fn cleanup_stale_snapshots_removes_orphans_and_keeps_live() -> Result<()>
|
||||
#[tokio::test]
|
||||
async fn cleanup_stale_snapshots_removes_stale_rollouts() -> Result<()> {
|
||||
let dir = tempdir()?;
|
||||
let codex_home = dir.path();
|
||||
let codex_home = dir.path().abs();
|
||||
let snapshot_dir = codex_home.join(SNAPSHOT_DIR);
|
||||
fs::create_dir_all(&snapshot_dir).await?;
|
||||
|
||||
let stale_session = ThreadId::new();
|
||||
let stale_snapshot = snapshot_dir.join(format!("{stale_session}.123.sh"));
|
||||
let rollout_path = write_rollout_stub(codex_home, stale_session).await?;
|
||||
let rollout_path = write_rollout_stub(&codex_home, stale_session).await?;
|
||||
fs::write(&stale_snapshot, "stale").await?;
|
||||
|
||||
set_file_mtime(&rollout_path, SNAPSHOT_RETENTION + Duration::from_secs(60))?;
|
||||
|
||||
cleanup_stale_snapshots(codex_home, ThreadId::new()).await?;
|
||||
cleanup_stale_snapshots(&codex_home, ThreadId::new()).await?;
|
||||
|
||||
assert_eq!(stale_snapshot.exists(), false);
|
||||
Ok(())
|
||||
@@ -451,18 +461,18 @@ async fn cleanup_stale_snapshots_removes_stale_rollouts() -> Result<()> {
|
||||
#[tokio::test]
|
||||
async fn cleanup_stale_snapshots_skips_active_session() -> Result<()> {
|
||||
let dir = tempdir()?;
|
||||
let codex_home = dir.path();
|
||||
let codex_home = dir.path().abs();
|
||||
let snapshot_dir = codex_home.join(SNAPSHOT_DIR);
|
||||
fs::create_dir_all(&snapshot_dir).await?;
|
||||
|
||||
let active_session = ThreadId::new();
|
||||
let active_snapshot = snapshot_dir.join(format!("{active_session}.123.sh"));
|
||||
let rollout_path = write_rollout_stub(codex_home, active_session).await?;
|
||||
let rollout_path = write_rollout_stub(&codex_home, active_session).await?;
|
||||
fs::write(&active_snapshot, "active").await?;
|
||||
|
||||
set_file_mtime(&rollout_path, SNAPSHOT_RETENTION + Duration::from_secs(60))?;
|
||||
|
||||
cleanup_stale_snapshots(codex_home, active_session).await?;
|
||||
cleanup_stale_snapshots(&codex_home, active_session).await?;
|
||||
|
||||
assert_eq!(active_snapshot.exists(), true);
|
||||
Ok(())
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user