@team-agent/installer 0.5.69 → 0.5.71
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/mod.rs +42 -8
- package/crates/team-agent/src/compiler.rs +27 -37
- package/crates/team-agent/src/leader/start.rs +1 -1
- package/crates/team-agent/src/lifecycle/launch/pi_mcp.rs +130 -95
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +2 -12
- package/crates/team-agent/src/lifecycle/restart/common.rs +2 -12
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +23 -7
- package/crates/team-agent/src/lifecycle/restart.rs +2 -1
- package/crates/team-agent/src/lifecycle/tests/pi_catalog_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/pi_command_red.rs +36 -19
- package/crates/team-agent/src/lifecycle/tests/pi_compiler_red.rs +58 -22
- package/crates/team-agent/src/lifecycle/tests/pi_dual_entry_red.rs +27 -9
- package/crates/team-agent/src/lifecycle/tests/pi_executable_mcp_red.rs +212 -26
- package/crates/team-agent/src/lifecycle/tests/pi_session_delivery_red.rs +2 -2
- package/crates/team-agent/src/provider/adapters/pi.rs +22 -28
- package/package.json +4 -4
- package/skills/team-agent/SKILL.md +7 -1
|
@@ -2522,10 +2522,10 @@ fn write_restart_agent_failed_event(
|
|
|
2522
2522
|
"session_id": decision.session_id.as_ref().map(|session| session.as_str()),
|
|
2523
2523
|
"phase": phase,
|
|
2524
2524
|
"error": error,
|
|
2525
|
-
"action":
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
decision.
|
|
2525
|
+
"action": restart_failure_action(
|
|
2526
|
+
&decision.agent_id,
|
|
2527
|
+
phase,
|
|
2528
|
+
decision.session_id.as_ref(),
|
|
2529
2529
|
),
|
|
2530
2530
|
"log": format!(
|
|
2531
2531
|
".team/logs/coordinator.log and .team/runtime/state.json agent={}",
|
|
@@ -3008,14 +3008,30 @@ fn restart_failure_next_actions(failed_agents: &[RestartFailedAgent]) -> Vec<Str
|
|
|
3008
3008
|
failed_agents
|
|
3009
3009
|
.iter()
|
|
3010
3010
|
.map(|failure| {
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
failure.
|
|
3011
|
+
restart_failure_action(
|
|
3012
|
+
&failure.agent_id,
|
|
3013
|
+
&failure.phase,
|
|
3014
|
+
failure.session_id.as_ref(),
|
|
3014
3015
|
)
|
|
3015
3016
|
})
|
|
3016
3017
|
.collect()
|
|
3017
3018
|
}
|
|
3018
3019
|
|
|
3020
|
+
pub(crate) fn restart_failure_action(
|
|
3021
|
+
agent_id: &AgentId,
|
|
3022
|
+
phase: &str,
|
|
3023
|
+
session_id: Option<&SessionId>,
|
|
3024
|
+
) -> String {
|
|
3025
|
+
if phase == "spawn" && session_id.is_none() {
|
|
3026
|
+
return format!(
|
|
3027
|
+
"worker {agent_id} failed before a pane was created; inspect the reported provider error, then rerun `team-agent restart --allow-fresh`"
|
|
3028
|
+
);
|
|
3029
|
+
}
|
|
3030
|
+
format!(
|
|
3031
|
+
"inspect worker {agent_id} output, then reset that worker with `team-agent reset-agent {agent_id}` or rerun `team-agent restart --allow-fresh`"
|
|
3032
|
+
)
|
|
3033
|
+
}
|
|
3034
|
+
|
|
3019
3035
|
fn start_mode_wire(mode: StartMode) -> &'static str {
|
|
3020
3036
|
match mode {
|
|
3021
3037
|
StartMode::Resumed => "resumed",
|
|
@@ -92,7 +92,8 @@ pub(crate) use rebuild::restart_with_transport_with_session_convergence_deadline
|
|
|
92
92
|
// timer + worker timing writer so `lifecycle::launch::launch_with_transport_in_workspace`
|
|
93
93
|
// can emit the same instrumentation event family with `source="launch"`.
|
|
94
94
|
pub(crate) use rebuild::{
|
|
95
|
-
provider_wire_from_state,
|
|
95
|
+
provider_wire_from_state, restart_failure_action, write_worker_spawn_timing_event,
|
|
96
|
+
RestartPhaseTimer,
|
|
96
97
|
};
|
|
97
98
|
pub use rebuild::{restart, restart_with_session_convergence_deadline};
|
|
98
99
|
pub(crate) use rebuild::{
|
|
@@ -18,8 +18,8 @@ fn command_for_model(model: &str) -> Vec<String> {
|
|
|
18
18
|
build_pi_command_argv(PiCommandRequest {
|
|
19
19
|
executable: Path::new("/verified/pi"),
|
|
20
20
|
extension: Path::new("/workspace/.team/runtime/pi/t1/w1/team-mcp.ts"),
|
|
21
|
-
model,
|
|
22
|
-
effort: ProviderEffort::Medium,
|
|
21
|
+
model: Some(model),
|
|
22
|
+
effort: Some(ProviderEffort::Medium),
|
|
23
23
|
system_prompt: "worker contract",
|
|
24
24
|
tool_categories: &["mcp_team"],
|
|
25
25
|
session_dir: Path::new("/workspace/.team/runtime/pi/t1/w1/sessions"),
|
|
@@ -7,8 +7,8 @@ fn request(session: PiSessionSelector<'static>) -> PiCommandRequest<'static> {
|
|
|
7
7
|
PiCommandRequest {
|
|
8
8
|
executable: Path::new("/verified/pi"),
|
|
9
9
|
extension: Path::new("/workspace/.team/runtime/pi/team-a/worker-a/team-mcp.ts"),
|
|
10
|
-
model: "team-agent/qwen3.8-27b",
|
|
11
|
-
effort: ProviderEffort::High,
|
|
10
|
+
model: Some("team-agent/qwen3.8-27b"),
|
|
11
|
+
effort: Some(ProviderEffort::High),
|
|
12
12
|
system_prompt: "frozen worker prompt",
|
|
13
13
|
tool_categories: &["mcp_team", "fs_read", "fs_write", "execute_bash"],
|
|
14
14
|
session_dir: Path::new("/workspace/.team/runtime/pi/team-a/worker-a/sessions"),
|
|
@@ -18,7 +18,20 @@ fn request(session: PiSessionSelector<'static>) -> PiCommandRequest<'static> {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
#[test]
|
|
21
|
-
fn
|
|
21
|
+
fn pi_command_omits_unset_model_and_thinking_flags() {
|
|
22
|
+
let mut request = request(PiSessionSelector::Fresh {
|
|
23
|
+
session_id: "a2320d4e-7b3a-44ae-b3b8-d5de57033b01",
|
|
24
|
+
});
|
|
25
|
+
request.model = None;
|
|
26
|
+
request.effort = None;
|
|
27
|
+
let argv = build_pi_command_argv(request).expect("Pi provider defaults");
|
|
28
|
+
|
|
29
|
+
assert!(!argv.iter().any(|arg| arg == "--model"), "argv={argv:?}");
|
|
30
|
+
assert!(!argv.iter().any(|arg| arg == "--thinking"), "argv={argv:?}");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#[test]
|
|
34
|
+
fn pi_command_preserves_direct_pi_defaults_with_only_team_runtime_additions() {
|
|
22
35
|
let fresh = build_pi_command_argv(request(PiSessionSelector::Fresh {
|
|
23
36
|
session_id: "da8c3622-2378-4d05-a26c-e826a6ef6d63",
|
|
24
37
|
}))
|
|
@@ -27,23 +40,14 @@ fn pi_command_is_exact_regular_tui_and_has_no_ambient_flags() {
|
|
|
27
40
|
fresh,
|
|
28
41
|
[
|
|
29
42
|
"/verified/pi",
|
|
30
|
-
"--no-extensions",
|
|
31
43
|
"-e",
|
|
32
44
|
"/workspace/.team/runtime/pi/team-a/worker-a/team-mcp.ts",
|
|
33
|
-
"--no-approve",
|
|
34
|
-
"--no-context-files",
|
|
35
|
-
"--no-skills",
|
|
36
|
-
"--no-prompt-templates",
|
|
37
|
-
"--tui-mode",
|
|
38
|
-
"regular",
|
|
39
45
|
"--model",
|
|
40
46
|
"team-agent/qwen3.8-27b",
|
|
41
47
|
"--thinking",
|
|
42
48
|
"high",
|
|
43
|
-
"--system-prompt",
|
|
49
|
+
"--append-system-prompt",
|
|
44
50
|
"frozen worker prompt",
|
|
45
|
-
"--tools",
|
|
46
|
-
"bash,edit,mcp,read,write",
|
|
47
51
|
"--session-dir",
|
|
48
52
|
"/workspace/.team/runtime/pi/team-a/worker-a/sessions",
|
|
49
53
|
"--session-id",
|
|
@@ -88,6 +92,14 @@ fn pi_command_is_exact_regular_tui_and_has_no_ambient_flags() {
|
|
|
88
92
|
"--force",
|
|
89
93
|
"--trust",
|
|
90
94
|
"--sandbox",
|
|
95
|
+
"--no-extensions",
|
|
96
|
+
"--no-approve",
|
|
97
|
+
"--no-context-files",
|
|
98
|
+
"--no-skills",
|
|
99
|
+
"--no-prompt-templates",
|
|
100
|
+
"--tui-mode",
|
|
101
|
+
"--tools",
|
|
102
|
+
"--system-prompt",
|
|
91
103
|
] {
|
|
92
104
|
assert!(
|
|
93
105
|
!argv.iter().any(|arg| arg == forbidden),
|
|
@@ -98,23 +110,26 @@ fn pi_command_is_exact_regular_tui_and_has_no_ambient_flags() {
|
|
|
98
110
|
}
|
|
99
111
|
|
|
100
112
|
#[test]
|
|
101
|
-
fn
|
|
113
|
+
fn pi_command_keeps_direct_plugins_skills_context_auth_and_tools() {
|
|
102
114
|
let argv = build_pi_command_argv(request(PiSessionSelector::Fresh {
|
|
103
115
|
session_id: "76f682b1-ecc3-4586-850f-ab2e2bb04cb3",
|
|
104
116
|
}))
|
|
105
117
|
.expect("fresh Pi command");
|
|
106
118
|
|
|
107
|
-
for
|
|
119
|
+
for forbidden in [
|
|
108
120
|
"--no-context-files",
|
|
109
121
|
"--no-extensions",
|
|
110
122
|
"--no-approve",
|
|
111
123
|
"--no-skills",
|
|
112
124
|
"--no-prompt-templates",
|
|
125
|
+
"--tools",
|
|
126
|
+
"--system-prompt",
|
|
127
|
+
"--tui-mode",
|
|
113
128
|
] {
|
|
114
129
|
assert_eq!(
|
|
115
|
-
argv.iter().filter(|arg| arg.as_str() ==
|
|
116
|
-
|
|
117
|
-
"
|
|
130
|
+
argv.iter().filter(|arg| arg.as_str() == forbidden).count(),
|
|
131
|
+
0,
|
|
132
|
+
"TeamMate Pi must preserve the direct Pi default instead of adding {forbidden}; argv={argv:?}"
|
|
118
133
|
);
|
|
119
134
|
}
|
|
120
135
|
assert!(argv.windows(2).any(|pair| {
|
|
@@ -124,5 +139,7 @@ fn pi_command_uses_no_context_no_extensions_no_approve() {
|
|
|
124
139
|
]
|
|
125
140
|
}));
|
|
126
141
|
assert_eq!(argv.iter().filter(|arg| arg.as_str() == "-e").count(), 1);
|
|
127
|
-
assert!(
|
|
142
|
+
assert!(argv
|
|
143
|
+
.windows(2)
|
|
144
|
+
.any(|pair| pair == ["--append-system-prompt", "frozen worker prompt"]));
|
|
128
145
|
}
|
|
@@ -14,6 +14,13 @@ const CATALOG: &[u8] = include_bytes!("fixtures/pi_list_models_g0.stdout.txt");
|
|
|
14
14
|
static NEXT_ROLE: AtomicU32 = AtomicU32::new(0);
|
|
15
15
|
|
|
16
16
|
fn compile_pi_role(front_matter: &str) -> Result<CompiledRole, ModelError> {
|
|
17
|
+
compile_pi_role_with_team(front_matter, &Value::Map(Vec::new()))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
fn compile_pi_role_with_team(
|
|
21
|
+
front_matter: &str,
|
|
22
|
+
team_meta: &Value,
|
|
23
|
+
) -> Result<CompiledRole, ModelError> {
|
|
17
24
|
let seq = NEXT_ROLE.fetch_add(1, Ordering::Relaxed);
|
|
18
25
|
let root = std::env::temp_dir().join(format!(
|
|
19
26
|
"team-agent-pi-compiler-{}-{seq}",
|
|
@@ -26,7 +33,7 @@ fn compile_pi_role(front_matter: &str) -> Result<CompiledRole, ModelError> {
|
|
|
26
33
|
format!("---\n{front_matter}\n---\nPi worker contract.\n"),
|
|
27
34
|
)
|
|
28
35
|
.expect("write role fixture");
|
|
29
|
-
let result = compile_role_agent(&role,
|
|
36
|
+
let result = compile_role_agent(&role, team_meta, "/workspace");
|
|
30
37
|
std::fs::remove_dir_all(root).expect("remove role fixture root");
|
|
31
38
|
result
|
|
32
39
|
}
|
|
@@ -48,8 +55,8 @@ fn command(tools: &[&str], effort: ProviderEffort, model: &str) -> Result<Vec<St
|
|
|
48
55
|
build_pi_command_argv(PiCommandRequest {
|
|
49
56
|
executable: Path::new("/verified/pi"),
|
|
50
57
|
extension: Path::new("/workspace/.team/runtime/pi/t1/pi-worker/team-mcp.ts"),
|
|
51
|
-
model,
|
|
52
|
-
effort,
|
|
58
|
+
model: Some(model),
|
|
59
|
+
effort: Some(effort),
|
|
53
60
|
system_prompt: "Pi worker contract.",
|
|
54
61
|
tool_categories: tools,
|
|
55
62
|
session_dir: Path::new("/workspace/.team/runtime/pi/t1/pi-worker/sessions"),
|
|
@@ -62,19 +69,15 @@ fn command(tools: &[&str], effort: ProviderEffort, model: &str) -> Result<Vec<St
|
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
#[test]
|
|
65
|
-
fn
|
|
72
|
+
fn pi_role_requires_mcp_team_and_preserves_provider_defaults() {
|
|
66
73
|
let positive = compile_pi_role(&valid_role_with(""));
|
|
67
74
|
assert!(positive.is_ok(), "fully explicit Pi role must compile");
|
|
68
75
|
|
|
69
|
-
let missing_model = valid_role_with("").replace("model: team-agent/qwen3.8-27b\n", "");
|
|
70
|
-
let missing_effort = valid_role_with("").replace("effort: medium\n", "");
|
|
71
76
|
let missing_mcp = valid_role_with("").replace(" - mcp_team\n", "");
|
|
72
77
|
let unqualified =
|
|
73
78
|
valid_role_with("").replace("model: team-agent/qwen3.8-27b", "model: qwen3.8-27b");
|
|
74
79
|
|
|
75
80
|
for (label, role) in [
|
|
76
|
-
("model", missing_model),
|
|
77
|
-
("effort", missing_effort),
|
|
78
81
|
("mcp_team", missing_mcp),
|
|
79
82
|
("qualified exact model", unqualified),
|
|
80
83
|
] {
|
|
@@ -85,13 +88,45 @@ fn pi_role_requires_qualified_model_explicit_effort_and_mcp_team() {
|
|
|
85
88
|
);
|
|
86
89
|
}
|
|
87
90
|
|
|
91
|
+
let defaults = valid_role_with("")
|
|
92
|
+
.replace("model: team-agent/qwen3.8-27b\n", "")
|
|
93
|
+
.replace("effort: medium\n", "");
|
|
94
|
+
let team_meta = Value::Map(vec![
|
|
95
|
+
(
|
|
96
|
+
"provider_models".to_string(),
|
|
97
|
+
Value::Map(vec![(
|
|
98
|
+
"pi".to_string(),
|
|
99
|
+
Value::Str("team-default/forced-model".to_string()),
|
|
100
|
+
)]),
|
|
101
|
+
),
|
|
102
|
+
(
|
|
103
|
+
"default_model".to_string(),
|
|
104
|
+
Value::Str("global-default/forced-model".to_string()),
|
|
105
|
+
),
|
|
106
|
+
(
|
|
107
|
+
"provider_effort".to_string(),
|
|
108
|
+
Value::Str("high".to_string()),
|
|
109
|
+
),
|
|
110
|
+
]);
|
|
111
|
+
let compiled = compile_pi_role_with_team(&defaults, &team_meta)
|
|
112
|
+
.expect("Pi model and effort may use provider defaults");
|
|
113
|
+
assert_eq!(
|
|
114
|
+
compiled.agent.get("model"),
|
|
115
|
+
Some(&Value::Null),
|
|
116
|
+
"omitted Pi model must not inherit a synthesized framework default"
|
|
117
|
+
);
|
|
118
|
+
assert!(
|
|
119
|
+
compiled.agent.get("effort").is_none(),
|
|
120
|
+
"omitted Pi effort must not inherit a synthesized framework default"
|
|
121
|
+
);
|
|
122
|
+
|
|
88
123
|
let models = parse_pi_list_models_table(CATALOG).expect("catalog fixture");
|
|
89
124
|
assert!(select_exact_pi_model(&models, "team-agent/qwen3.8-27b").is_ok());
|
|
90
125
|
assert!(select_exact_pi_model(&models, "foo/bar").is_err());
|
|
91
126
|
}
|
|
92
127
|
|
|
93
128
|
#[test]
|
|
94
|
-
fn
|
|
129
|
+
fn pi_role_uses_standard_bypass_field_without_fabricating_argv() {
|
|
95
130
|
let compiled = compile_pi_role(&valid_role_with("")).expect("true acknowledgement");
|
|
96
131
|
assert_eq!(
|
|
97
132
|
compiled.agent.get("dangerously_skip_permissions"),
|
|
@@ -122,10 +157,16 @@ fn pi_role_requires_intrinsic_unrestricted_ack_true() {
|
|
|
122
157
|
"dangerously_skip_permissions: true",
|
|
123
158
|
"dangerously_skip_permissions: false",
|
|
124
159
|
);
|
|
125
|
-
let
|
|
126
|
-
|
|
160
|
+
let compiled = compile_pi_role(&false_ack).expect("standard false bypass value");
|
|
161
|
+
assert_eq!(
|
|
162
|
+
compiled.agent.get("dangerously_skip_permissions"),
|
|
163
|
+
Some(&Value::Bool(false))
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
let missing_ack = valid_role_with("").replace("dangerously_skip_permissions: true\n", "");
|
|
167
|
+
let text = compile_error(compile_pi_role(&missing_ack)).to_string();
|
|
127
168
|
assert!(
|
|
128
|
-
text.contains("
|
|
169
|
+
text.contains("missing front matter field dangerously_skip_permissions"),
|
|
129
170
|
"got {text}"
|
|
130
171
|
);
|
|
131
172
|
}
|
|
@@ -153,7 +194,7 @@ fn pi_max_effort_is_supported_without_model_suffix() {
|
|
|
153
194
|
}
|
|
154
195
|
|
|
155
196
|
#[test]
|
|
156
|
-
fn
|
|
197
|
+
fn pi_role_tools_validate_team_capabilities_without_replacing_direct_pi_tools() {
|
|
157
198
|
assert_eq!(pi_tool_mapping("mcp_team"), PiToolMapping::Mcp);
|
|
158
199
|
assert_eq!(
|
|
159
200
|
pi_tool_mapping("fs_read"),
|
|
@@ -194,14 +235,9 @@ fn pi_tools_allowlist_keeps_mcp_and_rejects_unknown_categories() {
|
|
|
194
235
|
ProviderEffort::Medium,
|
|
195
236
|
"team-agent/qwen3.8-27b",
|
|
196
237
|
)
|
|
197
|
-
.expect("known
|
|
198
|
-
|
|
199
|
-
.
|
|
200
|
-
|
|
201
|
-
.map(|pair| pair[1].as_str())
|
|
202
|
-
.expect("--tools value");
|
|
203
|
-
assert_eq!(
|
|
204
|
-
tools, "bash,edit,find,grep,ls,mcp,read,write",
|
|
205
|
-
"tools must be sorted and deduplicated"
|
|
238
|
+
.expect("known standard role tools");
|
|
239
|
+
assert!(
|
|
240
|
+
!argv.iter().any(|arg| arg == "--tools"),
|
|
241
|
+
"role tools validate Team Agent capabilities but must not replace direct Pi tools: {argv:?}"
|
|
206
242
|
);
|
|
207
243
|
}
|
|
@@ -224,12 +224,27 @@ fn pi_leader_and_teammate_body(hermetic: &HermeticTestEnv) {
|
|
|
224
224
|
);
|
|
225
225
|
let leader = parse_pi_leader_args(&leader_argv[2..])
|
|
226
226
|
.expect("leader exact model and effort must compile into the shared plan input");
|
|
227
|
-
assert_eq!(leader.model, "team-agent/qwen3.8-27b");
|
|
228
|
-
assert_eq!(leader.effort, ProviderEffort::Max);
|
|
227
|
+
assert_eq!(leader.model.as_deref(), Some("team-agent/qwen3.8-27b"));
|
|
228
|
+
assert_eq!(leader.effort, Some(ProviderEffort::Max));
|
|
229
|
+
|
|
230
|
+
let defaults = parse_pi_leader_args(&[]).expect("leader provider defaults");
|
|
231
|
+
assert_eq!(defaults.model, None);
|
|
232
|
+
assert_eq!(defaults.effort, None);
|
|
233
|
+
assert_eq!(
|
|
234
|
+
parse_pi_leader_args(&["--thinking".to_string(), "medium".to_string()])
|
|
235
|
+
.expect("explicit thinking only")
|
|
236
|
+
.effort,
|
|
237
|
+
Some(ProviderEffort::Medium)
|
|
238
|
+
);
|
|
239
|
+
assert_eq!(
|
|
240
|
+
parse_pi_leader_args(&["--model".to_string(), "team-agent/qwen3.8-27b".to_string(),])
|
|
241
|
+
.expect("explicit model only")
|
|
242
|
+
.model
|
|
243
|
+
.as_deref(),
|
|
244
|
+
Some("team-agent/qwen3.8-27b")
|
|
245
|
+
);
|
|
229
246
|
|
|
230
247
|
for invalid in [
|
|
231
|
-
vec!["--thinking".to_string(), "medium".to_string()],
|
|
232
|
-
vec!["--model".to_string(), "team-agent/qwen3.8-27b".to_string()],
|
|
233
248
|
vec![
|
|
234
249
|
"--model".to_string(),
|
|
235
250
|
"qwen3.8-27b".to_string(),
|
|
@@ -247,7 +262,7 @@ fn pi_leader_and_teammate_body(hermetic: &HermeticTestEnv) {
|
|
|
247
262
|
] {
|
|
248
263
|
assert!(
|
|
249
264
|
parse_pi_leader_args(&invalid).is_err(),
|
|
250
|
-
"leader input must refuse
|
|
265
|
+
"leader input must refuse ambiguous or materializer-owned fields: {invalid:?}"
|
|
251
266
|
);
|
|
252
267
|
}
|
|
253
268
|
|
|
@@ -267,12 +282,15 @@ fn pi_leader_and_teammate_body(hermetic: &HermeticTestEnv) {
|
|
|
267
282
|
|
|
268
283
|
std::fs::write(
|
|
269
284
|
&role,
|
|
270
|
-
"---\nname: worker-a\nrole: developer\nprovider: pi\nauth_mode: subscription\
|
|
285
|
+
"---\nname: worker-a\nrole: developer\nprovider: pi\nauth_mode: subscription\ntools:\n - mcp_team\ndangerously_skip_permissions: true\n---\nworker contract\n",
|
|
271
286
|
)
|
|
272
|
-
.expect("write
|
|
287
|
+
.expect("write provider-default teammate role");
|
|
288
|
+
let defaults = compile_role_agent(&role, &Value::Map(Vec::new()), "/workspace")
|
|
289
|
+
.expect("provider: pi teammate may use Pi model and effort defaults");
|
|
290
|
+
assert_eq!(defaults.agent.get("model"), Some(&Value::Null));
|
|
273
291
|
assert!(
|
|
274
|
-
|
|
275
|
-
"
|
|
292
|
+
defaults.agent.get("effort").is_none(),
|
|
293
|
+
"omitted Pi model and effort must remain absent"
|
|
276
294
|
);
|
|
277
295
|
std::fs::remove_dir_all(root).expect("remove role fixture");
|
|
278
296
|
|