@team-agent/installer 0.5.65 → 0.5.68

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 (208) hide show
  1. package/Cargo.lock +8 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/Cargo.toml +6 -0
  4. package/crates/team-agent/src/cli/adapters.rs +6 -1
  5. package/crates/team-agent/src/cli/diagnose.rs +82 -0
  6. package/crates/team-agent/src/cli/emit.rs +3 -3
  7. package/crates/team-agent/src/cli/grok_slot.rs +299 -0
  8. package/crates/team-agent/src/cli/leader.rs +21 -7
  9. package/crates/team-agent/src/cli/leaders.rs +125 -1
  10. package/crates/team-agent/src/cli/mod.rs +20 -0
  11. package/crates/team-agent/src/cli/send/presentation.rs +7 -1
  12. package/crates/team-agent/src/cli/spec.rs +2 -0
  13. package/crates/team-agent/src/cli/status_port/compact.rs +28 -0
  14. package/crates/team-agent/src/cli/status_port/snapshot.rs +25 -1
  15. package/crates/team-agent/src/cli/tests/base.rs +26 -3
  16. package/crates/team-agent/src/cli/tests/compile.rs +1 -1
  17. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +129 -19
  18. package/crates/team-agent/src/cli/tests/mod.rs +2 -2
  19. package/crates/team-agent/src/cli/tests/run_delegation.rs +1 -1
  20. package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +387 -6
  21. package/crates/team-agent/src/cli/tests/status_send.rs +79 -10
  22. package/crates/team-agent/src/cli/tests/verb_validate.rs +1 -1
  23. package/crates/team-agent/src/communication_mode/mod.rs +6 -4
  24. package/crates/team-agent/src/compiler/tests.rs +23 -20
  25. package/crates/team-agent/src/compiler.rs +84 -5
  26. package/crates/team-agent/src/conpty/backend.rs +16 -0
  27. package/crates/team-agent/src/coordinator/backoff.rs +55 -0
  28. package/crates/team-agent/src/coordinator/conpty_shim.rs +82 -0
  29. package/crates/team-agent/src/coordinator/health.rs +173 -0
  30. package/crates/team-agent/src/coordinator/mod.rs +31 -0
  31. package/crates/team-agent/src/coordinator/orphan.rs +31 -0
  32. package/crates/team-agent/src/coordinator/runtime_detectors.rs +30 -0
  33. package/crates/team-agent/src/coordinator/runtime_observation.rs +25 -0
  34. package/crates/team-agent/src/coordinator/steps/abnormal.rs +59 -0
  35. package/crates/team-agent/src/coordinator/steps/delivery.rs +10 -0
  36. package/crates/team-agent/src/coordinator/steps/health_sync.rs +10 -0
  37. package/crates/team-agent/src/coordinator/steps/mod.rs +22 -0
  38. package/crates/team-agent/src/coordinator/steps/persist.rs +10 -0
  39. package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +10 -0
  40. package/crates/team-agent/src/coordinator/steps/session_gate.rs +10 -0
  41. package/crates/team-agent/src/coordinator/tick.rs +133 -22
  42. package/crates/team-agent/src/coordinator/types.rs +49 -0
  43. package/crates/team-agent/src/db/message_store.rs +39 -5
  44. package/crates/team-agent/src/layout/worker_env.rs +20 -3
  45. package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
  46. package/crates/team-agent/src/leader/provider_attribution.rs +53 -0
  47. package/crates/team-agent/src/leader/registry.rs +65 -0
  48. package/crates/team-agent/src/leader/start.rs +1080 -58
  49. package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1 -1
  50. package/crates/team-agent/src/lifecycle/display.rs +56 -0
  51. package/crates/team-agent/src/lifecycle/helpers.rs +57 -0
  52. package/crates/team-agent/src/lifecycle/launch/add_agent.rs +332 -14
  53. package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +69 -2
  54. package/crates/team-agent/src/lifecycle/launch/agent_state.rs +196 -2
  55. package/crates/team-agent/src/lifecycle/launch/clone_agent.rs +101 -11
  56. package/crates/team-agent/src/lifecycle/launch/cursor_create_chat.rs +229 -0
  57. package/crates/team-agent/src/lifecycle/launch/cursor_mcp.rs +332 -0
  58. package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +204 -457
  59. package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +24 -0
  60. package/crates/team-agent/src/lifecycle/launch/grok_per_seat.rs +260 -0
  61. package/crates/team-agent/src/lifecycle/launch/identity.rs +146 -0
  62. package/crates/team-agent/src/lifecycle/launch/layout.rs +60 -0
  63. package/crates/team-agent/src/lifecycle/launch/leader_context.rs +112 -0
  64. package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +530 -0
  65. package/crates/team-agent/src/lifecycle/launch/ownership.rs +40 -0
  66. package/crates/team-agent/src/lifecycle/launch/plan.rs +31 -0
  67. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +66 -0
  68. package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +78 -0
  69. package/crates/team-agent/src/lifecycle/launch/readiness.rs +85 -38
  70. package/crates/team-agent/src/lifecycle/launch/role_source.rs +44 -44
  71. package/crates/team-agent/src/lifecycle/launch/spawn.rs +101 -4
  72. package/crates/team-agent/src/lifecycle/launch/spec_state.rs +244 -48
  73. package/crates/team-agent/src/lifecycle/launch/state_projection.rs +109 -2
  74. package/crates/team-agent/src/lifecycle/launch/worker_env.rs +214 -1
  75. package/crates/team-agent/src/lifecycle/launch.rs +209 -43
  76. package/crates/team-agent/src/lifecycle/lock.rs +42 -1
  77. package/crates/team-agent/src/lifecycle/mod.rs +11 -0
  78. package/crates/team-agent/src/lifecycle/pane_input_lock.rs +161 -0
  79. package/crates/team-agent/src/lifecycle/profile_launch.rs +98 -0
  80. package/crates/team-agent/src/lifecycle/profile_smoke.rs +51 -1
  81. package/crates/team-agent/src/lifecycle/restart/agent.rs +213 -3
  82. package/crates/team-agent/src/lifecycle/restart/common.rs +373 -18
  83. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +27 -0
  84. package/crates/team-agent/src/lifecycle/restart/preflight.rs +25 -0
  85. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +165 -15
  86. package/crates/team-agent/src/lifecycle/restart/remove.rs +196 -5
  87. package/crates/team-agent/src/lifecycle/restart/selection.rs +72 -0
  88. package/crates/team-agent/src/lifecycle/restart/team_state.rs +22 -0
  89. package/crates/team-agent/src/lifecycle/restart.rs +29 -0
  90. package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1 -1
  91. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +3 -4
  92. package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +1 -1
  93. package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +2 -2
  94. package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +4 -4
  95. package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +309 -0
  96. package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +159 -174
  97. package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +2 -2
  98. package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +15 -13
  99. package/crates/team-agent/src/lifecycle/tests/core.rs +4 -18
  100. package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +1 -1
  101. package/crates/team-agent/src/lifecycle/tests/cursor_mcp_overlay.rs +287 -0
  102. package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +229 -0
  103. package/crates/team-agent/src/lifecycle/tests/cursor_restart_resume_red.rs +353 -0
  104. package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +1 -1
  105. package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +1 -1
  106. package/crates/team-agent/src/lifecycle/tests/g1_silent_faces.rs +784 -0
  107. package/crates/team-agent/src/lifecycle/tests/gate_fixtures.rs +562 -0
  108. package/crates/team-agent/src/lifecycle/tests/grok_effort_argv_red.rs +239 -0
  109. package/crates/team-agent/src/lifecycle/tests/grok_mcp_overlay_red.rs +498 -0
  110. package/crates/team-agent/src/lifecycle/tests/grok_require_explicit_model_red.rs +250 -0
  111. package/crates/team-agent/src/lifecycle/tests/grok_restart_resume_red.rs +293 -0
  112. package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +3 -0
  113. package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +3 -3
  114. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +65 -40
  115. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +20 -32
  116. package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +17 -9
  117. package/crates/team-agent/src/lifecycle/tests/mcp_tool_name_format_red.rs +69 -0
  118. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +24 -23
  119. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +98 -62
  120. package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +2 -2
  121. package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +1 -1
  122. package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +1 -1
  123. package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +3 -1
  124. package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -2
  125. package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +1 -1
  126. package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +1 -1
  127. package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +41 -8
  128. package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +3 -3
  129. package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1 -1
  130. package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +1 -1
  131. package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +1 -1
  132. package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1 -1
  133. package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1 -1
  134. package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +1 -1
  135. package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +60 -2
  136. package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +1 -1
  137. package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +2 -2
  138. package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +69 -26
  139. package/crates/team-agent/src/lifecycle/tests.rs +23 -15
  140. package/crates/team-agent/src/lifecycle/types.rs +61 -2
  141. package/crates/team-agent/src/lifecycle/worker_command_context.rs +200 -33
  142. package/crates/team-agent/src/mcp_server/tests/scoped.rs +100 -1
  143. package/crates/team-agent/src/mcp_server/tests.rs +196 -2
  144. package/crates/team-agent/src/messaging/delivery.rs +405 -40
  145. package/crates/team-agent/src/messaging/helpers.rs +2 -0
  146. package/crates/team-agent/src/messaging/leader_receiver.rs +53 -1
  147. package/crates/team-agent/src/messaging/results.rs +4 -0
  148. package/crates/team-agent/src/messaging/send.rs +34 -0
  149. package/crates/team-agent/src/messaging/tests/dup_inject.rs +406 -0
  150. package/crates/team-agent/src/messaging/tests/e23.rs +9 -0
  151. package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +66 -0
  152. package/crates/team-agent/src/messaging/tests/main_preserved.rs +1 -1
  153. package/crates/team-agent/src/messaging/tests/mod.rs +1 -0
  154. package/crates/team-agent/src/messaging/types.rs +4 -1
  155. package/crates/team-agent/src/model/enums.rs +14 -5
  156. package/crates/team-agent/src/model/permissions.rs +3 -0
  157. package/crates/team-agent/src/model/spec.rs +129 -6
  158. package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +3 -1
  159. package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +3 -1
  160. package/crates/team-agent/src/model/testdata/team.spec.yaml +3 -1
  161. package/crates/team-agent/src/os_probe.rs +73 -2
  162. package/crates/team-agent/src/provider/adapter.rs +231 -4
  163. package/crates/team-agent/src/provider/adapters/claude.rs +5 -2
  164. package/crates/team-agent/src/provider/adapters/codex.rs +5 -2
  165. package/crates/team-agent/src/provider/adapters/copilot.rs +5 -2
  166. package/crates/team-agent/src/provider/adapters/cursor_agent.rs +86 -0
  167. package/crates/team-agent/src/provider/adapters/grok.rs +157 -0
  168. package/crates/team-agent/src/provider/adapters/mod.rs +2 -0
  169. package/crates/team-agent/src/provider/bypass_flags.rs +124 -0
  170. package/crates/team-agent/src/provider/classify.rs +4 -2
  171. package/crates/team-agent/src/provider/faults.rs +2 -1
  172. package/crates/team-agent/src/provider/mod.rs +13 -0
  173. package/crates/team-agent/src/provider/session/capture.rs +158 -20
  174. package/crates/team-agent/src/provider/session/context_fork/claude.rs +38 -1
  175. package/crates/team-agent/src/provider/session/context_fork/codex.rs +72 -1
  176. package/crates/team-agent/src/provider/session/context_fork/outcome.rs +57 -1
  177. package/crates/team-agent/src/provider/session/context_fork.rs +77 -3
  178. package/crates/team-agent/src/provider/session/mod.rs +18 -0
  179. package/crates/team-agent/src/provider/session/resume.rs +57 -0
  180. package/crates/team-agent/src/provider/session_scan/claude.rs +125 -1
  181. package/crates/team-agent/src/provider/session_scan/codex.rs +94 -1
  182. package/crates/team-agent/src/provider/session_scan/common.rs +204 -2
  183. package/crates/team-agent/src/provider/session_scan/copilot.rs +33 -1
  184. package/crates/team-agent/src/provider/session_scan/cursor.rs +538 -0
  185. package/crates/team-agent/src/provider/session_scan/grok.rs +288 -0
  186. package/crates/team-agent/src/provider/session_scan.rs +8 -0
  187. package/crates/team-agent/src/provider/submit_now.rs +94 -0
  188. package/crates/team-agent/src/provider/tests/adapter.rs +304 -0
  189. package/crates/team-agent/src/provider/types.rs +4 -2
  190. package/crates/team-agent/src/provider/wire.rs +23 -1
  191. package/crates/team-agent/src/state/persist.rs +301 -8
  192. package/crates/team-agent/src/state/repository.rs +5 -0
  193. package/crates/team-agent/src/tmux_backend/tests.rs +1737 -42
  194. package/crates/team-agent/src/tmux_backend.rs +1083 -75
  195. package/crates/team-agent/src/transport/test_support.rs +20 -0
  196. package/crates/team-agent/src/transport/tests/wire.rs +28 -2
  197. package/crates/team-agent/src/transport.rs +265 -1
  198. package/npm/install.mjs +129 -73
  199. package/package.json +4 -4
  200. package/skills/team-agent/SKILL.md +33 -238
  201. package/skills/team-agent/command-coverage.json +31 -0
  202. package/crates/team-agent/src/lifecycle/launch/approval.rs +0 -134
  203. package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +0 -59
  204. package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +0 -483
  205. package/crates/team-agent/src/lifecycle/launch/fork_pending.rs +0 -109
  206. package/crates/team-agent/src/lifecycle/launch/fork_state.rs +0 -447
  207. package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +0 -1187
  208. package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +0 -327
