mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Spread AbsolutePathBuf (#17792)
Mechanical change to promote absolute paths through code.
This commit is contained in:
Generated
+3
@@ -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",
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -211,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]
|
||||
|
||||
@@ -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?;
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
+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(())
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use codex_network_proxy::NetworkProxy;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Stdio;
|
||||
@@ -40,7 +41,7 @@ pub(crate) struct SpawnChildRequest<'a> {
|
||||
pub program: PathBuf,
|
||||
pub args: Vec<String>,
|
||||
pub arg0: Option<&'a str>,
|
||||
pub cwd: PathBuf,
|
||||
pub cwd: AbsolutePathBuf,
|
||||
pub network_sandbox_policy: NetworkSandboxPolicy,
|
||||
pub network: Option<&'a NetworkProxy>,
|
||||
pub stdio_policy: StdioPolicy,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -27,6 +25,7 @@ use codex_protocol::models::MessagePhase;
|
||||
use codex_protocol::models::ResponseInputItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_rollout::state_db;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_stream_parser::strip_proposed_plan_blocks;
|
||||
use futures::Future;
|
||||
use tracing::debug;
|
||||
@@ -35,10 +34,10 @@ use tracing::instrument;
|
||||
const GENERATED_IMAGE_ARTIFACTS_DIR: &str = "generated_images";
|
||||
|
||||
pub(crate) fn image_generation_artifact_path(
|
||||
codex_home: &Path,
|
||||
codex_home: &AbsolutePathBuf,
|
||||
session_id: &str,
|
||||
call_id: &str,
|
||||
) -> PathBuf {
|
||||
) -> AbsolutePathBuf {
|
||||
let sanitize = |value: &str| {
|
||||
let mut sanitized: String = value
|
||||
.chars()
|
||||
@@ -104,11 +103,11 @@ pub(crate) fn raw_assistant_output_text_from_item(item: &ResponseItem) -> Option
|
||||
}
|
||||
|
||||
async fn save_image_generation_result(
|
||||
codex_home: &std::path::Path,
|
||||
codex_home: &AbsolutePathBuf,
|
||||
session_id: &str,
|
||||
call_id: &str,
|
||||
result: &str,
|
||||
) -> Result<PathBuf> {
|
||||
) -> Result<AbsolutePathBuf> {
|
||||
let bytes = BASE64_STANDARD
|
||||
.decode(result.trim().as_bytes())
|
||||
.map_err(|err| {
|
||||
@@ -361,7 +360,7 @@ pub(crate) async fn handle_non_tool_response_item(
|
||||
if let TurnItem::ImageGeneration(image_item) = &mut turn_item {
|
||||
let session_id = sess.conversation_id.to_string();
|
||||
match save_image_generation_result(
|
||||
turn_context.config.codex_home.as_path(),
|
||||
&turn_context.config.codex_home,
|
||||
&session_id,
|
||||
&image_item.id,
|
||||
&image_item.result,
|
||||
@@ -369,15 +368,15 @@ pub(crate) async fn handle_non_tool_response_item(
|
||||
.await
|
||||
{
|
||||
Ok(path) => {
|
||||
image_item.saved_path = Some(path.to_string_lossy().into_owned());
|
||||
image_item.saved_path = Some(path);
|
||||
let image_output_path = image_generation_artifact_path(
|
||||
turn_context.config.codex_home.as_path(),
|
||||
&turn_context.config.codex_home,
|
||||
&session_id,
|
||||
"<image_id>",
|
||||
);
|
||||
let image_output_dir = image_output_path
|
||||
.parent()
|
||||
.unwrap_or(turn_context.config.codex_home.as_path());
|
||||
.unwrap_or_else(|| turn_context.config.codex_home.clone());
|
||||
let message: ResponseItem = DeveloperInstructions::new(format!(
|
||||
"Generated images are saved to {} as {} by default.\nIf you need to use a generated image at another path, copy it and leave the original in place unless the user explicitly asks you to delete it.",
|
||||
image_output_dir.display(),
|
||||
@@ -389,13 +388,13 @@ pub(crate) async fn handle_non_tool_response_item(
|
||||
}
|
||||
Err(err) => {
|
||||
let output_path = image_generation_artifact_path(
|
||||
turn_context.config.codex_home.as_path(),
|
||||
&turn_context.config.codex_home,
|
||||
&session_id,
|
||||
&image_item.id,
|
||||
);
|
||||
let output_dir = output_path
|
||||
.parent()
|
||||
.unwrap_or(turn_context.config.codex_home.as_path());
|
||||
.unwrap_or_else(|| turn_context.config.codex_home.clone());
|
||||
tracing::warn!(
|
||||
call_id = %image_item.id,
|
||||
output_dir = %output_dir.display(),
|
||||
|
||||
@@ -9,6 +9,7 @@ use codex_protocol::items::TurnItem;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::MessagePhase;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_utils_absolute_path::test_support::PathExt;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
fn assistant_output_text(text: &str) -> ResponseItem {
|
||||
@@ -128,12 +129,12 @@ fn completed_item_defers_mailbox_delivery_for_image_generation_calls() {
|
||||
#[tokio::test]
|
||||
async fn save_image_generation_result_saves_base64_to_png_in_codex_home() {
|
||||
let codex_home = tempfile::tempdir().expect("create codex home");
|
||||
let expected_path =
|
||||
image_generation_artifact_path(codex_home.path(), "session-1", "ig_save_base64");
|
||||
let codex_home = codex_home.path().abs();
|
||||
let expected_path = image_generation_artifact_path(&codex_home, "session-1", "ig_save_base64");
|
||||
let _ = std::fs::remove_file(&expected_path);
|
||||
|
||||
let saved_path =
|
||||
save_image_generation_result(codex_home.path(), "session-1", "ig_save_base64", "Zm9v")
|
||||
save_image_generation_result(&codex_home, "session-1", "ig_save_base64", "Zm9v")
|
||||
.await
|
||||
.expect("image should be saved");
|
||||
|
||||
@@ -146,8 +147,9 @@ async fn save_image_generation_result_saves_base64_to_png_in_codex_home() {
|
||||
async fn save_image_generation_result_rejects_data_url_payload() {
|
||||
let result = "data:image/jpeg;base64,Zm9v";
|
||||
let codex_home = tempfile::tempdir().expect("create codex home");
|
||||
let codex_home = codex_home.path().abs();
|
||||
|
||||
let err = save_image_generation_result(codex_home.path(), "session-1", "ig_456", result)
|
||||
let err = save_image_generation_result(&codex_home, "session-1", "ig_456", result)
|
||||
.await
|
||||
.expect_err("data url payload should error");
|
||||
assert!(matches!(err, CodexErr::InvalidRequest(_)));
|
||||
@@ -156,8 +158,8 @@ async fn save_image_generation_result_rejects_data_url_payload() {
|
||||
#[tokio::test]
|
||||
async fn save_image_generation_result_overwrites_existing_file() {
|
||||
let codex_home = tempfile::tempdir().expect("create codex home");
|
||||
let existing_path =
|
||||
image_generation_artifact_path(codex_home.path(), "session-1", "ig_overwrite");
|
||||
let codex_home = codex_home.path().abs();
|
||||
let existing_path = image_generation_artifact_path(&codex_home, "session-1", "ig_overwrite");
|
||||
std::fs::create_dir_all(
|
||||
existing_path
|
||||
.parent()
|
||||
@@ -166,10 +168,9 @@ async fn save_image_generation_result_overwrites_existing_file() {
|
||||
.expect("create image output dir");
|
||||
std::fs::write(&existing_path, b"existing").expect("seed existing image");
|
||||
|
||||
let saved_path =
|
||||
save_image_generation_result(codex_home.path(), "session-1", "ig_overwrite", "Zm9v")
|
||||
.await
|
||||
.expect("image should be saved");
|
||||
let saved_path = save_image_generation_result(&codex_home, "session-1", "ig_overwrite", "Zm9v")
|
||||
.await
|
||||
.expect("image should be saved");
|
||||
|
||||
assert_eq!(saved_path, existing_path);
|
||||
assert_eq!(std::fs::read(&saved_path).expect("saved file"), b"foo");
|
||||
@@ -179,13 +180,13 @@ async fn save_image_generation_result_overwrites_existing_file() {
|
||||
#[tokio::test]
|
||||
async fn save_image_generation_result_sanitizes_call_id_for_codex_home_output_path() {
|
||||
let codex_home = tempfile::tempdir().expect("create codex home");
|
||||
let expected_path = image_generation_artifact_path(codex_home.path(), "session-1", "../ig/..");
|
||||
let codex_home = codex_home.path().abs();
|
||||
let expected_path = image_generation_artifact_path(&codex_home, "session-1", "../ig/..");
|
||||
let _ = std::fs::remove_file(&expected_path);
|
||||
|
||||
let saved_path =
|
||||
save_image_generation_result(codex_home.path(), "session-1", "../ig/..", "Zm9v")
|
||||
.await
|
||||
.expect("image should be saved");
|
||||
let saved_path = save_image_generation_result(&codex_home, "session-1", "../ig/..", "Zm9v")
|
||||
.await
|
||||
.expect("image should be saved");
|
||||
|
||||
assert_eq!(saved_path, expected_path);
|
||||
assert_eq!(std::fs::read(&saved_path).expect("saved file"), b"foo");
|
||||
@@ -195,7 +196,8 @@ async fn save_image_generation_result_sanitizes_call_id_for_codex_home_output_pa
|
||||
#[tokio::test]
|
||||
async fn save_image_generation_result_rejects_non_standard_base64() {
|
||||
let codex_home = tempfile::tempdir().expect("create codex home");
|
||||
let err = save_image_generation_result(codex_home.path(), "session-1", "ig_urlsafe", "_-8")
|
||||
let codex_home = codex_home.path().abs();
|
||||
let err = save_image_generation_result(&codex_home, "session-1", "ig_urlsafe", "_-8")
|
||||
.await
|
||||
.expect_err("non-standard base64 should error");
|
||||
assert!(matches!(err, CodexErr::InvalidRequest(_)));
|
||||
@@ -204,8 +206,9 @@ async fn save_image_generation_result_rejects_non_standard_base64() {
|
||||
#[tokio::test]
|
||||
async fn save_image_generation_result_rejects_non_base64_data_urls() {
|
||||
let codex_home = tempfile::tempdir().expect("create codex home");
|
||||
let codex_home = codex_home.path().abs();
|
||||
let err = save_image_generation_result(
|
||||
codex_home.path(),
|
||||
&codex_home,
|
||||
"session-1",
|
||||
"ig_svg",
|
||||
"data:image/svg+xml,<svg/>",
|
||||
|
||||
@@ -131,7 +131,7 @@ pub(crate) async fn execute_user_shell_command(
|
||||
let exec_command = maybe_wrap_shell_lc_with_snapshot(
|
||||
&display_command,
|
||||
session_shell.as_ref(),
|
||||
turn_context.cwd.as_path(),
|
||||
&turn_context.cwd,
|
||||
&turn_context.shell_environment_policy.r#set,
|
||||
&exec_env_map,
|
||||
);
|
||||
@@ -149,7 +149,7 @@ pub(crate) async fn execute_user_shell_command(
|
||||
process_id: None,
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
command: display_command.clone(),
|
||||
cwd: cwd.to_path_buf(),
|
||||
cwd: cwd.clone(),
|
||||
parsed_cmd: parsed_cmd.clone(),
|
||||
source: ExecCommandSource::UserShell,
|
||||
interaction_input: None,
|
||||
@@ -218,7 +218,7 @@ pub(crate) async fn execute_user_shell_command(
|
||||
process_id: None,
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
command: display_command.clone(),
|
||||
cwd: cwd.to_path_buf(),
|
||||
cwd: cwd.clone(),
|
||||
parsed_cmd: parsed_cmd.clone(),
|
||||
source: ExecCommandSource::UserShell,
|
||||
interaction_input: None,
|
||||
@@ -242,7 +242,7 @@ pub(crate) async fn execute_user_shell_command(
|
||||
process_id: None,
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
command: display_command.clone(),
|
||||
cwd: cwd.to_path_buf(),
|
||||
cwd: cwd.clone(),
|
||||
parsed_cmd: parsed_cmd.clone(),
|
||||
source: ExecCommandSource::UserShell,
|
||||
interaction_input: None,
|
||||
@@ -286,7 +286,7 @@ pub(crate) async fn execute_user_shell_command(
|
||||
process_id: None,
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
command: display_command,
|
||||
cwd: cwd.to_path_buf(),
|
||||
cwd,
|
||||
parsed_cmd,
|
||||
source: ExecCommandSource::UserShell,
|
||||
interaction_input: None,
|
||||
|
||||
@@ -18,8 +18,8 @@ use codex_protocol::protocol::PatchApplyEndEvent;
|
||||
use codex_protocol::protocol::PatchApplyStatus;
|
||||
use codex_protocol::protocol::TurnDiffEvent;
|
||||
use codex_shell_command::parse_command::parse_command;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -64,7 +64,7 @@ pub(crate) enum ToolEventFailure {
|
||||
pub(crate) async fn emit_exec_command_begin(
|
||||
ctx: ToolEventCtx<'_>,
|
||||
command: &[String],
|
||||
cwd: &Path,
|
||||
cwd: &AbsolutePathBuf,
|
||||
parsed_cmd: &[ParsedCommand],
|
||||
source: ExecCommandSource,
|
||||
interaction_input: Option<String>,
|
||||
@@ -78,7 +78,7 @@ pub(crate) async fn emit_exec_command_begin(
|
||||
process_id: process_id.map(str::to_owned),
|
||||
turn_id: ctx.turn.sub_id.clone(),
|
||||
command: command.to_vec(),
|
||||
cwd: cwd.to_path_buf(),
|
||||
cwd: cwd.clone(),
|
||||
parsed_cmd: parsed_cmd.to_vec(),
|
||||
source,
|
||||
interaction_input,
|
||||
@@ -90,7 +90,7 @@ pub(crate) async fn emit_exec_command_begin(
|
||||
pub(crate) enum ToolEmitter {
|
||||
Shell {
|
||||
command: Vec<String>,
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
source: ExecCommandSource,
|
||||
parsed_cmd: Vec<ParsedCommand>,
|
||||
freeform: bool,
|
||||
@@ -101,7 +101,7 @@ pub(crate) enum ToolEmitter {
|
||||
},
|
||||
UnifiedExec {
|
||||
command: Vec<String>,
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
source: ExecCommandSource,
|
||||
parsed_cmd: Vec<ParsedCommand>,
|
||||
process_id: Option<String>,
|
||||
@@ -111,7 +111,7 @@ pub(crate) enum ToolEmitter {
|
||||
impl ToolEmitter {
|
||||
pub fn shell(
|
||||
command: Vec<String>,
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
source: ExecCommandSource,
|
||||
freeform: bool,
|
||||
) -> Self {
|
||||
@@ -134,7 +134,7 @@ impl ToolEmitter {
|
||||
|
||||
pub fn unified_exec(
|
||||
command: &[String],
|
||||
cwd: PathBuf,
|
||||
cwd: AbsolutePathBuf,
|
||||
source: ExecCommandSource,
|
||||
process_id: Option<String>,
|
||||
) -> Self {
|
||||
@@ -163,11 +163,7 @@ impl ToolEmitter {
|
||||
emit_exec_stage(
|
||||
ctx,
|
||||
ExecCommandInput::new(
|
||||
command,
|
||||
cwd.as_path(),
|
||||
parsed_cmd,
|
||||
*source,
|
||||
/*interaction_input*/ None,
|
||||
command, cwd, parsed_cmd, *source, /*interaction_input*/ None,
|
||||
/*process_id*/ None,
|
||||
),
|
||||
stage,
|
||||
@@ -273,7 +269,7 @@ impl ToolEmitter {
|
||||
ctx,
|
||||
ExecCommandInput::new(
|
||||
command,
|
||||
cwd.as_path(),
|
||||
cwd,
|
||||
parsed_cmd,
|
||||
*source,
|
||||
/*interaction_input*/ None,
|
||||
@@ -365,7 +361,7 @@ impl ToolEmitter {
|
||||
|
||||
struct ExecCommandInput<'a> {
|
||||
command: &'a [String],
|
||||
cwd: &'a Path,
|
||||
cwd: &'a AbsolutePathBuf,
|
||||
parsed_cmd: &'a [ParsedCommand],
|
||||
source: ExecCommandSource,
|
||||
interaction_input: Option<&'a str>,
|
||||
@@ -375,7 +371,7 @@ struct ExecCommandInput<'a> {
|
||||
impl<'a> ExecCommandInput<'a> {
|
||||
fn new(
|
||||
command: &'a [String],
|
||||
cwd: &'a Path,
|
||||
cwd: &'a AbsolutePathBuf,
|
||||
parsed_cmd: &'a [ParsedCommand],
|
||||
source: ExecCommandSource,
|
||||
interaction_input: Option<&'a str>,
|
||||
@@ -479,7 +475,7 @@ async fn emit_exec_end(
|
||||
process_id: exec_input.process_id.map(str::to_owned),
|
||||
turn_id: ctx.turn.sub_id.clone(),
|
||||
command: exec_input.command.to_vec(),
|
||||
cwd: exec_input.cwd.to_path_buf(),
|
||||
cwd: exec_input.cwd.clone(),
|
||||
parsed_cmd: exec_input.parsed_cmd.to_vec(),
|
||||
source: exec_input.source,
|
||||
interaction_input: exec_input.interaction_input.map(str::to_owned),
|
||||
|
||||
@@ -60,7 +60,7 @@ async fn emit_js_repl_exec_begin(
|
||||
) {
|
||||
let emitter = ToolEmitter::shell(
|
||||
vec!["js_repl".to_string()],
|
||||
turn.cwd.to_path_buf(),
|
||||
turn.cwd.clone(),
|
||||
ExecCommandSource::Agent,
|
||||
/*freeform*/ false,
|
||||
);
|
||||
@@ -79,7 +79,7 @@ async fn emit_js_repl_exec_end(
|
||||
let exec_output = build_js_repl_exec_output(output, error, duration);
|
||||
let emitter = ToolEmitter::shell(
|
||||
vec!["js_repl".to_string()],
|
||||
turn.cwd.to_path_buf(),
|
||||
turn.cwd.clone(),
|
||||
ExecCommandSource::Agent,
|
||||
/*freeform*/ false,
|
||||
);
|
||||
|
||||
@@ -77,7 +77,7 @@ async fn emit_js_repl_exec_end_sends_event() {
|
||||
assert_eq!(event.call_id, "call-1");
|
||||
assert_eq!(event.turn_id, turn.sub_id);
|
||||
assert_eq!(event.command, vec!["js_repl".to_string()]);
|
||||
assert_eq!(event.cwd, turn.cwd.to_path_buf());
|
||||
assert_eq!(event.cwd, turn.cwd);
|
||||
assert_eq!(event.source, ExecCommandSource::Agent);
|
||||
assert_eq!(event.interaction_input, None);
|
||||
assert_eq!(event.stdout, "hello");
|
||||
|
||||
@@ -483,7 +483,7 @@ impl ShellHandler {
|
||||
let source = ExecCommandSource::Agent;
|
||||
let emitter = ToolEmitter::shell(
|
||||
exec_params.command.clone(),
|
||||
exec_params.cwd.to_path_buf(),
|
||||
exec_params.cwd.clone(),
|
||||
source,
|
||||
freeform,
|
||||
);
|
||||
|
||||
@@ -2,6 +2,8 @@ use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_protocol::models::ShellCommandToolCallParams;
|
||||
use core_test_support::PathBufExt;
|
||||
use core_test_support::test_path_buf;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use crate::codex::make_session_and_context;
|
||||
@@ -125,8 +127,8 @@ async fn shell_command_handler_to_exec_params_uses_session_shell_and_turn_contex
|
||||
#[test]
|
||||
fn shell_command_handler_respects_explicit_login_flag() {
|
||||
let (_tx, shell_snapshot) = watch::channel(Some(Arc::new(ShellSnapshot {
|
||||
path: PathBuf::from("/tmp/snapshot.sh"),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
path: test_path_buf("/tmp/snapshot.sh").abs(),
|
||||
cwd: test_path_buf("/tmp").abs(),
|
||||
})));
|
||||
let shell = Shell {
|
||||
shell_type: ShellType::Bash,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user