Skip to main content

objectiveai_sdk/cli/command/
agent_arguments.rs

1/// Agent identity + response routing args carried per call.
2///
3/// When passed as `Some(&AgentArguments)` to a [`CommandExecutor`],
4/// subprocess-spawning executors (e.g. [`binary::BinaryExecutor`])
5/// apply ALL nine fields to the spawned child's env atomically —
6/// `Some(v)` → set, `None` → `env_remove` so the parent's value for
7/// that var can't leak through. `None` for the whole bag means
8/// "inherit parent env unmodified". In-process executors (e.g.
9/// [`plugin::PluginExecutor`]) ignore the bag.
10///
11/// Field ↔ env-var mapping (same as `EnvConfigBuilder` in
12/// `objectiveai-cli/src/run.rs`):
13///
14/// - `agent_instance_hierarchy` ↔ `OBJECTIVEAI_AGENT_INSTANCE_HIERARCHY`
15/// - `agent_id` ↔ `OBJECTIVEAI_AGENT_ID`
16/// - `agent_full_id` ↔ `OBJECTIVEAI_AGENT_FULL_ID`
17/// - `agent_remote` ↔ `OBJECTIVEAI_AGENT_REMOTE`
18/// - `response_id` ↔ `OBJECTIVEAI_RESPONSE_ID`
19/// - `response_ids` ↔ `OBJECTIVEAI_RESPONSE_IDS`
20/// - `plugin_owner` ↔ `OBJECTIVEAI_PLUGIN_OWNER`
21/// - `plugin_repository` ↔ `OBJECTIVEAI_PLUGIN_REPOSITORY`
22/// - `plugin_version` ↔ `OBJECTIVEAI_PLUGIN_VERSION`
23///
24/// The three `plugin_*` fields are the PLUGIN CALLER identity —
25/// which installed plugin originated this request. UNSPOOFABLE by
26/// design: the daemon's own `plugins run` is the only writer (it
27/// stamps the nested command scope in-process and the plugin child's
28/// env informationally); wire requests and the CLI environment can
29/// NEVER assert them — the daemon ignores any inbound claim. They
30/// appear only in daemon-AUTHORED payloads (e.g. user requests).
31#[derive(
32    Debug,
33    Clone,
34    Default,
35    PartialEq,
36    Eq,
37    serde::Serialize,
38    serde::Deserialize,
39    schemars::JsonSchema,
40)]
41#[schemars(rename = "cli.command.AgentArguments")]
42pub struct AgentArguments {
43    #[serde(default, skip_serializing_if = "Option::is_none")]
44    #[schemars(extend("omitempty" = true))]
45    pub agent_instance_hierarchy: Option<String>,
46    #[serde(default, skip_serializing_if = "Option::is_none")]
47    #[schemars(extend("omitempty" = true))]
48    pub agent_id: Option<String>,
49    #[serde(default, skip_serializing_if = "Option::is_none")]
50    #[schemars(extend("omitempty" = true))]
51    pub agent_full_id: Option<String>,
52    #[serde(default, skip_serializing_if = "Option::is_none")]
53    #[schemars(extend("omitempty" = true))]
54    pub agent_remote: Option<String>,
55    #[serde(default, skip_serializing_if = "Option::is_none")]
56    #[schemars(extend("omitempty" = true))]
57    pub response_id: Option<String>,
58    #[serde(default, skip_serializing_if = "Option::is_none")]
59    #[schemars(extend("omitempty" = true))]
60    pub response_ids: Option<String>,
61    #[serde(default, skip_serializing_if = "Option::is_none")]
62    #[schemars(extend("omitempty" = true))]
63    pub plugin_owner: Option<String>,
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    #[schemars(extend("omitempty" = true))]
66    pub plugin_repository: Option<String>,
67    #[serde(default, skip_serializing_if = "Option::is_none")]
68    #[schemars(extend("omitempty" = true))]
69    pub plugin_version: Option<String>,
70}
71
72impl AgentArguments {
73    /// Apply this bag to a child-process command: every `Some(v)`
74    /// stamps the matching env var, every `None` env-removes it so
75    /// the parent's value can't leak through. Called by
76    /// [`binary::BinaryExecutor`]; available for any executor that
77    /// spawns a subprocess.
78    #[cfg(feature = "cli-executor")]
79    pub fn apply_to_command(&self, command: &mut tokio::process::Command) {
80        let pairs: [(&str, &Option<String>); 9] = [
81            (
82                "OBJECTIVEAI_AGENT_INSTANCE_HIERARCHY",
83                &self.agent_instance_hierarchy,
84            ),
85            ("OBJECTIVEAI_AGENT_ID", &self.agent_id),
86            ("OBJECTIVEAI_AGENT_FULL_ID", &self.agent_full_id),
87            ("OBJECTIVEAI_AGENT_REMOTE", &self.agent_remote),
88            ("OBJECTIVEAI_RESPONSE_ID", &self.response_id),
89            ("OBJECTIVEAI_RESPONSE_IDS", &self.response_ids),
90            ("OBJECTIVEAI_PLUGIN_OWNER", &self.plugin_owner),
91            ("OBJECTIVEAI_PLUGIN_REPOSITORY", &self.plugin_repository),
92            ("OBJECTIVEAI_PLUGIN_VERSION", &self.plugin_version),
93        ];
94        for (name, value) in pairs {
95            match value {
96                Some(v) => {
97                    command.env(name, v);
98                }
99                None => {
100                    command.env_remove(name);
101                }
102            }
103        }
104    }
105}