@@ -1,7 +1,27 @@
1
+ //! ---
2
+ //! purpose: 从 spec 里的一个 agent 定义,编译出 worker 启动所需的系统提示词与工具串
3
+ //! contract:
4
+ //! provides:
5
+ //! - name: WorkerCommandAgent
6
+ //! what: 从 YAML 或 JSON 读出的单 agent 命令上下文
7
+ //! - name: compile_worker_system_prompt
8
+ //! what: 按身份、runtime 契约、通信模式、角色正文、输出契约、权限说明拼系统提示词
9
+ //! - name: resolved_tool_strings_for_command
10
+ //! what: 解析出该 agent 的工具串,声明了 bypass 时追加 dangerous_auto_approve 哨兵
11
+ //! depends:
12
+ //! - crate::model::permissions
13
+ //! - crate::communication_mode
14
+ //! - crate::provider::bypass_flags
15
+ //! boundary:
16
+ //! - 只产出字符串,不 spawn 进程、不写盘
17
+ //! - bypass 只认 agent 自身声明,不从 team/runtime/leader argv 继承
18
+ //! - provider 没有 bypass argv 定义时报错,不静默降级
19
+ //! maturity: wired
20
+ //! ---
1
21
  use std::path::Path;
2
22
 
3
23
  use crate::communication_mode::CommunicationMode;
