@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
@@ -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",
@@ -394,6 +390,8 @@ fn compile_role_agent_with_mode(
394
390
  let id = required_string(&meta, role_path, "name")?;
395
391
  let role = required_string(&meta, role_path, "role")?;
396
392
  let provider = required_string(&meta, role_path, "provider")?;
393
+ require_explicit_grok_role_model(&meta, role_path, &provider)?;
394
+ require_explicit_cursor_role_model(&meta, role_path, &provider)?;
397
395
  let model = resolve_model(&meta, team_meta, &provider);
398
396
  let auth_mode = string_field(&meta, "auth_mode")
399
397
  .or_else(|| string_field(team_meta, "default_auth_mode"))
@@ -421,7 +419,12 @@ fn compile_role_agent_with_mode(
421
419
  ]),
422
420
  ),
423
421
  ("tools", list_str(tools)),
424
- ("permission_mode", Value::Str("restricted".to_string())),
422
+ // 0.5.66 bypass 单源:compiler 透传角色 md 的 `dangerously_skip_permissions`
423
+ // (必填 bool,spec 校验保证存在)。取代旧"恒发 permission_mode: restricted"。
424
+ (
425
+ "dangerously_skip_permissions",
426
+ Value::Bool(required_dangerously_skip_permissions(&meta, role_path)?),
427
+ ),
425
428
  (
426
429
  "communication_mode",
427
430
  Value::Str(communication_mode.as_str().to_string()),
@@ -482,6 +485,15 @@ fn compile_role_agent_with_mode(
482
485
  effort.as_str()
483
486
  )));
484
487
  }
488
+ if provider_enum == Provider::CursorAgent {
489
+ return Err(ModelError::Validation(format!(
490
+ "{}: cursor_agent does not support effort '{}'. \
491
+ The Cursor CLI has no `--effort` flag; the framework must not drop the field and still launch. \
492
+ Remove effort from the role file (do not map it into `--model[effort=]` until that form is measured).",
493
+ role_path.display(),
494
+ effort.as_str()
495
+ )));
496
+ }
485
497
  agent_items.push(("effort", Value::Str(effort.as_str().to_string())));
486
498
  }
487
499
  Ok(CompiledRole {
@@ -549,6 +561,73 @@ fn required_string(meta: &Value, path: &Path, key: &str) -> Result<String, Model
549
561
  })
550
562
  }
551
563
 
564
+ /// 缺 model 时框架会填内建默认(grok 上是 grok-4),席位因此静默拿到
565
+ /// 与角色文件无关的模型和上下文窗口,而 argv 看起来完全正常。
566
+ /// 角色文件必须自己写死;内建默认、team 级默认、CLI 全局默认都是隐式来源。
567
+ fn require_explicit_grok_role_model(
568
+ meta: &Value,
569
+ path: &Path,
570
+ provider: &str,
571
+ ) -> Result<(), ModelError> {
572
+ if parse_canonical_provider(provider) != Some(Provider::Grok) {
573
+ return Ok(());
574
+ }
575
+ if string_field(meta, "model").is_some_and(|value| !value.trim().is_empty()) {
576
+ return Ok(());
577
+ }
578
+ Err(ModelError::Validation(format!(
579
+ "{}: missing front matter field model. \
580
+ Without an explicit model the framework fills a built-in default (grok-4 for grok), \
581
+ so the seat silently gets a model and context window that are not in the role file, \
582
+ while argv still looks normal. The role file must name the model itself; \
583
+ built-in defaults, team-level defaults, and the Grok CLI global default are all implicit sources. Example:\n\
584
+ model: grok-4.6",
585
+ path.display()
586
+ )))
587
+ }
588
+
589
+ /// 缺 model 时框架曾填 builtin `sonnet-4-thinking`,本机 shim 还会剥掉该默认。
590
+ /// 角色必须自己写死;内建默认、team 级默认、CLI 全局默认都是隐式来源。
591
+ fn require_explicit_cursor_role_model(
592
+ meta: &Value,
593
+ path: &Path,
594
+ provider: &str,
595
+ ) -> Result<(), ModelError> {
596
+ if parse_canonical_provider(provider) != Some(Provider::CursorAgent) {
597
+ return Ok(());
598
+ }
599
+ if string_field(meta, "model").is_some_and(|value| !value.trim().is_empty()) {
600
+ return Ok(());
601
+ }
602
+ Err(ModelError::Validation(format!(
603
+ "{}: missing front matter field model. \
604
+ Without an explicit model the framework fills a built-in default (sonnet-4-thinking for cursor_agent), \
605
+ so the seat silently gets a model that is not in the role file, while argv still looks normal. \
606
+ The role file must name the model itself; built-in defaults, team-level defaults, and the Cursor CLI default are all implicit sources. Example:\n\
607
+ model: sonnet-4-thinking",
608
+ path.display()
609
+ )))
610
+ }
611
+
612
+ /// 0.5.66 bypass 单源:角色 md 的 `dangerously_skip_permissions` 必填 bool。
613
+ /// 缺 = 编译失败(消息含硬串);非 bool = 同 fail-loud。
614
+ fn required_dangerously_skip_permissions(
615
+ meta: &Value,
616
+ path: &Path,
617
+ ) -> Result<bool, ModelError> {
618
+ match meta.get("dangerously_skip_permissions") {
619
+ Some(Value::Bool(value)) => Ok(*value),
620
+ Some(_) => Err(ModelError::Validation(format!(
621
+ "{}: 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.",
622
+ path.display()
623
+ ))),
624
+ None => Err(ModelError::Validation(format!(
625
+ "{}: missing front matter field dangerously_skip_permissions. This field must be declared explicitly; it controls whether the agent launches with permission prompts bypassed.",
626
+ path.display()
627
+ ))),
628
+ }
629
+ }
630
+
552
631
  fn optional_string_value(meta: &Value, key: &str) -> Value {
553
632
  match string_field(meta, key) {
554
633
  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)))
