@team-agent/installer 0.5.64 → 0.5.66
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/Cargo.toml +5 -0
- package/crates/team-agent/src/cli/adapters.rs +6 -1
- package/crates/team-agent/src/cli/emit.rs +15 -0
- package/crates/team-agent/src/cli/mod.rs +9 -8
- package/crates/team-agent/src/cli/send/presentation.rs +1 -0
- package/crates/team-agent/src/cli/spec.rs +3 -0
- package/crates/team-agent/src/cli/tests/compile.rs +1 -1
- package/crates/team-agent/src/cli/tests/mod.rs +33 -2
- package/crates/team-agent/src/cli/tests/run_delegation.rs +1 -1
- package/crates/team-agent/src/cli/tests/status_send.rs +13 -3
- package/crates/team-agent/src/cli/tests/verb_validate.rs +1 -1
- package/crates/team-agent/src/cli/types.rs +7 -0
- package/crates/team-agent/src/compiler/tests.rs +23 -20
- package/crates/team-agent/src/compiler.rs +25 -5
- package/crates/team-agent/src/conpty/backend.rs +16 -0
- package/crates/team-agent/src/coordinator/health.rs +17 -0
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +55 -2
- package/crates/team-agent/src/db/migration.rs +2 -1
- package/crates/team-agent/src/db/schema.rs +16 -8
- package/crates/team-agent/src/leader/lease.rs +1 -2
- package/crates/team-agent/src/leader/start.rs +175 -10
- package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +0 -2
- package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +7 -2
- package/crates/team-agent/src/lifecycle/launch/agent_state.rs +137 -2
- package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +1 -2
- package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +5 -0
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +39 -2
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +105 -48
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +0 -2
- package/crates/team-agent/src/lifecycle/launch.rs +117 -21
- package/crates/team-agent/src/lifecycle/restart/agent.rs +89 -3
- package/crates/team-agent/src/lifecycle/restart/common.rs +9 -12
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +59 -15
- package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +4 -4
- package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +4 -4
- package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +24 -32
- package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +13 -11
- package/crates/team-agent/src/lifecycle/tests/core.rs +0 -18
- package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +3 -0
- package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +3 -3
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +17 -30
- package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +4 -4
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +11 -7
- package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +3 -3
- package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests.rs +11 -7
- package/crates/team-agent/src/lifecycle/types.rs +0 -2
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +115 -22
- package/crates/team-agent/src/mcp_server/helpers.rs +1 -0
- package/crates/team-agent/src/messaging/delivery.rs +29 -0
- package/crates/team-agent/src/messaging/helpers.rs +2 -0
- package/crates/team-agent/src/messaging/leader_receiver.rs +12 -0
- package/crates/team-agent/src/messaging/mod.rs +2 -0
- package/crates/team-agent/src/messaging/results.rs +5 -0
- package/crates/team-agent/src/messaging/send.rs +9 -0
- package/crates/team-agent/src/messaging/tests/main_preserved.rs +1 -1
- package/crates/team-agent/src/messaging/tests/runtime.rs +32 -0
- package/crates/team-agent/src/messaging/types.rs +2 -0
- package/crates/team-agent/src/messaging/wait.rs +366 -0
- package/crates/team-agent/src/messaging/watchers.rs +153 -6
- package/crates/team-agent/src/model/spec.rs +129 -6
- package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +3 -1
- package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +3 -1
- package/crates/team-agent/src/model/testdata/team.spec.yaml +3 -1
- package/crates/team-agent/src/provider/adapters/claude.rs +5 -2
- package/crates/team-agent/src/provider/adapters/codex.rs +5 -2
- package/crates/team-agent/src/provider/adapters/copilot.rs +5 -2
- package/crates/team-agent/src/provider/bypass_flags.rs +80 -0
- package/crates/team-agent/src/provider/mod.rs +2 -0
- package/crates/team-agent/src/tmux_backend.rs +35 -12
- package/crates/team-agent/src/transport/test_support.rs +20 -0
- package/crates/team-agent/src/transport.rs +28 -11
- package/package.json +4 -4
- package/crates/team-agent/src/lifecycle/launch/approval.rs +0 -134
- package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +0 -1187
- package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +0 -327
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
|
@@ -57,5 +57,10 @@ serial_test = { version = "3", features = ["file_locks"] }
|
|
|
57
57
|
name = "e2e"
|
|
58
58
|
path = "tests/e2e/main.rs"
|
|
59
59
|
|
|
60
|
+
# 0.5.66 bypass 单源:E2E 验证角色字段 → 运行面 → drift 挡板 → force fresh spawn。
|
|
61
|
+
[[test]]
|
|
62
|
+
name = "e2e_bypass_single_source"
|
|
63
|
+
path = "tests/e2e_bypass_single_source.rs"
|
|
64
|
+
|
|
60
65
|
[lints]
|
|
61
66
|
workspace = true
|
|
@@ -75,7 +75,7 @@ pub fn cmd_init(args: &InitArgs) -> Result<CmdResult, CliError> {
|
|
|
75
75
|
if args.force || !default_agent_path.exists() {
|
|
76
76
|
std::fs::write(
|
|
77
77
|
&default_agent_path,
|
|
78
|
-
"---\nname: worker\nrole: Worker\nprovider: fake\ntools:\n - mcp_team\n---\n\nWait for instructions.\n",
|
|
78
|
+
"---\nname: worker\nrole: Worker\nprovider: fake\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nWait for instructions.\n",
|
|
79
79
|
)?;
|
|
80
80
|
}
|
|
81
81
|
if args.force || !state_path.exists() {
|
|
@@ -523,6 +523,11 @@ pub fn cmd_results(args: &ResultsArgs) -> Result<CmdResult, CliError> {
|
|
|
523
523
|
})
|
|
524
524
|
}
|
|
525
525
|
|
|
526
|
+
pub fn cmd_wait(args: &WaitArgs) -> Result<CmdResult, CliError> {
|
|
527
|
+
let result = messaging::wait_for_result(&args.workspace, &args.task_id)?;
|
|
528
|
+
Ok(CmdResult::from_json(result.to_json(), args.json))
|
|
529
|
+
}
|
|
530
|
+
|
|
526
531
|
/// `cmd_allow_peer_talk`(`parser.py allow-peer-talk`).
|
|
527
532
|
pub fn cmd_allow_peer_talk(args: &AllowPeerTalkArgs) -> Result<CmdResult, CliError> {
|
|
528
533
|
if args.team.is_some() {
|
|
@@ -196,6 +196,7 @@ fn dispatch(command: &str, args: &[String], cwd: &Path) -> Result<ExitCode, CliE
|
|
|
196
196
|
.map(emit_result)
|
|
197
197
|
}
|
|
198
198
|
"results" => cmd_results(&results_args(args, cwd)?).map(emit_result),
|
|
199
|
+
"wait" => cmd_wait(&wait_args(args, cwd)?).map(emit_result),
|
|
199
200
|
"diagnose" => cmd_diagnose(&diagnose_args(args, cwd)).map(emit_result),
|
|
200
201
|
"preflight" => cmd_preflight(&preflight_args(args, cwd)).map(emit_result),
|
|
201
202
|
"wait-ready" => cmd_wait_ready(&wait_ready_args(args, cwd)).map(emit_result),
|
|
@@ -243,6 +244,7 @@ const DISPATCH_COMMANDS: &[&str] = &[
|
|
|
243
244
|
"profile",
|
|
244
245
|
"collect",
|
|
245
246
|
"results",
|
|
247
|
+
"wait",
|
|
246
248
|
"diagnose",
|
|
247
249
|
"preflight",
|
|
248
250
|
"wait-ready",
|
|
@@ -403,6 +405,7 @@ fn command_help(command: Option<&str>) -> String {
|
|
|
403
405
|
Some("profile") => "usage: team-agent profile COMMAND NAME [--workspace WORKSPACE] [--team TEAM] [--auth-mode MODE] [--json]".to_string(),
|
|
404
406
|
Some("collect") => "usage: team-agent collect [--workspace WORKSPACE] [--team TEAM] [--result-file FILE] [--json]".to_string(),
|
|
405
407
|
Some("results") => "usage: team-agent results --case CASE_ID [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
408
|
+
Some("wait") => "usage: team-agent wait --task TASK [--workspace WORKSPACE] [--json]".to_string(),
|
|
406
409
|
Some("diagnose") => "usage: team-agent diagnose [--workspace WORKSPACE] [--team TEAM] [--json]".to_string(),
|
|
407
410
|
Some("preflight") => "usage: team-agent preflight [TEAMDIR] [--json]".to_string(),
|
|
408
411
|
Some("wait-ready") => "usage: team-agent wait-ready [--workspace WORKSPACE] [--team TEAM] [--timeout SECONDS] [--json]".to_string(),
|
|
@@ -1319,6 +1322,18 @@ fn watch_args(args: &[String], cwd: &Path) -> WatchArgs {
|
|
|
1319
1322
|
}
|
|
1320
1323
|
}
|
|
1321
1324
|
|
|
1325
|
+
fn wait_args(args: &[String], cwd: &Path) -> Result<WaitArgs, CliError> {
|
|
1326
|
+
let parsed = parse_args(args);
|
|
1327
|
+
let workspace = workspace(&parsed, cwd);
|
|
1328
|
+
Ok(WaitArgs {
|
|
1329
|
+
task_id: parsed
|
|
1330
|
+
.task
|
|
1331
|
+
.ok_or_else(|| CliError::Usage("wait requires --task <id>".to_string()))?,
|
|
1332
|
+
workspace,
|
|
1333
|
+
json: parsed.json,
|
|
1334
|
+
})
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1322
1337
|
fn approvals_args(args: &[String], cwd: &Path) -> ApprovalsArgs {
|
|
1323
1338
|
let parsed = parse_args(args);
|
|
1324
1339
|
ApprovalsArgs {
|
|
@@ -2245,14 +2245,15 @@ pub mod lifecycle_port {
|
|
|
2245
2245
|
/// runs, where the leader is never in the invoker's ancestry.
|
|
2246
2246
|
///
|
|
2247
2247
|
/// 0.4.x (CR R3): leader shell wrapper interaction. The leader pane's
|
|
2248
|
-
/// controlling process is the `sh -lc
|
|
2249
|
-
///
|
|
2250
|
-
///
|
|
2251
|
-
///
|
|
2252
|
-
///
|
|
2253
|
-
///
|
|
2254
|
-
///
|
|
2255
|
-
/// `leader_fallback_shell_protected_when_provider_exited`
|
|
2248
|
+
/// controlling process is the `sh -lc` wrapper that runs Claude as a
|
|
2249
|
+
/// CHILD. When Claude exits, the wrapper records the marker and replaces
|
|
2250
|
+
/// its command with the inert `/bin/sh -c` tail (which ignores INT/QUIT
|
|
2251
|
+
/// and does not read pane stdin). Because this function protects by
|
|
2252
|
+
/// `pane.pane_pid` (Source 1 & 2), that tail is covered by the same
|
|
2253
|
+
/// protection set — shutdown will NOT treat it as a stray process.
|
|
2254
|
+
/// This protection is currently untested; the former claim that
|
|
2255
|
+
/// `leader_fallback_shell_protected_when_provider_exited` verified it
|
|
2256
|
+
/// referred to a test absent from this repository (A-46).
|
|
2256
2257
|
///
|
|
2257
2258
|
/// Two leader-pane sources(N39 双来源,真机 grounded):
|
|
2258
2259
|
/// 1. **Session prefix**: tmux session starts with `team-agent-leader-`(契约 grounded;
|
|
@@ -108,6 +108,7 @@ pub(super) fn delivery_outcome_json(
|
|
|
108
108
|
"sender": opts.sender,
|
|
109
109
|
"message_id": outcome.message_id,
|
|
110
110
|
"message_status": outcome.message_status.0,
|
|
111
|
+
"ack_forced_off": outcome.ack_forced_off,
|
|
111
112
|
"verification": outcome.verification,
|
|
112
113
|
"stage": outcome.stage.map(delivery_stage_wire),
|
|
113
114
|
"reason": outcome.reason.map(delivery_refusal_wire),
|
|
@@ -78,6 +78,7 @@ pub(crate) enum DispatchKind {
|
|
|
78
78
|
Profile,
|
|
79
79
|
Collect,
|
|
80
80
|
Results,
|
|
81
|
+
Wait,
|
|
81
82
|
Diagnose,
|
|
82
83
|
Preflight,
|
|
83
84
|
WaitReady,
|
|
@@ -123,6 +124,7 @@ pub(crate) const ALL_DISPATCH_KINDS: &[DispatchKind] = &[
|
|
|
123
124
|
DispatchKind::Profile,
|
|
124
125
|
DispatchKind::Collect,
|
|
125
126
|
DispatchKind::Results,
|
|
127
|
+
DispatchKind::Wait,
|
|
126
128
|
DispatchKind::Diagnose,
|
|
127
129
|
DispatchKind::Preflight,
|
|
128
130
|
DispatchKind::WaitReady,
|
|
@@ -162,6 +164,7 @@ pub(crate) const COMMAND_SPECS: &[CommandSpec] = &[
|
|
|
162
164
|
CommandSpec { name: "status", tier: CommandTier::Core, category: CommandCategory::Daily, kind: CommandKind::Dispatch(DispatchKind::Status), summary: "show current team status", usage: "usage: team-agent status [AGENT] [--workspace WORKSPACE] [--team TEAM] [--summary|--json] [--detail]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
|
|
163
165
|
CommandSpec { name: "collect", tier: CommandTier::Core, category: CommandCategory::Daily, kind: CommandKind::Dispatch(DispatchKind::Collect), summary: "collect reported results", usage: "usage: team-agent collect [--workspace WORKSPACE] [--team TEAM] [--result-file FILE] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
|
|
164
166
|
CommandSpec { name: "results", tier: CommandTier::Core, category: CommandCategory::Daily, kind: CommandKind::Dispatch(DispatchKind::Results), summary: "read reported results for a case", usage: "usage: team-agent results --case CASE_ID [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
|
|
167
|
+
CommandSpec { name: "wait", tier: CommandTier::Core, category: CommandCategory::Daily, kind: CommandKind::Dispatch(DispatchKind::Wait), summary: "block until a task result is stored", usage: "usage: team-agent wait --task TASK [--workspace WORKSPACE] [--json]", default_help: false, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
|
|
165
168
|
CommandSpec { name: "restart", tier: CommandTier::Core, category: CommandCategory::TeamLifecycle, kind: CommandKind::Dispatch(DispatchKind::Restart), summary: "restart the selected team", usage: "usage: team-agent restart [WORKSPACE] [--team TEAM] [--allow-fresh] [--session-converge-deadline SECONDS] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, alias_of: None, sunset: None, action: None, governance: None },
|
|
166
169
|
CommandSpec { name: "shutdown", tier: CommandTier::Core, category: CommandCategory::TeamLifecycle, kind: CommandKind::Dispatch(DispatchKind::Shutdown), summary: "stop the selected team", usage: "usage: team-agent shutdown [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
|
|
167
170
|
CommandSpec { name: "add-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::AddAgent), summary: "add or force-recreate a worker", usage: "usage: team-agent add-agent AGENT --role-file FILE [--force] [--workspace WORKSPACE] [--team TEAM] [--no-display] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, alias_of: None, sunset: None, action: None, governance: None },
|
|
@@ -10,7 +10,7 @@ fn compile_team_dir(tag: &str) -> std::path::PathBuf {
|
|
|
10
10
|
.unwrap();
|
|
11
11
|
std::fs::write(
|
|
12
12
|
team.join("agents").join("worker.md"),
|
|
13
|
-
"---\nname: worker\nrole: Worker\nprovider: fake\nmodel: fake\ntools:\n - mcp_team\n---\n\nWorker role.\n",
|
|
13
|
+
"---\nname: worker\nrole: Worker\nprovider: fake\nmodel: fake\ntools:\n - mcp_team\ndangerously_skip_permissions: false\n---\n\nWorker role.\n",
|
|
14
14
|
)
|
|
15
15
|
.unwrap();
|
|
16
16
|
team
|
|
@@ -42,8 +42,39 @@ fn seed_status_workspace() -> std::path::PathBuf {
|
|
|
42
42
|
dir
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
fn seed_send_workspace() -> std::path::PathBuf {
|
|
46
|
+
let dir = std::env::temp_dir().join(format!(
|
|
47
|
+
"ta-cli-send-{}-{}",
|
|
48
|
+
std::process::id(),
|
|
49
|
+
std::time::SystemTime::now()
|
|
50
|
+
.duration_since(std::time::UNIX_EPOCH)
|
|
51
|
+
.unwrap()
|
|
52
|
+
.as_nanos()
|
|
53
|
+
));
|
|
54
|
+
let team = dir.join(".team");
|
|
55
|
+
std::fs::create_dir_all(team.join("runtime")).unwrap();
|
|
56
|
+
let state = json!({
|
|
57
|
+
"active_team_key": "teamA",
|
|
58
|
+
"teams": {
|
|
59
|
+
"teamA": {
|
|
60
|
+
"agents": {
|
|
61
|
+
"alice": {"provider": "codex"},
|
|
62
|
+
"drifted": {"provider": "codex", "status": "session_drift"}
|
|
63
|
+
},
|
|
64
|
+
"tasks": [{"id": "t-1"}]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
std::fs::write(
|
|
69
|
+
team.join("runtime").join("state.json"),
|
|
70
|
+
serde_json::to_vec_pretty(&state).unwrap(),
|
|
71
|
+
)
|
|
72
|
+
.unwrap();
|
|
73
|
+
dir
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const DELEG_VALID_ROLE: &str = "---\nname: implementer\nrole: Implementation Engineer\nprovider: fake\nmodel: fake\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nImplement bounded tasks.\n";
|
|
77
|
+
const DELEG_INVALID_ROLE: &str = "---\nname: broken\nrole: Broken Worker\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nNo provider field.\n";
|
|
47
78
|
const DELEG_TEAM_MD: &str =
|
|
48
79
|
"---\nname: clidelegteam\nobjective: Delegation probe.\nprovider: fake\n---\n\nteam.\n";
|
|
49
80
|
|
|
@@ -182,7 +182,7 @@ fn run_dispatches_quick_start_compiles_spec() {
|
|
|
182
182
|
.unwrap();
|
|
183
183
|
std::fs::write(
|
|
184
184
|
dir.join("agents").join("implementer.md"),
|
|
185
|
-
"---\nname: implementer\nrole: Impl\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nImpl.\n",
|
|
185
|
+
"---\nname: implementer\nrole: Impl\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nImpl.\n",
|
|
186
186
|
)
|
|
187
187
|
.unwrap();
|
|
188
188
|
let argv = vec![
|
|
@@ -453,11 +453,13 @@ fn status_port_status_detail_full_keeps_uncompacted_events() {
|
|
|
453
453
|
// =========================================================================
|
|
454
454
|
|
|
455
455
|
fn send_args_fixture() -> SendArgs {
|
|
456
|
+
let workspace = seed_send_workspace();
|
|
457
|
+
let _ = crate::message_store::MessageStore::open(&workspace).unwrap();
|
|
456
458
|
SendArgs {
|
|
457
459
|
target: Some("alice".into()),
|
|
458
460
|
message: vec!["hello".into(), "world".into(), "foo".into()],
|
|
459
461
|
targets: None,
|
|
460
|
-
workspace
|
|
462
|
+
workspace,
|
|
461
463
|
team: Some("teamA".into()),
|
|
462
464
|
task: Some("t-1".into()),
|
|
463
465
|
sender: TrustedSender::leader(),
|
|
@@ -565,9 +567,17 @@ fn cmd_send_joins_message_with_single_space() {
|
|
|
565
567
|
CmdOutput::Json(ref v) => {
|
|
566
568
|
assert!(v.get("ok").is_some(), "send result Json must carry `ok`");
|
|
567
569
|
if v.get("ok").and_then(|ok| ok.as_bool()) == Some(true) {
|
|
570
|
+
let delivered = v.get("delivered").and_then(|x| x.as_bool()) == Some(true);
|
|
571
|
+
// hermetic tier 假设:此 fixture 无法产生真实 tmux Delivered。
|
|
572
|
+
// 若 delivered=true 命中说明 tier 前提变了(例如 seed 开了 Delivered 通路),
|
|
573
|
+
// 应立即失败以强制重新审视,而不是悄悄跑一条从未验过的分支。
|
|
574
|
+
assert!(
|
|
575
|
+
!delivered,
|
|
576
|
+
"hermetic seed 不应产生 delivered=true;tier 假设变了"
|
|
577
|
+
);
|
|
568
578
|
assert_eq!(
|
|
569
579
|
v.get("reminder").and_then(|reminder| reminder.as_str()),
|
|
570
|
-
Some(
|
|
580
|
+
Some("Message queued; coordinator will notify when the worker receives it. Do not poll the worker terminal with capture-pane.")
|
|
571
581
|
);
|
|
572
582
|
}
|
|
573
583
|
}
|
|
@@ -689,7 +699,7 @@ fn cmd_send_failed_outcome_yields_error_exit() {
|
|
|
689
699
|
// DeliveryOutcome ok=false (e.g. refused) -> from_json -> ExitCode::Error (parser.py:507).
|
|
690
700
|
// A failed send to a target must propagate non-zero exit reporting through CmdResult.
|
|
691
701
|
let args = SendArgs {
|
|
692
|
-
target: Some("
|
|
702
|
+
target: Some("drifted".into()),
|
|
693
703
|
no_ack: false,
|
|
694
704
|
no_wait: false,
|
|
695
705
|
watch_result: false,
|
|
@@ -677,6 +677,13 @@ pub struct ResultsArgs {
|
|
|
677
677
|
pub json: bool,
|
|
678
678
|
}
|
|
679
679
|
|
|
680
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
681
|
+
pub struct WaitArgs {
|
|
682
|
+
pub task_id: String,
|
|
683
|
+
pub workspace: PathBuf,
|
|
684
|
+
pub json: bool,
|
|
685
|
+
}
|
|
686
|
+
|
|
680
687
|
/// `diagnose`(`parser.py:298`) runtime health report, distinct from `doctor`.
|
|
681
688
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
682
689
|
pub struct DiagnoseArgs {
|
|
@@ -105,6 +105,7 @@ role: Implementation Engineer
|
|
|
105
105
|
provider: codex
|
|
106
106
|
model: gpt-5.5
|
|
107
107
|
auth_mode: subscription
|
|
108
|
+
dangerously_skip_permissions: false
|
|
108
109
|
tools:
|
|
109
110
|
- fs_read
|
|
110
111
|
- fs_write
|
|
@@ -189,7 +190,7 @@ fn front_matter_non_object_errors() {
|
|
|
189
190
|
// separators=(",",":"))` with the workspace path templated to __WS__.
|
|
190
191
|
// (team-agent-public v0.2.11, /tmp/probe_compiler.py.)
|
|
191
192
|
|
|
192
|
-
const BASE_NOPROFILE_JSON: &str = r#"{"version":1,"team":{"name":"doc-team","mode":"supervisor_worker","objective":"Compile role docs.","workspace":"__WS__"},"leader":{"id":"leader","role":"leader","provider":"codex","model":"gpt-5.5","tools":["fs_read","fs_list","mcp_team"],"context_policy":{"keep_user_thread":true,"receive_worker_outputs":"business_messages_and_short_summaries","max_worker_result_tokens":2000}},"agents":[{"id":"implementer","role":"Implementation Engineer","provider":"codex","model":"gpt-5.5","auth_mode":"subscription","working_directory":"__WS__","system_prompt":{"inline":"Implement bounded tasks and report result_envelope_v1.","file":null},"tools":["fs_read","fs_write","execute_bash","mcp_team"],"
|
|
193
|
+
const BASE_NOPROFILE_JSON: &str = r#"{"version":1,"team":{"name":"doc-team","mode":"supervisor_worker","objective":"Compile role docs.","workspace":"__WS__"},"leader":{"id":"leader","role":"leader","provider":"codex","model":"gpt-5.5","tools":["fs_read","fs_list","mcp_team"],"context_policy":{"keep_user_thread":true,"receive_worker_outputs":"business_messages_and_short_summaries","max_worker_result_tokens":2000}},"agents":[{"id":"implementer","role":"Implementation Engineer","provider":"codex","model":"gpt-5.5","auth_mode":"subscription","working_directory":"__WS__","system_prompt":{"inline":"Implement bounded tasks and report result_envelope_v1.","file":null},"tools":["fs_read","fs_write","execute_bash","mcp_team"],"dangerously_skip_permissions":false,"communication_mode":"leader_centric","preferred_for":["implementer","Implementation Engineer"],"avoid_for":[],"output_contract":{"format":"result_envelope_v1","required_fields":["task_id","status","summary","artifacts"]}}],"routing":{"default_assignee":"implementer","rules":[{"id":"route-implementer","match":{"assignee":["implementer"]},"assign_to":"implementer","priority":10}]},"communication":{"protocol":"mcp_inbox","topology":"leader_centered","worker_to_worker":true,"ack_timeout_sec":60,"result_format":"result_envelope_v1","message_store":{"sqlite":".team/runtime/team.db","mirror_files":".team/messages"}},"runtime":{"backend":"tmux","display_backend":"adaptive","session_name":"team-doc-team","auto_launch":true,"require_user_approval_before_launch":true,"max_active_agents":1,"startup_order":["implementer"],"fast":false,"tick_interval_sec":2,"push_min_interval_sec":60,"stuck_timeout_sec":300},"context":{"state_file":"team_state.md","artifact_dir":".team/artifacts","log_dir":".team/logs","summarization":{"worker_full_logs":"retain_outside_leader_context","state_update":"after_each_result"}},"tasks":[{"id":"task_initial","title":"Initial document-driven team task","type":"implementation","assignee":"implementer","deps":[],"acceptance":["Worker reports valid result_envelope_v1"],"status":"pending","requires_tools":["mcp_team"],"files":[],"risk":"low"}]}"#;
|
|
193
194
|
|
|
194
195
|
#[test]
|
|
195
196
|
fn compile_base_noprofile_matches_python_dict_order_and_values() {
|
|
@@ -254,12 +255,13 @@ provider: codex
|
|
|
254
255
|
tools:
|
|
255
256
|
- fs_read
|
|
256
257
|
- mcp_team
|
|
258
|
+
dangerously_skip_permissions: false
|
|
257
259
|
---
|
|
258
260
|
|
|
259
261
|
Implement bounded tasks.
|
|
260
262
|
";
|
|
261
263
|
|
|
262
|
-
const RUNTIME_DEFAULTS_JSON: &str = r#"{"version":1,"team":{"name":"doc-team","mode":"supervisor_worker","objective":"Compile role docs.","workspace":"__WS__"},"leader":{"id":"leader","role":"leader","provider":"codex","model":null,"tools":["fs_read","fs_list","mcp_team"],"context_policy":{"keep_user_thread":true,"receive_worker_outputs":"business_messages_and_short_summaries","max_worker_result_tokens":2000}},"agents":[{"id":"implementer","role":"Implementation Engineer","provider":"codex","model":"gpt-5.4","auth_mode":"subscription","working_directory":"__WS__","system_prompt":{"inline":"Implement bounded tasks.","file":null},"tools":["fs_read","mcp_team"],"
|
|
264
|
+
const RUNTIME_DEFAULTS_JSON: &str = r#"{"version":1,"team":{"name":"doc-team","mode":"supervisor_worker","objective":"Compile role docs.","workspace":"__WS__"},"leader":{"id":"leader","role":"leader","provider":"codex","model":null,"tools":["fs_read","fs_list","mcp_team"],"context_policy":{"keep_user_thread":true,"receive_worker_outputs":"business_messages_and_short_summaries","max_worker_result_tokens":2000}},"agents":[{"id":"implementer","role":"Implementation Engineer","provider":"codex","model":"gpt-5.4","auth_mode":"subscription","working_directory":"__WS__","system_prompt":{"inline":"Implement bounded tasks.","file":null},"tools":["fs_read","mcp_team"],"dangerously_skip_permissions":false,"communication_mode":"leader_centric","preferred_for":["implementer","Implementation Engineer"],"avoid_for":[],"output_contract":{"format":"result_envelope_v1","required_fields":["task_id","status","summary","artifacts"]}}],"routing":{"default_assignee":"implementer","rules":[{"id":"route-implementer","match":{"assignee":["implementer"]},"assign_to":"implementer","priority":10}]},"communication":{"protocol":"mcp_inbox","topology":"leader_centered","worker_to_worker":true,"ack_timeout_sec":60,"result_format":"result_envelope_v1","message_store":{"sqlite":".team/runtime/team.db","mirror_files":".team/messages"}},"runtime":{"backend":"tmux","display_backend":"ghostty_window","session_name":"team-doc-team","auto_launch":true,"require_user_approval_before_launch":true,"max_active_agents":1,"startup_order":["implementer"],"fast":true,"tick_interval_sec":1,"push_min_interval_sec":3,"stuck_timeout_sec":5},"context":{"state_file":"team_state.md","artifact_dir":".team/artifacts","log_dir":".team/logs","summarization":{"worker_full_logs":"retain_outside_leader_context","state_update":"after_each_result"}},"tasks":[{"id":"task_initial","title":"Initial document-driven team task","type":"implementation","assignee":"implementer","deps":[],"acceptance":["Worker reports valid result_envelope_v1"],"status":"pending","requires_tools":["mcp_team"],"files":[],"risk":"low"}]}"#;
|
|
263
265
|
|
|
264
266
|
#[test]
|
|
265
267
|
fn compile_runtime_front_matter_defaults_match_python() {
|
|
@@ -294,12 +296,13 @@ role: Editor and Defender
|
|
|
294
296
|
provider: claude_code
|
|
295
297
|
tools:
|
|
296
298
|
- mcp_team
|
|
299
|
+
dangerously_skip_permissions: false
|
|
297
300
|
---
|
|
298
301
|
|
|
299
302
|
Edit and defend the argument.
|
|
300
303
|
";
|
|
301
304
|
|
|
302
|
-
const ALIAS_JSON: &str = r#"{"version":1,"team":{"name":"debate-team","mode":"supervisor_worker","objective":"Compile thin role docs.","workspace":"__WS__"},"leader":{"id":"leader","role":"leader","provider":"codex","model":null,"tools":["fs_read","fs_list","mcp_team"],"context_policy":{"keep_user_thread":true,"receive_worker_outputs":"business_messages_and_short_summaries","max_worker_result_tokens":2000}},"agents":[{"id":"editor","role":"Editor and Defender","provider":"claude_code","model":"claude-sonnet-4-6","auth_mode":"subscription","working_directory":"__WS__","system_prompt":{"inline":"Edit and defend the argument.","file":null},"tools":["mcp_team"],"
|
|
305
|
+
const ALIAS_JSON: &str = r#"{"version":1,"team":{"name":"debate-team","mode":"supervisor_worker","objective":"Compile thin role docs.","workspace":"__WS__"},"leader":{"id":"leader","role":"leader","provider":"codex","model":null,"tools":["fs_read","fs_list","mcp_team"],"context_policy":{"keep_user_thread":true,"receive_worker_outputs":"business_messages_and_short_summaries","max_worker_result_tokens":2000}},"agents":[{"id":"editor","role":"Editor and Defender","provider":"claude_code","model":"claude-sonnet-4-6","auth_mode":"subscription","working_directory":"__WS__","system_prompt":{"inline":"Edit and defend the argument.","file":null},"tools":["mcp_team"],"dangerously_skip_permissions":false,"communication_mode":"leader_centric","preferred_for":["editor","Editor and Defender"],"avoid_for":[],"output_contract":{"format":"result_envelope_v1","required_fields":["task_id","status","summary","artifacts"]}}],"routing":{"default_assignee":"editor","rules":[{"id":"route-editor","match":{"assignee":["editor"]},"assign_to":"editor","priority":10}]},"communication":{"protocol":"mcp_inbox","topology":"leader_centered","worker_to_worker":true,"ack_timeout_sec":60,"result_format":"result_envelope_v1","message_store":{"sqlite":".team/runtime/team.db","mirror_files":".team/messages"}},"runtime":{"backend":"tmux","display_backend":"none","session_name":"team-debate-team","auto_launch":true,"require_user_approval_before_launch":true,"max_active_agents":1,"startup_order":["editor"],"fast":false,"tick_interval_sec":2,"push_min_interval_sec":60,"stuck_timeout_sec":300},"context":{"state_file":"team_state.md","artifact_dir":".team/artifacts","log_dir":".team/logs","summarization":{"worker_full_logs":"retain_outside_leader_context","state_update":"after_each_result"}},"tasks":[{"id":"task_initial","title":"Initial document-driven team task","type":"implementation","assignee":"editor","deps":[],"acceptance":["Worker reports valid result_envelope_v1"],"status":"pending","requires_tools":["mcp_team"],"files":[],"risk":"low"}]}"#;
|
|
303
306
|
|
|
304
307
|
#[test]
|
|
305
308
|
fn compile_provider_models_claude_code_falls_back_to_claude_alias() {
|
|
@@ -325,6 +328,7 @@ name: implementer
|
|
|
325
328
|
role: Implementation Engineer
|
|
326
329
|
provider: codex
|
|
327
330
|
auth_mode: subscription
|
|
331
|
+
dangerously_skip_permissions: false
|
|
328
332
|
tools:
|
|
329
333
|
- mcp_team
|
|
330
334
|
---
|
|
@@ -332,7 +336,7 @@ tools:
|
|
|
332
336
|
Implement bounded tasks.
|
|
333
337
|
";
|
|
334
338
|
|
|
335
|
-
const BUILTIN_JSON: &str = r#"{"version":1,"team":{"name":"default-model-team","mode":"supervisor_worker","objective":"Compile role docs without model fields.","workspace":"__WS__"},"leader":{"id":"leader","role":"leader","provider":"codex","model":null,"tools":["fs_read","fs_list","mcp_team"],"context_policy":{"keep_user_thread":true,"receive_worker_outputs":"business_messages_and_short_summaries","max_worker_result_tokens":2000}},"agents":[{"id":"implementer","role":"Implementation Engineer","provider":"codex","model":"gpt-5.5","auth_mode":"subscription","working_directory":"__WS__","system_prompt":{"inline":"Implement bounded tasks.","file":null},"tools":["mcp_team"],"
|
|
339
|
+
const BUILTIN_JSON: &str = r#"{"version":1,"team":{"name":"default-model-team","mode":"supervisor_worker","objective":"Compile role docs without model fields.","workspace":"__WS__"},"leader":{"id":"leader","role":"leader","provider":"codex","model":null,"tools":["fs_read","fs_list","mcp_team"],"context_policy":{"keep_user_thread":true,"receive_worker_outputs":"business_messages_and_short_summaries","max_worker_result_tokens":2000}},"agents":[{"id":"implementer","role":"Implementation Engineer","provider":"codex","model":"gpt-5.5","auth_mode":"subscription","working_directory":"__WS__","system_prompt":{"inline":"Implement bounded tasks.","file":null},"tools":["mcp_team"],"dangerously_skip_permissions":false,"communication_mode":"leader_centric","preferred_for":["implementer","Implementation Engineer"],"avoid_for":[],"output_contract":{"format":"result_envelope_v1","required_fields":["task_id","status","summary","artifacts"]}}],"routing":{"default_assignee":"implementer","rules":[{"id":"route-implementer","match":{"assignee":["implementer"]},"assign_to":"implementer","priority":10}]},"communication":{"protocol":"mcp_inbox","topology":"leader_centered","worker_to_worker":true,"ack_timeout_sec":60,"result_format":"result_envelope_v1","message_store":{"sqlite":".team/runtime/team.db","mirror_files":".team/messages"}},"runtime":{"backend":"tmux","display_backend":"none","session_name":"team-default-model-team","auto_launch":true,"require_user_approval_before_launch":true,"max_active_agents":1,"startup_order":["implementer"],"fast":false,"tick_interval_sec":2,"push_min_interval_sec":60,"stuck_timeout_sec":300},"context":{"state_file":"team_state.md","artifact_dir":".team/artifacts","log_dir":".team/logs","summarization":{"worker_full_logs":"retain_outside_leader_context","state_update":"after_each_result"}},"tasks":[{"id":"task_initial","title":"Initial document-driven team task","type":"implementation","assignee":"implementer","deps":[],"acceptance":["Worker reports valid result_envelope_v1"],"status":"pending","requires_tools":["mcp_team"],"files":[],"risk":"low"}]}"#;
|
|
336
340
|
|
|
337
341
|
#[test]
|
|
338
342
|
fn compile_subscription_without_model_uses_builtin_provider_default() {
|
|
@@ -351,6 +355,7 @@ role: Alpha Worker
|
|
|
351
355
|
provider: codex
|
|
352
356
|
model: gpt-5.5
|
|
353
357
|
auth_mode: subscription
|
|
358
|
+
dangerously_skip_permissions: false
|
|
354
359
|
tools:
|
|
355
360
|
- mcp_team
|
|
356
361
|
---
|
|
@@ -365,6 +370,7 @@ role: Bravo Worker
|
|
|
365
370
|
provider: codex
|
|
366
371
|
model: gpt-5.5
|
|
367
372
|
auth_mode: subscription
|
|
373
|
+
dangerously_skip_permissions: false
|
|
368
374
|
tools:
|
|
369
375
|
- mcp_team
|
|
370
376
|
---
|
|
@@ -372,7 +378,7 @@ tools:
|
|
|
372
378
|
Bravo body.
|
|
373
379
|
";
|
|
374
380
|
|
|
375
|
-
const TWO_AGENTS_JSON: &str = r#"{"version":1,"team":{"name":"doc-team","mode":"supervisor_worker","objective":"Compile role docs.","workspace":"__WS__"},"leader":{"id":"leader","role":"leader","provider":"codex","model":"gpt-5.5","tools":["fs_read","fs_list","mcp_team"],"context_policy":{"keep_user_thread":true,"receive_worker_outputs":"business_messages_and_short_summaries","max_worker_result_tokens":2000}},"agents":[{"id":"alpha","role":"Alpha Worker","provider":"codex","model":"gpt-5.5","auth_mode":"subscription","working_directory":"__WS__","system_prompt":{"inline":"Alpha body.","file":null},"tools":["mcp_team"],"
|
|
381
|
+
const TWO_AGENTS_JSON: &str = r#"{"version":1,"team":{"name":"doc-team","mode":"supervisor_worker","objective":"Compile role docs.","workspace":"__WS__"},"leader":{"id":"leader","role":"leader","provider":"codex","model":"gpt-5.5","tools":["fs_read","fs_list","mcp_team"],"context_policy":{"keep_user_thread":true,"receive_worker_outputs":"business_messages_and_short_summaries","max_worker_result_tokens":2000}},"agents":[{"id":"alpha","role":"Alpha Worker","provider":"codex","model":"gpt-5.5","auth_mode":"subscription","working_directory":"__WS__","system_prompt":{"inline":"Alpha body.","file":null},"tools":["mcp_team"],"dangerously_skip_permissions":false,"communication_mode":"leader_centric","preferred_for":["alpha","Alpha Worker"],"avoid_for":[],"output_contract":{"format":"result_envelope_v1","required_fields":["task_id","status","summary","artifacts"]}},{"id":"bravo","role":"Bravo Worker","provider":"codex","model":"gpt-5.5","auth_mode":"subscription","working_directory":"__WS__","system_prompt":{"inline":"Bravo body.","file":null},"tools":["mcp_team"],"dangerously_skip_permissions":false,"communication_mode":"leader_centric","preferred_for":["bravo","Bravo Worker"],"avoid_for":[],"output_contract":{"format":"result_envelope_v1","required_fields":["task_id","status","summary","artifacts"]}}],"routing":{"default_assignee":"alpha","rules":[{"id":"route-alpha","match":{"assignee":["alpha"]},"assign_to":"alpha","priority":10},{"id":"route-bravo","match":{"assignee":["bravo"]},"assign_to":"bravo","priority":10}]},"communication":{"protocol":"mcp_inbox","topology":"leader_centered","worker_to_worker":true,"ack_timeout_sec":60,"result_format":"result_envelope_v1","message_store":{"sqlite":".team/runtime/team.db","mirror_files":".team/messages"}},"runtime":{"backend":"tmux","display_backend":"adaptive","session_name":"team-doc-team","auto_launch":true,"require_user_approval_before_launch":true,"max_active_agents":2,"startup_order":["alpha","bravo"],"fast":false,"tick_interval_sec":2,"push_min_interval_sec":60,"stuck_timeout_sec":300},"context":{"state_file":"team_state.md","artifact_dir":".team/artifacts","log_dir":".team/logs","summarization":{"worker_full_logs":"retain_outside_leader_context","state_update":"after_each_result"}},"tasks":[{"id":"task_initial","title":"Initial document-driven team task","type":"implementation","assignee":"alpha","deps":[],"acceptance":["Worker reports valid result_envelope_v1"],"status":"pending","requires_tools":["mcp_team"],"files":[],"risk":"low"}]}"#;
|
|
376
382
|
|
|
377
383
|
#[test]
|
|
378
384
|
fn compile_two_agents_sorted_by_filename_with_routing_and_startup_order() {
|
|
@@ -394,6 +400,7 @@ name: implementer
|
|
|
394
400
|
role: Implementation Engineer
|
|
395
401
|
model: gpt-5.5
|
|
396
402
|
auth_mode: subscription
|
|
403
|
+
dangerously_skip_permissions: false
|
|
397
404
|
tools:
|
|
398
405
|
- mcp_team
|
|
399
406
|
---
|
|
@@ -419,6 +426,7 @@ role: Implementation Engineer
|
|
|
419
426
|
provider: codex
|
|
420
427
|
model: gpt-5.5
|
|
421
428
|
auth_mode: compatible_api
|
|
429
|
+
dangerously_skip_permissions: false
|
|
422
430
|
tools:
|
|
423
431
|
- mcp_team
|
|
424
432
|
---
|
|
@@ -448,7 +456,7 @@ fn compile_compatible_api_without_profile_errors() {
|
|
|
448
456
|
const TM_CODEX: &str = "---\nname: T\nprovider: codex\n---\nx\n";
|
|
449
457
|
|
|
450
458
|
fn role_nomodel(provider: &str) -> String {
|
|
451
|
-
format!("---\nname: w\nrole: R\nprovider: {provider}\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nbody\n")
|
|
459
|
+
format!("---\nname: w\nrole: R\nprovider: {provider}\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nbody\n")
|
|
452
460
|
}
|
|
453
461
|
|
|
454
462
|
fn agent0(spec: &Value) -> &Value {
|
|
@@ -626,7 +634,7 @@ fn fix_a5_session_name_override_wins() {
|
|
|
626
634
|
|
|
627
635
|
#[test]
|
|
628
636
|
fn fix_a6_tools_shell_maps_to_execute_bash() {
|
|
629
|
-
let role = "---\nname: w\nrole: R\nprovider: codex\nauth_mode: subscription\ntools:\n - shell\n - mcp_team\n---\nb\n";
|
|
637
|
+
let role = "---\nname: w\nrole: R\nprovider: codex\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - shell\n - mcp_team\n---\nb\n";
|
|
630
638
|
let team = build_team(TM_CODEX, &[("w.md", role)], &[]);
|
|
631
639
|
let spec = compile_team(&team).expect("shell must normalize to execute_bash and compile");
|
|
632
640
|
assert_eq!(
|
|
@@ -637,7 +645,7 @@ fn fix_a6_tools_shell_maps_to_execute_bash() {
|
|
|
637
645
|
|
|
638
646
|
#[test]
|
|
639
647
|
fn fix_a6_missing_tools_errors() {
|
|
640
|
-
let role = "---\nname: w\nrole: R\nprovider: codex\nauth_mode: subscription\n---\nb\n";
|
|
648
|
+
let role = "---\nname: w\nrole: R\nprovider: codex\nauth_mode: subscription\ndangerously_skip_permissions: false\n---\nb\n";
|
|
641
649
|
let team = build_team(TM_CODEX, &[("w.md", role)], &[]);
|
|
642
650
|
let err = compile_team(&team).unwrap_err();
|
|
643
651
|
assert!(
|
|
@@ -648,7 +656,7 @@ fn fix_a6_missing_tools_errors() {
|
|
|
648
656
|
|
|
649
657
|
#[test]
|
|
650
658
|
fn fix_a6_tools_not_a_list_errors() {
|
|
651
|
-
let role = "---\nname: w\nrole: R\nprovider: codex\nauth_mode: subscription\ntools: justastring\n---\nb\n";
|
|
659
|
+
let role = "---\nname: w\nrole: R\nprovider: codex\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools: justastring\n---\nb\n";
|
|
652
660
|
let team = build_team(TM_CODEX, &[("w.md", role)], &[]);
|
|
653
661
|
let err = compile_team(&team).unwrap_err();
|
|
654
662
|
assert!(
|
|
@@ -661,7 +669,7 @@ fn fix_a6_tools_not_a_list_errors() {
|
|
|
661
669
|
|
|
662
670
|
#[test]
|
|
663
671
|
fn fix_a7_empty_body_inline_falls_back_to_role() {
|
|
664
|
-
let role = "---\nname: w\nrole: Reviewer Role\nprovider: codex\nauth_mode: subscription\ntools:\n - mcp_team\n---\n";
|
|
672
|
+
let role = "---\nname: w\nrole: Reviewer Role\nprovider: codex\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n";
|
|
665
673
|
let team = build_team(TM_CODEX, &[("w.md", role)], &[]);
|
|
666
674
|
let spec = compile_team(&team).unwrap();
|
|
667
675
|
assert_eq!(
|
|
@@ -675,7 +683,7 @@ fn fix_a7_empty_body_inline_falls_back_to_role() {
|
|
|
675
683
|
#[test]
|
|
676
684
|
fn fix_a8_official_api_without_profile_errors() {
|
|
677
685
|
// official_api (any non-subscription) without profile MUST NOT silently compile.
|
|
678
|
-
let role = "---\nname: w\nrole: R\nprovider: codex\nauth_mode: official_api\ntools:\n - mcp_team\n---\nb\n";
|
|
686
|
+
let role = "---\nname: w\nrole: R\nprovider: codex\nauth_mode: official_api\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\nb\n";
|
|
679
687
|
let team = build_team(TM_CODEX, &[("w.md", role)], &[]);
|
|
680
688
|
let err = compile_team(&team).unwrap_err();
|
|
681
689
|
assert!(
|
|
@@ -706,14 +714,9 @@ fn fix_a8_compatible_api_error_names_the_auth_mode() {
|
|
|
706
714
|
#[test]
|
|
707
715
|
fn fix_a9_bool_coercion_yes_and_one_are_true() {
|
|
708
716
|
for v in ["yes", "1"] {
|
|
709
|
-
let tm = format!("---\nname: T\nprovider: codex\
|
|
717
|
+
let tm = format!("---\nname: T\nprovider: codex\nworker_to_worker: {v}\n---\nx\n");
|
|
710
718
|
let team = build_team(&tm, &[("w.md", &role_nomodel("codex"))], &[]);
|
|
711
719
|
let spec = compile_team(&team).unwrap();
|
|
712
|
-
assert_eq!(
|
|
713
|
-
get_path(&spec, &["runtime", "dangerous_auto_approve"]),
|
|
714
|
-
Some(&Value::Bool(true)),
|
|
715
|
-
"value {v}"
|
|
716
|
-
);
|
|
717
720
|
assert_eq!(
|
|
718
721
|
get_path(&spec, &["communication", "worker_to_worker"]),
|
|
719
722
|
Some(&Value::Bool(true)),
|
|
@@ -726,11 +729,11 @@ fn fix_a9_bool_coercion_yes_and_one_are_true() {
|
|
|
726
729
|
fn fix_a9_bool_coercion_no_is_python_truthy() {
|
|
727
730
|
// FLAG: Python bool("no") is TRUE (only 0/false/False are falsy). The contract
|
|
728
731
|
// note said "no->false" but Python golden is no->true; we lock Python.
|
|
729
|
-
let tm = "---\nname: T\nprovider: codex\
|
|
732
|
+
let tm = "---\nname: T\nprovider: codex\nworker_to_worker: no\n---\nx\n";
|
|
730
733
|
let team = build_team(tm, &[("w.md", &role_nomodel("codex"))], &[]);
|
|
731
734
|
let spec = compile_team(&team).unwrap();
|
|
732
735
|
assert_eq!(
|
|
733
|
-
get_path(&spec, &["
|
|
736
|
+
get_path(&spec, &["communication", "worker_to_worker"]),
|
|
734
737
|
Some(&Value::Bool(true))
|
|
735
738
|
);
|
|
736
739
|
}
|
|
@@ -753,7 +756,7 @@ fn fix_a9_int_coercion_of_quoted_string() {
|
|
|
753
756
|
fn fix_a10_crlf_role_doc_front_matter_is_parsed() {
|
|
754
757
|
// Windows-authored \r\n doc: read_text universal-newlines normalizes before
|
|
755
758
|
// the "---\n" check, so the front matter parses (current keeps \r\n → no FM).
|
|
756
|
-
let crlf_role = "---\r\nname: crlfworker\r\nrole: R\r\nprovider: codex\r\nauth_mode: subscription\r\ntools:\r\n - mcp_team\r\n---\r\n\r\nbody\r\n";
|
|
759
|
+
let crlf_role = "---\r\nname: crlfworker\r\nrole: R\r\nprovider: codex\r\nauth_mode: subscription\r\ndangerously_skip_permissions: false\r\ntools:\r\n - mcp_team\r\n---\r\n\r\nbody\r\n";
|
|
757
760
|
let team = build_team(TM_CODEX, &[("w.md", crlf_role)], &[]);
|
|
758
761
|
let spec = compile_team(&team).expect("CRLF role doc must parse its front matter");
|
|
759
762
|
assert_eq!(
|
|
@@ -299,10 +299,6 @@ pub fn compile_team(team_dir: &Path) -> Result<Value, ModelError> {
|
|
|
299
299
|
Value::Int(max_active_agents(agent_ids.len())),
|
|
300
300
|
),
|
|
301
301
|
("startup_order", list_str(agent_ids)),
|
|
302
|
-
(
|
|
303
|
-
"dangerous_auto_approve",
|
|
304
|
-
bool_field(&team_meta, "dangerous_auto_approve", false),
|
|
305
|
-
),
|
|
306
302
|
("fast", bool_field(&team_meta, "fast", false)),
|
|
307
303
|
(
|
|
308
304
|
"tick_interval_sec",
|
|
@@ -421,7 +417,12 @@ fn compile_role_agent_with_mode(
|
|
|
421
417
|
]),
|
|
422
418
|
),
|
|
423
419
|
("tools", list_str(tools)),
|
|
424
|
-
|
|
420
|
+
// 0.5.66 bypass 单源:compiler 透传角色 md 的 `dangerously_skip_permissions`
|
|
421
|
+
// (必填 bool,spec 校验保证存在)。取代旧"恒发 permission_mode: restricted"。
|
|
422
|
+
(
|
|
423
|
+
"dangerously_skip_permissions",
|
|
424
|
+
Value::Bool(required_dangerously_skip_permissions(&meta, role_path)?),
|
|
425
|
+
),
|
|
425
426
|
(
|
|
426
427
|
"communication_mode",
|
|
427
428
|
Value::Str(communication_mode.as_str().to_string()),
|
|
@@ -549,6 +550,25 @@ fn required_string(meta: &Value, path: &Path, key: &str) -> Result<String, Model
|
|
|
549
550
|
})
|
|
550
551
|
}
|
|
551
552
|
|
|
553
|
+
/// 0.5.66 bypass 单源:角色 md 的 `dangerously_skip_permissions` 必填 bool。
|
|
554
|
+
/// 缺 = 编译失败(消息含硬串);非 bool = 同 fail-loud。
|
|
555
|
+
fn required_dangerously_skip_permissions(
|
|
556
|
+
meta: &Value,
|
|
557
|
+
path: &Path,
|
|
558
|
+
) -> Result<bool, ModelError> {
|
|
559
|
+
match meta.get("dangerously_skip_permissions") {
|
|
560
|
+
Some(Value::Bool(value)) => Ok(*value),
|
|
561
|
+
Some(_) => Err(ModelError::Validation(format!(
|
|
562
|
+
"{}: front matter field dangerously_skip_permissions must be a boolean. This field must be declared explicitly; it controls whether the agent launches with permission prompts bypassed.",
|
|
563
|
+
path.display()
|
|
564
|
+
))),
|
|
565
|
+
None => Err(ModelError::Validation(format!(
|
|
566
|
+
"{}: missing front matter field dangerously_skip_permissions. This field must be declared explicitly; it controls whether the agent launches with permission prompts bypassed.",
|
|
567
|
+
path.display()
|
|
568
|
+
))),
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
552
572
|
fn optional_string_value(meta: &Value, key: &str) -> Value {
|
|
553
573
|
match string_field(meta, key) {
|
|
554
574
|
Some(s) => Value::Str(s),
|
|
@@ -644,6 +644,22 @@ impl Transport for ConPtyBackend {
|
|
|
644
644
|
Ok(SetEnvOutcome::InternalizedAtSpawn)
|
|
645
645
|
}
|
|
646
646
|
|
|
647
|
+
fn set_window_option(
|
|
648
|
+
&self,
|
|
649
|
+
_session: &SessionName,
|
|
650
|
+
_window: &WindowName,
|
|
651
|
+
_option: &str,
|
|
652
|
+
_value: &str,
|
|
653
|
+
) -> Result<(), TransportError> {
|
|
654
|
+
// Design §Transport Boundary: window options are tmux concepts; ConPTY
|
|
655
|
+
// has no per-window option registry, so this is a typed capability
|
|
656
|
+
// refusal. Callers must `let _ =` this (A-55 lockdown is best-effort).
|
|
657
|
+
Err(TransportError::MuxUnavailable {
|
|
658
|
+
backend: BackendKind::ConPty,
|
|
659
|
+
detail: "set-window-option is tmux-only".to_string(),
|
|
660
|
+
})
|
|
661
|
+
}
|
|
662
|
+
|
|
647
663
|
fn has_pane(&self, pane: &PaneId) -> Result<Option<bool>, TransportError> {
|
|
648
664
|
let resp = self.dispatch(Op::HasPane, serde_json::json!({"pane_id": pane.as_str()}))?;
|
|
649
665
|
Ok(Some(resp.result["present"].as_bool().unwrap_or(false)))
|