@team-agent/installer 0.5.103 → 0.5.105
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 +2 -0
- package/crates/team-agent/src/cli/emit.rs +102 -5
- package/crates/team-agent/src/cli/mod.rs +8 -14
- package/crates/team-agent/src/cli/spec.rs +3 -3
- package/crates/team-agent/src/cli/tests/run_delegation.rs +1 -0
- package/crates/team-agent/src/cli/types.rs +3 -1
- package/crates/team-agent/src/compiler/tests.rs +19 -27
- package/crates/team-agent/src/compiler.rs +11 -105
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +6 -2
- package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +10 -6
- package/crates/team-agent/src/lifecycle/launch/clone_agent.rs +2 -5
- package/crates/team-agent/src/lifecycle/launch/identity.rs +9 -13
- package/crates/team-agent/src/lifecycle/launch/role_source.rs +2 -26
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +1 -0
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +7 -2
- package/crates/team-agent/src/lifecycle/launch.rs +4 -1
- package/crates/team-agent/src/lifecycle/mod.rs +1 -0
- package/crates/team-agent/src/lifecycle/profile_launch.rs +0 -12
- package/crates/team-agent/src/lifecycle/restart/agent.rs +31 -0
- package/crates/team-agent/src/lifecycle/restart/avatar.rs +0 -1
- package/crates/team-agent/src/lifecycle/restart/common.rs +1 -1
- package/crates/team-agent/src/lifecycle/restart.rs +1 -1
- package/crates/team-agent/src/lifecycle/role_config.rs +1123 -0
- package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +7 -19
- package/crates/team-agent/src/lifecycle/tests/grok_require_explicit_model_red.rs +8 -41
- package/crates/team-agent/src/lifecycle/tests/pi_compiler_red.rs +14 -8
- package/crates/team-agent/src/lifecycle/tests/pi_dual_entry_red.rs +8 -4
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +4 -4
- package/crates/team-agent/src/model/enums.rs +0 -9
- package/crates/team-agent/src/model/spec.rs +2 -2
- package/crates/team-agent/src/provider/adapters/cursor_agent.rs +7 -8
- package/crates/team-agent/src/provider/adapters/grok.rs +5 -18
- package/crates/team-agent/src/provider/tests/adapter.rs +15 -24
- package/crates/team-agent/src/provider/wire.rs +2 -29
- package/crates/team-agent/src/state/repository.rs +2 -1
- package/package.json +4 -4
- package/skills/team-agent/SKILL.md +83 -33
- package/skills/team-agent/references/team-agent-operator.md +54 -523
|
@@ -31,35 +31,23 @@ use team_agent::transport::test_support::OfflineTransport;
|
|
|
31
31
|
|
|
32
32
|
#[test]
|
|
33
33
|
#[serial(env)]
|
|
34
|
-
fn
|
|
34
|
+
fn cursor_role_missing_model_uses_provider_default() {
|
|
35
35
|
let ws = tmp_dir("cursor-no-model");
|
|
36
36
|
let team = write_role_team(&ws, "cursortm", "cursor_writer", None, None);
|
|
37
37
|
let transport = OfflineTransport::new();
|
|
38
|
-
|
|
38
|
+
quick_start_with_transport_in_workspace(
|
|
39
39
|
&ws,
|
|
40
40
|
&team,
|
|
41
41
|
None,
|
|
42
42
|
true,
|
|
43
43
|
Some("cursortm"),
|
|
44
44
|
&transport,
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
Err(error) => error.to_string(),
|
|
49
|
-
};
|
|
50
|
-
let lower = err.to_ascii_lowercase();
|
|
51
|
-
assert!(
|
|
52
|
-
lower.contains("model"),
|
|
53
|
-
"error must name the missing field; err={err}"
|
|
54
|
-
);
|
|
55
|
-
assert!(
|
|
56
|
-
lower.contains("built-in") || lower.contains("builtin") || err.contains("内建"),
|
|
57
|
-
"error must name the built-in default; err={err}"
|
|
58
|
-
);
|
|
45
|
+
)
|
|
46
|
+
.expect("Cursor may use its provider-native model default");
|
|
47
|
+
let argv = first_spawn_argv(&transport);
|
|
59
48
|
assert!(
|
|
60
|
-
|
|
61
|
-
"
|
|
62
|
-
transport.spawn_records()
|
|
49
|
+
!argv.iter().any(|arg| arg == "--model"),
|
|
50
|
+
"an omitted model must not fabricate a --model argument: {argv:?}"
|
|
63
51
|
);
|
|
64
52
|
}
|
|
65
53
|
|
|
@@ -27,11 +27,10 @@ use serial_test::serial;
|
|
|
27
27
|
use team_agent::lifecycle::quick_start_with_transport_in_workspace;
|
|
28
28
|
use team_agent::transport::test_support::OfflineTransport;
|
|
29
29
|
|
|
30
|
-
///
|
|
31
|
-
/// 40e91fba 上会成功启动(有 overlay / 有 spawn)⇒ 本条红。
|
|
30
|
+
/// An omitted Grok role model preserves the provider-native default.
|
|
32
31
|
#[test]
|
|
33
32
|
#[serial(env)]
|
|
34
|
-
fn
|
|
33
|
+
fn grok_role_missing_model_uses_provider_default() {
|
|
35
34
|
let ws = tmp_dir("grok-no-model");
|
|
36
35
|
let home = tmp_dir("grok-no-model-home");
|
|
37
36
|
seed_grok_home(&home, Some(&ws));
|
|
@@ -46,51 +45,19 @@ fn grok_role_missing_model_refuses_to_start() {
|
|
|
46
45
|
);
|
|
47
46
|
let transport = OfflineTransport::new();
|
|
48
47
|
|
|
49
|
-
|
|
48
|
+
quick_start_with_transport_in_workspace(
|
|
50
49
|
&ws,
|
|
51
50
|
&team,
|
|
52
51
|
None,
|
|
53
52
|
true,
|
|
54
53
|
Some("grokteam"),
|
|
55
54
|
&transport,
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
let
|
|
59
|
-
Ok(report) => panic!(
|
|
60
|
-
"grok role without model must refuse to start; a built-in default \
|
|
61
|
-
would silently pick the model; report={report:?}"
|
|
62
|
-
),
|
|
63
|
-
Err(error) => error.to_string(),
|
|
64
|
-
};
|
|
65
|
-
let lower = err.to_ascii_lowercase();
|
|
66
|
-
assert!(
|
|
67
|
-
lower.contains("model"),
|
|
68
|
-
"error must name the missing field; err={err}"
|
|
69
|
-
);
|
|
70
|
-
assert!(
|
|
71
|
-
lower.contains("built-in") || lower.contains("builtin") || err.contains("内建"),
|
|
72
|
-
"error must name the built-in default the framework would fill; err={err}"
|
|
73
|
-
);
|
|
74
|
-
assert!(
|
|
75
|
-
lower.contains("implicit") || err.contains("隐式"),
|
|
76
|
-
"error must reject every implicit source, not just one fallback; err={err}"
|
|
77
|
-
);
|
|
78
|
-
assert!(
|
|
79
|
-
err.contains("model: grok-4.6") || err.contains("model: grok-4"),
|
|
80
|
-
"error must show how to write the field; err={err}"
|
|
81
|
-
);
|
|
82
|
-
assert!(
|
|
83
|
-
!err.to_ascii_lowercase().contains("worktree") && !err.contains("then retry"),
|
|
84
|
-
"must not promise a remedy this version cannot honor; err={err}"
|
|
85
|
-
);
|
|
86
|
-
assert!(
|
|
87
|
-
transport.spawn_records().is_empty(),
|
|
88
|
-
"refusal must happen before spawn; records={:?}",
|
|
89
|
-
transport.spawn_records()
|
|
90
|
-
);
|
|
55
|
+
)
|
|
56
|
+
.expect("Grok may use its provider-native model default");
|
|
57
|
+
let argv = first_spawn_argv(&transport);
|
|
91
58
|
assert!(
|
|
92
|
-
!
|
|
93
|
-
"
|
|
59
|
+
!argv.iter().any(|arg| arg == "--model"),
|
|
60
|
+
"an omitted model must not fabricate a --model argument: {argv:?}"
|
|
94
61
|
);
|
|
95
62
|
}
|
|
96
63
|
|
|
@@ -86,7 +86,8 @@ fn pi_role_ignores_tools_and_preserves_provider_defaults() {
|
|
|
86
86
|
|
|
87
87
|
let defaults = valid_role_with("")
|
|
88
88
|
.replace("model: team-agent/qwen3.8-27b\n", "")
|
|
89
|
-
.replace("effort: medium\n", "")
|
|
89
|
+
.replace("effort: medium\n", "")
|
|
90
|
+
.replace("dangerously_skip_permissions: true\n", "");
|
|
90
91
|
let team_meta = Value::Map(vec![
|
|
91
92
|
(
|
|
92
93
|
"provider_models".to_string(),
|
|
@@ -105,16 +106,21 @@ fn pi_role_ignores_tools_and_preserves_provider_defaults() {
|
|
|
105
106
|
),
|
|
106
107
|
]);
|
|
107
108
|
let compiled = compile_pi_role_with_team(&defaults, &team_meta)
|
|
108
|
-
.expect("
|
|
109
|
+
.expect("omitted model and effort preserve provider-native defaults");
|
|
109
110
|
assert_eq!(
|
|
110
111
|
compiled.agent.get("model"),
|
|
111
|
-
Some(&Value::
|
|
112
|
-
"omitted Pi model
|
|
112
|
+
Some(&Value::Null),
|
|
113
|
+
"omitted Pi model stays unset rather than using team metadata"
|
|
113
114
|
);
|
|
114
115
|
assert_eq!(
|
|
115
116
|
compiled.agent.get("effort"),
|
|
116
|
-
|
|
117
|
-
"omitted Pi effort
|
|
117
|
+
None,
|
|
118
|
+
"omitted Pi effort stays unset rather than using team metadata"
|
|
119
|
+
);
|
|
120
|
+
assert_eq!(
|
|
121
|
+
compiled.agent.get("dangerously_skip_permissions"),
|
|
122
|
+
Some(&Value::Bool(false)),
|
|
123
|
+
"omitted Pi bypass defaults to safe false"
|
|
118
124
|
);
|
|
119
125
|
|
|
120
126
|
let models = parse_pi_list_models_table(CATALOG).expect("catalog fixture");
|
|
@@ -162,10 +168,10 @@ fn pi_role_uses_standard_bypass_field_without_fabricating_argv() {
|
|
|
162
168
|
|
|
163
169
|
let missing_ack = valid_role_with("").replace("dangerously_skip_permissions: true\n", "");
|
|
164
170
|
let compiled = compile_pi_role(&missing_ack)
|
|
165
|
-
.expect("omitted bypass metadata defaults to
|
|
171
|
+
.expect("omitted bypass metadata defaults to false");
|
|
166
172
|
assert_eq!(
|
|
167
173
|
compiled.agent.get("dangerously_skip_permissions"),
|
|
168
|
-
Some(&Value::Bool(
|
|
174
|
+
Some(&Value::Bool(false))
|
|
169
175
|
);
|
|
170
176
|
}
|
|
171
177
|
|
|
@@ -216,18 +216,22 @@ fn pi_leader_and_teammate_body(hermetic: &HermeticTestEnv) {
|
|
|
216
216
|
|
|
217
217
|
std::fs::write(
|
|
218
218
|
&role,
|
|
219
|
-
"---\nname: worker-a\nrole: developer\nprovider: pi\nauth_mode: subscription\ntools:\n - mcp_team\
|
|
219
|
+
"---\nname: worker-a\nrole: developer\nprovider: pi\nauth_mode: subscription\ntools:\n - mcp_team\n---\nworker contract\n",
|
|
220
220
|
)
|
|
221
221
|
.expect("write provider-default teammate role");
|
|
222
222
|
let defaults = compile_role_agent(&role, &Value::Map(Vec::new()), "/workspace")
|
|
223
|
-
.expect("provider: pi teammate may
|
|
223
|
+
.expect("provider: pi teammate may preserve provider-native defaults");
|
|
224
224
|
assert_eq!(
|
|
225
225
|
defaults.agent.get("model"),
|
|
226
|
-
Some(&Value::
|
|
226
|
+
Some(&Value::Null)
|
|
227
227
|
);
|
|
228
228
|
assert_eq!(
|
|
229
229
|
defaults.agent.get("effort"),
|
|
230
|
-
|
|
230
|
+
None
|
|
231
|
+
);
|
|
232
|
+
assert_eq!(
|
|
233
|
+
defaults.agent.get("dangerously_skip_permissions"),
|
|
234
|
+
Some(&Value::Bool(false))
|
|
231
235
|
);
|
|
232
236
|
std::fs::remove_dir_all(root).expect("remove role fixture");
|
|
233
237
|
|
|
@@ -58,7 +58,7 @@ pub(crate) struct WorkerCommandAgent {
|
|
|
58
58
|
system_prompt_file: Option<String>,
|
|
59
59
|
output_contract_format: Option<String>,
|
|
60
60
|
communication_mode: CommunicationMode,
|
|
61
|
-
/// Missing `dangerously_skip_permissions` defaults to
|
|
61
|
+
/// Missing `dangerously_skip_permissions` defaults to false; only explicit true enables bypass.
|
|
62
62
|
dangerously_skip_permissions: bool,
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -110,9 +110,9 @@ impl WorkerCommandAgent {
|
|
|
110
110
|
.get("communication_mode")
|
|
111
111
|
.and_then(crate::model::yaml::Value::as_str),
|
|
112
112
|
)?,
|
|
113
|
-
dangerously_skip_permissions:
|
|
113
|
+
dangerously_skip_permissions: matches!(
|
|
114
114
|
agent.get("dangerously_skip_permissions"),
|
|
115
|
-
Some(crate::model::yaml::Value::Bool(
|
|
115
|
+
Some(crate::model::yaml::Value::Bool(true))
|
|
116
116
|
),
|
|
117
117
|
})
|
|
118
118
|
}
|
|
@@ -167,7 +167,7 @@ impl WorkerCommandAgent {
|
|
|
167
167
|
dangerously_skip_permissions: agent
|
|
168
168
|
.get("dangerously_skip_permissions")
|
|
169
169
|
.and_then(serde_json::Value::as_bool)
|
|
170
|
-
.unwrap_or(
|
|
170
|
+
.unwrap_or(false),
|
|
171
171
|
})
|
|
172
172
|
}
|
|
173
173
|
}
|
|
@@ -213,15 +213,6 @@ pub enum Backend {
|
|
|
213
213
|
Pty,
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
/// agent permission mode(schema `agent.permission_mode`;compiler 恒发 `restricted`)。
|
|
217
|
-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
218
|
-
#[serde(rename_all = "snake_case")]
|
|
219
|
-
pub enum PermissionMode {
|
|
220
|
-
Restricted,
|
|
221
|
-
Ask,
|
|
222
|
-
Trusted,
|
|
223
|
-
}
|
|
224
|
-
|
|
225
216
|
/// tmux pane 存活态(`state.py:336-341`)。**`Unknown` 既不可当 dead 也不可当 live**
|
|
226
217
|
/// (owner-gate `state.py:382` 用 `!= LIVE`)—— 穷尽 match,不 fallthrough(§11)。
|
|
227
218
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
@@ -1136,7 +1136,7 @@ mod tests {
|
|
|
1136
1136
|
}
|
|
1137
1137
|
|
|
1138
1138
|
// 0.5.66 bypass 单源 §4.1:7 个生命周期入口共享编译后的 spec。
|
|
1139
|
-
// 缺失 role frontmatter
|
|
1139
|
+
// 缺失 role frontmatter 值时编译为安全默认 false;此处锁定公共编译入口的结果。
|
|
1140
1140
|
#[test]
|
|
1141
1141
|
fn test_all_7_triggers_use_compiled_default_bypass() {
|
|
1142
1142
|
let team = std::env::temp_dir().join(format!(
|
|
@@ -1165,7 +1165,7 @@ mod tests {
|
|
|
1165
1165
|
.expect("compiled spec has a worker");
|
|
1166
1166
|
assert_eq!(
|
|
1167
1167
|
agent.get("dangerously_skip_permissions"),
|
|
1168
|
-
Some(&Yaml::Bool(
|
|
1168
|
+
Some(&Yaml::Bool(false))
|
|
1169
1169
|
);
|
|
1170
1170
|
let _ = std::fs::remove_dir_all(&team);
|
|
1171
1171
|
}
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
//! maturity: wired
|
|
12
12
|
//! ---
|
|
13
13
|
//!
|
|
14
|
-
//! Cursor `agent` CLI(与 `cursor-agent`
|
|
15
|
-
//! `.team/scripts/cursor_seat.sh`
|
|
16
|
-
//! `--trust --sandbox disabled --
|
|
14
|
+
//! Cursor `agent` CLI(与 `cursor-agent` 同二进制)。默认不跳过信任或沙箱设置。
|
|
15
|
+
//! 显式 bypass 时才使用 `.team/scripts/cursor_seat.sh` 的 trusted/unsandboxed 选项:
|
|
16
|
+
//! `--workspace <物理路径> [--trust --sandbox disabled --force] [--model]`
|
|
17
17
|
//! Role 不入 argv,写 `<workspace>/.cursor/rules/*.mdc` + `alwaysApply: true`。
|
|
18
18
|
//! MCP 无 `--mcp-config`;身份必须写进 `.cursor/mcp.json` 的 env 表
|
|
19
19
|
//! (cursor 不把父进程 TEAM_AGENT_* 传给 MCP 子进程)。
|
|
@@ -57,14 +57,13 @@ pub(crate) fn cursor_agent_base_command(
|
|
|
57
57
|
effort: Option<crate::model::enums::ProviderEffort>,
|
|
58
58
|
) -> Result<Vec<String>, ProviderError> {
|
|
59
59
|
let mut argv = vec!["agent".to_string()];
|
|
60
|
-
// --trust 跳过 Workspace Trust 闸(已实测)。无此 flag 会停在 Do you trust。
|
|
61
|
-
argv.push("--trust".to_string());
|
|
62
60
|
if dangerously_skip_permissions {
|
|
61
|
+
// Explicit bypass also opts into Cursor's trusted, unsandboxed launch.
|
|
62
|
+
argv.push("--trust".to_string());
|
|
63
|
+
argv.push("--sandbox".to_string());
|
|
64
|
+
argv.push("disabled".to_string());
|
|
63
65
|
argv.push("--force".to_string());
|
|
64
66
|
}
|
|
65
|
-
// 与 cursor_seat.sh 主路径一致。沙箱实际隔离面未再拆,但 flag 本身已实测。
|
|
66
|
-
argv.push("--sandbox".to_string());
|
|
67
|
-
argv.push("disabled".to_string());
|
|
68
67
|
if let Some(model) = model {
|
|
69
68
|
argv.push("--model".to_string());
|
|
70
69
|
argv.push(model.to_string());
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//!
|
|
2
2
|
//! purpose: Grok CLI argv + permission deny 映射;MCP 不走 CLI flag
|
|
3
3
|
//! contract: launch 写 `<cwd>/.grok/config.toml`;未登录 / 目录未信任必须拒绝启动。
|
|
4
|
-
//!
|
|
4
|
+
//! 未指定 model 时不注入 `--model`,由 Grok CLI 使用原生默认。
|
|
5
5
|
//! 未知工具映射记成 Unsupported,不发明 deny 名
|
|
6
6
|
//! mcp_injection: dir_scoped —— 每席必须独立工作目录;同 cwd 多席会静默串台,故拒绝。
|
|
7
7
|
//! 对照 claude/codex 是 argv 作用域(`--mcp-config`),同目录多席没有这个问题。
|
|
@@ -71,23 +71,10 @@ pub(crate) fn grok_base_command(
|
|
|
71
71
|
if dangerously_skip_permissions {
|
|
72
72
|
argv.push("--always-approve".to_string());
|
|
73
73
|
}
|
|
74
|
-
let model =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
"grok launch requires an explicit model. \
|
|
79
|
-
Without an explicit model the framework fills a built-in default (grok-4 for grok), \
|
|
80
|
-
so the seat silently gets a model and context window that are not in the role file, \
|
|
81
|
-
while argv still looks normal. The role file must name the model itself; \
|
|
82
|
-
built-in defaults, team-level defaults, and the Grok CLI global default are all implicit sources. \
|
|
83
|
-
Set the role front-matter field, for example:\n\
|
|
84
|
-
model: grok-4.6"
|
|
85
|
-
.to_string(),
|
|
86
|
-
));
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
argv.push("--model".to_string());
|
|
90
|
-
argv.push(model.to_string());
|
|
74
|
+
if let Some(model) = model.map(str::trim).filter(|value| !value.is_empty()) {
|
|
75
|
+
argv.push("--model".to_string());
|
|
76
|
+
argv.push(model.to_string());
|
|
77
|
+
}
|
|
91
78
|
if let Some(effort) = effort {
|
|
92
79
|
// Grok accepts `--effort` as alias for `--reasoning-effort`.
|
|
93
80
|
argv.push("--effort".to_string());
|
|
@@ -308,21 +308,14 @@ fn test_grok_build_command_bypass_flag_when_dangerous() {
|
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
#[test]
|
|
311
|
-
fn
|
|
311
|
+
fn test_grok_build_command_omits_missing_model() {
|
|
312
312
|
let adapter = get_adapter(Provider::Grok);
|
|
313
|
-
let
|
|
313
|
+
let argv = adapter
|
|
314
314
|
.build_command(AuthMode::Subscription, None, None, None)
|
|
315
|
-
.
|
|
316
|
-
let text = err.to_string();
|
|
317
|
-
assert!(
|
|
318
|
-
text.to_ascii_lowercase().contains("model"),
|
|
319
|
-
"error must name model; got {text}"
|
|
320
|
-
);
|
|
315
|
+
.expect("missing model preserves the provider-native default");
|
|
321
316
|
assert!(
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|| text.contains("内建"),
|
|
325
|
-
"error must name the built-in default the framework would fill; got {text}"
|
|
317
|
+
!argv.iter().any(|arg| arg == "--model"),
|
|
318
|
+
"missing model must not fabricate a --model argument: {argv:?}"
|
|
326
319
|
);
|
|
327
320
|
assert!(
|
|
328
321
|
argv_contains_adjacent(
|
|
@@ -348,12 +341,8 @@ fn test_cursor_agent_build_command_includes_workspace_flag() {
|
|
|
348
341
|
"cursor argv must carry `--workspace {{workspace}}`: {argv:?}"
|
|
349
342
|
);
|
|
350
343
|
assert!(
|
|
351
|
-
|
|
352
|
-
"
|
|
353
|
-
);
|
|
354
|
-
assert!(
|
|
355
|
-
argv_contains_adjacent(&argv, &["--sandbox", "disabled"]),
|
|
356
|
-
"cursor argv must carry documented --sandbox disabled: {argv:?}"
|
|
344
|
+
!argv.iter().any(|arg| arg == "--trust" || arg == "--sandbox"),
|
|
345
|
+
"safe Cursor command must not opt into trust or disabled sandbox: {argv:?}"
|
|
357
346
|
);
|
|
358
347
|
assert!(
|
|
359
348
|
!argv.iter().any(|a| a == "--rules" || a == "--append-system-prompt"),
|
|
@@ -509,8 +498,11 @@ fn test_cursor_fresh_plan_has_no_session_id_and_no_resume() {
|
|
|
509
498
|
plan.argv
|
|
510
499
|
);
|
|
511
500
|
assert!(
|
|
512
|
-
|
|
513
|
-
|
|
501
|
+
!plan
|
|
502
|
+
.argv
|
|
503
|
+
.iter()
|
|
504
|
+
.any(|arg| arg == "--trust" || arg == "--sandbox"),
|
|
505
|
+
"default Cursor plan must not opt into trust or disabled sandbox; argv={:?}",
|
|
514
506
|
plan.argv
|
|
515
507
|
);
|
|
516
508
|
}
|
|
@@ -550,10 +542,10 @@ fn test_cursor_resume_plan_requires_chat_id_and_never_emits_empty_resume() {
|
|
|
550
542
|
}
|
|
551
543
|
|
|
552
544
|
#[test]
|
|
553
|
-
fn
|
|
545
|
+
fn pi_wire_roundtrip_requires_backing() {
|
|
554
546
|
use crate::provider::wire::{
|
|
555
|
-
aliases,
|
|
556
|
-
|
|
547
|
+
aliases, command_name, parse_canonical_provider, parse_provider, provider_wire,
|
|
548
|
+
requires_resume_backing,
|
|
557
549
|
};
|
|
558
550
|
|
|
559
551
|
let provider = Provider::Pi;
|
|
@@ -563,7 +555,6 @@ fn pi_wire_roundtrip_requires_backing_and_has_no_builtin_model() {
|
|
|
563
555
|
assert_eq!(aliases(provider), &["pi"]);
|
|
564
556
|
assert_eq!(command_name(provider), "pi");
|
|
565
557
|
assert!(requires_resume_backing(provider));
|
|
566
|
-
assert_eq!(builtin_provider_model(provider), None);
|
|
567
558
|
}
|
|
568
559
|
|
|
569
560
|
#[test]
|
|
@@ -88,31 +88,6 @@ pub(crate) fn requires_resume_backing(provider: Provider) -> bool {
|
|
|
88
88
|
)
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
pub(crate) fn provider_model_keys(provider: Provider) -> &'static [&'static str] {
|
|
92
|
-
match provider {
|
|
93
|
-
Provider::Claude => &["claude", "claude_code"],
|
|
94
|
-
Provider::ClaudeCode => &["claude_code", "claude"],
|
|
95
|
-
Provider::Codex => &["codex"],
|
|
96
|
-
Provider::Copilot => &["copilot"],
|
|
97
|
-
Provider::GeminiCli => &["gemini_cli"],
|
|
98
|
-
Provider::Grok => &["grok"],
|
|
99
|
-
Provider::CursorAgent => &["cursor_agent"],
|
|
100
|
-
Provider::Pi => &["pi"],
|
|
101
|
-
Provider::Fake => &["fake"],
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
pub(crate) fn builtin_provider_model(provider: Provider) -> Option<&'static str> {
|
|
106
|
-
match provider {
|
|
107
|
-
Provider::Claude | Provider::ClaudeCode => Some("claude-sonnet-4-6"),
|
|
108
|
-
Provider::Codex => Some("gpt-5.5"),
|
|
109
|
-
// grok: compiler 另有缺 model 硬闸,这里的 builtin 不再被 grok 角色吃到。
|
|
110
|
-
// cursor_agent: 禁止隐式默认(本机 shim 会剥 sonnet-4-thinking);缺 model 编译失败。
|
|
111
|
-
Provider::Grok => Some("grok-4"),
|
|
112
|
-
Provider::CursorAgent | Provider::Pi => None,
|
|
113
|
-
Provider::Copilot | Provider::GeminiCli | Provider::Fake => None,
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
91
|
|
|
117
92
|
/// All aliases (including the canonical wire form) that parse to `provider`.
|
|
118
93
|
/// The canonical wire form is always the first entry, so callers that need
|
|
@@ -235,10 +210,8 @@ mod tests {
|
|
|
235
210
|
"model spec must validate providers through typed helpers"
|
|
236
211
|
);
|
|
237
212
|
assert!(
|
|
238
|
-
compiler_rs.contains("parse_canonical_provider")
|
|
239
|
-
|
|
240
|
-
&& compiler_rs.contains("wire_builtin_provider_model"),
|
|
241
|
-
"compiler effort/model defaults must use typed provider helpers"
|
|
213
|
+
compiler_rs.contains("parse_canonical_provider"),
|
|
214
|
+
"compiler must validate providers through typed helpers"
|
|
242
215
|
);
|
|
243
216
|
assert!(
|
|
244
217
|
delivery_rs.contains("parse_canonical_provider")
|
|
@@ -162,7 +162,8 @@ impl<'a> StateRepository<'a> {
|
|
|
162
162
|
test_support::before_commit();
|
|
163
163
|
let team_key = match intent {
|
|
164
164
|
StateWriteIntent::ResultCollection { owner_team_id } => owner_team_id,
|
|
165
|
-
StateWriteIntent::ForkAgent { team_key, .. }
|
|
165
|
+
StateWriteIntent::ForkAgent { team_key, .. }
|
|
166
|
+
| StateWriteIntent::StartAgent { team_key, .. } => Some(team_key),
|
|
166
167
|
_ => {
|
|
167
168
|
return Err(StateError::SaveFailed(
|
|
168
169
|
"intent does not accept a bounded Team delta".into(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-agent/installer",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.105",
|
|
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.105",
|
|
24
|
+
"@team-agent/cli-darwin-x64": "0.5.105",
|
|
25
|
+
"@team-agent/cli-linux-x64": "0.5.105"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node npm/bincheck.mjs",
|
|
@@ -1,39 +1,89 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: team-agent
|
|
3
|
-
description: Use
|
|
3
|
+
description: Use when the user asks to create, operate, inspect, or stop a Team Agent team. Treat the CLI as the public interface.
|
|
4
4
|
requires_team_agent: ">=0.5.0"
|
|
5
|
-
last_verified_against: "0.5.66"
|
|
6
5
|
---
|
|
7
6
|
# Team Agent
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
team
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
|
|
8
|
+
The current assistant is the **leader**. Workers use separate role files and report through Team Agent. Start from a tmux-addressable terminal or supported agent pane, in the workspace root (the parent of the team directory).
|
|
9
|
+
|
|
10
|
+
## Decide before creating a team
|
|
11
|
+
|
|
12
|
+
**The user decides each worker's provider and permission bypass. Do not silently choose Pi, true, or false.** Reuse an explicit user policy when it covers the worker; otherwise explain the choices and ask before starting.
|
|
13
|
+
|
|
14
|
+
Use existing resources rather than establishing new accounts: `team-agent doctor --workspace . --json` reports discovered tools, but installed does not mean authenticated and an absent entry or unknown auth is not proof of unavailability. For providers supported by `models`, use `team-agent models --provider NAME` to check exact model IDs. Do not inspect credentials, automatically log in, or silently substitute a provider after a failed probe.
|
|
15
|
+
|
|
16
|
+
| Choice | What to explain |
|
|
17
|
+
|---|---|
|
|
18
|
+
| Pi | One Agent tool can use several model catalogs; explicit models require a qualified ID such as `openai-codex/gpt-6-luna` |
|
|
19
|
+
| Codex | The user's existing Codex workflow, native tools, permissions and resumable sessions |
|
|
20
|
+
| Claude / Claude Code | The user's existing Claude workflow, models, native tools and session behavior |
|
|
21
|
+
| Bypass `true` | Fewer permission interruptions, but broader automatic execution authority |
|
|
22
|
+
| Bypass `false` | Keep the selected tool's native permissions; human confirmation may interrupt unattended work |
|
|
23
|
+
|
|
24
|
+
False is **not** a read-only or sandbox guarantee. Native permission capabilities differ between tools. Suggest combinations based on the user's installed tools, subscriptions and task: for example, an explicitly chosen Codex implementer and Claude reviewer, or two Pi workers when those are the available resources. Different workers may use different providers; an existing worker cannot change provider.
|
|
25
|
+
|
|
26
|
+
## Create and start
|
|
27
|
+
|
|
28
|
+
Keep the goal in `TEAM.md` and each worker's instructions in `agents/*.md`. The filename stem is the worker's in-team short name.
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
.team/current/
|
|
32
|
+
TEAM.md
|
|
33
|
+
agents/reviewer.md
|
|
14
34
|
```
|
|
15
|
-
|
|
16
|
-
**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
|
|
35
|
+
|
|
36
|
+
For a **new plan**, record the user's two decisions in every role. This example assumes the user selected Pi and bypass false; it is not a default:
|
|
37
|
+
|
|
38
|
+
```markdown
|
|
39
|
+
---
|
|
40
|
+
provider: pi
|
|
41
|
+
dangerously_skip_permissions: false
|
|
42
|
+
---
|
|
43
|
+
Review the change and report concrete regression risks.
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
team-agent quick-start .team/current
|
|
48
|
+
team-agent send reviewer "Review this change"
|
|
49
|
+
team-agent inbox reviewer -n 3
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Traditional file-based quick-start/compile behavior remains supported, including legacy minimal role documents; no conversion to CLI-created roles is required. Legacy interpretation is not permission to silently choose settings for a new user plan. Model and effort may remain unset; preserve the existing file compiler's native/default interpretation rather than inventing a model or effort.
|
|
53
|
+
|
|
54
|
+
To add a **new** seat to an existing team, provider and bypass must be explicitly defined in the CLI or role file. A complete traditional role file needs no repeated flags. If both sources define a field, equal values are accepted and **conflicting values are rejected before any write or startup**:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
# These values must be the user's choices, not inferred defaults.
|
|
58
|
+
team-agent add-agent analyst --provider pi --bypass true --prompt "Analyze the proposed change"
|
|
59
|
+
# auditor.md already contains the user's provider and bypass decisions.
|
|
60
|
+
team-agent add-agent auditor --role-file /absolute/path/to/auditor.md
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Add creates and immediately starts the seat. Ordinary add rejects an occupied ID, including stopped seats. Disaster recovery has one explicit exception: `add-agent ID --role-file FILE --force` may reuse the existing force-recreate lifecycle only when the registered old pane is positively confirmed dead and no live target cohort exists. A live seat or unknown/missing death proof is refused; force is not a routine configuration-update shortcut. External role files are imported into the team's `agents/ID.md` and left untouched. A file already at that path is used in place, without copying itself.
|
|
64
|
+
|
|
65
|
+
## Change and start an existing worker
|
|
66
|
+
|
|
67
|
+
**Stop first if it is running.** Start never implicitly kills or restarts a running worker:
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
team-agent stop-agent reviewer
|
|
71
|
+
team-agent start-agent reviewer --effort high --bypass true
|
|
72
|
+
team-agent stop-agent reviewer
|
|
73
|
+
team-agent start-agent reviewer --prompt "Review only regression risks; do not change product code"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Start updates the same role file and recompiles it before normal startup. Omitted options preserve the file's values and body; `--prompt` replaces the permanent role body, not a one-time task message. `--bypass true` and `--bypass false` are explicit values, not bare switches. Model, effort and an existing profile can also be set. Direct file edits followed by start use the same reload path.
|
|
77
|
+
|
|
78
|
+
A different `--provider`, including a manually edited role provider, is rejected before launch. A matching provider is allowed. Create a separate ID to use a different engine. If startup fails, the role file and runtime spec are restored to their pre-command contents; the command reports failure, not an applied update.
|
|
79
|
+
|
|
80
|
+
Profiles keep provider authentication, endpoints and custom-model settings local. Imported roles use this team's/workspace's `profiles`, never the original external directory. Prepare a named profile locally through the public profile commands; do not search for or copy external credentials. Never put secrets in `TEAM.md` or a role file. See [the operator reference](references/team-agent-operator.md) for existing profile and field details. Supported metadata also includes `agent_id` (legacy `name`), `role`, `auth_mode`, and `communication_mode`; old `tools`, `permission_mode`, and `label` keys do not configure a worker.
|
|
81
|
+
|
|
82
|
+
## Inspect and stop
|
|
83
|
+
|
|
84
|
+
Use `team-agent status --json` for readiness and the installed command's `--help` for syntax. `ok: true` with `ready: false` means the team is not ready; follow the reported action. Accepted/queued send is not the worker's reply: wait for a natural response or result. Use `<workspace>::<team>/<agent>` when a fully qualified recipient is needed.
|
|
85
|
+
|
|
86
|
+
Worker MCP provides `send_message`, `report_result`, and `get_team_status`. Lifecycle actions remain on the authorized CLI. See [nested teams](references/team-in-team.md) only when needed. Stop the selected team with `team-agent shutdown --workspace .` when authorized.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
This skill is a concise public guide. Do not infer a tested version from package metadata; verify the installed CLI with `team-agent --version` when version-specific behavior matters.
|