4
- use crate::lifecycle::types::{DangerousApproval, LifecycleError};
24
+ use crate::lifecycle::types::LifecycleError;
5
25
  use crate::model::enums::{Enforcement, Provider};
6
26
  use crate::model::ids::AgentId;
7
27
  use crate::model::permissions::{resolve_permissions, AgentPermissionInput};
@@ -13,9 +33,9 @@ output. All communication must go through Team Agent MCP tools.
13
33
 
14
34
  ## Communication (mandatory)
15
35
 
16
- - Coordinate with teammate: team_orchestrator.send_message(to='<agent_id>', content='...')
17
- - Broadcast to all teammates: team_orchestrator.send_message(to='*', content='...')
18
- - Task complete: team_orchestrator.report_result(summary='...') — call exactly once
36
+ - Coordinate with teammate: {send_message}(to='<agent_id>', content='...')
37
+ - Broadcast to all teammates: {send_message}(to='*', content='...')
38
+ - Task complete: {report_result}(summary='...') — call exactly once
19
39
 
20
40
  ## Rules
21
41
 
@@ -29,7 +49,7 @@ output. All communication must go through Team Agent MCP tools.
29
49
  // semantics (leader-attach dependence + fallback status) that the
30
50
  // generic runtime section deliberately leaves out.
