mirror of
https://github.com/openai/codex.git
synced 2026-05-29 15:30:22 +00:00
[codex] Improve built-in tool schema docs (#24794)
## Summary - Clarify default, omission, and bounded behavior across built-in tool schemas, including unified exec, classic shell, Code Mode exec/wait, multi-agent, agent job, MCP resource, image, goal, plan, tool_search, and test-sync fields. - Convert update_plan status to an enum and add short field descriptions where the schema previously relied on surrounding context. - Remove the dedicated permission-approval schema test and keep only updates to existing expected-spec tests. ## Validation - Ran `just fmt`. - Ran `git diff --check`. - Did not run clippy or tests, per request. Regression has been eval [here](https://openai.slack.com/archives/C09GDSP1J9X/p1779905065496949) and we proved there are no regressions
This commit is contained in:
@@ -17,8 +17,8 @@ const EXEC_DESCRIPTION_TEMPLATE: &str = r#"Run JavaScript code to orchestrate/co
|
||||
- Runs raw JavaScript -- no Node, no file system, no network access, no console.
|
||||
- Accepts raw JavaScript source text, not JSON, quoted strings, or markdown code fences.
|
||||
- You may optionally start the tool input with a first-line pragma like `// @exec: {"yield_time_ms": 10000, "max_output_tokens": 1000}`.
|
||||
- `yield_time_ms` asks `exec` to yield early after that many milliseconds if the script is still running.
|
||||
- `max_output_tokens` sets the token budget for direct `exec` results. By default the result is truncated to 10000 tokens.
|
||||
- `yield_time_ms` asks `exec` to yield early if the script is still running. Defaults to 10000 ms.
|
||||
- `max_output_tokens` sets the token budget for direct `exec` results. Defaults to 10000 tokens.
|
||||
- When the JS code is fully evaluated, the isolate's lifetime ends and unawaited promises are silently discarded.
|
||||
|
||||
- Global helpers:
|
||||
@@ -34,9 +34,9 @@ const EXEC_DESCRIPTION_TEMPLATE: &str = r#"Run JavaScript code to orchestrate/co
|
||||
- `yield_control()`: yields the accumulated output to the model immediately while the script keeps running."#;
|
||||
const WAIT_DESCRIPTION_TEMPLATE: &str = r#"- Use `wait` only after `exec` returns `Script running with cell ID ...`.
|
||||
- `cell_id` identifies the running `exec` cell to resume.
|
||||
- `yield_time_ms` controls how long to wait for more output before yielding again. If omitted, `wait` uses its default wait timeout.
|
||||
- `max_tokens` limits how much new output this wait call returns.
|
||||
- `terminate: true` stops the running cell instead of waiting for more output.
|
||||
- `yield_time_ms` controls how long to wait for more output before yielding again. Defaults to 10000 ms.
|
||||
- `max_tokens` limits how much new output this wait call returns. Defaults to 10000 tokens.
|
||||
- `terminate: true` stops the running cell; false or omitted waits for output.
|
||||
- `wait` returns only the new output since the last yield, or the final completion or termination result for that cell.
|
||||
- If the cell is still running, `wait` may yield again with the same `cell_id`.
|
||||
- If the cell has already finished, `wait` returns the completed result and closes the cell."#;
|
||||
|
||||
@@ -12,20 +12,19 @@ pub(crate) fn create_wait_tool() -> ToolSpec {
|
||||
(
|
||||
"yield_time_ms".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"How long to wait (in milliseconds) for more output before yielding again."
|
||||
.to_string(),
|
||||
"Wait before yielding more output. Defaults to 10000 ms.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"max_tokens".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Maximum number of output tokens to return for this wait call.".to_string(),
|
||||
"Output token budget for this wait call. Defaults to 10000 tokens.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"terminate".to_string(),
|
||||
JsonSchema::boolean(Some(
|
||||
"Whether to terminate the running exec cell.".to_string(),
|
||||
"True stops the running exec cell; false or omitted waits for output.".to_string(),
|
||||
)),
|
||||
),
|
||||
]);
|
||||
@@ -77,20 +76,21 @@ mod tests {
|
||||
(
|
||||
"max_tokens".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Maximum number of output tokens to return for this wait call."
|
||||
"Output token budget for this wait call. Defaults to 10000 tokens."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"terminate".to_string(),
|
||||
JsonSchema::boolean(Some(
|
||||
"Whether to terminate the running exec cell.".to_string(),
|
||||
"True stops the running exec cell; false or omitted waits for output."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"yield_time_ms".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"How long to wait (in milliseconds) for more output before yielding again."
|
||||
"Wait before yielding more output. Defaults to 10000 ms."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
|
||||
@@ -4,6 +4,14 @@ use codex_tools::ToolSpec;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub fn create_spawn_agents_on_csv_tool() -> ToolSpec {
|
||||
let mut output_schema = JsonSchema::object(
|
||||
BTreeMap::new(),
|
||||
/*required*/ None,
|
||||
/*additional_properties*/ None,
|
||||
);
|
||||
output_schema.description =
|
||||
Some("JSON Schema for each worker result. Omit to accept any result object.".to_string());
|
||||
|
||||
let properties = BTreeMap::from([
|
||||
(
|
||||
"csv_path".to_string(),
|
||||
@@ -19,12 +27,15 @@ pub fn create_spawn_agents_on_csv_tool() -> ToolSpec {
|
||||
(
|
||||
"id_column".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Optional column name to use as stable item id.".to_string(),
|
||||
"CSV column to use as stable item id. Omit to use row numbers.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"output_csv_path".to_string(),
|
||||
JsonSchema::string(Some("Optional output CSV path for exported results.".to_string())),
|
||||
JsonSchema::string(Some(
|
||||
"Output CSV path for exported results. Omit to create one next to the input CSV."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"max_concurrency".to_string(),
|
||||
@@ -36,20 +47,17 @@ pub fn create_spawn_agents_on_csv_tool() -> ToolSpec {
|
||||
(
|
||||
"max_workers".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Alias for max_concurrency. Set to 1 to run sequentially.".to_string(),
|
||||
"Alias for max_concurrency. Defaults to 16 and is capped by config.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"max_runtime_seconds".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Maximum runtime per worker before it is failed. Defaults to 1800 seconds."
|
||||
"Maximum runtime per worker before failure. Defaults to 1800 seconds; config may set a different default."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"output_schema".to_string(),
|
||||
JsonSchema::object(BTreeMap::new(), /*required*/ None, /*additional_properties*/ None),
|
||||
),
|
||||
("output_schema".to_string(), output_schema),
|
||||
]);
|
||||
|
||||
ToolSpec::Function(ResponsesApiTool {
|
||||
@@ -64,6 +72,13 @@ pub fn create_spawn_agents_on_csv_tool() -> ToolSpec {
|
||||
}
|
||||
|
||||
pub fn create_report_agent_job_result_tool() -> ToolSpec {
|
||||
let mut result_schema = JsonSchema::object(
|
||||
BTreeMap::new(),
|
||||
/*required*/ None,
|
||||
/*additional_properties*/ None,
|
||||
);
|
||||
result_schema.description = Some("Result object for this job item.".to_string());
|
||||
|
||||
let properties = BTreeMap::from([
|
||||
(
|
||||
"job_id".to_string(),
|
||||
@@ -73,14 +88,11 @@ pub fn create_report_agent_job_result_tool() -> ToolSpec {
|
||||
"item_id".to_string(),
|
||||
JsonSchema::string(Some("Identifier of the job item.".to_string())),
|
||||
),
|
||||
(
|
||||
"result".to_string(),
|
||||
JsonSchema::object(BTreeMap::new(), /*required*/ None, /*additional_properties*/ None),
|
||||
),
|
||||
("result".to_string(), result_schema),
|
||||
(
|
||||
"stop".to_string(),
|
||||
JsonSchema::boolean(Some(
|
||||
"Optional. When true, cancels the remaining job items after this result is recorded."
|
||||
"True cancels remaining job items after this result is recorded; false or omitted continues the job."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
|
||||
@@ -3,6 +3,16 @@ use codex_tools::JsonSchema;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
fn described_object(description: &str) -> JsonSchema {
|
||||
let mut schema = JsonSchema::object(
|
||||
BTreeMap::new(),
|
||||
/*required*/ None,
|
||||
/*additional_properties*/ None,
|
||||
);
|
||||
schema.description = Some(description.to_string());
|
||||
schema
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_agents_on_csv_tool_requires_csv_and_instruction() {
|
||||
assert_eq!(
|
||||
@@ -30,13 +40,15 @@ fn spawn_agents_on_csv_tool_requires_csv_and_instruction() {
|
||||
(
|
||||
"id_column".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Optional column name to use as stable item id.".to_string(),
|
||||
"CSV column to use as stable item id. Omit to use row numbers."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"output_csv_path".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Optional output CSV path for exported results.".to_string(),
|
||||
"Output CSV path for exported results. Omit to create one next to the input CSV."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
@@ -49,22 +61,21 @@ fn spawn_agents_on_csv_tool_requires_csv_and_instruction() {
|
||||
(
|
||||
"max_workers".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Alias for max_concurrency. Set to 1 to run sequentially.".to_string(),
|
||||
"Alias for max_concurrency. Defaults to 16 and is capped by config."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"max_runtime_seconds".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Maximum runtime per worker before it is failed. Defaults to 1800 seconds."
|
||||
"Maximum runtime per worker before failure. Defaults to 1800 seconds; config may set a different default."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"output_schema".to_string(),
|
||||
JsonSchema::object(
|
||||
BTreeMap::new(),
|
||||
/*required*/ None,
|
||||
/*additional_properties*/ None,
|
||||
described_object(
|
||||
"JSON Schema for each worker result. Omit to accept any result object.",
|
||||
),
|
||||
),
|
||||
]), Some(vec!["csv_path".to_string(), "instruction".to_string()]), Some(false.into())),
|
||||
@@ -95,16 +106,12 @@ fn report_agent_job_result_tool_requires_result_payload() {
|
||||
),
|
||||
(
|
||||
"result".to_string(),
|
||||
JsonSchema::object(
|
||||
BTreeMap::new(),
|
||||
/*required*/ None,
|
||||
/*additional_properties*/ None,
|
||||
),
|
||||
described_object("Result object for this job item."),
|
||||
),
|
||||
(
|
||||
"stop".to_string(),
|
||||
JsonSchema::boolean(Some(
|
||||
"Optional. When true, cancels the remaining job items after this result is recorded."
|
||||
"True cancels remaining job items after this result is recorded; false or omitted continues the job."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
|
||||
@@ -37,7 +37,8 @@ pub fn create_create_goal_tool() -> ToolSpec {
|
||||
(
|
||||
"token_budget".to_string(),
|
||||
JsonSchema::integer(Some(
|
||||
"Optional positive token budget for the new active goal.".to_string(),
|
||||
"Positive token budget for the new goal. Omit unless explicitly requested."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
]);
|
||||
|
||||
@@ -8,14 +8,13 @@ pub fn create_list_mcp_resources_tool() -> ToolSpec {
|
||||
(
|
||||
"server".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Optional MCP server name. When omitted, lists resources from every configured server."
|
||||
.to_string(),
|
||||
"MCP server name. Omit to list resources from every configured server.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"cursor".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Opaque cursor returned by a previous list_mcp_resources call for the same server."
|
||||
"Opaque cursor from a previous list_mcp_resources call; omit for the first page."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
@@ -36,14 +35,14 @@ pub fn create_list_mcp_resource_templates_tool() -> ToolSpec {
|
||||
(
|
||||
"server".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Optional MCP server name. When omitted, lists resource templates from all configured servers."
|
||||
"MCP server name. Omit to list resource templates from every configured server."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"cursor".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Opaque cursor returned by a previous list_mcp_resource_templates call for the same server."
|
||||
"Opaque cursor from a previous list_mcp_resource_templates call; omit for the first page."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
|
||||
@@ -16,14 +16,14 @@ fn list_mcp_resources_tool_matches_expected_spec() {
|
||||
(
|
||||
"server".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Optional MCP server name. When omitted, lists resources from every configured server."
|
||||
"MCP server name. Omit to list resources from every configured server."
|
||||
.to_string(),
|
||||
),),
|
||||
),
|
||||
(
|
||||
"cursor".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Opaque cursor returned by a previous list_mcp_resources call for the same server."
|
||||
"Opaque cursor from a previous list_mcp_resources call; omit for the first page."
|
||||
.to_string(),
|
||||
),),
|
||||
),
|
||||
@@ -46,14 +46,14 @@ fn list_mcp_resource_templates_tool_matches_expected_spec() {
|
||||
(
|
||||
"server".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Optional MCP server name. When omitted, lists resource templates from all configured servers."
|
||||
"MCP server name. Omit to list resource templates from every configured server."
|
||||
.to_string(),
|
||||
),),
|
||||
),
|
||||
(
|
||||
"cursor".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Opaque cursor returned by a previous list_mcp_resource_templates call for the same server."
|
||||
"Opaque cursor from a previous list_mcp_resource_templates call; omit for the first page."
|
||||
.to_string(),
|
||||
),),
|
||||
),
|
||||
|
||||
@@ -11,9 +11,11 @@ use std::collections::BTreeMap;
|
||||
pub const MULTI_AGENT_V1_NAMESPACE: &str = "multi_agent_v1";
|
||||
const MULTI_AGENT_V1_NAMESPACE_DESCRIPTION: &str = "Tools for spawning and managing sub-agents.";
|
||||
|
||||
const SPAWN_AGENT_INHERITED_MODEL_GUIDANCE: &str = "Spawned agents inherit your current model by default. Omit `model` to use that preferred default; set `model` only when an explicit override is needed.";
|
||||
const SPAWN_AGENT_MODEL_OVERRIDE_DESCRIPTION: &str = "Optional model override for the new agent. Leave unset to inherit the same model as the parent, which is the preferred default. Only set this when the user explicitly asks for a different model or the task clearly requires one.";
|
||||
const SPAWN_AGENT_SERVICE_TIER_OVERRIDE_DESCRIPTION: &str = "Optional service tier override for the new agent. Leave unset unless the user explicitly asks for one.";
|
||||
const SPAWN_AGENT_INHERITED_MODEL_GUIDANCE: &str = "Spawned agents inherit your current model by default. If provided, `model` specifies the model to use for the spawned agent.";
|
||||
const SPAWN_AGENT_MODEL_OVERRIDE_DESCRIPTION: &str =
|
||||
"Model override for the new agent. Omit to inherit the parent model.";
|
||||
const SPAWN_AGENT_SERVICE_TIER_OVERRIDE_DESCRIPTION: &str =
|
||||
"Service tier override for the new agent. Omit unless explicitly requested.";
|
||||
const MAX_MODEL_OVERRIDES_IN_SPAWN_AGENT_DESCRIPTION: usize = 5;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@@ -125,7 +127,7 @@ pub fn create_send_input_tool_v1() -> ToolSpec {
|
||||
(
|
||||
"interrupt".to_string(),
|
||||
JsonSchema::boolean(Some(
|
||||
"When true, stop the agent's current task and handle this immediately. When false (default), queue this message."
|
||||
"True interrupts the current task and handles this message immediately; false or omitted queues it."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
@@ -258,7 +260,7 @@ pub fn create_list_agents_tool() -> ToolSpec {
|
||||
let properties = BTreeMap::from([(
|
||||
"path_prefix".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Optional task-path prefix (not ending with trailing slash). Accepts the same relative or absolute task-path syntax."
|
||||
"Task-path prefix filter without a trailing slash. Omit to list all live agents."
|
||||
.to_string(),
|
||||
)),
|
||||
)]);
|
||||
@@ -555,7 +557,7 @@ fn spawn_agent_common_properties_v1(agent_type_description: &str) -> BTreeMap<St
|
||||
(
|
||||
"fork_context".to_string(),
|
||||
JsonSchema::boolean(Some(
|
||||
"When true, fork the current thread history into the new agent before sending the initial prompt. This must be used when you want the new agent to have exactly the same context as you."
|
||||
"True forks the current thread history into the new agent; false or omitted starts with only the initial prompt."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
@@ -568,7 +570,7 @@ fn spawn_agent_common_properties_v1(agent_type_description: &str) -> BTreeMap<St
|
||||
(
|
||||
"reasoning_effort".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Optional reasoning effort override for the new agent. Replaces the inherited reasoning effort."
|
||||
"Reasoning effort override for the new agent. Omit to inherit the parent effort."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
@@ -607,7 +609,7 @@ fn spawn_agent_common_properties_v2(agent_type_description: &str) -> BTreeMap<St
|
||||
(
|
||||
"reasoning_effort".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Optional reasoning effort override for the new agent. Replaces the inherited reasoning effort."
|
||||
"Reasoning effort override for the new agent. Omit to inherit the parent effort."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
@@ -807,7 +809,7 @@ fn wait_agent_tool_parameters_v1(options: WaitAgentTimeoutOptions) -> JsonSchema
|
||||
(
|
||||
"timeout_ms".to_string(),
|
||||
JsonSchema::number(Some(format!(
|
||||
"Optional timeout in milliseconds. Defaults to {}, min {}, max {}. Prefer longer waits (minutes) to avoid busy polling.",
|
||||
"Timeout in milliseconds. Defaults to {}, min {}, max {}. Prefer longer waits (minutes) to avoid busy polling.",
|
||||
options.default_timeout_ms, options.min_timeout_ms, options.max_timeout_ms,
|
||||
))),
|
||||
),
|
||||
@@ -824,7 +826,7 @@ fn wait_agent_tool_parameters_v2(options: WaitAgentTimeoutOptions) -> JsonSchema
|
||||
let properties = BTreeMap::from([(
|
||||
"timeout_ms".to_string(),
|
||||
JsonSchema::number(Some(format!(
|
||||
"Optional timeout in milliseconds. Defaults to {}, min {}, max {}.",
|
||||
"Timeout in milliseconds. Defaults to {}, min {}, max {}.",
|
||||
options.default_timeout_ms, options.min_timeout_ms, options.max_timeout_ms,
|
||||
))),
|
||||
)]);
|
||||
|
||||
@@ -306,7 +306,7 @@ fn wait_agent_tool_v2_uses_timeout_only_summary_output() {
|
||||
properties
|
||||
.get("timeout_ms")
|
||||
.and_then(|schema| schema.description.as_deref()),
|
||||
Some("Optional timeout in milliseconds. Defaults to 30000, min 10000, max 3600000.")
|
||||
Some("Timeout in milliseconds. Defaults to 30000, min 10000, max 3600000.")
|
||||
);
|
||||
assert_eq!(parameters.required.as_ref(), None);
|
||||
assert_eq!(
|
||||
@@ -338,9 +338,7 @@ fn list_agents_tool_includes_path_prefix_and_agent_fields() {
|
||||
properties
|
||||
.get("path_prefix")
|
||||
.and_then(|schema| schema.description.as_deref()),
|
||||
Some(
|
||||
"Optional task-path prefix (not ending with trailing slash). Accepts the same relative or absolute task-path syntax."
|
||||
)
|
||||
Some("Task-path prefix filter without a trailing slash. Omit to list all live agents.")
|
||||
);
|
||||
assert_eq!(
|
||||
output_schema.expect("list_agents output schema")["properties"]["agents"]["items"]["required"],
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
use codex_tools::JsonSchema;
|
||||
use codex_tools::ResponsesApiTool;
|
||||
use codex_tools::ToolSpec;
|
||||
use serde_json::json;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub fn create_update_plan_tool() -> ToolSpec {
|
||||
let plan_item_properties = BTreeMap::from([
|
||||
("step".to_string(), JsonSchema::string(/*description*/ None)),
|
||||
(
|
||||
"step".to_string(),
|
||||
JsonSchema::string(Some("Task step text.".to_string())),
|
||||
),
|
||||
(
|
||||
"status".to_string(),
|
||||
JsonSchema::string(Some("One of: pending, in_progress, completed".to_string())),
|
||||
JsonSchema::string_enum(
|
||||
vec![json!("pending"), json!("in_progress"), json!("completed")],
|
||||
Some("Step status.".to_string()),
|
||||
),
|
||||
),
|
||||
]);
|
||||
|
||||
let properties = BTreeMap::from([
|
||||
(
|
||||
"explanation".to_string(),
|
||||
JsonSchema::string(/*description*/ None),
|
||||
JsonSchema::string(Some(
|
||||
"Optional explanation for this plan update.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"plan".to_string(),
|
||||
|
||||
@@ -28,7 +28,7 @@ pub(crate) fn create_exec_command_tool_with_environment_id(
|
||||
(
|
||||
"workdir".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Optional working directory to run the command in; defaults to the turn cwd."
|
||||
"Working directory for the command. Defaults to the turn cwd."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
@@ -41,20 +41,20 @@ pub(crate) fn create_exec_command_tool_with_environment_id(
|
||||
(
|
||||
"tty".to_string(),
|
||||
JsonSchema::boolean(Some(
|
||||
"Whether to allocate a TTY for the command. Defaults to false (plain pipes); set to true to open a PTY and access TTY process."
|
||||
"True allocates a PTY for the command; false or omitted uses plain pipes."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"yield_time_ms".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"How long to wait (in milliseconds) for output before yielding.".to_string(),
|
||||
"Wait before yielding output. Defaults to 10000 ms; effective range is 250-30000 ms.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"max_output_tokens".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Maximum number of tokens to return. Excess output will be truncated.".to_string(),
|
||||
"Output token budget. Defaults to 10000 tokens; larger requests may be capped by policy.".to_string(),
|
||||
)),
|
||||
),
|
||||
]);
|
||||
@@ -62,7 +62,8 @@ pub(crate) fn create_exec_command_tool_with_environment_id(
|
||||
properties.insert(
|
||||
"login".to_string(),
|
||||
JsonSchema::boolean(Some(
|
||||
"Whether to run the shell with -l/-i semantics. Defaults to true.".to_string(),
|
||||
"True runs the shell with -l/-i semantics; false disables them. Defaults to true."
|
||||
.to_string(),
|
||||
)),
|
||||
);
|
||||
}
|
||||
@@ -70,7 +71,8 @@ pub(crate) fn create_exec_command_tool_with_environment_id(
|
||||
properties.insert(
|
||||
"environment_id".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Optional environment id from the <environment_context> block. If omitted, uses the primary environment.".to_string(),
|
||||
"Environment id from <environment_context>. Omit to use the primary environment."
|
||||
.to_string(),
|
||||
)),
|
||||
);
|
||||
}
|
||||
@@ -111,19 +113,19 @@ pub fn create_write_stdin_tool() -> ToolSpec {
|
||||
(
|
||||
"chars".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Bytes to write to stdin (may be empty to poll).".to_string(),
|
||||
"Bytes to write to stdin. Defaults to empty, which polls without writing.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"yield_time_ms".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"How long to wait (in milliseconds) for output before yielding.".to_string(),
|
||||
"Wait before yielding output. Non-empty writes default to 250 ms and cap at 30000 ms; empty polls wait 5000-300000 ms by default.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"max_output_tokens".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Maximum number of tokens to return. Excess output will be truncated.".to_string(),
|
||||
"Output token budget. Defaults to 10000 tokens; larger requests may be capped by policy.".to_string(),
|
||||
)),
|
||||
),
|
||||
]);
|
||||
@@ -149,19 +151,19 @@ pub fn create_shell_command_tool(options: CommandToolOptions) -> ToolSpec {
|
||||
(
|
||||
"command".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"The shell script to execute in the user's default shell".to_string(),
|
||||
"Shell script to run in the user's default shell.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"workdir".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"The working directory to execute the command in".to_string(),
|
||||
"Working directory for the command. Defaults to the turn cwd.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"timeout_ms".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"The timeout for the command in milliseconds".to_string(),
|
||||
"Maximum command runtime. Defaults to 10000 ms.".to_string(),
|
||||
)),
|
||||
),
|
||||
]);
|
||||
@@ -169,7 +171,7 @@ pub fn create_shell_command_tool(options: CommandToolOptions) -> ToolSpec {
|
||||
properties.insert(
|
||||
"login".to_string(),
|
||||
JsonSchema::boolean(Some(
|
||||
"Whether to run the shell with login shell semantics. Defaults to true."
|
||||
"True runs with login shell semantics; false disables them. Defaults to true."
|
||||
.to_string(),
|
||||
)),
|
||||
);
|
||||
@@ -281,92 +283,108 @@ fn unified_exec_output_schema() -> Value {
|
||||
fn create_approval_parameters(
|
||||
exec_permission_approvals_enabled: bool,
|
||||
) -> BTreeMap<String, JsonSchema> {
|
||||
let mut sandbox_permission_values = vec![json!("use_default")];
|
||||
if exec_permission_approvals_enabled {
|
||||
sandbox_permission_values.push(json!("with_additional_permissions"));
|
||||
}
|
||||
sandbox_permission_values.push(json!("require_escalated"));
|
||||
let sandbox_permissions_description = if exec_permission_approvals_enabled {
|
||||
"Per-command sandbox override. Defaults to `use_default`; use `with_additional_permissions` with `additional_permissions`, or `require_escalated` for unsandboxed execution."
|
||||
} else {
|
||||
"Per-command sandbox override. Defaults to `use_default`; use `require_escalated` for unsandboxed execution."
|
||||
};
|
||||
|
||||
let mut properties = BTreeMap::from([
|
||||
(
|
||||
"sandbox_permissions".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
if exec_permission_approvals_enabled {
|
||||
"Sandbox permissions for the command. Use \"with_additional_permissions\" to request additional sandboxed filesystem or network permissions (preferred), or \"require_escalated\" to request running without sandbox restrictions; defaults to \"use_default\"."
|
||||
} else {
|
||||
"Sandbox permissions for the command. Set to \"require_escalated\" to request running without sandbox restrictions; defaults to \"use_default\"."
|
||||
}
|
||||
.to_string(),
|
||||
)),
|
||||
JsonSchema::string_enum(
|
||||
sandbox_permission_values,
|
||||
Some(sandbox_permissions_description.to_string()),
|
||||
),
|
||||
),
|
||||
(
|
||||
"justification".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
r#"Only set if sandbox_permissions is \"require_escalated\".
|
||||
Request approval from the user to run this command outside the sandbox.
|
||||
Phrased as a simple question that summarizes the purpose of the
|
||||
command as it relates to the task at hand - e.g. 'Do you want to
|
||||
fetch and pull the latest version of this git branch?'"#
|
||||
.to_string(),
|
||||
"User-facing approval question for `require_escalated`; omit otherwise.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"prefix_rule".to_string(),
|
||||
JsonSchema::array(JsonSchema::string(/*description*/ None), Some(
|
||||
r#"Only specify when sandbox_permissions is `require_escalated`.
|
||||
Suggest a prefix command pattern that will allow you to fulfill similar requests from the user in the future.
|
||||
Should be a short but reasonable prefix, e.g. [\"git\", \"pull\"] or [\"uv\", \"run\"] or [\"pytest\"]."#.to_string(),
|
||||
r#"Reusable approval prefix for `cmd`, only with `sandbox_permissions: "require_escalated"`; for example ["git", "pull"]."#.to_string(),
|
||||
)),
|
||||
),
|
||||
]);
|
||||
|
||||
if exec_permission_approvals_enabled {
|
||||
properties.insert(
|
||||
"additional_permissions".to_string(),
|
||||
permission_profile_schema(),
|
||||
let mut additional_permissions = permission_profile_schema();
|
||||
additional_permissions.description = Some(
|
||||
"Sandboxed filesystem or network access for this command; only with `sandbox_permissions: \"with_additional_permissions\"`."
|
||||
.to_string(),
|
||||
);
|
||||
properties.insert("additional_permissions".to_string(), additional_permissions);
|
||||
}
|
||||
|
||||
properties
|
||||
}
|
||||
|
||||
fn permission_profile_schema() -> JsonSchema {
|
||||
JsonSchema::object(
|
||||
let mut schema = JsonSchema::object(
|
||||
BTreeMap::from([
|
||||
("network".to_string(), network_permissions_schema()),
|
||||
("file_system".to_string(), file_system_permissions_schema()),
|
||||
]),
|
||||
/*required*/ None,
|
||||
Some(false.into()),
|
||||
)
|
||||
);
|
||||
schema.description = Some("Filesystem or network access request.".to_string());
|
||||
schema
|
||||
}
|
||||
|
||||
fn network_permissions_schema() -> JsonSchema {
|
||||
JsonSchema::object(
|
||||
let mut schema = JsonSchema::object(
|
||||
BTreeMap::from([(
|
||||
"enabled".to_string(),
|
||||
JsonSchema::boolean(Some("Set to true to request network access.".to_string())),
|
||||
JsonSchema::boolean(Some(
|
||||
"True requests network access; false or omitted requests none.".to_string(),
|
||||
)),
|
||||
)]),
|
||||
/*required*/ None,
|
||||
Some(false.into()),
|
||||
)
|
||||
);
|
||||
schema.description = Some("Network access request.".to_string());
|
||||
schema
|
||||
}
|
||||
|
||||
fn file_system_permissions_schema() -> JsonSchema {
|
||||
JsonSchema::object(
|
||||
let mut schema = JsonSchema::object(
|
||||
BTreeMap::from([
|
||||
(
|
||||
"read".to_string(),
|
||||
JsonSchema::array(
|
||||
JsonSchema::string(/*description*/ None),
|
||||
Some("Absolute paths to grant read access to.".to_string()),
|
||||
Some(
|
||||
"Absolute paths to grant read access; omit when none are needed."
|
||||
.to_string(),
|
||||
),
|
||||
),
|
||||
),
|
||||
(
|
||||
"write".to_string(),
|
||||
JsonSchema::array(
|
||||
JsonSchema::string(/*description*/ None),
|
||||
Some("Absolute paths to grant write access to.".to_string()),
|
||||
Some(
|
||||
"Absolute paths to grant write access; omit when none are needed."
|
||||
.to_string(),
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
/*required*/ None,
|
||||
Some(false.into()),
|
||||
)
|
||||
);
|
||||
schema.description = Some("Filesystem access request.".to_string());
|
||||
schema
|
||||
}
|
||||
|
||||
fn windows_shell_guidance() -> &'static str {
|
||||
|
||||
@@ -31,7 +31,7 @@ fn exec_command_tool_matches_expected_spec() {
|
||||
(
|
||||
"workdir".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Optional working directory to run the command in; defaults to the turn cwd."
|
||||
"Working directory for the command. Defaults to the turn cwd."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
@@ -44,27 +44,26 @@ fn exec_command_tool_matches_expected_spec() {
|
||||
(
|
||||
"tty".to_string(),
|
||||
JsonSchema::boolean(Some(
|
||||
"Whether to allocate a TTY for the command. Defaults to false (plain pipes); set to true to open a PTY and access TTY process."
|
||||
"True allocates a PTY for the command; false or omitted uses plain pipes."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"yield_time_ms".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"How long to wait (in milliseconds) for output before yielding.".to_string(),
|
||||
"Wait before yielding output. Defaults to 10000 ms; effective range is 250-30000 ms.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"max_output_tokens".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Maximum number of tokens to return. Excess output will be truncated."
|
||||
.to_string(),
|
||||
"Output token budget. Defaults to 10000 tokens; larger requests may be capped by policy.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"login".to_string(),
|
||||
JsonSchema::boolean(Some(
|
||||
"Whether to run the shell with -l/-i semantics. Defaults to true.".to_string(),
|
||||
"True runs the shell with -l/-i semantics; false disables them. Defaults to true.".to_string(),
|
||||
)),
|
||||
),
|
||||
]);
|
||||
@@ -103,19 +102,19 @@ fn write_stdin_tool_matches_expected_spec() {
|
||||
(
|
||||
"chars".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Bytes to write to stdin (may be empty to poll).".to_string(),
|
||||
"Bytes to write to stdin. Defaults to empty, which polls without writing.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"yield_time_ms".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"How long to wait (in milliseconds) for output before yielding.".to_string(),
|
||||
"Wait before yielding output. Non-empty writes default to 250 ms and cap at 30000 ms; empty polls wait 5000-300000 ms by default.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"max_output_tokens".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Maximum number of tokens to return. Excess output will be truncated.".to_string(),
|
||||
"Output token budget. Defaults to 10000 tokens; larger requests may be capped by policy.".to_string(),
|
||||
)),
|
||||
),
|
||||
]);
|
||||
@@ -201,25 +200,25 @@ Examples of valid command strings:
|
||||
(
|
||||
"command".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"The shell script to execute in the user's default shell".to_string(),
|
||||
"Shell script to run in the user's default shell.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"workdir".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"The working directory to execute the command in".to_string(),
|
||||
"Working directory for the command. Defaults to the turn cwd.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"timeout_ms".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"The timeout for the command in milliseconds".to_string(),
|
||||
"Maximum command runtime. Defaults to 10000 ms.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"login".to_string(),
|
||||
JsonSchema::boolean(Some(
|
||||
"Whether to run the shell with login shell semantics. Defaults to true."
|
||||
"True runs with login shell semantics; false disables them. Defaults to true."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
|
||||
@@ -20,7 +20,7 @@ pub fn create_test_sync_tool() -> ToolSpec {
|
||||
(
|
||||
"timeout_ms".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Maximum time in milliseconds to wait at the barrier".to_string(),
|
||||
"Maximum barrier wait in milliseconds. Defaults to 1000.".to_string(),
|
||||
)),
|
||||
),
|
||||
]);
|
||||
@@ -29,13 +29,13 @@ pub fn create_test_sync_tool() -> ToolSpec {
|
||||
(
|
||||
"sleep_before_ms".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Optional delay in milliseconds before any other action".to_string(),
|
||||
"Delay before any other action. Defaults to no delay.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"sleep_after_ms".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Optional delay in milliseconds after completing the barrier".to_string(),
|
||||
"Delay after completing the barrier. Defaults to no delay.".to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
|
||||
@@ -35,7 +35,7 @@ fn test_sync_tool_matches_expected_spec() {
|
||||
(
|
||||
"timeout_ms".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Maximum time in milliseconds to wait at the barrier"
|
||||
"Maximum barrier wait in milliseconds. Defaults to 1000."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
@@ -47,14 +47,14 @@ fn test_sync_tool_matches_expected_spec() {
|
||||
(
|
||||
"sleep_after_ms".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Optional delay in milliseconds after completing the barrier"
|
||||
"Delay after completing the barrier. Defaults to no delay."
|
||||
.to_string(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"sleep_before_ms".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Optional delay in milliseconds before any other action".to_string(),
|
||||
"Delay before any other action. Defaults to no delay.".to_string(),
|
||||
)),
|
||||
),
|
||||
]), /*required*/ None, Some(false.into())),
|
||||
|
||||
@@ -16,7 +16,7 @@ pub(crate) fn create_tool_search_tool(
|
||||
(
|
||||
"limit".to_string(),
|
||||
JsonSchema::number(Some(format!(
|
||||
"Maximum number of tools to return (defaults to {default_limit})."
|
||||
"Maximum number of tools to return. Defaults to {default_limit}."
|
||||
))),
|
||||
),
|
||||
]);
|
||||
@@ -98,7 +98,7 @@ mod tests {
|
||||
(
|
||||
"limit".to_string(),
|
||||
JsonSchema::number(Some(
|
||||
"Maximum number of tools to return (defaults to 8)."
|
||||
"Maximum number of tools to return. Defaults to 8."
|
||||
.to_string(),
|
||||
),),
|
||||
),
|
||||
|
||||
@@ -15,7 +15,7 @@ pub struct ViewImageToolOptions {
|
||||
pub fn create_view_image_tool(options: ViewImageToolOptions) -> ToolSpec {
|
||||
let mut properties = BTreeMap::from([(
|
||||
"path".to_string(),
|
||||
JsonSchema::string(Some("Local filesystem path to an image file".to_string())),
|
||||
JsonSchema::string(Some("Local filesystem path to an image file.".to_string())),
|
||||
)]);
|
||||
if options.can_request_original_image_detail {
|
||||
properties.insert(
|
||||
@@ -23,7 +23,7 @@ pub fn create_view_image_tool(options: ViewImageToolOptions) -> ToolSpec {
|
||||
JsonSchema::string_enum(
|
||||
vec![json!("high"), json!("original")],
|
||||
Some(
|
||||
"Optional detail override. Supported values are `high` and `original`; omit this field for default high resized behavior. Use `original` to preserve the file's original resolution instead of resizing to fit. This is important when high-fidelity image perception or precise localization is needed, especially for CUA agents.".to_string(),
|
||||
"Image detail level. Defaults to `high`; use `original` to preserve exact resolution.".to_string(),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -32,7 +32,7 @@ pub fn create_view_image_tool(options: ViewImageToolOptions) -> ToolSpec {
|
||||
properties.insert(
|
||||
"environment_id".to_string(),
|
||||
JsonSchema::string(Some(
|
||||
"Optional selected environment id to target. Omit this to use the primary environment."
|
||||
"Environment id from <environment_context>. Omit to use the primary environment."
|
||||
.to_string(),
|
||||
)),
|
||||
);
|
||||
|
||||
@@ -163,7 +163,7 @@ async fn search_tool_enabled_by_default_adds_tool_search() -> Result<()> {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {"type": "string", "description": "Search query for deferred tools."},
|
||||
"limit": {"type": "number", "description": "Maximum number of tools to return (defaults to 8)."},
|
||||
"limit": {"type": "number", "description": "Maximum number of tools to return. Defaults to 8."},
|
||||
},
|
||||
"required": ["query"],
|
||||
"additionalProperties": false,
|
||||
|
||||
@@ -183,7 +183,7 @@ async fn spawn_agent_description_lists_visible_models_and_reasoning_efforts() ->
|
||||
);
|
||||
assert!(
|
||||
description.contains(
|
||||
"Spawned agents inherit your current model by default. Omit `model` to use that preferred default; set `model` only when an explicit override is needed."
|
||||
"Spawned agents inherit your current model by default. If provided, `model` specifies the model to use for the spawned agent."
|
||||
),
|
||||
"expected inherited-model guidance in spawn_agent description: {description:?}"
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user