@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.
Files changed (105) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/Cargo.toml +5 -0
  4. package/crates/team-agent/src/cli/adapters.rs +6 -1
  5. package/crates/team-agent/src/cli/emit.rs +15 -0
  6. package/crates/team-agent/src/cli/mod.rs +9 -8
  7. package/crates/team-agent/src/cli/send/presentation.rs +1 -0
  8. package/crates/team-agent/src/cli/spec.rs +3 -0
  9. package/crates/team-agent/src/cli/tests/compile.rs +1 -1
  10. package/crates/team-agent/src/cli/tests/mod.rs +33 -2
  11. package/crates/team-agent/src/cli/tests/run_delegation.rs +1 -1
  12. package/crates/team-agent/src/cli/tests/status_send.rs +13 -3
  13. package/crates/team-agent/src/cli/tests/verb_validate.rs +1 -1
  14. package/crates/team-agent/src/cli/types.rs +7 -0
  15. package/crates/team-agent/src/compiler/tests.rs +23 -20
  16. package/crates/team-agent/src/compiler.rs +25 -5
  17. package/crates/team-agent/src/conpty/backend.rs +16 -0
  18. package/crates/team-agent/src/coordinator/health.rs +17 -0
  19. package/crates/team-agent/src/coordinator/steps/abnormal.rs +55 -2
  20. package/crates/team-agent/src/db/migration.rs +2 -1
  21. package/crates/team-agent/src/db/schema.rs +16 -8
  22. package/crates/team-agent/src/leader/lease.rs +1 -2
  23. package/crates/team-agent/src/leader/start.rs +175 -10
  24. package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1 -1
  25. package/crates/team-agent/src/lifecycle/launch/add_agent.rs +0 -2
  26. package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +7 -2
  27. package/crates/team-agent/src/lifecycle/launch/agent_state.rs +137 -2
  28. package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +1 -2
  29. package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +5 -0
  30. package/crates/team-agent/src/lifecycle/launch/spawn.rs +39 -2
  31. package/crates/team-agent/src/lifecycle/launch/spec_state.rs +105 -48
  32. package/crates/team-agent/src/lifecycle/launch/state_projection.rs +0 -2
  33. package/crates/team-agent/src/lifecycle/launch.rs +117 -21
  34. package/crates/team-agent/src/lifecycle/restart/agent.rs +89 -3
  35. package/crates/team-agent/src/lifecycle/restart/common.rs +9 -12
  36. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +59 -15
  37. package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1 -1
  38. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +4 -4
  39. package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +1 -1
  40. package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +2 -2
  41. package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +4 -4
  42. package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +24 -32
  43. package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +2 -2
  44. package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +13 -11
  45. package/crates/team-agent/src/lifecycle/tests/core.rs +0 -18
  46. package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +1 -1
  47. package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +1 -1
  48. package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +1 -1
  49. package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +3 -0
  50. package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +3 -3
  51. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +2 -2
  52. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +17 -30
  53. package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +1 -1
  54. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +4 -4
  55. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +1 -1
  56. package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +2 -2
  57. package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +1 -1
  58. package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +1 -1
  59. package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +1 -1
  60. package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -2
  61. package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +1 -1
  62. package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +1 -1
  63. package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +11 -7
  64. package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +3 -3
  65. package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1 -1
  66. package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +1 -1
  67. package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +1 -1
  68. package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1 -1
  69. package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1 -1
  70. package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +1 -1
  71. package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +1 -1
  72. package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +1 -1
  73. package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +2 -2
  74. package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +1 -1
  75. package/crates/team-agent/src/lifecycle/tests.rs +11 -7
  76. package/crates/team-agent/src/lifecycle/types.rs +0 -2
  77. package/crates/team-agent/src/lifecycle/worker_command_context.rs +115 -22
  78. package/crates/team-agent/src/mcp_server/helpers.rs +1 -0
  79. package/crates/team-agent/src/messaging/delivery.rs +29 -0
  80. package/crates/team-agent/src/messaging/helpers.rs +2 -0
  81. package/crates/team-agent/src/messaging/leader_receiver.rs +12 -0
  82. package/crates/team-agent/src/messaging/mod.rs +2 -0
  83. package/crates/team-agent/src/messaging/results.rs +5 -0
  84. package/crates/team-agent/src/messaging/send.rs +9 -0
  85. package/crates/team-agent/src/messaging/tests/main_preserved.rs +1 -1
  86. package/crates/team-agent/src/messaging/tests/runtime.rs +32 -0
  87. package/crates/team-agent/src/messaging/types.rs +2 -0
  88. package/crates/team-agent/src/messaging/wait.rs +366 -0
  89. package/crates/team-agent/src/messaging/watchers.rs +153 -6
  90. package/crates/team-agent/src/model/spec.rs +129 -6
  91. package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +3 -1
  92. package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +3 -1
  93. package/crates/team-agent/src/model/testdata/team.spec.yaml +3 -1
  94. package/crates/team-agent/src/provider/adapters/claude.rs +5 -2
  95. package/crates/team-agent/src/provider/adapters/codex.rs +5 -2
  96. package/crates/team-agent/src/provider/adapters/copilot.rs +5 -2
  97. package/crates/team-agent/src/provider/bypass_flags.rs +80 -0
  98. package/crates/team-agent/src/provider/mod.rs +2 -0
  99. package/crates/team-agent/src/tmux_backend.rs +35 -12
  100. package/crates/team-agent/src/transport/test_support.rs +20 -0
  101. package/crates/team-agent/src/transport.rs +28 -11
  102. package/package.json +4 -4
  103. package/crates/team-agent/src/lifecycle/launch/approval.rs +0 -134
  104. package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +0 -1187
  105. package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +0 -327