@@ -1,3 +1,29 @@
1
+ //! ---
2
+ //! purpose: coordinator daemon 进程的宿主——装配 transport/registry,跑主循环,把 tick 的崩溃收进退避与事件
3
+ //! contract:
4
+ //! provides:
5
+ //! - name: run_daemon
6
+ //! what: `team-agent coordinator --workspace ..` 子进程入口:解析 transport、构造 Coordinator、进主循环
7
+ //! - name: backoff_sleep_sec
8
+ //! what: 连续失败次数到睡眠秒数的纯函数(5→10→20→40→60→60,封顶 BACKOFF_MAX_SEC)
9
+ //! - name: resolve_tick_interval
10
+ //! what: 解析 tick 间隔并顺带确保 message store schema 可打开
11
+ //! depends:
12
+ //! - super::health
13
+ //! - super::tick
14
+ //! - super::types
15
+ //! - super::conpty_shim
16
+ //! - crate::state::persist
17
+ //! - crate::transport_factory
18
+ //! - crate::tmux_backend
19
+ //! - crate::message_store
20
+ //! - crate::event_log
21
+ //! boundary:
22
+ //! - 不做单次 tick 的编排内容,tick 体在 tick.rs
23
+ //! - 不决定健康与否,只消费 health.rs 的判定与路径
24
+ //! - transport 解析失败时降级为 tmux workspace 后端保活,但不静默——降级原因写进 boot metadata
25
+ //! maturity: wired
26
+ //! ---
1
27
  //!
2
28
  //! daemon 主循环面(`__main__.py`)—— 退避序列 + tick 间隔解析 + 子进程入口。
3
29
 
