mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
app-server: include experimental skill metadata in exec approval requests (#13929)
## Summary
This change surfaces skill metadata on command approval requests so
app-server clients can tell when an approval came from a skill script
and identify the originating `SKILL.md`.
- add `skill_metadata` to exec approval events in the shared protocol
- thread skill metadata through core shell escalation and delegated
approval handling for skill-triggered approvals
- expose the field in app-server v2 as experimental `skillMetadata`
- regenerate the JSON/TypeScript schemas and cover the new field in
protocol, transport, core, and TUI tests
## Why
Skill-triggered approvals already carry skill context inside core, but
app-server clients could not see which skill caused the prompt. Sending
the skill metadata with the approval request makes it possible for
clients to present better approval UX and connect the prompt back to the
relevant skill definition.
## example event in app-server-v2
verified that we see this event when experimental api is on:
```
< {
< "id": 11,
< "method": "item/commandExecution/requestApproval",
< "params": {
< "additionalPermissions": {
< "fileSystem": null,
< "macos": {
< "accessibility": false,
< "automations": {
< "bundle_ids": [
< "com.apple.Notes"
< ]
< },
< "calendar": false,
< "preferences": "read_only"
< },
< "network": null
< },
< "approvalId": "25d600ee-5a3c-4746-8d17-e2e61fb4c563",
< "availableDecisions": [
< "accept",
< "acceptForSession",
< "cancel"
< ],
< "command": "/Applications/ChatGPT.app/Contents/Resources/CodexAppServer_CodexAppServerBundledSkills.bundle/Contents/Resources/skills/apple-notes/scripts/notes_info",
< "commandActions": [
< {
< "command": "/Applications/ChatGPT.app/Contents/Resources/CodexAppServer_CodexAppServerBundledSkills.bundle/Contents/Resources/skills/apple-notes/scripts/notes_info",
< "type": "unknown"
< }
< ],
< "cwd": "/Applications/ChatGPT.app/Contents/Resources/CodexAppServer_CodexAppServerBundledSkills.bundle/Contents/Resources/skills/apple-notes",
< "itemId": "call_jZp3xFpNg4D8iKAD49cvEvZy",
< "skillMetadata": {
< "pathToSkillsMd": "/Applications/ChatGPT.app/Contents/Resources/CodexAppServer_CodexAppServerBundledSkills.bundle/Contents/Resources/skills/apple-notes/SKILL.md"
< },
< "threadId": "019ccc10-b7d3-7ff2-84fe-3a75e7681e69",
< "turnId": "019ccc10-b848-76f1-81b3-4a1fa225493f"
< }
< }`
```
& verified that this is the event when experimental api is off:
```
< {
< "id": 13,
< "method": "item/commandExecution/requestApproval",
< "params": {
< "approvalId": "5fbbf776-261b-4cf8-899b-c125b547f2c0",
< "availableDecisions": [
< "accept",
< "acceptForSession",
< "cancel"
< ],
< "command": "/Applications/ChatGPT.app/Contents/Resources/CodexAppServer_CodexAppServerBundledSkills.bundle/Contents/Resources/skills/apple-notes/scripts/notes_info",
< "commandActions": [
< {
< "command": "/Applications/ChatGPT.app/Contents/Resources/CodexAppServer_CodexAppServerBundledSkills.bundle/Contents/Resources/skills/apple-notes/scripts/notes_info",
< "type": "unknown"
< }
< ],
< "cwd": "/Users/celia/code/codex/codex-rs",
< "itemId": "call_OV2DHzTgYcbYtWaTTBWlocOt",
< "threadId": "019ccc16-2a2b-7be1-8500-e00d45b892d4",
< "turnId": "019ccc16-2a8e-7961-98ec-649600e7d06a"
< }
< }
```
This commit is contained in:
committed by
GitHub
Unverified
parent
da3689f0ef
commit
340f9c9ecb
@@ -4221,4 +4221,4 @@
|
||||
}
|
||||
],
|
||||
"title": "ClientRequest"
|
||||
}
|
||||
}
|
||||
@@ -286,6 +286,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"CommandExecutionRequestApprovalSkillMetadata": {
|
||||
"properties": {
|
||||
"pathToSkillsMd": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"pathToSkillsMd"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"MacOsAutomationPermission": {
|
||||
"oneOf": [
|
||||
{
|
||||
|
||||
@@ -1866,6 +1866,17 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"skill_metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ExecApprovalRequestSkillMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional skill metadata when the approval was triggered by a skill script."
|
||||
},
|
||||
"turn_id": {
|
||||
"default": "",
|
||||
"description": "Turn ID that this command belongs to. Uses `#[serde(default)]` for backwards compatibility.",
|
||||
@@ -3442,6 +3453,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ExecApprovalRequestSkillMetadata": {
|
||||
"properties": {
|
||||
"path_to_skills_md": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"path_to_skills_md"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExecCommandSource": {
|
||||
"enum": [
|
||||
"agent",
|
||||
@@ -7678,6 +7700,17 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"skill_metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ExecApprovalRequestSkillMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional skill metadata when the approval was triggered by a skill script."
|
||||
},
|
||||
"turn_id": {
|
||||
"default": "",
|
||||
"description": "Turn ID that this command belongs to. Uses `#[serde(default)]` for backwards compatibility.",
|
||||
|
||||
@@ -440,6 +440,17 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"CommandExecutionRequestApprovalSkillMetadata": {
|
||||
"properties": {
|
||||
"pathToSkillsMd": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"pathToSkillsMd"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"DynamicToolCallParams": {
|
||||
"properties": {
|
||||
"arguments": true,
|
||||
|
||||
@@ -1762,6 +1762,17 @@
|
||||
"title": "CommandExecutionRequestApprovalResponse",
|
||||
"type": "object"
|
||||
},
|
||||
"CommandExecutionRequestApprovalSkillMetadata": {
|
||||
"properties": {
|
||||
"pathToSkillsMd": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"pathToSkillsMd"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"CustomPrompt": {
|
||||
"properties": {
|
||||
"argument_hint": {
|
||||
@@ -3182,6 +3193,17 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"skill_metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ExecApprovalRequestSkillMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional skill metadata when the approval was triggered by a skill script."
|
||||
},
|
||||
"turn_id": {
|
||||
"default": "",
|
||||
"description": "Turn ID that this command belongs to. Uses `#[serde(default)]` for backwards compatibility.",
|
||||
@@ -4759,6 +4781,17 @@
|
||||
],
|
||||
"title": "EventMsg"
|
||||
},
|
||||
"ExecApprovalRequestSkillMetadata": {
|
||||
"properties": {
|
||||
"path_to_skills_md": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"path_to_skills_md"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExecCommandApprovalParams": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
@@ -17075,4 +17108,4 @@
|
||||
},
|
||||
"title": "CodexAppServerProtocol",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
@@ -4999,6 +4999,17 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"skill_metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ExecApprovalRequestSkillMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional skill metadata when the approval was triggered by a skill script."
|
||||
},
|
||||
"turn_id": {
|
||||
"default": "",
|
||||
"description": "Turn ID that this command belongs to. Uses `#[serde(default)]` for backwards compatibility.",
|
||||
@@ -6576,6 +6587,17 @@
|
||||
],
|
||||
"title": "EventMsg"
|
||||
},
|
||||
"ExecApprovalRequestSkillMetadata": {
|
||||
"properties": {
|
||||
"path_to_skills_md": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"path_to_skills_md"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ExecCommandSource": {
|
||||
"enum": [
|
||||
"agent",
|
||||
@@ -15330,4 +15352,4 @@
|
||||
},
|
||||
"title": "CodexAppServerProtocolV2",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
@@ -33,4 +33,4 @@
|
||||
],
|
||||
"title": "WindowsSandboxSetupStartParams",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
@@ -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 { ExecApprovalRequestSkillMetadata } from "./ExecApprovalRequestSkillMetadata";
|
||||
import type { ExecPolicyAmendment } from "./ExecPolicyAmendment";
|
||||
import type { NetworkApprovalContext } from "./NetworkApprovalContext";
|
||||
import type { NetworkPolicyAmendment } from "./NetworkPolicyAmendment";
|
||||
@@ -53,6 +54,10 @@ proposed_network_policy_amendments?: Array<NetworkPolicyAmendment>,
|
||||
* Optional additional filesystem permissions requested for this command.
|
||||
*/
|
||||
additional_permissions?: PermissionProfile,
|
||||
/**
|
||||
* Optional skill metadata when the approval was triggered by a skill script.
|
||||
*/
|
||||
skill_metadata?: ExecApprovalRequestSkillMetadata,
|
||||
/**
|
||||
* Ordered list of decisions the client may present for this prompt.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// 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.
|
||||
|
||||
export type ExecApprovalRequestSkillMetadata = { path_to_skills_md: string, };
|
||||
@@ -53,6 +53,7 @@ export type { ElicitationRequestEvent } from "./ElicitationRequestEvent";
|
||||
export type { ErrorEvent } from "./ErrorEvent";
|
||||
export type { EventMsg } from "./EventMsg";
|
||||
export type { ExecApprovalRequestEvent } from "./ExecApprovalRequestEvent";
|
||||
export type { ExecApprovalRequestSkillMetadata } from "./ExecApprovalRequestSkillMetadata";
|
||||
export type { ExecCommandApprovalParams } from "./ExecCommandApprovalParams";
|
||||
export type { ExecCommandApprovalResponse } from "./ExecCommandApprovalResponse";
|
||||
export type { ExecCommandBeginEvent } from "./ExecCommandBeginEvent";
|
||||
|
||||
+5
@@ -4,6 +4,7 @@
|
||||
import type { AdditionalPermissionProfile } from "./AdditionalPermissionProfile";
|
||||
import type { CommandAction } from "./CommandAction";
|
||||
import type { CommandExecutionApprovalDecision } from "./CommandExecutionApprovalDecision";
|
||||
import type { CommandExecutionRequestApprovalSkillMetadata } from "./CommandExecutionRequestApprovalSkillMetadata";
|
||||
import type { ExecPolicyAmendment } from "./ExecPolicyAmendment";
|
||||
import type { NetworkApprovalContext } from "./NetworkApprovalContext";
|
||||
import type { NetworkPolicyAmendment } from "./NetworkPolicyAmendment";
|
||||
@@ -43,6 +44,10 @@ commandActions?: Array<CommandAction> | null,
|
||||
* Optional additional permissions requested for this command.
|
||||
*/
|
||||
additionalPermissions?: AdditionalPermissionProfile | null,
|
||||
/**
|
||||
* Optional skill metadata when the approval was triggered by a skill script.
|
||||
*/
|
||||
skillMetadata?: CommandExecutionRequestApprovalSkillMetadata | null,
|
||||
/**
|
||||
* Optional proposed execpolicy amendment to allow similar commands without prompting.
|
||||
*/
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// 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.
|
||||
|
||||
export type CommandExecutionRequestApprovalSkillMetadata = { pathToSkillsMd: string, };
|
||||
@@ -53,6 +53,7 @@ export type { CommandExecutionApprovalDecision } from "./CommandExecutionApprova
|
||||
export type { CommandExecutionOutputDeltaNotification } from "./CommandExecutionOutputDeltaNotification";
|
||||
export type { CommandExecutionRequestApprovalParams } from "./CommandExecutionRequestApprovalParams";
|
||||
export type { CommandExecutionRequestApprovalResponse } from "./CommandExecutionRequestApprovalResponse";
|
||||
export type { CommandExecutionRequestApprovalSkillMetadata } from "./CommandExecutionRequestApprovalSkillMetadata";
|
||||
export type { CommandExecutionStatus } from "./CommandExecutionStatus";
|
||||
export type { Config } from "./Config";
|
||||
export type { ConfigBatchWriteParams } from "./ConfigBatchWriteParams";
|
||||
|
||||
@@ -2206,6 +2206,10 @@ mod tests {
|
||||
command_execution_request_approval_ts.contains("additionalPermissions"),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
command_execution_request_approval_ts.contains("skillMetadata"),
|
||||
true
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -2668,6 +2672,10 @@ export type Config = { stableField: Keep, unstableField: string | null } & ({ [k
|
||||
command_execution_request_approval_json.contains("additionalPermissions"),
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
command_execution_request_approval_json.contains("skillMetadata"),
|
||||
false
|
||||
);
|
||||
|
||||
let client_request_json = fs::read_to_string(output_dir.join("ClientRequest.json"))?;
|
||||
assert_eq!(
|
||||
@@ -2679,6 +2687,7 @@ export type Config = { stableField: Keep, unstableField: string | null } & ({ [k
|
||||
fs::read_to_string(output_dir.join("codex_app_server_protocol.schemas.json"))?;
|
||||
assert_eq!(bundle_json.contains("mockExperimentalField"), false);
|
||||
assert_eq!(bundle_json.contains("additionalPermissions"), false);
|
||||
assert_eq!(bundle_json.contains("skillMetadata"), false);
|
||||
assert_eq!(bundle_json.contains("MockExperimentalMethodParams"), false);
|
||||
assert_eq!(
|
||||
bundle_json.contains("MockExperimentalMethodResponse"),
|
||||
@@ -2688,6 +2697,7 @@ export type Config = { stableField: Keep, unstableField: string | null } & ({ [k
|
||||
fs::read_to_string(output_dir.join("codex_app_server_protocol.v2.schemas.json"))?;
|
||||
assert_eq!(flat_v2_bundle_json.contains("mockExperimentalField"), false);
|
||||
assert_eq!(flat_v2_bundle_json.contains("additionalPermissions"), false);
|
||||
assert_eq!(flat_v2_bundle_json.contains("skillMetadata"), false);
|
||||
assert_eq!(
|
||||
flat_v2_bundle_json.contains("MockExperimentalMethodParams"),
|
||||
false
|
||||
|
||||
@@ -1562,6 +1562,7 @@ mod tests {
|
||||
}),
|
||||
macos: None,
|
||||
}),
|
||||
skill_metadata: None,
|
||||
proposed_execpolicy_amendment: None,
|
||||
proposed_network_policy_amendments: None,
|
||||
available_decisions: None,
|
||||
@@ -1572,4 +1573,31 @@ mod tests {
|
||||
Some("item/commandExecution/requestApproval.additionalPermissions")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_execution_request_approval_skill_metadata_is_marked_experimental() {
|
||||
let params = v2::CommandExecutionRequestApprovalParams {
|
||||
thread_id: "thr_123".to_string(),
|
||||
turn_id: "turn_123".to_string(),
|
||||
item_id: "call_123".to_string(),
|
||||
approval_id: None,
|
||||
reason: None,
|
||||
network_approval_context: None,
|
||||
command: Some("cat file".to_string()),
|
||||
cwd: None,
|
||||
command_actions: None,
|
||||
additional_permissions: None,
|
||||
skill_metadata: Some(v2::CommandExecutionRequestApprovalSkillMetadata {
|
||||
path_to_skills_md: PathBuf::from("/tmp/SKILLS.md"),
|
||||
}),
|
||||
proposed_execpolicy_amendment: None,
|
||||
proposed_network_policy_amendments: None,
|
||||
available_decisions: None,
|
||||
};
|
||||
let reason = crate::experimental_api::ExperimentalApi::experimental_reason(¶ms);
|
||||
assert_eq!(
|
||||
reason,
|
||||
Some("item/commandExecution/requestApproval.skillMetadata")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::protocol::common::AuthMode;
|
||||
use codex_experimental_api_macros::ExperimentalApi;
|
||||
use codex_protocol::account::PlanType;
|
||||
use codex_protocol::approvals::ElicitationRequest as CoreElicitationRequest;
|
||||
use codex_protocol::approvals::ExecApprovalRequestSkillMetadata as CoreExecApprovalRequestSkillMetadata;
|
||||
use codex_protocol::approvals::ExecPolicyAmendment as CoreExecPolicyAmendment;
|
||||
use codex_protocol::approvals::NetworkApprovalContext as CoreNetworkApprovalContext;
|
||||
use codex_protocol::approvals::NetworkApprovalProtocol as CoreNetworkApprovalProtocol;
|
||||
@@ -2827,6 +2828,14 @@ impl From<CoreSkillMetadata> for SkillMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CoreExecApprovalRequestSkillMetadata> for CommandExecutionRequestApprovalSkillMetadata {
|
||||
fn from(value: CoreExecApprovalRequestSkillMetadata) -> Self {
|
||||
Self {
|
||||
path_to_skills_md: value.path_to_skills_md,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CoreSkillInterface> for SkillInterface {
|
||||
fn from(value: CoreSkillInterface) -> Self {
|
||||
Self {
|
||||
@@ -4300,6 +4309,11 @@ pub struct CommandExecutionRequestApprovalParams {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional = nullable)]
|
||||
pub additional_permissions: Option<AdditionalPermissionProfile>,
|
||||
/// Optional skill metadata when the approval was triggered by a skill script.
|
||||
#[experimental("item/commandExecution/requestApproval.skillMetadata")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional = nullable)]
|
||||
pub skill_metadata: Option<CommandExecutionRequestApprovalSkillMetadata>,
|
||||
/// Optional proposed execpolicy amendment to allow similar commands without prompting.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional = nullable)]
|
||||
@@ -4321,9 +4335,17 @@ impl CommandExecutionRequestApprovalParams {
|
||||
// We need a generic outbound compatibility design for stripping or
|
||||
// otherwise handling experimental server->client payloads.
|
||||
self.additional_permissions = None;
|
||||
self.skill_metadata = None;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct CommandExecutionRequestApprovalSkillMetadata {
|
||||
pub path_to_skills_md: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
@@ -5098,6 +5120,7 @@ mod tests {
|
||||
},
|
||||
"macos": null
|
||||
},
|
||||
"skillMetadata": null,
|
||||
"proposedExecpolicyAmendment": null,
|
||||
"proposedNetworkPolicyAmendments": null,
|
||||
"availableDecisions": null
|
||||
@@ -5133,6 +5156,7 @@ mod tests {
|
||||
"calendar": false
|
||||
}
|
||||
},
|
||||
"skillMetadata": null,
|
||||
"proposedExecpolicyAmendment": null,
|
||||
"proposedNetworkPolicyAmendments": null,
|
||||
"availableDecisions": null
|
||||
@@ -5150,6 +5174,35 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_execution_request_approval_accepts_skill_metadata() {
|
||||
let params = serde_json::from_value::<CommandExecutionRequestApprovalParams>(json!({
|
||||
"threadId": "thr_123",
|
||||
"turnId": "turn_123",
|
||||
"itemId": "call_123",
|
||||
"command": "cat file",
|
||||
"cwd": "/tmp",
|
||||
"commandActions": null,
|
||||
"reason": null,
|
||||
"networkApprovalContext": null,
|
||||
"additionalPermissions": null,
|
||||
"skillMetadata": {
|
||||
"pathToSkillsMd": "/tmp/SKILLS.md"
|
||||
},
|
||||
"proposedExecpolicyAmendment": null,
|
||||
"proposedNetworkPolicyAmendments": null,
|
||||
"availableDecisions": null
|
||||
}))
|
||||
.expect("skill metadata should deserialize");
|
||||
|
||||
assert_eq!(
|
||||
params.skill_metadata,
|
||||
Some(CommandExecutionRequestApprovalSkillMetadata {
|
||||
path_to_skills_md: PathBuf::from("/tmp/SKILLS.md"),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_exec_params_default_optional_streaming_flags() {
|
||||
let params = serde_json::from_value::<CommandExecParams>(json!({
|
||||
|
||||
@@ -1507,6 +1507,7 @@ impl CodexClient {
|
||||
cwd,
|
||||
command_actions,
|
||||
additional_permissions,
|
||||
skill_metadata,
|
||||
proposed_execpolicy_amendment,
|
||||
proposed_network_policy_amendments,
|
||||
available_decisions,
|
||||
@@ -1541,6 +1542,9 @@ impl CodexClient {
|
||||
if let Some(additional_permissions) = additional_permissions.as_ref() {
|
||||
println!("< additional permissions: {additional_permissions:?}");
|
||||
}
|
||||
if let Some(skill_metadata) = skill_metadata.as_ref() {
|
||||
println!("< skill metadata: {skill_metadata:?}");
|
||||
}
|
||||
if let Some(execpolicy_amendment) = proposed_execpolicy_amendment.as_ref() {
|
||||
println!("< proposed execpolicy amendment: {execpolicy_amendment:?}");
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ use codex_app_server_protocol::CommandExecutionApprovalDecision;
|
||||
use codex_app_server_protocol::CommandExecutionOutputDeltaNotification;
|
||||
use codex_app_server_protocol::CommandExecutionRequestApprovalParams;
|
||||
use codex_app_server_protocol::CommandExecutionRequestApprovalResponse;
|
||||
use codex_app_server_protocol::CommandExecutionRequestApprovalSkillMetadata;
|
||||
use codex_app_server_protocol::CommandExecutionStatus;
|
||||
use codex_app_server_protocol::ContextCompactedNotification;
|
||||
use codex_app_server_protocol::DeprecationNoticeNotification;
|
||||
@@ -437,6 +438,7 @@ pub(crate) async fn apply_bespoke_event_handling(
|
||||
proposed_execpolicy_amendment,
|
||||
proposed_network_policy_amendments,
|
||||
additional_permissions,
|
||||
skill_metadata,
|
||||
parsed_cmd,
|
||||
..
|
||||
} = ev;
|
||||
@@ -508,6 +510,8 @@ pub(crate) async fn apply_bespoke_event_handling(
|
||||
});
|
||||
let additional_permissions =
|
||||
additional_permissions.map(V2AdditionalPermissionProfile::from);
|
||||
let skill_metadata =
|
||||
skill_metadata.map(CommandExecutionRequestApprovalSkillMetadata::from);
|
||||
|
||||
let params = CommandExecutionRequestApprovalParams {
|
||||
thread_id: conversation_id.to_string(),
|
||||
@@ -520,6 +524,7 @@ pub(crate) async fn apply_bespoke_event_handling(
|
||||
cwd,
|
||||
command_actions,
|
||||
additional_permissions,
|
||||
skill_metadata,
|
||||
proposed_execpolicy_amendment: proposed_execpolicy_amendment_v2,
|
||||
proposed_network_policy_amendments: proposed_network_policy_amendments_v2,
|
||||
available_decisions: Some(available_decisions),
|
||||
|
||||
@@ -671,6 +671,7 @@ pub(crate) async fn route_outgoing_envelope(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::error_code::OVERLOADED_ERROR_CODE;
|
||||
use codex_app_server_protocol::CommandExecutionRequestApprovalSkillMetadata;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
@@ -991,6 +992,9 @@ mod tests {
|
||||
macos: None,
|
||||
},
|
||||
),
|
||||
skill_metadata: Some(CommandExecutionRequestApprovalSkillMetadata {
|
||||
path_to_skills_md: PathBuf::from("/tmp/SKILLS.md"),
|
||||
}),
|
||||
proposed_execpolicy_amendment: None,
|
||||
proposed_network_policy_amendments: None,
|
||||
available_decisions: None,
|
||||
@@ -1006,6 +1010,7 @@ mod tests {
|
||||
.expect("request should be delivered to the connection");
|
||||
let json = serde_json::to_value(message).expect("request should serialize");
|
||||
assert_eq!(json["params"].get("additionalPermissions"), None);
|
||||
assert_eq!(json["params"].get("skillMetadata"), None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -1053,6 +1058,9 @@ mod tests {
|
||||
macos: None,
|
||||
},
|
||||
),
|
||||
skill_metadata: Some(CommandExecutionRequestApprovalSkillMetadata {
|
||||
path_to_skills_md: PathBuf::from("/tmp/SKILLS.md"),
|
||||
}),
|
||||
proposed_execpolicy_amendment: None,
|
||||
proposed_network_policy_amendments: None,
|
||||
available_decisions: None,
|
||||
@@ -1079,6 +1087,12 @@ mod tests {
|
||||
"macos": null,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
json["params"]["skillMetadata"],
|
||||
json!({
|
||||
"pathToSkillsMd": "/tmp/SKILLS.md",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -71,6 +71,7 @@ use codex_otel::current_span_w3c_trace_context;
|
||||
use codex_otel::set_parent_from_w3c_trace_context;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::approvals::ElicitationRequestEvent;
|
||||
use codex_protocol::approvals::ExecApprovalRequestSkillMetadata;
|
||||
use codex_protocol::approvals::ExecPolicyAmendment;
|
||||
use codex_protocol::approvals::NetworkPolicyAmendment;
|
||||
use codex_protocol::approvals::NetworkPolicyRuleAction;
|
||||
@@ -2721,6 +2722,7 @@ impl Session {
|
||||
network_approval_context: Option<NetworkApprovalContext>,
|
||||
proposed_execpolicy_amendment: Option<ExecPolicyAmendment>,
|
||||
additional_permissions: Option<PermissionProfile>,
|
||||
skill_metadata: Option<ExecApprovalRequestSkillMetadata>,
|
||||
available_decisions: Option<Vec<ReviewDecision>>,
|
||||
) -> ReviewDecision {
|
||||
// command-level approvals use `call_id`.
|
||||
@@ -2774,6 +2776,7 @@ impl Session {
|
||||
proposed_execpolicy_amendment,
|
||||
proposed_network_policy_amendments,
|
||||
additional_permissions,
|
||||
skill_metadata,
|
||||
available_decisions: Some(available_decisions),
|
||||
parsed_cmd,
|
||||
});
|
||||
|
||||
@@ -332,6 +332,7 @@ async fn handle_exec_approval(
|
||||
network_approval_context,
|
||||
proposed_execpolicy_amendment,
|
||||
additional_permissions,
|
||||
skill_metadata,
|
||||
available_decisions,
|
||||
..
|
||||
} = event;
|
||||
@@ -346,6 +347,7 @@ async fn handle_exec_approval(
|
||||
network_approval_context,
|
||||
proposed_execpolicy_amendment,
|
||||
additional_permissions,
|
||||
skill_metadata,
|
||||
available_decisions,
|
||||
);
|
||||
let decision = await_approval_with_cancel(
|
||||
|
||||
@@ -371,6 +371,7 @@ impl NetworkApprovalService {
|
||||
Some(network_approval_context.clone()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
available_decisions,
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -188,6 +188,7 @@ impl Approvable<ShellRequest> for ShellRuntime {
|
||||
.proposed_execpolicy_amendment()
|
||||
.cloned(),
|
||||
req.additional_permissions.clone(),
|
||||
None,
|
||||
available_decisions,
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -31,6 +31,7 @@ use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::ExecApprovalRequestSkillMetadata;
|
||||
use codex_protocol::protocol::NetworkPolicyRuleAction;
|
||||
use codex_protocol::protocol::ReviewDecision;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
@@ -410,6 +411,14 @@ impl CoreShellActionProvider {
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
let skill_metadata = match decision_source {
|
||||
DecisionSource::SkillScript { skill } => {
|
||||
Some(ExecApprovalRequestSkillMetadata {
|
||||
path_to_skills_md: skill.path_to_skills_md.clone(),
|
||||
})
|
||||
}
|
||||
DecisionSource::PrefixRule | DecisionSource::UnmatchedCommandFallback => None,
|
||||
};
|
||||
session
|
||||
.request_command_approval(
|
||||
&turn,
|
||||
@@ -421,6 +430,7 @@ impl CoreShellActionProvider {
|
||||
None,
|
||||
None,
|
||||
additional_permissions,
|
||||
skill_metadata,
|
||||
Some(available_decisions),
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -154,6 +154,7 @@ impl Approvable<UnifiedExecRequest> for UnifiedExecRuntime<'_> {
|
||||
.proposed_execpolicy_amendment()
|
||||
.cloned(),
|
||||
req.additional_permissions.clone(),
|
||||
None,
|
||||
available_decisions,
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -7,6 +7,7 @@ use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::ExecApprovalRequestEvent;
|
||||
use codex_protocol::protocol::ExecApprovalRequestSkillMetadata;
|
||||
use codex_protocol::protocol::Op;
|
||||
use codex_protocol::protocol::RejectConfig;
|
||||
use codex_protocol::protocol::ReviewDecision;
|
||||
@@ -242,6 +243,14 @@ permissions:
|
||||
..Default::default()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
approval.skill_metadata,
|
||||
Some(ExecApprovalRequestSkillMetadata {
|
||||
path_to_skills_md: test
|
||||
.codex_home_path()
|
||||
.join("skills/mbolin-test-skill/agents/openai.yaml"),
|
||||
})
|
||||
);
|
||||
|
||||
test.codex
|
||||
.submit(Op::ExecApproval {
|
||||
|
||||
@@ -228,6 +228,7 @@ async fn run_codex_tool_session_inner(
|
||||
parsed_cmd,
|
||||
network_approval_context: _,
|
||||
additional_permissions: _,
|
||||
skill_metadata: _,
|
||||
available_decisions: _,
|
||||
} = ev;
|
||||
handle_exec_approval_request(
|
||||
|
||||
@@ -90,6 +90,13 @@ pub struct NetworkPolicyAmendment {
|
||||
pub action: NetworkPolicyRuleAction,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(rename_all = "snake_case")]
|
||||
pub struct ExecApprovalRequestSkillMetadata {
|
||||
pub path_to_skills_md: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, TS)]
|
||||
pub struct ExecApprovalRequestEvent {
|
||||
/// Identifier for the associated command execution item.
|
||||
@@ -128,6 +135,10 @@ pub struct ExecApprovalRequestEvent {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub additional_permissions: Option<PermissionProfile>,
|
||||
/// Optional skill metadata when the approval was triggered by a skill script.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub skill_metadata: Option<ExecApprovalRequestSkillMetadata>,
|
||||
/// Ordered list of decisions the client may present for this prompt.
|
||||
///
|
||||
/// When absent, clients should derive the legacy default set from the
|
||||
|
||||
@@ -56,6 +56,7 @@ use ts_rs::TS;
|
||||
pub use crate::approvals::ApplyPatchApprovalRequestEvent;
|
||||
pub use crate::approvals::ElicitationAction;
|
||||
pub use crate::approvals::ExecApprovalRequestEvent;
|
||||
pub use crate::approvals::ExecApprovalRequestSkillMetadata;
|
||||
pub use crate::approvals::ExecPolicyAmendment;
|
||||
pub use crate::approvals::NetworkApprovalContext;
|
||||
pub use crate::approvals::NetworkApprovalProtocol;
|
||||
|
||||
@@ -3915,6 +3915,7 @@ mod tests {
|
||||
proposed_execpolicy_amendment: None,
|
||||
proposed_network_policy_amendments: None,
|
||||
additional_permissions: None,
|
||||
skill_metadata: None,
|
||||
available_decisions: None,
|
||||
parsed_cmd: Vec::new(),
|
||||
},
|
||||
@@ -5078,6 +5079,7 @@ mod tests {
|
||||
proposed_execpolicy_amendment: None,
|
||||
proposed_network_policy_amendments: None,
|
||||
additional_permissions: None,
|
||||
skill_metadata: None,
|
||||
available_decisions: None,
|
||||
parsed_cmd: Vec::new(),
|
||||
},
|
||||
@@ -5169,6 +5171,7 @@ mod tests {
|
||||
proposed_execpolicy_amendment: None,
|
||||
proposed_network_policy_amendments: None,
|
||||
additional_permissions: None,
|
||||
skill_metadata: None,
|
||||
available_decisions: None,
|
||||
parsed_cmd: Vec::new(),
|
||||
},
|
||||
|
||||
@@ -402,6 +402,7 @@ mod tests {
|
||||
proposed_execpolicy_amendment: None,
|
||||
proposed_network_policy_amendments: None,
|
||||
additional_permissions: None,
|
||||
skill_metadata: None,
|
||||
available_decisions: None,
|
||||
parsed_cmd: Vec::new(),
|
||||
},
|
||||
@@ -545,6 +546,7 @@ mod tests {
|
||||
proposed_execpolicy_amendment: None,
|
||||
proposed_network_policy_amendments: None,
|
||||
additional_permissions: None,
|
||||
skill_metadata: None,
|
||||
available_decisions: None,
|
||||
parsed_cmd: Vec::new(),
|
||||
},
|
||||
@@ -634,6 +636,7 @@ mod tests {
|
||||
proposed_execpolicy_amendment: None,
|
||||
proposed_network_policy_amendments: None,
|
||||
additional_permissions: None,
|
||||
skill_metadata: None,
|
||||
available_decisions: None,
|
||||
parsed_cmd: Vec::new(),
|
||||
},
|
||||
|
||||
@@ -3188,6 +3188,7 @@ async fn exec_approval_emits_proposed_command_and_decision_history() {
|
||||
proposed_execpolicy_amendment: None,
|
||||
proposed_network_policy_amendments: None,
|
||||
additional_permissions: None,
|
||||
skill_metadata: None,
|
||||
available_decisions: None,
|
||||
parsed_cmd: vec![],
|
||||
};
|
||||
@@ -3238,6 +3239,7 @@ async fn exec_approval_uses_approval_id_when_present() {
|
||||
proposed_execpolicy_amendment: None,
|
||||
proposed_network_policy_amendments: None,
|
||||
additional_permissions: None,
|
||||
skill_metadata: None,
|
||||
available_decisions: None,
|
||||
parsed_cmd: vec![],
|
||||
}),
|
||||
@@ -3279,6 +3281,7 @@ async fn exec_approval_decision_truncates_multiline_and_long_commands() {
|
||||
proposed_execpolicy_amendment: None,
|
||||
proposed_network_policy_amendments: None,
|
||||
additional_permissions: None,
|
||||
skill_metadata: None,
|
||||
available_decisions: None,
|
||||
parsed_cmd: vec![],
|
||||
};
|
||||
@@ -3334,6 +3337,7 @@ async fn exec_approval_decision_truncates_multiline_and_long_commands() {
|
||||
proposed_execpolicy_amendment: None,
|
||||
proposed_network_policy_amendments: None,
|
||||
additional_permissions: None,
|
||||
skill_metadata: None,
|
||||
available_decisions: None,
|
||||
parsed_cmd: vec![],
|
||||
};
|
||||
@@ -7965,6 +7969,7 @@ async fn approval_modal_exec_snapshot() -> anyhow::Result<()> {
|
||||
])),
|
||||
proposed_network_policy_amendments: None,
|
||||
additional_permissions: None,
|
||||
skill_metadata: None,
|
||||
available_decisions: None,
|
||||
parsed_cmd: vec![],
|
||||
};
|
||||
@@ -8026,6 +8031,7 @@ async fn approval_modal_exec_without_reason_snapshot() -> anyhow::Result<()> {
|
||||
])),
|
||||
proposed_network_policy_amendments: None,
|
||||
additional_permissions: None,
|
||||
skill_metadata: None,
|
||||
available_decisions: None,
|
||||
parsed_cmd: vec![],
|
||||
};
|
||||
@@ -8074,6 +8080,7 @@ async fn approval_modal_exec_multiline_prefix_hides_execpolicy_option_snapshot()
|
||||
proposed_execpolicy_amendment: Some(ExecPolicyAmendment::new(command)),
|
||||
proposed_network_policy_amendments: None,
|
||||
additional_permissions: None,
|
||||
skill_metadata: None,
|
||||
available_decisions: None,
|
||||
parsed_cmd: vec![],
|
||||
};
|
||||
@@ -8441,6 +8448,7 @@ async fn status_widget_and_approval_modal_snapshot() {
|
||||
])),
|
||||
proposed_network_policy_amendments: None,
|
||||
additional_permissions: None,
|
||||
skill_metadata: None,
|
||||
available_decisions: None,
|
||||
parsed_cmd: vec![],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user