31
51
  const RESULT_ENVELOPE_OUTPUT_CONTRACT: &str =
32
- "Final completion must call team_orchestrator.report_result exactly once with a short summary \
52
+ "Final completion must call {report_result} exactly once with a short summary \
33
53
  and optional status/changes/tests; the MCP runtime injects the result into the attached leader pane. \
34
54
  If no leader is attached, the tool returns a fallback/failed result instead of completion.";
35
55
 
@@ -42,9 +62,21 @@ pub(crate) struct WorkerCommandAgent {
42
62
  system_prompt_file: Option<String>,
43
63
  output_contract_format: Option<String>,
44
64
  communication_mode: CommunicationMode,
65
+ /// 0.5.66 bypass 单源:agent-level `dangerously_skip_permissions`(必填 bool)。
66
+ dangerously_skip_permissions: bool,
45
67
  }
46
68
 
47
69
  impl WorkerCommandAgent {
70
+ /// ---
71
+ /// purpose: 从 spec 的 YAML agent 节点读出命令上下文
72
+ /// params:
73
+ /// agent: 单个 agent 的 YAML 节点
74
+ /// fallback_id: agent 节点没写 id 时的兜底 id
75
+ /// provider: 已解析的 provider
76
+ /// returns: 填好的 WorkerCommandAgent
77
+ /// errors: communication_mode 取值非法时返回 LifecycleError
78
+ /// contract_id: lifecycle.worker_command_agent.from_source
79
+ /// ---
48
80
  pub(crate) fn from_yaml(
49
81
  agent: &crate::model::yaml::Value,
50
82
  fallback_id: Option<&str>,
@@ -92,9 +124,23 @@ impl WorkerCommandAgent {
92
124
  .get("communication_mode")
93
125
  .and_then(crate::model::yaml::Value::as_str),
94
126
  )?,
127
+ dangerously_skip_permissions: matches!(
128
+ agent.get("dangerously_skip_permissions"),
129
+ Some(crate::model::yaml::Value::Bool(true))
130
+ ),
95
131
  })
96
132
  }
97
133
 
134
+ /// ---
135
+ /// purpose: 从 runtime state 的 JSON agent 节点读出命令上下文
136
+ /// params:
137
+ /// agent: 单个 agent 的 JSON 节点
138
+ /// fallback_id: agent 节点没写 id 时的兜底 id
139
+ /// provider: 已解析的 provider
140
+ /// returns: 填好的 WorkerCommandAgent
141
+ /// errors: communication_mode 取值非法时返回 LifecycleError
142
+ /// contract_id: lifecycle.worker_command_agent.from_source
143
+ /// ---
98
144
  pub(crate) fn from_json(
99
145
  agent: &serde_json::Value,
100
146
  fallback_id: Option<&str>,
@@ -142,10 +188,19 @@ impl WorkerCommandAgent {
142
188
  .get("communication_mode")
143
189
  .and_then(serde_json::Value::as_str),
144
190
  )?,
191
+ dangerously_skip_permissions: agent
192
+ .get("dangerously_skip_permissions")
193
+ .and_then(serde_json::Value::as_bool)
194
+ .unwrap_or(false),
145
195
  })
