@team-agent/installer 0.5.85 → 0.5.89
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 -2
- package/crates/team-agent/src/cli/emit.rs +56 -12
- package/crates/team-agent/src/cli/mod.rs +0 -1
- package/crates/team-agent/src/cli/status_port/brief.rs +187 -7
- package/crates/team-agent/src/compiler/tests.rs +13 -24
- package/crates/team-agent/src/compiler.rs +5 -57
- package/crates/team-agent/src/leader/start.rs +0 -2
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +0 -1
- package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +0 -1
- package/crates/team-agent/src/lifecycle/launch/agent_state.rs +0 -6
- package/crates/team-agent/src/lifecycle/launch/identity.rs +39 -22
- package/crates/team-agent/src/lifecycle/launch/layout.rs +0 -1
- package/crates/team-agent/src/lifecycle/launch/leader_context.rs +0 -1
- package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +0 -58
- package/crates/team-agent/src/lifecycle/launch/ownership.rs +0 -1
- package/crates/team-agent/src/lifecycle/launch/pi_mcp.rs +0 -3
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +14 -24
- package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +0 -1
- package/crates/team-agent/src/lifecycle/launch/readiness.rs +0 -1
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +5 -9
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +0 -1
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +0 -1
- package/crates/team-agent/src/lifecycle/launch/worker_env.rs +0 -1
- package/crates/team-agent/src/lifecycle/launch.rs +0 -9
- package/crates/team-agent/src/lifecycle/restart/common.rs +5 -9
- package/crates/team-agent/src/lifecycle/tests/b0_reader_hideone_audit_contract.rs +3 -3
- package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +1 -3
- package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +42 -46
- package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +30 -44
- package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +32 -77
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +2 -13
- package/crates/team-agent/src/lifecycle/tests/pi_catalog_red.rs +0 -1
- package/crates/team-agent/src/lifecycle/tests/pi_command_red.rs +0 -2
- package/crates/team-agent/src/lifecycle/tests/pi_compiler_red.rs +14 -85
- package/crates/team-agent/src/lifecycle/tests/pi_dual_entry_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/pi_executable_mcp_red.rs +0 -5
- package/crates/team-agent/src/lifecycle/tests/pi_session_delivery_red.rs +0 -1
- package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +4 -4
- package/crates/team-agent/src/lifecycle/tests.rs +0 -1
- package/crates/team-agent/src/lifecycle/types.rs +0 -10
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +4 -248
- package/crates/team-agent/src/model/enums.rs +17 -38
- package/crates/team-agent/src/model/mod.rs +0 -1
- package/crates/team-agent/src/model/spec.rs +31 -91
- package/crates/team-agent/src/provider/adapter.rs +48 -50
- package/crates/team-agent/src/provider/adapters/claude.rs +5 -30
- package/crates/team-agent/src/provider/adapters/codex.rs +5 -25
- package/crates/team-agent/src/provider/adapters/copilot.rs +8 -41
- package/crates/team-agent/src/provider/adapters/cursor_agent.rs +4 -8
- package/crates/team-agent/src/provider/adapters/grok.rs +5 -58
- package/crates/team-agent/src/provider/adapters/mod.rs +5 -7
- package/crates/team-agent/src/provider/adapters/pi.rs +1 -56
- package/crates/team-agent/src/provider/bypass_flags.rs +1 -2
- package/crates/team-agent/src/provider/session/capture.rs +4 -4
- package/crates/team-agent/src/provider/tests/adapter.rs +7 -24
- package/crates/team-agent/src/provider/types.rs +1 -1
- package/crates/team-agent/src/provider/wire.rs +1 -1
- package/package.json +4 -4
- package/schemas/team.schema.json +4 -9
- package/crates/team-agent/src/lifecycle/tests/g1_silent_faces.rs +0 -784
- package/crates/team-agent/src/model/permissions.rs +0 -721
|
@@ -30,7 +30,6 @@ use std::process::Command;
|
|
|
30
30
|
use crate::lifecycle::*;
|
|
31
31
|
use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
|
|
32
32
|
use crate::model::ids::AgentId;
|
|
33
|
-
use crate::model::permissions::{self, AgentPermissionInput};
|
|
34
33
|
use crate::model::yaml::{self, Value};
|
|
35
34
|
use crate::state::persist::load_runtime_state;
|
|
36
35
|
use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};
|
|
@@ -32,7 +32,6 @@ use crate::lifecycle::profile_launch::parse_provider;
|
|
|
32
32
|
use crate::lifecycle::*;
|
|
33
33
|
use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
|
|
34
34
|
use crate::model::ids::AgentId;
|
|
35
|
-
use crate::model::permissions::{self, AgentPermissionInput};
|
|
36
35
|
use crate::model::yaml::{self, Value};
|
|
37
36
|
use crate::state::persist::load_runtime_state;
|
|
38
37
|
use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};
|
|
@@ -123,11 +122,6 @@ pub(super) fn spawn_agents(
|
|
|
123
122
|
)?;
|
|
124
123
|
let system_prompt =
|
|
125
124
|
crate::lifecycle::worker_command_context::compile_worker_system_prompt(&command_agent)?;
|
|
126
|
-
let tools = crate::lifecycle::worker_command_context::resolved_tool_strings_for_command(
|
|
127
|
-
&command_agent,
|
|
128
|
-
provider,
|
|
129
|
-
)?;
|
|
130
|
-
let resolved_tool_refs: Vec<&str> = tools.iter().map(String::as_str).collect();
|
|
131
125
|
// 0.5.66 bypass 单源:per-agent safety 只服务 env 注入与审计,不参与 argv 决策。
|
|
132
126
|
let safety = effective_runtime_config_for_worker_spawn(agent, provider)?;
|
|
133
127
|
let mcp_team_id = runtime_team_key_for_spec(spec_path, spec, session_name);
|
|
@@ -158,7 +152,7 @@ pub(super) fn spawn_agents(
|
|
|
158
152
|
// 0.4.x provider effort MVP step 4 + 7: resolve effort and emit
|
|
159
153
|
// unsupported warning event when the spec asked for an effort the
|
|
160
154
|
// provider can't satisfy.
|
|
161
|
-
let agent_effort = provider_effort_for_spawn(agent, provider)
|
|
155
|
+
let agent_effort = provider_effort_for_spawn(agent, provider)?;
|
|
162
156
|
if let Some(event_value) = provider_effort_event_if_dropped(agent, provider, agent_id_raw) {
|
|
163
157
|
let _ = crate::event_log::EventLog::new(workspace)
|
|
164
158
|
.write("provider.effort_unsupported", event_value);
|
|
@@ -168,7 +162,10 @@ pub(super) fn spawn_agents(
|
|
|
168
162
|
mcp_config: Some(&mcp_config),
|
|
169
163
|
system_prompt: Some(system_prompt.as_str()),
|
|
170
164
|
model: command_model,
|
|
171
|
-
|
|
165
|
+
dangerously_skip_permissions: matches!(
|
|
166
|
+
agent.get("dangerously_skip_permissions"),
|
|
167
|
+
Some(Value::Bool(true))
|
|
168
|
+
),
|
|
172
169
|
profile_launch: Some(&profile_launch),
|
|
173
170
|
// Layer 1 self-healing (architect probe 2026-06-22): expose
|
|
174
171
|
// agent_id as a display-name hint so Claude / Copilot
|
|
@@ -186,7 +183,6 @@ pub(super) fn spawn_agents(
|
|
|
186
183
|
model: command_model,
|
|
187
184
|
effort: agent_effort,
|
|
188
185
|
system_prompt: &system_prompt,
|
|
189
|
-
tool_categories: &resolved_tool_refs,
|
|
190
186
|
team_mcp_tools: &["send_message", "report_result"],
|
|
191
187
|
mcp_config: &mcp_config,
|
|
192
188
|
session_scope: crate::lifecycle::launch::pi_mcp::PiSessionScope::Isolated,
|
|
@@ -62,7 +62,6 @@ use std::process::Command;
|
|
|
62
62
|
use crate::lifecycle::*;
|
|
63
63
|
use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
|
|
64
64
|
use crate::model::ids::AgentId;
|
|
65
|
-
use crate::model::permissions::{self, AgentPermissionInput};
|
|
66
65
|
use crate::model::yaml::{self, Value};
|
|
67
66
|
use crate::state::persist::load_runtime_state;
|
|
68
67
|
use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};
|
|
@@ -37,7 +37,6 @@ use crate::lifecycle::profile_launch::parse_provider;
|
|
|
37
37
|
use crate::lifecycle::*;
|
|
38
38
|
use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
|
|
39
39
|
use crate::model::ids::AgentId;
|
|
40
|
-
use crate::model::permissions::{self, AgentPermissionInput};
|
|
41
40
|
use crate::model::yaml::{self, Value};
|
|
42
41
|
use crate::state::persist::load_runtime_state;
|
|
43
42
|
use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};
|
|
@@ -30,7 +30,6 @@ use std::process::Command;
|
|
|
30
30
|
use crate::lifecycle::*;
|
|
31
31
|
use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
|
|
32
32
|
use crate::model::ids::AgentId;
|
|
33
|
-
use crate::model::permissions::{self, AgentPermissionInput};
|
|
34
33
|
use crate::model::yaml::{self, Value};
|
|
35
34
|
use crate::state::persist::load_runtime_state;
|
|
36
35
|
use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};
|
|
@@ -17,7 +17,6 @@ use std::process::Command;
|
|
|
17
17
|
|
|
18
18
|
use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
|
|
19
19
|
use crate::model::ids::AgentId;
|
|
20
|
-
use crate::model::permissions::{self, AgentPermissionInput};
|
|
21
20
|
use crate::model::yaml::{self, Value};
|
|
22
21
|
use crate::state::persist::load_runtime_state;
|
|
23
22
|
use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};
|
|
@@ -133,13 +132,6 @@ pub fn launch_with_transport_in_workspace(
|
|
|
133
132
|
session_name.as_str()
|
|
134
133
|
)));
|
|
135
134
|
}
|
|
136
|
-
let permissions = spec_agents(&spec)
|
|
137
|
-
.into_iter()
|
|
138
|
-
.map(|agent| PermissionSummary {
|
|
139
|
-
agent_id: agent,
|
|
140
|
-
raw: serde_json::json!({"source": "compiled_spec"}),
|
|
141
|
-
})
|
|
142
|
-
.collect::<Vec<_>>();
|
|
143
135
|
let routes = spec_routes(&spec);
|
|
144
136
|
let started = if dry_run {
|
|
145
137
|
Vec::new()
|
|
@@ -208,7 +200,6 @@ pub fn launch_with_transport_in_workspace(
|
|
|
208
200
|
dry_run,
|
|
209
201
|
tmux_endpoint: transport.tmux_endpoint(),
|
|
210
202
|
routes,
|
|
211
|
-
permissions,
|
|
212
203
|
leader_receiver_attached: false,
|
|
213
204
|
leader_bind_stage: None,
|
|
214
205
|
leader_bind_reason: None,
|
|
@@ -301,11 +301,6 @@ pub(super) fn spawn_agent_window(
|
|
|
301
301
|
)?;
|
|
302
302
|
let system_prompt =
|
|
303
303
|
crate::lifecycle::worker_command_context::compile_worker_system_prompt(&command_agent)?;
|
|
304
|
-
let tools = crate::lifecycle::worker_command_context::resolved_tool_strings_for_command(
|
|
305
|
-
&command_agent,
|
|
306
|
-
provider,
|
|
307
|
-
)?;
|
|
308
|
-
let resolved_tool_refs: Vec<&str> = tools.iter().map(String::as_str).collect();
|
|
309
304
|
// owner_team_id resolution priority (Issue 2 fix):
|
|
310
305
|
// 1. caller's explicit override (restart paths pass `selected.team_key`)
|
|
311
306
|
// 2. agent row's persisted `owner_team_id` (set by prior launch/restart)
|
|
@@ -360,7 +355,7 @@ pub(super) fn spawn_agent_window(
|
|
|
360
355
|
let command_model = profile_launch.command_overrides.model.as_deref().or(model);
|
|
361
356
|
// 0.4.x provider effort MVP: restart/resume preserves effort from the
|
|
362
357
|
// persisted agent JSON (state's agent["effort"] field, set by launch).
|
|
363
|
-
let restart_effort = crate::lifecycle::launch::provider_effort_for_spawn_json(agent, provider)
|
|
358
|
+
let restart_effort = crate::lifecycle::launch::provider_effort_for_spawn_json(agent, provider)?;
|
|
364
359
|
if let Some(event_value) = crate::lifecycle::launch::provider_effort_event_if_dropped_json(
|
|
365
360
|
agent,
|
|
366
361
|
provider,
|
|
@@ -374,7 +369,10 @@ pub(super) fn spawn_agent_window(
|
|
|
374
369
|
mcp_config: Some(&mcp_config),
|
|
375
370
|
system_prompt: Some(system_prompt.as_str()),
|
|
376
371
|
model: command_model,
|
|
377
|
-
|
|
372
|
+
dangerously_skip_permissions: agent
|
|
373
|
+
.get("dangerously_skip_permissions")
|
|
374
|
+
.and_then(serde_json::Value::as_bool)
|
|
375
|
+
.unwrap_or(false),
|
|
378
376
|
profile_launch: Some(&profile_launch),
|
|
379
377
|
agent_id_hint: Some(agent_id.as_str()),
|
|
380
378
|
effort: restart_effort,
|
|
@@ -399,7 +397,6 @@ pub(super) fn spawn_agent_window(
|
|
|
399
397
|
model: command_model,
|
|
400
398
|
effort: restart_effort,
|
|
401
399
|
system_prompt: &system_prompt,
|
|
402
|
-
tool_categories: &resolved_tool_refs,
|
|
403
400
|
team_mcp_tools: &["send_message", "report_result"],
|
|
404
401
|
mcp_config: &mcp_config,
|
|
405
402
|
session_scope: crate::lifecycle::launch::pi_mcp::PiSessionScope::Isolated,
|
|
@@ -744,7 +741,6 @@ fn merge_command_context_fields(
|
|
|
744
741
|
};
|
|
745
742
|
for field in [
|
|
746
743
|
"role",
|
|
747
|
-
"tools",
|
|
748
744
|
"system_prompt",
|
|
749
745
|
"output_contract",
|
|
750
746
|
"provider",
|
|
@@ -99,17 +99,17 @@ fn stale_snapshot_cannot_flip_status_or_diagnose_ok_readiness() {
|
|
|
99
99
|
let case = B0HideOneCase::new("status-diagnose-hide-one");
|
|
100
100
|
let root = case.current_state();
|
|
101
101
|
save_runtime_state(&case.workspace, &root).expect("seed current root state");
|
|
102
|
-
let
|
|
102
|
+
let tmux = brief_probe::install_native_tmux(
|
|
103
103
|
&case.workspace.join("probe"),
|
|
104
104
|
NEW_ENDPOINT,
|
|
105
105
|
SESSION,
|
|
106
106
|
WORKER,
|
|
107
107
|
"%new",
|
|
108
|
-
"
|
|
108
|
+
"bash",
|
|
109
109
|
);
|
|
110
110
|
let _path = case.env.with_env(
|
|
111
111
|
"PATH",
|
|
112
|
-
&brief_probe::path_with_probe(&
|
|
112
|
+
&brief_probe::path_with_probe(&tmux, std::env::var_os("PATH").as_deref()),
|
|
113
113
|
);
|
|
114
114
|
|
|
115
115
|
let status_before = case.run_json(&[
|
|
@@ -97,15 +97,13 @@ fn managed_profile_command_plan_omits_mcp_config_even_when_mcp_config_is_availab
|
|
|
97
97
|
}
|
|
98
98
|
}),
|
|
99
99
|
};
|
|
100
|
-
let tools = ["mcp_team"];
|
|
101
|
-
|
|
102
100
|
let plan = get_adapter(Provider::Claude)
|
|
103
101
|
.build_command_plan(ProviderCommandContext {
|
|
104
102
|
auth_mode: AuthMode::CompatibleApi,
|
|
105
103
|
mcp_config: Some(&config),
|
|
106
104
|
system_prompt: Some("worker prompt requiring native MCP"),
|
|
107
105
|
model: Some("profile-effective-haiku"),
|
|
108
|
-
|
|
106
|
+
dangerously_skip_permissions: false,
|
|
109
107
|
profile_launch: Some(&profile),
|
|
110
108
|
agent_id_hint: None,
|
|
111
109
|
effort: None,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
//! ---
|
|
2
|
-
//! purpose: 回归——clone-agent
|
|
2
|
+
//! purpose: 回归——clone-agent 分身保留源席 tools,编译 spec 不投影 tools
|
|
3
3
|
//! contract:
|
|
4
4
|
//! provides:
|
|
5
5
|
//! - name: clone_agent_preserves_source_tools
|
|
6
|
-
//! what: 真跑 clone-agent 后,dynamic-role-files
|
|
6
|
+
//! what: 真跑 clone-agent 后,dynamic-role-files 保留源席 tools,runtime spec 不投影 tools
|
|
7
7
|
//! requires:
|
|
8
8
|
//! - name: source-six-set-vs-leader-three-set
|
|
9
9
|
//! what: 同一 team 里 leader 三件套 + 源席六件套的共享冲突面
|
|
@@ -275,6 +275,10 @@ fn role_tools(path: &Path) -> BTreeSet<String> {
|
|
|
275
275
|
.collect()
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
+
fn set_of(items: &[&str]) -> BTreeSet<String> {
|
|
279
|
+
items.iter().map(|s| (*s).to_string()).collect()
|
|
280
|
+
}
|
|
281
|
+
|
|
278
282
|
fn load_runtime_spec(workspace: &Path) -> Value {
|
|
279
283
|
let runtime = workspace.join(".team").join("runtime");
|
|
280
284
|
let spec = std::fs::read_dir(&runtime)
|
|
@@ -287,17 +291,7 @@ fn load_runtime_spec(workspace: &Path) -> Value {
|
|
|
287
291
|
.expect("parse spec")
|
|
288
292
|
}
|
|
289
293
|
|
|
290
|
-
fn
|
|
291
|
-
node.get("tools")
|
|
292
|
-
.and_then(Value::as_list)
|
|
293
|
-
.unwrap_or_else(|| panic!("tools list missing"))
|
|
294
|
-
.iter()
|
|
295
|
-
.filter_map(Value::as_str)
|
|
296
|
-
.map(str::to_string)
|
|
297
|
-
.collect()
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
fn spec_tools(workspace: &Path, agent: &str) -> BTreeSet<String> {
|
|
294
|
+
fn spec_projects_tools(workspace: &Path, agent: &str) -> bool {
|
|
301
295
|
let parsed = load_runtime_spec(workspace);
|
|
302
296
|
let agents = parsed
|
|
303
297
|
.get("agents")
|
|
@@ -305,22 +299,18 @@ fn spec_tools(workspace: &Path, agent: &str) -> BTreeSet<String> {
|
|
|
305
299
|
.expect("spec agents");
|
|
306
300
|
for row in agents {
|
|
307
301
|
if row.get("id").and_then(Value::as_str) == Some(agent) {
|
|
308
|
-
return
|
|
302
|
+
return row.get("tools").is_some();
|
|
309
303
|
}
|
|
310
304
|
}
|
|
311
305
|
panic!("agent {agent} missing from runtime spec");
|
|
312
306
|
}
|
|
313
307
|
|
|
314
|
-
fn
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
fn set_of(items: &[&str]) -> BTreeSet<String> {
|
|
323
|
-
items.iter().map(|s| (*s).to_string()).collect()
|
|
308
|
+
fn leader_spec_projects_tools(workspace: &Path) -> bool {
|
|
309
|
+
load_runtime_spec(workspace)
|
|
310
|
+
.get("leader")
|
|
311
|
+
.expect("spec leader")
|
|
312
|
+
.get("tools")
|
|
313
|
+
.is_some()
|
|
324
314
|
}
|
|
325
315
|
|
|
326
316
|
fn clone_ok(case: &Case, source: &str, dest: &str, phase: &str) {
|
|
@@ -347,7 +337,7 @@ fn clone_ok(case: &Case, source: &str, dest: &str, phase: &str) {
|
|
|
347
337
|
);
|
|
348
338
|
}
|
|
349
339
|
|
|
350
|
-
/// clone 后分身 role
|
|
340
|
+
/// clone 后分身 role 保留源席 tools;编译 spec 不投影 tools。
|
|
351
341
|
#[test]
|
|
352
342
|
fn clone_agent_preserves_source_tools() {
|
|
353
343
|
let case = Case::start();
|
|
@@ -380,21 +370,18 @@ fn clone_agent_preserves_source_tools() {
|
|
|
380
370
|
String::from_utf8_lossy(&qs.stdout)
|
|
381
371
|
);
|
|
382
372
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
set_of(SIX),
|
|
387
|
-
"fixture source must be the six-set"
|
|
373
|
+
assert!(
|
|
374
|
+
!spec_projects_tools(&case.workspace, SOURCE),
|
|
375
|
+
"compiled source spec must not project tools metadata"
|
|
388
376
|
);
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
set_of(THREE),
|
|
393
|
-
"leader must stay the three-set ceiling so the conflict surface exists; leader={leader_spec:?}"
|
|
377
|
+
assert!(
|
|
378
|
+
!leader_spec_projects_tools(&case.workspace),
|
|
379
|
+
"compiled leader spec must not project tools metadata"
|
|
394
380
|
);
|
|
395
381
|
|
|
396
382
|
clone_ok(&case, SOURCE, CLONE, "first_clone");
|
|
397
383
|
|
|
384
|
+
let source_role = role_tools(&case.workspace.join("agents").join(format!("{SOURCE}.md")));
|
|
398
385
|
let clone_role = role_tools(
|
|
399
386
|
&case
|
|
400
387
|
.workspace
|
|
@@ -402,23 +389,32 @@ fn clone_agent_preserves_source_tools() {
|
|
|
402
389
|
.join("dynamic-role-files")
|
|
403
390
|
.join(format!("{CLONE}.md")),
|
|
404
391
|
);
|
|
405
|
-
let clone_spec = spec_tools(&case.workspace, CLONE);
|
|
406
392
|
assert_eq!(
|
|
407
|
-
clone_role,
|
|
408
|
-
"clone role tools must equal source; source={
|
|
393
|
+
clone_role, source_role,
|
|
394
|
+
"clone role tools must equal source; source={source_role:?} clone={clone_role:?}"
|
|
409
395
|
);
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
"clone spec
|
|
396
|
+
assert!(
|
|
397
|
+
!spec_projects_tools(&case.workspace, CLONE),
|
|
398
|
+
"compiled clone spec must not project tools metadata"
|
|
413
399
|
);
|
|
414
400
|
|
|
415
|
-
let
|
|
416
|
-
assert_eq!(
|
|
401
|
+
let narrow_source_role = role_tools(&case.workspace.join("agents").join(format!("{NARROW}.md")));
|
|
402
|
+
assert_eq!(narrow_source_role, set_of(THREE), "narrow source fixture");
|
|
417
403
|
clone_ok(&case, NARROW, NARROW_CLONE, "second_clone");
|
|
418
|
-
let
|
|
404
|
+
let narrow_clone_role = role_tools(
|
|
405
|
+
&case
|
|
406
|
+
.workspace
|
|
407
|
+
.join(".team")
|
|
408
|
+
.join("dynamic-role-files")
|
|
409
|
+
.join(format!("{NARROW_CLONE}.md")),
|
|
410
|
+
);
|
|
419
411
|
assert_eq!(
|
|
420
|
-
|
|
421
|
-
"narrow clone must keep the three-set (preserve, do not widen); got={
|
|
412
|
+
narrow_clone_role, narrow_source_role,
|
|
413
|
+
"narrow clone role must keep the three-set (preserve, do not widen); got={narrow_clone_role:?}"
|
|
414
|
+
);
|
|
415
|
+
assert!(
|
|
416
|
+
!spec_projects_tools(&case.workspace, NARROW_CLONE),
|
|
417
|
+
"compiled narrow clone spec must not project tools metadata"
|
|
422
418
|
);
|
|
423
419
|
|
|
424
420
|
case.shutdown();
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
//! 1. worker argv golden (§C1 + C-5-1/3) — copilot_argv_* tests
|
|
18
18
|
//! 2. per-worker AGENTS.md == compiled prompt — copilot_agents_md_* tests (C-1-1/2,
|
|
19
19
|
//! identity-first per the 0.3.4 B2/D6 lock)
|
|
20
|
-
//! 3.
|
|
20
|
+
//! 3. tools metadata transparency (C-2-1, no prompt permission note or argv deny flags)
|
|
21
21
|
//! 4. fork = structured CapabilityUnsupported (C-4-2/3)
|
|
22
22
|
//! 5. startup recognizer supports copilot trust/ready; turn-state Unknown never idles
|
|
23
23
|
//! (N11, C-3-1/4)
|
|
@@ -62,12 +62,12 @@ const ANCESTRY_KEY: &str = "TEAM_AGENT_TEST_PROCESS_ANCESTRY_ARGV_JSON";
|
|
|
62
62
|
const NEUTRAL_ANCESTRY: &str = "[\"/bin/zsh\"]";
|
|
63
63
|
const COPILOT_TMP_PREFIX: &str = "ta-rs-copilot";
|
|
64
64
|
|
|
65
|
-
/// Face 1+3 (§C1 golden + C-5-3, verdict
|
|
66
|
-
///
|
|
67
|
-
///
|
|
68
|
-
///
|
|
69
|
-
///
|
|
70
|
-
///
|
|
65
|
+
/// Face 1+3 (§C1 golden + C-5-3, verdict case `copilot_argv_golden_per_role_tools`):
|
|
66
|
+
/// a copilot worker (mcp_team only) gets the full §C1 argv — noise control,
|
|
67
|
+
/// inline/`@file` MCP config carrying team_orchestrator, a pre-assigned
|
|
68
|
+
/// `--session-id` equal to the persisted `_pending_session_id`, and `-C <workspace>`.
|
|
69
|
+
/// Tools metadata must not derive deny flags; non-dangerous workers still omit the
|
|
70
|
+
/// --allow-all family flag.
|
|
71
71
|
#[test]
|
|
72
72
|
#[serial(env)]
|
|
73
73
|
fn copilot_argv_golden_restricted_role() {
|
|
@@ -176,21 +176,11 @@ team_orchestrator or @<.team/runtime/mcp/worker_a.json>; got {value:?}"
|
|
|
176
176
|
flag_value(argv, "-C")
|
|
177
177
|
));
|
|
178
178
|
}
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
for denied in ["shell", "write"] {
|
|
183
|
-
if !deny_tools.iter().any(|tool| tool.contains(denied)) {
|
|
184
|
-
failures.push(format!(
|
|
185
|
-
"C-5-3: restricted copilot worker must carry --deny-tool '{denied}'; deny={deny_tools:?}"
|
|
186
|
-
));
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
// RC-15 (C-5-2 v2): network deny is `--deny-tool 'url'` (the help has only shell/
|
|
190
|
-
// write/mcp/url tool kinds; v1's --deny-url is not the v2 form).
|
|
191
|
-
if !deny_tools.iter().any(|tool| tool.contains("url")) {
|
|
179
|
+
// Tools metadata is transparent compatibility data; it must not derive
|
|
180
|
+
// provider-specific deny flags (shell/write/url or any other category).
|
|
181
|
+
if argv.iter().any(|arg| arg == "--deny-tool") {
|
|
192
182
|
failures.push(format!(
|
|
193
|
-
"
|
|
183
|
+
"C-5-3: tools metadata must not derive copilot --deny-tool flags; argv={argv:?}"
|
|
194
184
|
));
|
|
195
185
|
}
|
|
196
186
|
// RC-10 (C-3-5/C-5-2): mcp_team is allowlisted by server name (no approval prompt).
|
|
@@ -289,8 +279,8 @@ used for the dangerous tier; argv={argv:?}"
|
|
|
289
279
|
/// the spawn env must carry COPILOT_CUSTOM_INSTRUCTIONS_DIRS pointing at the
|
|
290
280
|
/// per-worker dir (path contains the agent id; works with NO profile configured), the
|
|
291
281
|
/// AGENTS.md there must BE the compiled worker system prompt (single source: identity
|
|
292
|
-
/// first, runtime contract, role body, output contract,
|
|
293
|
-
///
|
|
282
|
+
/// first, runtime contract, role body, and output contract), and no global
|
|
283
|
+
/// ~/.copilot/AGENTS.md may be written.
|
|
294
284
|
#[test]
|
|
295
285
|
#[serial(env)]
|
|
296
286
|
fn copilot_agents_md_is_the_compiled_prompt_and_env_points_at_it() {
|
|
@@ -362,7 +352,6 @@ with the identity section FIRST; head={:?}",
|
|
|
362
352
|
"# Team Agent Teammate Runtime Contract",
|
|
363
353
|
"COPILOT ROLE BODY SENTINEL",
|
|
364
354
|
"report_result exactly once",
|
|
365
|
-
"Permission note: these tools are prompt-only for this provider and not hard-enforced:",
|
|
366
355
|
] {
|
|
367
356
|
if !agents_md.contains(marker) {
|
|
368
357
|
failures.push(format!(
|
|
@@ -386,14 +375,11 @@ with the identity section FIRST; head={:?}",
|
|
|
386
375
|
);
|
|
387
376
|
}
|
|
388
377
|
|
|
389
|
-
/// Face 3
|
|
390
|
-
///
|
|
391
|
-
/// prompt-only. Observable single-source: the compiled prompt's Permission note (built
|
|
392
|
-
/// from PROVIDER_ENFORCEMENT) for a full-capability role must list exactly
|
|
393
|
-
/// fs_list/fs_read/git_diff/provider_builtin (sorted).
|
|
378
|
+
/// Face 3: tools metadata is transparent compatibility data and must not produce
|
|
379
|
+
/// a provider-specific Permission note in the compiled Copilot prompt.
|
|
394
380
|
#[test]
|
|
395
381
|
#[serial(env)]
|
|
396
|
-
fn
|
|
382
|
+
fn copilot_prompt_omits_removed_permission_note() {
|
|
397
383
|
let _hermetic = enter_hermetic("copilot-permission-note");
|
|
398
384
|
let _guard = EnvGuard::set(&[(ANCESTRY_KEY, NEUTRAL_ANCESTRY)]);
|
|
399
385
|
let ws = tmp_ws("enforcement");
|
|
@@ -428,21 +414,21 @@ fn copilot_permission_note_registers_fs_tools_prompt_only() {
|
|
|
428
414
|
.env
|
|
429
415
|
.get("COPILOT_CUSTOM_INSTRUCTIONS_DIRS")
|
|
430
416
|
.cloned()
|
|
431
|
-
.
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
.
|
|
438
|
-
|
|
417
|
+
.expect("Copilot spawn must carry COPILOT_CUSTOM_INSTRUCTIONS_DIRS");
|
|
418
|
+
assert!(
|
|
419
|
+
!dir.is_empty(),
|
|
420
|
+
"Copilot instructions directory must not be empty"
|
|
421
|
+
);
|
|
422
|
+
let agents_md = std::fs::read_to_string(Path::new(&dir).join("AGENTS.md"))
|
|
423
|
+
.expect("per-worker Copilot AGENTS.md must be readable");
|
|
424
|
+
let identity = "You are Team Agent worker `worker_a` with role `Copilot Worker`.";
|
|
425
|
+
assert!(
|
|
426
|
+
agents_md.starts_with(identity) && agents_md.contains("COPILOT ROLE BODY SENTINEL"),
|
|
427
|
+
"C-2-1: compiled Copilot prompt must include its identity and role body; content={agents_md:?}"
|
|
428
|
+
);
|
|
439
429
|
assert!(
|
|
440
|
-
|
|
441
|
-
"C-2-1:
|
|
442
|
-
fs_read/fs_list/git_diff/provider_builtin as prompt-only (sorted in the Permission \
|
|
443
|
-
note; execute_bash/fs_write/network/mcp_team are hard); note={note_line:?} \
|
|
444
|
-
agents_md_present={}",
|
|
445
|
-
!agents_md.is_empty()
|
|
430
|
+
!agents_md.contains("Permission note:"),
|
|
431
|
+
"C-2-1: removed provider tool enforcement must not emit a Permission note; content={agents_md:?}"
|
|
446
432
|
);
|
|
447
433
|
}
|
|
448
434
|
|