package/Cargo.lock CHANGED
@@ -575,7 +575,7 @@ dependencies = [
575
575
 
576
576
  [[package]]
577
577
  name = "team-agent"
578
- version = "0.5.64"
578
+ version = "0.5.66"
579
579
  dependencies = [
580
580
  "anyhow",
581
581
  "chrono",
package/Cargo.toml CHANGED
@@ -9,7 +9,7 @@ members = ["crates/team-agent", "crates/win-conpty-phase0", "crates/conpty-trans
9
9
 
10
10
  [workspace.package]
11
11
  edition = "2021"
12
- version = "0.5.64"
12
+ version = "0.5.66"
13
13
  license = "AGPL-3.0"
14
14
  rust-version = "1.95"
15
15
 
@@ -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 "...; exec ${SHELL} -l"` that
2249
- /// runs Claude as a CHILD. When Claude exits and the shell wrapper falls
2250
- /// back via `exec ${SHELL} -l`, the controlling PID is REPLACED in-place
2251
- /// by the interactive shell (same pane_pid). Because this function
2252
- /// protects by `pane.pane_pid` (Source 1 & 2), the fallback interactive
2253
- /// shell is already covered by the same protection set shutdown will
2254
- /// NOT treat the fallback shell as a stray process. Verified by the
2255
- /// `leader_fallback_shell_protected_when_provider_exited` test.
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
- const DELEG_VALID_ROLE: &str = "---\nname: implementer\nrole: Implementation Engineer\nprovider: fake\nmodel: fake\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nImplement bounded tasks.\n";
46
- const DELEG_INVALID_ROLE: &str = "---\nname: broken\nrole: Broken Worker\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nNo provider field.\n";
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: PathBuf::from("."),
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(crate::cli::SEND_REMINDER)
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("nonexistent".into()),
702
+ target: Some("drifted".into()),
693
703
  no_ack: false,
694
704
  no_wait: false,
695
705
  watch_result: false,
@@ -36,7 +36,7 @@ agents:
36
36
  - "git_diff"
37
37
  - "mcp_team"
38
38
  - "provider_builtin"
39
- permission_mode: "restricted"
39
+ dangerously_skip_permissions: false
40
40
  preferred_for:
41
41
  - "implementation"
42
42
  avoid_for: []
@@ -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"],"permission_mode":"restricted","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"],"dangerous_auto_approve":false,"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
+ 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"],"permission_mode":"restricted","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"],"dangerous_auto_approve":true,"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"}]}"#;
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"],"permission_mode":"restricted","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"],"dangerous_auto_approve":false,"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"}]}"#;
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"],"permission_mode":"restricted","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"],"dangerous_auto_approve":false,"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"}]}"#;
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"],"permission_mode":"restricted","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"],"permission_mode":"restricted","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"],"dangerous_auto_approve":false,"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"}]}"#;
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\ndangerous_auto_approve: {v}\nworker_to_worker: {v}\n---\nx\n");
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\ndangerous_auto_approve: no\n---\nx\n";
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, &["runtime", "dangerous_auto_approve"]),
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
- ("permission_mode", Value::Str("restricted".to_string())),
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)))