@team-agent/installer 0.5.112 → 0.5.113
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/src/cli/adapters.rs +100 -44
- package/crates/team-agent/src/cli/emit.rs +127 -98
- package/crates/team-agent/src/cli/leaders.rs +32 -32
- package/crates/team-agent/src/cli/mod.rs +6 -6
- package/crates/team-agent/src/cli/named_address.rs +17 -16
- package/crates/team-agent/src/cli/route.rs +4 -4
- package/crates/team-agent/src/cli/send/presentation.rs +19 -18
- package/crates/team-agent/src/cli/send/resolve.rs +11 -11
- package/crates/team-agent/src/cli/send.rs +4 -4
- package/crates/team-agent/src/cli/spec.rs +29 -29
- package/crates/team-agent/src/cli/status.rs +7 -7
- package/crates/team-agent/src/cli/tests/base.rs +6 -6
- package/crates/team-agent/src/cli/tests/divergence.rs +3 -2
- package/crates/team-agent/src/cli/tests/lane_c.rs +1 -1
- package/crates/team-agent/src/cli/tests/named_address.rs +3 -3
- package/crates/team-agent/src/cli/tests/status_send.rs +7 -7
- package/crates/team-agent/src/cli/triage.rs +53 -2
- package/crates/team-agent/src/cli/types.rs +8 -8
- package/crates/team-agent/src/compiler/tests.rs +12 -0
- package/crates/team-agent/src/compiler.rs +0 -9
- package/crates/team-agent/src/leader/start.rs +5 -0
- package/crates/team-agent/src/leader/tests/identity.rs +21 -0
- package/crates/team-agent/src/lifecycle/launch/identity.rs +172 -13
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +325 -14
- package/crates/team-agent/src/lifecycle/launch.rs +2 -2
- package/crates/team-agent/src/lifecycle/profile_launch.rs +58 -46
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +97 -52
- package/crates/team-agent/src/lifecycle/restart.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +3 -2
- package/crates/team-agent/src/provider/adapter.rs +8 -59
- package/crates/team-agent/src/provider/adapters/claude.rs +6 -14
- package/crates/team-agent/src/provider/adapters/codex.rs +8 -18
- package/crates/team-agent/src/provider/adapters/copilot.rs +5 -22
- package/crates/team-agent/src/provider/adapters/cursor_agent.rs +8 -45
- package/crates/team-agent/src/provider/adapters/grok.rs +7 -31
- package/crates/team-agent/src/provider/adapters/pi.rs +7 -18
- package/crates/team-agent/src/provider/command_helpers.rs +45 -0
- package/crates/team-agent/src/provider/mod.rs +1 -0
- package/crates/team-agent/src/provider/model_catalog.rs +21 -28
- package/crates/team-agent/src/provider/tests/adapter.rs +70 -0
- package/crates/team-agent/src/provider/tests/command_helpers.rs +55 -0
- package/package.json +4 -4
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//! contract:
|
|
4
4
|
//! provides:
|
|
5
5
|
//! - name: cursor_agent_base_command
|
|
6
|
-
//! what: 只组已实测文档化 flag;
|
|
6
|
+
//! what: 只组已实测文档化 flag;MCP 由 launch overlay 写盘
|
|
7
7
|
//! boundary:
|
|
8
8
|
//! - 不调未文档化 flag(--system-prompt / --allowed-tools)
|
|
9
9
|
//! - 不写 grok 那种 cwd 独占闸
|
|
@@ -22,60 +22,23 @@
|
|
|
22
22
|
//! 随时可能消失。可以在注释里记录,代码里不许调。
|
|
23
23
|
//! `--allowed-tools` 正反行为未验证,不写「支持工具白名单」。
|
|
24
24
|
|
|
25
|
-
use crate::
|
|
26
|
-
use crate::provider::
|
|
27
|
-
use crate::provider::{McpConfig, ProviderError};
|
|
28
|
-
|
|
29
|
-
pub(crate) fn cursor_agent_launch_command(
|
|
30
|
-
adapter: &BasicProviderAdapter,
|
|
31
|
-
auth_mode: AuthMode,
|
|
32
|
-
mcp_config: Option<&McpConfig>,
|
|
33
|
-
system_prompt: Option<&str>,
|
|
34
|
-
model: Option<&str>,
|
|
35
|
-
dangerously_skip_permissions: bool,
|
|
36
|
-
) -> Result<Vec<String>, ProviderError> {
|
|
37
|
-
cursor_agent_base_command(
|
|
38
|
-
adapter,
|
|
39
|
-
auth_mode,
|
|
40
|
-
mcp_config,
|
|
41
|
-
system_prompt,
|
|
42
|
-
model,
|
|
43
|
-
dangerously_skip_permissions,
|
|
44
|
-
false,
|
|
45
|
-
None,
|
|
46
|
-
)
|
|
47
|
-
}
|
|
25
|
+
use crate::provider::ProviderError;
|
|
26
|
+
use crate::provider::command_helpers::{append_opt_pair, append_pair};
|
|
48
27
|
|
|
49
28
|
pub(crate) fn cursor_agent_base_command(
|
|
50
|
-
adapter: &BasicProviderAdapter,
|
|
51
|
-
auth_mode: AuthMode,
|
|
52
|
-
mcp_config: Option<&McpConfig>,
|
|
53
|
-
system_prompt: Option<&str>,
|
|
54
29
|
model: Option<&str>,
|
|
55
30
|
dangerously_skip_permissions: bool,
|
|
56
|
-
managed_mcp_config: bool,
|
|
57
|
-
effort: Option<crate::model::enums::ProviderEffort>,
|
|
58
31
|
) -> Result<Vec<String>, ProviderError> {
|
|
59
32
|
let mut argv = vec!["agent".to_string()];
|
|
60
33
|
if dangerously_skip_permissions {
|
|
61
34
|
// Explicit bypass also opts into Cursor's trusted, unsandboxed launch.
|
|
62
35
|
argv.push("--trust".to_string());
|
|
63
|
-
argv
|
|
64
|
-
argv.push("disabled".to_string());
|
|
36
|
+
append_pair(&mut argv, "--sandbox", "disabled");
|
|
65
37
|
argv.push("--force".to_string());
|
|
66
38
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
// Cursor `--effort` flag 不存在;框架在调用方丢掉 effort。绝不发明 flag。
|
|
72
|
-
let _ = effort;
|
|
73
|
-
// system_prompt 不入 argv(help 无 --rules / --append-system-prompt)。
|
|
74
|
-
// launch 写 `<workspace>/.cursor/rules/team-agent-role-<agent_id>.mdc`。
|
|
75
|
-
// mcp_config 也不入 argv(无 --mcp-config)。launch 写 `.cursor/mcp.json`
|
|
76
|
-
// 并 `agent mcp enable team_orchestrator`。这里收下以免静默丢弃。
|
|
77
|
-
let _ = (adapter, auth_mode, mcp_config, managed_mcp_config, system_prompt);
|
|
78
|
-
argv.push("--workspace".to_string());
|
|
79
|
-
argv.push("{workspace}".to_string());
|
|
39
|
+
append_opt_pair(&mut argv, "--model", model);
|
|
40
|
+
// Cursor has no `--effort`, system-prompt, or MCP config flag.
|
|
41
|
+
// Launch materializes role rules and the `.cursor/mcp.json` overlay.
|
|
42
|
+
append_pair(&mut argv, "--workspace", "{workspace}");
|
|
80
43
|
Ok(argv)
|
|
81
44
|
}
|
|
@@ -30,62 +30,38 @@
|
|
|
30
30
|
//! → `native_mcp_config: false`; MCP reaches the worker via launch-path
|
|
31
31
|
//! `<cwd>/.grok/config.toml` (`apply_grok_mcp_overlay`).
|
|
32
32
|
|
|
33
|
-
use crate::
|
|
34
|
-
use crate::provider::
|
|
35
|
-
use crate::provider::{
|
|
33
|
+
use crate::provider::adapter::next_session_token;
|
|
34
|
+
use crate::provider::ProviderError;
|
|
35
|
+
use crate::provider::command_helpers::{append_opt_pair, append_pair};
|
|
36
36
|
|
|
37
37
|
pub(crate) fn grok_launch_command(
|
|
38
|
-
adapter: &BasicProviderAdapter,
|
|
39
|
-
auth_mode: AuthMode,
|
|
40
|
-
mcp_config: Option<&McpConfig>,
|
|
41
38
|
system_prompt: Option<&str>,
|
|
42
39
|
model: Option<&str>,
|
|
43
40
|
dangerously_skip_permissions: bool,
|
|
44
41
|
) -> Result<Vec<String>, ProviderError> {
|
|
45
|
-
let mut argv = grok_base_command(
|
|
46
|
-
|
|
47
|
-
auth_mode,
|
|
48
|
-
mcp_config,
|
|
49
|
-
system_prompt,
|
|
50
|
-
model,
|
|
51
|
-
dangerously_skip_permissions,
|
|
52
|
-
false,
|
|
53
|
-
None,
|
|
54
|
-
)?;
|
|
55
|
-
argv.push("--session-id".to_string());
|
|
56
|
-
argv.push(next_session_token());
|
|
42
|
+
let mut argv = grok_base_command(system_prompt, model, dangerously_skip_permissions, None)?;
|
|
43
|
+
append_pair(&mut argv, "--session-id", next_session_token());
|
|
57
44
|
Ok(argv)
|
|
58
45
|
}
|
|
59
46
|
|
|
60
47
|
pub(crate) fn grok_base_command(
|
|
61
|
-
adapter: &BasicProviderAdapter,
|
|
62
|
-
auth_mode: AuthMode,
|
|
63
|
-
mcp_config: Option<&McpConfig>,
|
|
64
48
|
system_prompt: Option<&str>,
|
|
65
49
|
model: Option<&str>,
|
|
66
50
|
dangerously_skip_permissions: bool,
|
|
67
|
-
managed_mcp_config: bool,
|
|
68
51
|
effort: Option<crate::model::enums::ProviderEffort>,
|
|
69
52
|
) -> Result<Vec<String>, ProviderError> {
|
|
70
53
|
let mut argv = vec!["grok".to_string()];
|
|
71
54
|
if dangerously_skip_permissions {
|
|
72
55
|
argv.push("--always-approve".to_string());
|
|
73
56
|
}
|
|
74
|
-
|
|
75
|
-
argv.push("--model".to_string());
|
|
76
|
-
argv.push(model.to_string());
|
|
77
|
-
}
|
|
57
|
+
append_opt_pair(&mut argv, "--model", model.map(str::trim).filter(|value| !value.is_empty()));
|
|
78
58
|
if let Some(effort) = effort {
|
|
79
59
|
// Grok accepts `--effort` as alias for `--reasoning-effort`.
|
|
80
60
|
argv.push("--effort".to_string());
|
|
81
61
|
argv.push(effort.as_str().to_string());
|
|
82
62
|
}
|
|
83
|
-
|
|
84
|
-
argv.push("--rules".to_string());
|
|
85
|
-
argv.push(prompt.to_string());
|
|
86
|
-
}
|
|
63
|
+
append_opt_pair(&mut argv, "--rules", system_prompt);
|
|
87
64
|
// Grok CLI has no `--mcp-config` flag — the claude inline-MCP block is
|
|
88
65
|
// intentionally absent. Launch writes `<cwd>/.grok/config.toml`.
|
|
89
|
-
let _ = (adapter, auth_mode, mcp_config, managed_mcp_config);
|
|
90
66
|
Ok(argv)
|
|
91
67
|
}
|
|
@@ -14,6 +14,7 @@ use std::path::Path;
|
|
|
14
14
|
|
|
15
15
|
use crate::model::enums::ProviderEffort;
|
|
16
16
|
use crate::provider::ProviderError;
|
|
17
|
+
use crate::provider::command_helpers::{append_opt_pair, append_pair};
|
|
17
18
|
|
|
18
19
|
#[derive(Debug, Clone, Copy)]
|
|
19
20
|
pub(crate) enum PiSessionSelector<'a> {
|
|
@@ -60,38 +61,27 @@ pub(crate) fn build_pi_command_argv(
|
|
|
60
61
|
"-e".to_string(),
|
|
61
62
|
request.extension.to_string_lossy().into_owned(),
|
|
62
63
|
];
|
|
63
|
-
|
|
64
|
-
argv.push("--model".to_string());
|
|
65
|
-
argv.push(model.to_string());
|
|
66
|
-
}
|
|
64
|
+
append_opt_pair(&mut argv, "--model", request.model);
|
|
67
65
|
if let Some(effort) = request.effort {
|
|
68
66
|
argv.push("--thinking".to_string());
|
|
69
67
|
argv.push(effort.as_str().to_string());
|
|
70
68
|
}
|
|
71
|
-
argv.
|
|
72
|
-
"--append-system-prompt".to_string(),
|
|
73
|
-
request.system_prompt.to_string(),
|
|
74
|
-
]);
|
|
69
|
+
append_pair(&mut argv, "--append-system-prompt", request.system_prompt);
|
|
75
70
|
if let Some(session_dir) = request.session_dir {
|
|
76
|
-
argv.
|
|
77
|
-
"--session-dir".to_string(),
|
|
78
|
-
session_dir.to_string_lossy().into_owned(),
|
|
79
|
-
]);
|
|
71
|
+
append_pair(&mut argv, "--session-dir", session_dir.to_string_lossy().into_owned());
|
|
80
72
|
}
|
|
81
73
|
// The shared lifecycle materializer always revalidates resume root/path/header.
|
|
82
74
|
// Keep the pure argv builder usable before a fixture root is materialized, while
|
|
83
75
|
// refusing a missing exact file once the recorded path's parent exists.
|
|
84
76
|
match request.session {
|
|
85
77
|
PiSessionSelector::Fresh { session_id } if !session_id.trim().is_empty() => {
|
|
86
|
-
argv
|
|
87
|
-
argv.push(session_id.to_string());
|
|
78
|
+
append_pair(&mut argv, "--session-id", session_id);
|
|
88
79
|
}
|
|
89
80
|
PiSessionSelector::Resume { path }
|
|
90
81
|
if !path.as_os_str().is_empty()
|
|
91
82
|
&& (!path.parent().is_some_and(Path::is_dir) || path.is_file()) =>
|
|
92
83
|
{
|
|
93
|
-
argv
|
|
94
|
-
argv.push(path.to_string_lossy().into_owned());
|
|
84
|
+
append_pair(&mut argv, "--session", path.to_string_lossy().into_owned());
|
|
95
85
|
}
|
|
96
86
|
PiSessionSelector::Resume { path } if !path.as_os_str().is_empty() => {
|
|
97
87
|
return Err(ProviderError::ResumeUnavailable(format!(
|
|
@@ -105,7 +95,6 @@ pub(crate) fn build_pi_command_argv(
|
|
|
105
95
|
));
|
|
106
96
|
}
|
|
107
97
|
}
|
|
108
|
-
argv
|
|
109
|
-
argv.push(request.agent_id.to_string());
|
|
98
|
+
append_pair(&mut argv, "--name", request.agent_id);
|
|
110
99
|
Ok(argv)
|
|
111
100
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//! Pure append operations. Callers own ordering, filtering, escaping and env-unset policy.
|
|
2
|
+
use std::collections::BTreeMap;
|
|
3
|
+
|
|
4
|
+
pub(crate) fn append_pair(argv: &mut Vec<String>, flag: &str, value: impl Into<String>) {
|
|
5
|
+
argv.push(flag.to_string());
|
|
6
|
+
argv.push(value.into());
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
pub(crate) fn append_opt_pair(argv: &mut Vec<String>, flag: &str, value: Option<&str>) {
|
|
10
|
+
if let Some(value) = value {
|
|
11
|
+
append_pair(argv, flag, value);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
pub(crate) fn append_env_pair(env: &mut BTreeMap<String, String>, key: &str, value: Option<&str>) {
|
|
16
|
+
if let Some(value) = value {
|
|
17
|
+
env.insert(key.to_string(), value.to_string());
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
#[cfg(test)]
|
|
22
|
+
mod tests {
|
|
23
|
+
use super::*;
|
|
24
|
+
|
|
25
|
+
#[test]
|
|
26
|
+
fn append_preserves_order_and_raw_values() {
|
|
27
|
+
let mut argv = vec!["cli".to_string()];
|
|
28
|
+
append_opt_pair(&mut argv, "--absent", None);
|
|
29
|
+
append_opt_pair(&mut argv, "--empty", Some(""));
|
|
30
|
+
append_pair(&mut argv, "--raw", " \"'\\\n ".to_string());
|
|
31
|
+
append_pair(&mut argv, "--repeat", "first");
|
|
32
|
+
append_pair(&mut argv, "--repeat", "last");
|
|
33
|
+
assert_eq!(argv, ["cli", "--empty", "", "--raw", " \"'\\\n ", "--repeat", "first", "--repeat", "last"]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#[test]
|
|
37
|
+
fn env_append_preserves_absence_empty_values_and_last_write() {
|
|
38
|
+
let mut env = BTreeMap::from([("Z".to_string(), "old".to_string())]);
|
|
39
|
+
append_env_pair(&mut env, "Z", None);
|
|
40
|
+
assert_eq!(env["Z"], "old");
|
|
41
|
+
append_env_pair(&mut env, "A", Some(""));
|
|
42
|
+
append_env_pair(&mut env, "Z", Some(" \"'\\\n "));
|
|
43
|
+
assert_eq!(env.into_iter().collect::<Vec<_>>(), vec![("A".to_string(), "".to_string()), ("Z".to_string(), " \"'\\\n ".to_string())]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -9,6 +9,7 @@ use std::sync::mpsc;
|
|
|
9
9
|
use std::time::{Duration, Instant};
|
|
10
10
|
|
|
11
11
|
use crate::model::enums::Provider;
|
|
12
|
+
use crate::provider::wire::{parse_canonical_provider, provider_wire};
|
|
12
13
|
|
|
13
14
|
const CATALOG_TIMEOUT: Duration = Duration::from_secs(10);
|
|
14
15
|
const MAX_CATALOG_BYTES: u64 = 1024 * 1024;
|
|
@@ -126,29 +127,7 @@ fn display_provider(provider: &str) -> &str {
|
|
|
126
127
|
|
|
127
128
|
/// Only the documented catalog names are accepted; launch aliases stay unchanged.
|
|
128
129
|
pub fn parse_catalog_provider(name: &str) -> Option<Provider> {
|
|
129
|
-
|
|
130
|
-
"pi" => Some(Provider::Pi),
|
|
131
|
-
"cursor_agent" => Some(Provider::CursorAgent),
|
|
132
|
-
"codex" => Some(Provider::Codex),
|
|
133
|
-
"claude" => Some(Provider::Claude),
|
|
134
|
-
"claude_code" => Some(Provider::ClaudeCode),
|
|
135
|
-
"grok" => Some(Provider::Grok),
|
|
136
|
-
_ => None,
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
fn provider_wire_name(provider: Provider) -> &'static str {
|
|
141
|
-
match provider {
|
|
142
|
-
Provider::Pi => "pi",
|
|
143
|
-
Provider::CursorAgent => "cursor_agent",
|
|
144
|
-
Provider::Codex => "codex",
|
|
145
|
-
Provider::Claude => "claude",
|
|
146
|
-
Provider::ClaudeCode => "claude_code",
|
|
147
|
-
Provider::Copilot => "copilot",
|
|
148
|
-
Provider::GeminiCli => "gemini_cli",
|
|
149
|
-
Provider::Grok => "grok",
|
|
150
|
-
Provider::Fake => "fake",
|
|
151
|
-
}
|
|
130
|
+
parse_canonical_provider(name).filter(|provider| catalog_source(*provider).is_some())
|
|
152
131
|
}
|
|
153
132
|
|
|
154
133
|
#[derive(Clone, Copy)]
|
|
@@ -175,7 +154,7 @@ fn catalog_source(provider: Provider) -> Option<CatalogSource> {
|
|
|
175
154
|
pub fn discover_model_catalog(provider_name: &str) -> Result<Vec<ModelRecord>, CatalogError> {
|
|
176
155
|
let provider = parse_catalog_provider(provider_name).ok_or(CatalogError::UnsupportedProvider)?;
|
|
177
156
|
let source = catalog_source(provider).ok_or(CatalogError::UnsupportedProvider)?;
|
|
178
|
-
let requested_provider =
|
|
157
|
+
let requested_provider = provider_wire(provider);
|
|
179
158
|
let records = match source {
|
|
180
159
|
CatalogSource::Pi => {
|
|
181
160
|
let bytes = run_native("pi", &["--list-models"], None, None, &[], CATALOG_TIMEOUT, MAX_CATALOG_BYTES, &mut NoopObserver)?;
|
|
@@ -881,10 +860,24 @@ mod tests {
|
|
|
881
860
|
|
|
882
861
|
#[test]
|
|
883
862
|
fn provider_capability_is_explicit_and_exact() {
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
863
|
+
for (name, provider) in [
|
|
864
|
+
("pi", Provider::Pi),
|
|
865
|
+
("cursor_agent", Provider::CursorAgent),
|
|
866
|
+
("codex", Provider::Codex),
|
|
867
|
+
("claude", Provider::Claude),
|
|
868
|
+
("claude_code", Provider::ClaudeCode),
|
|
869
|
+
("grok", Provider::Grok),
|
|
870
|
+
] {
|
|
871
|
+
assert_eq!(parse_catalog_provider(name), Some(provider));
|
|
872
|
+
assert_eq!(provider_wire(provider), name);
|
|
873
|
+
assert!(catalog_source(provider).is_some());
|
|
874
|
+
}
|
|
875
|
+
for name in ["", "cloud", "cursor", "agent", "claude-code", "copilot", "gemini_cli", "fake", "Pi", "CODEX", " pi", "pi ", "pi/model", "codex:model", "claude_code_extra"] {
|
|
876
|
+
assert_eq!(parse_catalog_provider(name), None);
|
|
877
|
+
}
|
|
878
|
+
for provider in [Provider::Copilot, Provider::GeminiCli, Provider::Fake] {
|
|
879
|
+
assert!(catalog_source(provider).is_none());
|
|
880
|
+
}
|
|
888
881
|
}
|
|
889
882
|
|
|
890
883
|
#[test]
|
|
@@ -1,3 +1,73 @@
|
|
|
1
|
+
#[test]
|
|
2
|
+
fn trimmed_provider_builders_preserve_fresh_and_resume_argv() {
|
|
3
|
+
for (provider, base) in [
|
|
4
|
+
(
|
|
5
|
+
Provider::Grok,
|
|
6
|
+
vec!["grok", "--model", "test-model", "--rules", "role"],
|
|
7
|
+
),
|
|
8
|
+
(
|
|
9
|
+
Provider::CursorAgent,
|
|
10
|
+
vec!["agent", "--model", "test-model", "--workspace", "{workspace}"],
|
|
11
|
+
),
|
|
12
|
+
(
|
|
13
|
+
Provider::Copilot,
|
|
14
|
+
vec![
|
|
15
|
+
"copilot", "--no-color", "--no-auto-update", "--no-remote",
|
|
16
|
+
"--disable-builtin-mcps", "--allow-tool", "team_orchestrator",
|
|
17
|
+
"--model", "test-model",
|
|
18
|
+
],
|
|
19
|
+
),
|
|
20
|
+
] {
|
|
21
|
+
let adapter = get_adapter(provider);
|
|
22
|
+
let ctx = ProviderCommandContext {
|
|
23
|
+
auth_mode: AuthMode::Subscription,
|
|
24
|
+
mcp_config: None,
|
|
25
|
+
system_prompt: Some("role"),
|
|
26
|
+
model: Some("test-model"),
|
|
27
|
+
dangerously_skip_permissions: false,
|
|
28
|
+
profile_launch: None,
|
|
29
|
+
agent_id_hint: Some("worker"),
|
|
30
|
+
effort: None,
|
|
31
|
+
};
|
|
32
|
+
let mut expected = base.iter().map(|s| s.to_string()).collect::<Vec<_>>();
|
|
33
|
+
let fresh = adapter.build_command_plan(ctx).expect("fresh plan");
|
|
34
|
+
if provider != Provider::CursorAgent {
|
|
35
|
+
expected.push("--session-id".to_string());
|
|
36
|
+
expected.push(
|
|
37
|
+
fresh.expected_session_id.as_ref().expect("session hint").as_str().to_string(),
|
|
38
|
+
);
|
|
39
|
+
} else {
|
|
40
|
+
assert!(fresh.expected_session_id.is_none());
|
|
41
|
+
}
|
|
42
|
+
assert_eq!(fresh.argv, expected, "{provider:?} fresh argv");
|
|
43
|
+
let session = SessionId::new("existing-session");
|
|
44
|
+
let mut expected = base.iter().map(|s| s.to_string()).collect::<Vec<_>>();
|
|
45
|
+
expected.extend(["--resume".to_string(), session.as_str().to_string()]);
|
|
46
|
+
assert_eq!(
|
|
47
|
+
adapter
|
|
48
|
+
.build_resume_command_plan(Some(&session), ctx)
|
|
49
|
+
.expect("resume plan")
|
|
50
|
+
.argv,
|
|
51
|
+
expected,
|
|
52
|
+
"{provider:?} resume argv"
|
|
53
|
+
);
|
|
54
|
+
if provider == Provider::CursorAgent {
|
|
55
|
+
let mut expected = base.iter().map(|s| s.to_string()).collect::<Vec<_>>();
|
|
56
|
+
expected.insert(1, "--resume".to_string());
|
|
57
|
+
expected.insert(2, session.as_str().to_string());
|
|
58
|
+
assert_eq!(
|
|
59
|
+
adapter
|
|
60
|
+
.build_resume_command_with_context(
|
|
61
|
+
Some(&session), ctx.auth_mode, None, ctx.system_prompt, ctx.model, false,
|
|
62
|
+
)
|
|
63
|
+
.expect("legacy resume"),
|
|
64
|
+
expected,
|
|
65
|
+
"legacy Cursor resume must retain its distinct flag order"
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
1
71
|
#[test]
|
|
2
72
|
fn abnormal_dedup_key_uses_signature_and_optional_turn_id() {
|
|
3
73
|
// probe(/tmp/probe_idle.py read_fault_facts): the C8 dedup key is
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//! Exact argv characterization: no helper may trim, escape or reorder caller values.
|
|
2
|
+
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
|
|
3
|
+
use super::*;
|
|
4
|
+
use crate::model::enums::ProviderEffort;
|
|
5
|
+
use crate::provider::adapters::{claude, codex, copilot, cursor_agent, grok, pi};
|
|
6
|
+
use crate::provider::ProviderCommandOverrides;
|
|
7
|
+
|
|
8
|
+
#[test]
|
|
9
|
+
fn command_helpers_keep_provider_argv_bytes_and_order() {
|
|
10
|
+
let raw = " \"'\\\n ";
|
|
11
|
+
let mcp = McpConfig { raw: serde_json::json!({}) };
|
|
12
|
+
assert_eq!(claude::claude_base_command(
|
|
13
|
+
&BasicProviderAdapter { provider: Provider::Claude }, AuthMode::Subscription,
|
|
14
|
+
Some(&mcp), Some(raw), Some(""), false, false, Some(ProviderEffort::High),
|
|
15
|
+
).expect("claude"), ["claude", "--permission-mode", "default", "--model", "", "--effort", "high", "--append-system-prompt", raw, "--mcp-config", "{\"mcpServers\":{}}"]);
|
|
16
|
+
|
|
17
|
+
let overrides = ProviderCommandOverrides {
|
|
18
|
+
codex_profile: Some("".to_string()),
|
|
19
|
+
codex_config: vec!["model_reasoning_effort=low".to_string()],
|
|
20
|
+
..Default::default()
|
|
21
|
+
};
|
|
22
|
+
assert_eq!(codex::codex_base_command(
|
|
23
|
+
Some("resume"), AuthMode::Subscription, Some(&mcp), Some(raw), Some(""),
|
|
24
|
+
true, Some(&overrides), Some(ProviderEffort::Max),
|
|
25
|
+
), ["codex", "resume", "--no-alt-screen", "--disable", "shell_snapshot", "--disable", "apps", "--profile", "", "--dangerously-bypass-approvals-and-sandbox", "--model", "", "-c", "model_reasoning_effort=low", "-c", "model_reasoning_effort=max", "-c", "developer_instructions=\" \\\"'\\\\\\n \""]);
|
|
26
|
+
|
|
27
|
+
assert_eq!(copilot::copilot_base_command(
|
|
28
|
+
AuthMode::Subscription, Some(&mcp), Some(raw), Some(""), true,
|
|
29
|
+
), ["copilot", "--no-color", "--no-auto-update", "--no-remote", "--disable-builtin-mcps", "--allow-all", "--allow-tool", "team_orchestrator", "--model", "", "--additional-mcp-config", "{}"]);
|
|
30
|
+
assert_eq!(cursor_agent::cursor_agent_base_command(Some(raw), true).expect("cursor"),
|
|
31
|
+
["agent", "--trust", "--sandbox", "disabled", "--force", "--model", raw, "--workspace", "{workspace}"]);
|
|
32
|
+
assert_eq!(grok::grok_base_command(Some(raw), Some(" model-id "), true, Some(ProviderEffort::Medium)).expect("grok"),
|
|
33
|
+
["grok", "--always-approve", "--model", "model-id", "--effort", "medium", "--rules", raw]);
|
|
34
|
+
assert_eq!(grok::grok_base_command(Some(""), Some(" "), false, None).expect("grok empty"), ["grok", "--rules", ""]);
|
|
35
|
+
assert_eq!(pi::build_pi_command_argv(pi::PiCommandRequest {
|
|
36
|
+
extension: std::path::Path::new("extension.ts"), model: Some("vendor/model"),
|
|
37
|
+
effort: Some(ProviderEffort::XHigh), system_prompt: raw,
|
|
38
|
+
session_dir: Some(std::path::Path::new("sessions")),
|
|
39
|
+
session: pi::PiSessionSelector::Fresh { session_id: "session-id" }, agent_id: "worker",
|
|
40
|
+
}).expect("pi"), ["pi", "-e", "extension.ts", "--model", "vendor/model", "--thinking", "xhigh", "--append-system-prompt", raw, "--session-dir", "sessions", "--session-id", "session-id", "--name", "worker"]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
#[test]
|
|
44
|
+
fn command_helpers_keep_absent_options_and_safe_defaults() {
|
|
45
|
+
assert_eq!(claude::claude_base_command(
|
|
46
|
+
&BasicProviderAdapter { provider: Provider::Claude }, AuthMode::Subscription,
|
|
47
|
+
None, None, None, false, false, None,
|
|
48
|
+
).expect("claude"), ["claude", "--permission-mode", "default"]);
|
|
49
|
+
assert_eq!(codex::codex_base_command(None, AuthMode::Subscription, None, None, None, false, None, None),
|
|
50
|
+
["codex", "--no-alt-screen", "--disable", "shell_snapshot", "--disable", "apps"]);
|
|
51
|
+
assert_eq!(copilot::copilot_base_command(AuthMode::Subscription, None, None, None, false),
|
|
52
|
+
["copilot", "--no-color", "--no-auto-update", "--no-remote", "--disable-builtin-mcps", "--allow-tool", "team_orchestrator"]);
|
|
53
|
+
assert_eq!(cursor_agent::cursor_agent_base_command(None, false).expect("cursor"), ["agent", "--workspace", "{workspace}"]);
|
|
54
|
+
assert_eq!(grok::grok_base_command(None, None, false, None).expect("grok"), ["grok"]);
|
|
55
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-agent/installer",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.113",
|
|
4
4
|
"description": "npx installer for Team Agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"codex",
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"team-agent-installer": "npm/install.mjs"
|
|
21
21
|
},
|
|
22
22
|
"optionalDependencies": {
|
|
23
|
-
"@team-agent/cli-darwin-arm64": "0.5.
|
|
24
|
-
"@team-agent/cli-darwin-x64": "0.5.
|
|
25
|
-
"@team-agent/cli-linux-x64": "0.5.
|
|
23
|
+
"@team-agent/cli-darwin-arm64": "0.5.113",
|
|
24
|
+
"@team-agent/cli-darwin-x64": "0.5.113",
|
|
25
|
+
"@team-agent/cli-linux-x64": "0.5.113"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node npm/bincheck.mjs",
|