Attribute command executions to trusted plugin scripts (#35020)

## What changed

- Resolve shell and unified-exec commands against the trusted plugin roots loaded for each turn.
- Add optional `pluginId` and safe plugin-relative `scriptPath` fields to command execution items and legacy execution events, and propagate them through app-server notifications.
- Include the attribution in command execution analytics while rejecting absolute, unsafe, and unattributed script paths.

## Testing

- Cover attribution for cached curated and remote plugin scripts from command execution through core and app-server events.
- Verify analytics serialization and unsafe-path filtering.

GitOrigin-RevId: 02fac3a233284ccfc6642fa502a95f1881dba83d
This commit is contained in:
Kyle Brown authored and copyberry committed 2026-07-23 22:31:53 +00:00
1 parent 5bdbd3ee90
commit 84fa68b429
52 files changed
+858 -32

No files matched your search

@@ -269,6 +269,12 @@ pub enum ThreadItem {
#[ts(rename_all = "camelCase")]
CommandExecution {
id: String,
/// Trusted first-party plugin id when this command resolves to one plugin script.
#[serde(default)]
plugin_id: Option<String>,
/// Safe plugin-relative path when this command resolves to one plugin script.
#[serde(default)]
script_path: Option<String>,
/// The command to be executed.
command: String,
/// The command's working directory.
@@ -836,6 +842,8 @@ impl From<CoreTurnItem> for ThreadItem {
},
CoreTurnItem::CommandExecution(command) => ThreadItem::CommandExecution {
id: command.id,
plugin_id: command.plugin_id,
script_path: command.script_path,
command: shlex_join(&command.command),
cwd: command.cwd.clone().into(),
process_id: command.process_id,
@@ -2760,6 +2760,8 @@ fn core_turn_item_into_thread_item_converts_supported_variants() {
let command_item = TurnItem::CommandExecution(CommandExecutionItem {
id: "exec-1".to_string(),
plugin_id: Some("sample@openai-curated".to_string()),
script_path: Some("scripts/run.py".to_string()),
process_id: Some("pid-1".to_string()),
command: vec!["echo".to_string(), "done".to_string()],
cwd: PathUri::from_abs_path(&test_path_buf("/tmp").abs()),
@@ -2781,6 +2783,8 @@ fn core_turn_item_into_thread_item_converts_supported_variants() {
ThreadItem::from(command_item),
ThreadItem::CommandExecution {
id: "exec-1".to_string(),
plugin_id: Some("sample@openai-curated".to_string()),
script_path: Some("scripts/run.py".to_string()),
command: "echo done".to_string(),
cwd: LegacyAppPathString::from_abs_path(&test_path_buf("/tmp").abs()),
process_id: Some("pid-1".to_string()),