@@ -41,6 +67,13 @@ pub struct DaemonArgs {
41
67
  /// daemon 主循环(`main`,`__main__.py:25-98`)。写 pid/meta(source=boot)、装信号→STOP、孤儿自检、
42
68
  /// catch-all + 指数退避 + tick_error 去重/抑制、tick_recovered 重置、`result.stop || once` → break。
43
69
  /// §10:返 `Result`(顶层 bin 用 anyhow 收;§12 边界)。
70
+ /// ---
71
+ /// purpose: daemon 进程入口——解析 transport 后端、构造 Coordinator、跑主循环直到 stop 或 --once
72
+ /// params:
73
+ /// args: workspace(已 resolve)、once、tick_interval_sec、可选 team_key;team_key 为空时回落到 state.active_team_key
74
+ /// returns: 主循环正常退出(收到 STOP、tick 报 stop、或 --once 跑完一轮)时为 Ok
75
+ /// errors: 写 pid/metadata、事件日志或 tick 间隔解析失败时返回 DaemonError
76
+ /// ---
44
77
  pub fn run_daemon(args: DaemonArgs) -> Result<(), DaemonError> {
45
78
  // CP-1: the daemon's whole tick surface (has_session / capture / inject / list_windows / kill)
46
79
  // runs through this backend. Prefer the persisted runtime endpoint so attached explicit-socket
@@ -123,6 +156,14 @@ pub fn run_daemon(args: DaemonArgs) -> Result<(), DaemonError> {
123
156
  run_daemon_with_coordinator_and_boot_tmux(&args, &coordinator, Some(tmux_metadata))
124
157
  }
125
158
 
159
+ /// ---
160
+ /// purpose: 用调用方已装配好的 Coordinator 跑主循环(测试与内部复用入口,跳过 transport 解析)
161
+ /// params:
162
+ /// args: 与 run_daemon 同一份参数
163
+ /// coordinator: 已注入 provider registry 与 transport 的实例
164
+ /// returns: 与 run_daemon 相同的主循环退出语义
165
+ /// errors: 同 run_daemon
166
+ /// ---
126
167
  pub(crate) fn run_daemon_with_coordinator(
127
168
  args: &DaemonArgs,
128
169
  coordinator: &Coordinator,
@@ -520,6 +561,13 @@ fn current_ppid() -> u32 {
520
561
 
521
562
  /// 计算 tick 间隔(`_tick_interval`,`__main__.py:104-115`)。读 spec `runtime.tick_interval_sec`,
522
563
  /// 缺失/出错 → `DEFAULT_TICK_INTERVAL_SEC`;并确保 schema 存在(`MessageStore(workspace)`)。
564
+ /// ---
565
+ /// purpose: 取本 workspace 的 tick 间隔秒数,并顺带确保 message store schema 可打开
566
+ /// params:
567
+ /// workspace: 已 resolve 的 workspace 根
568
+ /// returns: tick 间隔秒数;当前实现恒为 DEFAULT_TICK_INTERVAL_SEC(spec 覆盖尚未接线)
569
+ /// errors: MessageStore::open 失败时返回 TickError
570
+ /// ---
523
571
  pub(super) fn resolve_tick_interval(workspace: &WorkspacePath) -> Result<f64, TickError> {
524
572
  let _ = MessageStore::open(workspace.as_path())?;
525
573
  Ok(DEFAULT_TICK_INTERVAL_SEC)
@@ -527,6 +575,13 @@ pub(super) fn resolve_tick_interval(workspace: &WorkspacePath) -> Result<f64, Ti
527
575
 
528
576
  /// 退避序列(`__main__.py:65`):`min(interval * 2^min(failures-1, 5), 60.0)` → 5→10→20→40→60→60s。
529
577
  /// unit test 锁死本序列(card §85)。**纯函数,无 I/O,可直接 impl 钉死**(但 ROUND-0 仍占位)。
578
+ /// ---
579
+ /// purpose: 由连续失败次数算出本轮该睡多少秒的纯函数
580
+ /// params:
581
+ /// interval: 基准 tick 间隔秒数
582
+ /// consecutive_failures: 至今连续失败次数;0 与 1 同视为首次失败
583
+ /// returns: interval * 2^min(failures-1, 5),上限 BACKOFF_MAX_SEC
584
+ /// ---
530
585
  pub(super) fn backoff_sleep_sec(interval: f64, consecutive_failures: u32) -> f64 {
531
586
  let failures = consecutive_failures.saturating_sub(1).min(5);
532
587
  let exp = i32::try_from(failures).unwrap_or(5);
@@ -1,3 +1,30 @@
1
+ //! ---
2
+ //! purpose: Windows ConPTY shim 的生命周期主人——定位并启动 windows-shim.exe、做 Hello 握手、把可复用的连接与 pid/pipe 名记进 state
3
+ //! contract:
4
+ //! provides:
5
+ //! - name: ensure_shim_running
6
+ //! what: 幂等入口:state 里记的 shim 还活着就重连,否则新起一个并握手
7
+ //! - name: reconnect_recorded_shim
8
+ //! what: 只按 state 记的 pipe 名重连并重做 Hello,绝不新起进程
9
+ //! - name: recorded_shim_pid
10
+ //! what: 读 state.transport.shim.pid,供停机路由
11
+ //! - name: recorded_shim_pipe_name
12
+ //! what: 读 state.transport.shim.pipe_name,供重连路由
13
+ //! - name: mark_transport_unavailable
14
+ //! what: shim 不可达时发 stale 家族事件并清掉 state 里的 pipe_ready
15
+ //! depends:
16
+ //! - conpty_transport
17
+ //! - crate::state::repository
18
+ //! - crate::state::persist
19
+ //! - crate::event_log
20
+ //! - crate::platform::process
21
+ //! boundary:
22
+ //! - pipe_token 绝不落 state:只经子进程环境变量传递并留在本进程内存里,state 只记 pid/pipe_name/pipe_ready
23
+ //! - 终态失败不静默回落 tmux,交由调用方按传输不可用处理
24
+ //! - 重连只证明线协议可用,不做新的令牌认证
25
+ //! - 整个文件仅在 Windows 编译;非 Windows 调用方必须自己 cfg 门控
26
+ //! maturity: wired
27
+ //! ---
1
28
  //!
2
29
  //! Windows ConPTY shim lifecycle manager.
3
30
  //!
@@ -75,16 +102,28 @@ pub struct ShimHandle {
75
102
  impl ShimHandle {
76
103
  /// The connected pipe client, ready for the factory to wire into
77
104
  /// `ConPtyBackend`. Callable exactly once — takes ownership.
105
+ /// ---
106
+ /// purpose: 取走已完成 Hello 握手的管道客户端,交给传输后端接线
107
+ /// returns: 首次调用返回客户端并转移所有权;再次调用为 None
108
+ /// ---
78
109
  pub fn take_client(&mut self) -> Option<NamedPipeClient> {
79
110
  self.client.take()
80
111
  }
81
112
 
82
113
  /// The shim pid (for status + shutdown routing).
114
+ /// ---
115
+ /// purpose: 给出 shim 进程号,用于状态展示与停机路由
116
+ /// returns: 启动时记下的 pid;子进程句柄已被 detach 或 drop 后该值仍可读
117
+ /// ---
83
118
  pub fn pid(&self) -> u32 {
84
119
  self.pid
85
120
  }
86
121
 
87
122
  /// The pipe name the shim listens on.
123
+ /// ---
124
+ /// purpose: 给出 shim 正在监听的命名管道名
125
+ /// returns: 形如 team-agent-conpty-<hash>-<team> 的管道全名
126
+ /// ---
88
127
  pub fn pipe_name(&self) -> &str {
89
128
  &self.pipe_name
90
129
  }
@@ -95,6 +134,11 @@ impl ShimHandle {
95
134
  /// an explicit `platform::process::terminate_pid` via
96
135
  /// `recorded_shim_pid`. Without this call, going out of scope
97
136
  /// would terminate the shim and orphan every worker.
137
+ /// ---
138
+ /// purpose: 放弃对 shim 子进程的所有权,使本句柄析构时不再杀它
139
+ /// returns: shim 的 pid,供调用方日后按精确 pid 显式终止
140
+ /// ---
141
+ /// 不调用它就让句柄离开作用域,会连带杀掉 shim 并使全部 worker 变成孤儿。
98
142
  pub fn detach(mut self) -> u32 {
99
143
  // Forget the child so its own Drop doesn't run either. The
100
144
  // shim survives until an explicit kill.
@@ -453,6 +497,12 @@ fn finalize(
453
497
  /// `None` when no shim is currently registered (Unix / never-launched
454
498
  /// Windows worker). Callers use `platform::process::terminate_pid`
455
499
  /// on the returned pid.
500
+ /// ---
501
+ /// purpose: 从 state 读出已登记的 shim 进程号
502
+ /// params:
503
+ /// workspace: workspace 根
504
+ /// returns: state.transport.shim.pid 存在且能转成 u32 时给值;没有登记过 shim(未起过或非 Windows 队)时为 None。只读状态,不做存活探测
505
+ /// ---
456
506
  pub fn recorded_shim_pid(workspace: &Path) -> Option<u32> {
457
507
  let state = crate::state::repository::StateRepository::new(workspace)
458
508
  .load_workspace_if_exists_without_migrations()
@@ -466,6 +516,12 @@ pub fn recorded_shim_pid(workspace: &Path) -> Option<u32> {
466
516
  }
467
517
 
468
518
  /// Read `state.transport.shim.pipe_name` for reconnect routing.
519
+ /// ---
520
+ /// purpose: 从 state 读出已登记的 shim 管道名
521
+ /// params:
522
+ /// workspace: workspace 根
523
+ /// returns: state.transport.shim.pipe_name 的字符串值;未登记时为 None
524
+ /// ---
469
525
  pub fn recorded_shim_pipe_name(workspace: &Path) -> Option<String> {
470
526
  let state = crate::state::repository::StateRepository::new(workspace)
471
527
  .load_workspace_if_exists_without_migrations()
@@ -497,6 +553,15 @@ pub fn recorded_shim_pipe_name(workspace: &Path) -> Option<String> {
497
553
  /// This gives the "coord can die, shim survives" invariant: a fresh
498
554
  /// coord that finds a live shim just reconnects; a fresh coord that
499
555
  /// finds no shim spawns one.
556
+ /// ---
557
+ /// purpose: 保证本 (workspace, team) 有一个可用的 shim——这是 coordinator 侧唯一的 shim 所有权入口
558
+ /// params:
559
+ /// workspace: workspace 根
560
+ /// team_key: 目标 team,进 Hello 请求的作用域
561
+ /// workspace_hash: 管道名里用的 workspace 摘要
562
+ /// returns: 已登记 pid 还活着且重连成功 → 复用原进程的句柄(不拥有子进程);否则新起一个并握手,返回拥有子进程的句柄
563
+ /// errors: 找不到 shim 可执行文件、spawn 失败、连接超时、Hello 失败或落盘 state 失败时返回对应 ShimError。重连失败不算错误,会直接落到新起那条路
564
+ /// ---
500
565
  pub fn ensure_shim_running(
501
566
  workspace: &Path,
502
567
  team_key: &str,
@@ -541,6 +606,15 @@ pub fn ensure_shim_running(
541
606
  /// Handle returned from this fn has no owned child (the shim is
542
607
  /// somebody else's — usually a previous coord instance). `detach()`
543
608
  /// on the handle is a no-op for the child.
609
+ /// ---
610
+ /// purpose: 只重连 state 里已登记的 shim 并重做 Hello,用来证明线协议仍然可用
611
+ /// params:
612
+ /// workspace: workspace 根
613
+ /// team_key: 目标 team,进 Hello 请求的作用域
614
+ /// _workspace_hash: 未使用;管道名直接取自 state,不再重算
615
+ /// returns: 连上且 Hello 回 ok 时给出不拥有子进程的句柄,对它调 detach 对子进程是空操作
616
+ /// errors: state 里没有管道名或多次连接均失败 → ConnectTimeout;连上但 Hello 回 ok=false → HelloFailed。本函数绝不新起进程
617
+ /// ---
544
618
  pub fn reconnect_recorded_shim(
545
619
  workspace: &Path,
546
620
  team_key: &str,
@@ -630,6 +704,14 @@ fn reconnect_hello(client: &mut NamedPipeClient, team_key: &str) -> Result<(), S
630
704
  /// The Windows-only `#[cfg]` gate is at the mod level (see
631
705
  /// `coordinator/mod.rs`); on Unix this file isn't compiled, so
632
706
  /// downstream callers must cfg-gate their reference themselves.
707
+ /// ---
708
+ /// purpose: 把「shim 不可达」这件事诚实地写出去——发 stale 家族事件,并清掉 state 里的就绪位
709
+ /// params:
710
+ /// workspace: workspace 根
711
+ /// reason: 不可达原因,同时进事件与 state.transport.shim.unavailable_reason
712
+ /// returns: 状态里本来就没有 shim 块时也返回 Ok。事件写入是尽力而为,写不出去不影响返回;清状态才是权威动作
713
+ /// errors: 读写 state 失败时返回 StateError;state JSON 损坏时按空状态继续
714
+ /// ---
633
715
  pub fn mark_transport_unavailable(workspace: &Path, reason: &str) -> Result<(), StateError> {
634
716
  // Best-effort event emission — a failed event write should not
635
717
  // fail the caller. The state-clearing step below is authoritative.