mirror of
https://github.com/openai/codex.git
synced 2026-05-15 00:32:51 +00:00
Adds the model-facing goal tools on top of the app-server API from PR 2. ## Why Once goals are persisted and exposed to clients, the model needs a small, constrained tool surface for goal workflows. The tool contract should let the model inspect goals, create them only when explicitly requested, and mark them complete without giving it broad control over user/runtime-owned state. ## What changed - Added `get_goal`, `create_goal`, and `update_goal` tool specs behind the `goals` feature flag. - Added core goal tool handlers that validate objectives and token budgets before mutating persisted state. - Constrained `create_goal` to create only when no goal exists, with optional `token_budget` only when a budget is explicitly provided. - Tightened the `create_goal` instructions so the model does not infer goals from ordinary task requests. - Constrained `update_goal` to expose only goal completion; pause, resume, clear, and budget-limited transitions remain user- or runtime-controlled. - Registered the goal tools in the tool registry and kept them out of review contexts where they should not appear. ## Verification - Added tool-registry coverage for feature gating and tool availability. - Added core session tests for create/get/update behavior, duplicate goal rejection, budget validation, and completion-only updates.
113 lines
4.0 KiB
Rust
113 lines
4.0 KiB
Rust
//! Responses API tool definitions for persisted thread goals.
|
|
//!
|
|
//! These specs expose goal read/update primitives to the model while keeping
|
|
//! usage accounting system-managed.
|
|
|
|
use crate::JsonSchema;
|
|
use crate::ResponsesApiTool;
|
|
use crate::ToolSpec;
|
|
use serde_json::json;
|
|
use std::collections::BTreeMap;
|
|
|
|
pub const GET_GOAL_TOOL_NAME: &str = "get_goal";
|
|
pub const CREATE_GOAL_TOOL_NAME: &str = "create_goal";
|
|
pub const UPDATE_GOAL_TOOL_NAME: &str = "update_goal";
|
|
|
|
pub fn create_get_goal_tool() -> ToolSpec {
|
|
ToolSpec::Function(ResponsesApiTool {
|
|
name: GET_GOAL_TOOL_NAME.to_string(),
|
|
description: "Get the current goal for this thread, including status, budgets, token and elapsed-time usage, and remaining token budget."
|
|
.to_string(),
|
|
strict: false,
|
|
defer_loading: None,
|
|
parameters: JsonSchema::object(BTreeMap::new(), Some(Vec::new()), Some(false.into())),
|
|
output_schema: None,
|
|
})
|
|
}
|
|
|
|
pub fn create_create_goal_tool() -> ToolSpec {
|
|
let properties = BTreeMap::from([
|
|
(
|
|
"objective".to_string(),
|
|
JsonSchema::string(Some(
|
|
"Required. The concrete objective to start pursuing. This starts a new active goal only when no goal is currently defined; if a goal already exists, this tool fails."
|
|
.to_string(),
|
|
)),
|
|
),
|
|
(
|
|
"token_budget".to_string(),
|
|
JsonSchema::integer(Some(
|
|
"Optional positive token budget for the new active goal.".to_string(),
|
|
)),
|
|
),
|
|
]);
|
|
|
|
ToolSpec::Function(ResponsesApiTool {
|
|
name: CREATE_GOAL_TOOL_NAME.to_string(),
|
|
description: format!(
|
|
r#"Create a goal only when explicitly requested by the user or system/developer instructions; do not infer goals from ordinary tasks.
|
|
Set token_budget only when an explicit token budget is requested. Fails if a goal exists; use {UPDATE_GOAL_TOOL_NAME} only for status."#
|
|
),
|
|
strict: false,
|
|
defer_loading: None,
|
|
parameters: JsonSchema::object(
|
|
properties,
|
|
/*required*/ Some(vec!["objective".to_string()]),
|
|
Some(false.into()),
|
|
),
|
|
output_schema: None,
|
|
})
|
|
}
|
|
|
|
pub fn create_update_goal_tool() -> ToolSpec {
|
|
let properties = BTreeMap::from([(
|
|
"status".to_string(),
|
|
JsonSchema::string_enum(
|
|
vec![json!("complete")],
|
|
Some(
|
|
"Required. Set to complete only when the objective is achieved and no required work remains."
|
|
.to_string(),
|
|
),
|
|
),
|
|
)]);
|
|
|
|
ToolSpec::Function(ResponsesApiTool {
|
|
name: UPDATE_GOAL_TOOL_NAME.to_string(),
|
|
description: r#"Update the existing goal.
|
|
Use this tool only to mark the goal achieved.
|
|
Set status to `complete` only when the objective has actually been achieved and no required work remains.
|
|
Do not mark a goal complete merely because its budget is nearly exhausted or because you are stopping work.
|
|
You cannot use this tool to pause, resume, or budget-limit a goal; those status changes are controlled by the user or system.
|
|
When marking a budgeted goal achieved with status `complete`, report the final token usage from the tool result to the user."#
|
|
.to_string(),
|
|
strict: false,
|
|
defer_loading: None,
|
|
parameters: JsonSchema::object(
|
|
properties,
|
|
/*required*/ Some(vec!["status".to_string()]),
|
|
Some(false.into()),
|
|
),
|
|
output_schema: None,
|
|
})
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn update_goal_tool_only_exposes_complete_status() {
|
|
let ToolSpec::Function(tool) = create_update_goal_tool() else {
|
|
panic!("update_goal should be a function tool");
|
|
};
|
|
let status = tool
|
|
.parameters
|
|
.properties
|
|
.as_ref()
|
|
.and_then(|properties| properties.get("status"))
|
|
.expect("status property should exist");
|
|
|
|
assert_eq!(status.enum_values, Some(vec![json!("complete")]));
|
|
}
|
|
}
|