146
196
  }
147
197
  }
148
198
 
199
+ /// ---
200
+ /// purpose: 拼出 worker 的系统提示词,身份段必须排在最前
201
+ /// returns: 各段以空行分隔的提示词,空段被丢弃
202
+ /// errors: 角色正文读取失败或权限解析失败时返回 LifecycleError
203
+ /// ---
149
204
  pub(crate) fn compile_worker_system_prompt(
150
205
  agent: &WorkerCommandAgent,
151
206
  ) -> Result<String, LifecycleError> {
@@ -153,13 +208,15 @@ pub(crate) fn compile_worker_system_prompt(
153
208
  // identity line anchors the very first section (live Python worker argv confirms).
154
209
  // C-1 cr verdict / B2 灵魂件 — identity 必须 FIRST(MUST-4 行为层守:空白上下文问
155
210
  // "你是谁"必须先答 Team Agent worker 身份)。runtime contract 跟后。
211
+ let send_message = mcp_tool_name(agent.provider, "team_orchestrator", "send_message");
212
+ let report_result = mcp_tool_name(agent.provider, "team_orchestrator", "report_result");
156
213
  let mut chunks = vec![
157
214
  identity_section(agent),
158
- runtime_contract_section(),
159
- agent.communication_mode.runtime_contract().to_string(),
215
+ runtime_contract_section(&send_message, &report_result),
216
+ agent.communication_mode.runtime_contract(&send_message),
160
217
  role_body(agent)?,
161
218
  ];
162
- if let Some(contract) = output_contract(agent) {
219
+ if let Some(contract) = output_contract(agent, &report_result) {
163
220
  chunks.push(contract);
164
221
  }
165
222
  if let Some(notes) = permission_notes(agent)? {
@@ -172,22 +229,72 @@ pub(crate) fn compile_worker_system_prompt(
172
229
  .join("\n\n"))
173
230
  }
174
231
 
232
+ /// ---
233
+ /// purpose: 解析该 agent 最终生效的工具串
234
+ /// params:
235
+ /// provider: 用于查 bypass argv 定义的 provider
236
+ /// returns: 排序后的工具串;agent 声明 bypass 时末尾追加 dangerous_auto_approve
237
+ /// errors: 权限解析失败,或声明了 bypass 但该 provider 没有 bypass argv 定义时返回 RequirementUnmet
238
+ /// ---
175
239
  pub(crate) fn resolved_tool_strings_for_command(
176
240
  agent: &WorkerCommandAgent,
177
241
  provider: Provider,
178
- safety: &DangerousApproval,
179
242
  ) -> Result<Vec<String>, LifecycleError> {
180
243
  let mut tools: Vec<String> = resolve_agent_permissions(agent, provider)?
181
244
  .sorted_tool_strings()
182
245
  .into_iter()
183
246
  .map(str::to_string)
184
247
  .collect();
185
- if safety.enabled && !tools.iter().any(|tool| tool == "dangerous_auto_approve") {
186
- tools.push("dangerous_auto_approve".to_string());
248
+ // 0.5.66 bypass 单源:读 agent 自己的 `dangerously_skip_permissions` bool,
249
+ // 不再收 team/runtime/leader argv 派生的 `DangerousApproval`。true → 加
250
+ // `dangerous_auto_approve` 哨兵(adapter 消费点不变);provider 无 bypass
251
+ // argv 定义 → fail-loud,不静默 fallback。
252
+ if agent.dangerously_skip_permissions {
253
+ let flag = crate::provider::bypass_flags::provider_bypass_flag(provider).ok_or_else(
254
+ || {
255
+ LifecycleError::RequirementUnmet(format!(
256
+ "provider {} has no bypass argv flag defined; dangerously_skip_permissions=true cannot be honored",
257
+ provider_display_name(provider)
258
+ ))
259
+ },
260
+ )?;
261
+ debug_assert!(flag.starts_with("--"));
262
+ if !tools.iter().any(|tool| tool == "dangerous_auto_approve") {
263
+ tools.push("dangerous_auto_approve".to_string());
264
+ }
187
265
  }
188
266
  Ok(tools)
189
267
  }
190
268
 
269
+ /// Provider-facing MCP tool name. Only verified call forms are filled in.
270
+ /// Unverified providers keep the historical dotted spelling.
271
+ fn mcp_tool_name(provider: Provider, server: &str, tool: &str) -> String {
272
+ match provider {
273
+ Provider::Claude | Provider::ClaudeCode => format!("mcp__{server}__{tool}"),
274
+ Provider::Grok => format!("{server}__{tool}"),
275
+ // CursorAgent / Codex / Copilot / GeminiCli / Fake: 未验证,沿用现状点号。
276
+ // CursorAgent 不可与 grok 同臂:仓库里没有活转录,`{server}__{tool}` 是推断。
277
+ Provider::CursorAgent
278
+ | Provider::Codex
279
+ | Provider::Copilot
280
+ | Provider::GeminiCli
281
+ | Provider::Fake => format!("{server}.{tool}"),
282
+ }
283
+ }
284
+
285
+ fn provider_display_name(provider: Provider) -> &'static str {
286
+ match provider {
287
+ Provider::Claude => "claude",
288
+ Provider::ClaudeCode => "claude_code",
289
+ Provider::Codex => "codex",
290
+ Provider::Copilot => "copilot",
291
+ Provider::GeminiCli => "gemini_cli",
292
+ Provider::Grok => "grok",
293
+ Provider::CursorAgent => "cursor_agent",
294
+ Provider::Fake => "fake",
295
+ }
296
+ }
297
+
191
298
  fn resolve_agent_permissions(
192
299
  agent: &WorkerCommandAgent,
193
300
  provider: Provider,
@@ -201,8 +308,10 @@ fn resolve_agent_permissions(
201
308
  .map_err(|e| LifecycleError::Compile(e.to_string()))
202
309
  }
203
310
 
204
- fn runtime_contract_section() -> String {
205
- RUNTIME_CONTRACT_SECTION.to_string()
311
+ fn runtime_contract_section(send_message: &str, report_result: &str) -> String {
312
+ RUNTIME_CONTRACT_SECTION
313
+ .replace("{send_message}", send_message)
314
+ .replace("{report_result}", report_result)
206
315
  }
207
316
 
208
317
  fn communication_mode(value: Option<&str>) -> Result<CommunicationMode, LifecycleError> {
@@ -236,9 +345,9 @@ fn role_body(agent: &WorkerCommandAgent) -> Result<String, LifecycleError> {
236
345
  Ok(chunks.join("\n\n"))
237
346
  }
238
347
 
239
- fn output_contract(agent: &WorkerCommandAgent) -> Option<String> {
348
+ fn output_contract(agent: &WorkerCommandAgent, report_result: &str) -> Option<String> {
240
349
  (agent.output_contract_format.as_deref() == Some("result_envelope_v1"))
241
- .then(|| RESULT_ENVELOPE_OUTPUT_CONTRACT.to_string())
350
+ .then(|| RESULT_ENVELOPE_OUTPUT_CONTRACT.replace("{report_result}", report_result))
242
351
  }
243
352
 
244
353
  fn permission_notes(agent: &WorkerCommandAgent) -> Result<Option<String>, LifecycleError> {
@@ -282,20 +391,6 @@ fn provider_default_prompt_only_tools(provider: Provider) -> &'static [&'static
282
391
  #[cfg(test)]
283
392
  mod tests {
284
393
  use super::*;
285
- use crate::lifecycle::types::DangerousApprovalSource;
286
-
287
- fn disabled_safety() -> DangerousApproval {
288
- DangerousApproval {
289
- enabled: false,
290
- source: DangerousApprovalSource::Disabled,
291
- inherited: false,
292
- provider: None,
293
- flag: None,
294
- worker_capability_above_leader: false,
295
- ancestry_binary_name: None,
296
- unexpected_binary: false,
297
- }
298
- }
299
394
 
300
395
  #[test]
301
396
  fn empty_tools_use_role_defaults_and_aliases_resolve_before_command() {
@@ -308,10 +403,10 @@ mod tests {
308
403
  system_prompt_file: None,
309
404
  output_contract_format: None,
310
405
  communication_mode: CommunicationMode::default(),
406
+ dangerously_skip_permissions: false,
311
407
  };
312
408
  let tools =
313
- resolved_tool_strings_for_command(&agent, Provider::ClaudeCode, &disabled_safety())
314
- .unwrap();
409
+ resolved_tool_strings_for_command(&agent, Provider::ClaudeCode).unwrap();
315
410
  assert_eq!(
316
411
  tools,
317
412
  [
@@ -330,11 +425,82 @@ mod tests {
330
425
  ..agent
331
426
  };
332
427
  let tools =
333
- resolved_tool_strings_for_command(&agent, Provider::ClaudeCode, &disabled_safety())
334
- .unwrap();
428
+ resolved_tool_strings_for_command(&agent, Provider::ClaudeCode).unwrap();
335
429
  assert_eq!(tools, ["fs_list", "fs_read", "fs_write", "mcp_team"]);
336
430
  }
337
431
 
432
+ // 0.5.66 bypass 单源 §4.1:true → tools 追加 `dangerous_auto_approve` 哨兵。
433
+ #[test]
434
+ fn test_true_field_produces_bypass_flag_argv() {
435
+ let agent = WorkerCommandAgent {
436
+ id: Some("dev".to_string()),
437
+ provider: Provider::ClaudeCode,
438
+ role: Some("developer".to_string()),
439
+ declared_tools: Some(vec!["mcp_team".to_string()]),
440
+ system_prompt_inline: None,
441
+ system_prompt_file: None,
442
+ output_contract_format: None,
443
+ communication_mode: CommunicationMode::default(),
444
+ dangerously_skip_permissions: true,
445
+ };
446
+ let tools = resolved_tool_strings_for_command(&agent, Provider::ClaudeCode).unwrap();
447
+ assert!(
448
+ tools.iter().any(|tool| tool == "dangerous_auto_approve"),
449
+ "true field must append the dangerous_auto_approve sentinel; tools={tools:?}"
450
+ );
451
+ assert!(
452
+ tools.iter().any(|tool| tool == "mcp_team"),
453
+ "normal tools must remain; tools={tools:?}"
454
+ );
455
+ }
456
+
457
+ // 0.5.66 bypass 单源 §4.1:false → 不追加哨兵。
458
+ #[test]
459
+ fn test_false_field_produces_no_bypass_flag_argv() {
460
+ let agent = WorkerCommandAgent {
461
+ id: Some("dev".to_string()),
462
+ provider: Provider::ClaudeCode,
463
+ role: Some("developer".to_string()),
464
+ declared_tools: Some(vec!["mcp_team".to_string()]),
465
+ system_prompt_inline: None,
466
+ system_prompt_file: None,
467
+ output_contract_format: None,
468
+ communication_mode: CommunicationMode::default(),
469
+ dangerously_skip_permissions: false,
470
+ };
471
+ let tools = resolved_tool_strings_for_command(&agent, Provider::ClaudeCode).unwrap();
472
+ assert!(
473
+ !tools.iter().any(|tool| tool == "dangerous_auto_approve"),
474
+ "false field must not append the sentinel; tools={tools:?}"
475
+ );
476
+ }
477
+
478
+ // 0.5.66 bypass 单源 §4.1:provider 未定义 bypass 参数 + true → fail-loud。
479
+ #[test]
480
+ fn test_provider_without_bypass_flag_definition_errors() {
481
+ let agent = WorkerCommandAgent {
482
+ id: Some("dev".to_string()),
483
+ provider: Provider::GeminiCli,
484
+ role: Some("developer".to_string()),
485
+ declared_tools: Some(vec!["mcp_team".to_string()]),
486
+ system_prompt_inline: None,
487
+ system_prompt_file: None,
488
+ output_contract_format: None,
489
+ communication_mode: CommunicationMode::default(),
490
+ dangerously_skip_permissions: true,
491
+ };
492
+ let err = resolved_tool_strings_for_command(&agent, Provider::GeminiCli).unwrap_err();
493
+ let msg = err.to_string();
494
+ assert!(
495
+ msg.contains("no bypass argv flag defined"),
496
+ "provider without bypass flag + true must fail-loud; got {msg}"
497
+ );
498
+ assert!(
499
+ msg.contains("gemini_cli"),
500
+ "error must name the provider; got {msg}"
501
+ );
502
+ }
503
+
338
504
  #[test]
339
505
  fn system_prompt_uses_identity_then_runtime_contract_python_order() {
340
506
  // #264 D6: Python truth source (prompt.py:39, live ps confirmed) builds
@@ -353,6 +519,7 @@ mod tests {
353
519
  system_prompt_file: None,
354
520
  output_contract_format: Some("result_envelope_v1".to_string()),
355
521
  communication_mode: CommunicationMode::default(),
522
+ dangerously_skip_permissions: false,
356
523
  };
357
524
  let prompt = compile_worker_system_prompt(&agent).unwrap();
358
525
  assert!(
@@ -1,3 +1,73 @@
1
+ /// ---
2
+ /// purpose: scoped MCP no-compaction update_state filesystem contract
3
+ /// contract:
4
+ /// provides:
5
+ /// - name: update_state_path_contracts
6
+ /// what: proves the raw no-compaction response preserves its state-file path in an owned fixture
7
+ /// depends:
8
+ /// - name: mcp_state_fixture
9
+ /// what: fixture-owned root and provenance from tests.rs
10
+ /// boundary:
11
+ /// - retain the raw {ok,state_file} result shape
12
+ /// - the owned selector checks workspace, state-file, and runtime paths remain fixture-owned
13
+ /// maturity: wired
14
+ /// ---
15
+
16
+ fn verify_cleanup_surviving_receipt(
17
+ receipt_path: &std::path::Path,
18
+ raw_workspace: &std::path::Path,
19
+ canonical_workspace: &std::path::Path,
20
+ state_file: &std::path::Path,
21
+ runtime_state: &std::path::Path,
22
+ ) {
23
+ let bytes = std::fs::read(receipt_path)
24
+ .expect("provenance receipt must survive fixture Drop");
25
+ let receipt: Value = serde_json::from_slice(&bytes).expect("provenance receipt must be JSON");
26
+ assert_eq!(receipt["schema"], json!("mcp-state-provenance-v1"));
27
+ let payload = &receipt["payload"];
28
+ let payload_bytes = serde_json::to_vec(payload).unwrap();
29
+ assert_eq!(
30
+ receipt["payload_sha256"],
31
+ json!(format!("{:x}", sha2::Sha256::digest(&payload_bytes)))
32
+ );
33
+ assert_eq!(
34
+ payload["raw_workspace"],
35
+ json!(raw_workspace.display().to_string())
36
+ );
37
+ assert_eq!(
38
+ payload["canonical_workspace"],
39
+ json!(canonical_workspace.display().to_string())
40
+ );
41
+ assert_eq!(
42
+ payload["resolved_state_file"],
43
+ json!(state_file.display().to_string())
44
+ );
45
+ assert_eq!(
46
+ payload["runtime_state_path"],
47
+ json!(runtime_state.display().to_string())
48
+ );
49
+ let facts = payload["facts"].as_array().unwrap();
50
+ for name in [
51
+ "root",
52
+ "raw_workspace",
53
+ "canonical_workspace",
54
+ "team_dir",
55
+ "runtime_dir",
56
+ "state_parent",
57
+ "runtime_state",
58
+ "state_file",
59
+ ] {
60
+ let fact = facts
61
+ .iter()
62
+ .find(|fact| fact["name"] == json!(name))
63
+ .unwrap_or_else(|| panic!("provenance fact missing: {name}"));
64
+ assert!(fact["path"].as_str().is_some());
65
+ assert!(fact["uid"].as_u64().is_some());
66
+ assert!(fact["mode"].as_u64().is_some());
67
+ assert!(fact["device"].as_u64().is_some());
68
+ }
69
+ }
70
+
1
71
  #[test]
2
72
  fn dispatch_send_message_worker_accepted_returned_verbatim() {
3
73
  // A-7: accepted requires a REAL stored message_id (no fabricated ids), so the
@@ -52,8 +122,10 @@
52
122
  // state_file (not a golden whitelist key), so the key vanishes.
53
123
  #[test]
54
124
  fn update_state_state_file_survives_no_compaction() {
125
+ let fixture = McpStateFixture::new("writable");
126
+ let expected = fixture.state_file("team_state.md");
55
127
  let tools = TeamOrchestratorTools::with_identity(
56
- &unique_ws("update-state-raw"),
128
+ &fixture.workspace,
57
129
  Some(AgentId::new("leader")),
58
130
  None,
59
131
  );
@@ -62,6 +134,33 @@
62
134
  assert!(v.get("state_file").and_then(Value::as_str).is_some(),
63
135
  "state_file must survive (update_state is not _compact_tool_result'd)");
64
136
  assert_eq!(keys(&v), vec!["ok", "state_file"]);
137
+ let returned_state_file = PathBuf::from(v["state_file"].as_str().unwrap());
138
+ assert_eq!(returned_state_file, expected);
139
+ let runtime_state = crate::state::persist::runtime_state_path(&fixture.workspace);
140
+ assert!(fixture.under_root(&returned_state_file));
141
+ assert!(fixture.under_root(&runtime_state));
142
+ let canonical_workspace =
143
+ crate::model::paths::canonical_run_workspace(&fixture.workspace).unwrap();
144
+ let receipt = fixture.record_provenance(&fixture.workspace, &returned_state_file);
145
+ let root = fixture.root.clone();
146
+ drop(fixture);
147
+ assert!(!root.exists(), "fixture root must be removed before receipt verification");
148
+ verify_cleanup_surviving_receipt(
149
+ &receipt,
150
+ &root.join("workspace"),
151
+ &canonical_workspace,
152
+ &returned_state_file,
153
+ &runtime_state,
154
+ );
155
+ let receipt_bytes = std::fs::read(&receipt).unwrap();
156
+ println!(
157
+ "F10_PROVENANCE_RECEIPT path={} sha256={:x} payload={}",
158
+ receipt.display(),
159
+ sha2::Sha256::digest(&receipt_bytes),
160
+ String::from_utf8_lossy(&receipt_bytes)
161
+ );
162
+ std::fs::remove_file(&receipt).unwrap();
163
+ assert!(!receipt.exists(), "cleanup must remove only the exact receipt path");
65
164
  }
66
165
 
67
166
  // ── #36 report_result setdefault: populated envelope keys WIN over args ─────