@team-agent/installer 0.5.85 → 0.5.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +2 -2
  4. package/crates/team-agent/src/cli/emit.rs +56 -12
  5. package/crates/team-agent/src/cli/mod.rs +0 -1
  6. package/crates/team-agent/src/cli/status_port/brief.rs +187 -7
  7. package/crates/team-agent/src/compiler/tests.rs +13 -24
  8. package/crates/team-agent/src/compiler.rs +5 -57
  9. package/crates/team-agent/src/leader/start.rs +0 -2
  10. package/crates/team-agent/src/lifecycle/launch/add_agent.rs +0 -1
  11. package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +0 -1
  12. package/crates/team-agent/src/lifecycle/launch/agent_state.rs +0 -6
  13. package/crates/team-agent/src/lifecycle/launch/identity.rs +39 -22
  14. package/crates/team-agent/src/lifecycle/launch/layout.rs +0 -1
  15. package/crates/team-agent/src/lifecycle/launch/leader_context.rs +0 -1
  16. package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +0 -58
  17. package/crates/team-agent/src/lifecycle/launch/ownership.rs +0 -1
  18. package/crates/team-agent/src/lifecycle/launch/pi_mcp.rs +0 -3
  19. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +14 -24
  20. package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +0 -1
  21. package/crates/team-agent/src/lifecycle/launch/readiness.rs +0 -1
  22. package/crates/team-agent/src/lifecycle/launch/spawn.rs +5 -9
  23. package/crates/team-agent/src/lifecycle/launch/spec_state.rs +0 -1
  24. package/crates/team-agent/src/lifecycle/launch/state_projection.rs +0 -1
  25. package/crates/team-agent/src/lifecycle/launch/worker_env.rs +0 -1
  26. package/crates/team-agent/src/lifecycle/launch.rs +0 -9
  27. package/crates/team-agent/src/lifecycle/restart/common.rs +5 -9
  28. package/crates/team-agent/src/lifecycle/tests/b0_reader_hideone_audit_contract.rs +3 -3
  29. package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +1 -3
  30. package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +42 -46
  31. package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +30 -44
  32. package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +32 -77
  33. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +2 -13
  34. package/crates/team-agent/src/lifecycle/tests/pi_catalog_red.rs +0 -1
  35. package/crates/team-agent/src/lifecycle/tests/pi_command_red.rs +0 -2
  36. package/crates/team-agent/src/lifecycle/tests/pi_compiler_red.rs +14 -85
  37. package/crates/team-agent/src/lifecycle/tests/pi_dual_entry_red.rs +1 -1
  38. package/crates/team-agent/src/lifecycle/tests/pi_executable_mcp_red.rs +0 -5
  39. package/crates/team-agent/src/lifecycle/tests/pi_session_delivery_red.rs +0 -1
  40. package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +4 -4
  41. package/crates/team-agent/src/lifecycle/tests.rs +0 -1
  42. package/crates/team-agent/src/lifecycle/types.rs +0 -10
  43. package/crates/team-agent/src/lifecycle/worker_command_context.rs +4 -248
  44. package/crates/team-agent/src/model/enums.rs +17 -38
  45. package/crates/team-agent/src/model/mod.rs +0 -1
  46. package/crates/team-agent/src/model/spec.rs +31 -91
  47. package/crates/team-agent/src/provider/adapter.rs +48 -50
  48. package/crates/team-agent/src/provider/adapters/claude.rs +5 -30
  49. package/crates/team-agent/src/provider/adapters/codex.rs +5 -25
  50. package/crates/team-agent/src/provider/adapters/copilot.rs +8 -41
  51. package/crates/team-agent/src/provider/adapters/cursor_agent.rs +4 -8
  52. package/crates/team-agent/src/provider/adapters/grok.rs +5 -58
  53. package/crates/team-agent/src/provider/adapters/mod.rs +5 -7
  54. package/crates/team-agent/src/provider/adapters/pi.rs +1 -56
  55. package/crates/team-agent/src/provider/bypass_flags.rs +1 -2
  56. package/crates/team-agent/src/provider/session/capture.rs +4 -4
  57. package/crates/team-agent/src/provider/tests/adapter.rs +7 -24
  58. package/crates/team-agent/src/provider/types.rs +1 -1
  59. package/crates/team-agent/src/provider/wire.rs +1 -1
  60. package/package.json +4 -4
  61. package/schemas/team.schema.json +4 -9
  62. package/crates/team-agent/src/lifecycle/tests/g1_silent_faces.rs +0 -784
  63. package/crates/team-agent/src/model/permissions.rs +0 -721
@@ -190,7 +190,7 @@ fn front_matter_non_object_errors() {
190
190
  // separators=(",",":"))` with the workspace path templated to __WS__.
191
191
  // (team-agent-public v0.2.11, /tmp/probe_compiler.py.)
192
192
 
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
+ 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","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},"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"}]}"#;
194
194
 
195
195
  #[test]
196
196
  fn compile_base_noprofile_matches_python_dict_order_and_values() {
@@ -261,7 +261,7 @@ dangerously_skip_permissions: false
261
261
  Implement bounded tasks.
262
262
  ";
263
263
 
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"}]}"#;
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,"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},"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"}]}"#;
265
265
 
266
266
  #[test]
267
267
  fn compile_runtime_front_matter_defaults_match_python() {
@@ -302,7 +302,7 @@ dangerously_skip_permissions: false
302
302
  Edit and defend the argument.
303
303
  ";
304
304
 
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"}]}"#;
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,"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},"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"}]}"#;
306
306
 
307
307
  #[test]
308
308
  fn compile_provider_models_claude_code_falls_back_to_claude_alias() {
@@ -336,7 +336,7 @@ tools:
336
336
  Implement bounded tasks.
337
337
  ";
338
338
 
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"}]}"#;
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,"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},"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"}]}"#;
340
340
 
341
341
  #[test]
342
342
  fn compile_subscription_without_model_uses_builtin_provider_default() {
@@ -378,7 +378,7 @@ tools:
378
378
  Bravo body.
379
379
  ";
380
380
 
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"}]}"#;
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","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},"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},"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"}]}"#;
382
382
 
383
383
  #[test]
384
384
  fn compile_two_agents_sorted_by_filename_with_routing_and_startup_order() {
@@ -633,36 +633,25 @@ fn fix_a5_session_name_override_wins() {
633
633
  // ── A6 tools ──
634
634
 
635
635
  #[test]
636
- fn fix_a6_tools_shell_maps_to_execute_bash() {
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";
636
+ fn fix_a6_tools_are_transparent_compatibility_metadata() {
637
+ let role = "---\nname: w\nrole: R\nprovider: codex\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - shell\n - provider_builtin\n---\nb\n";
638
638
  let team = build_team(TM_CODEX, &[("w.md", role)], &[]);
639
- let spec = compile_team(&team).expect("shell must normalize to execute_bash and compile");
640
- assert_eq!(
641
- agent0(&spec).get("tools"),
642
- Some(&list_str(vec!["execute_bash", "mcp_team"]))
643
- );
639
+ let spec = compile_team(&team).expect("tools metadata must not gate compilation");
640
+ assert!(agent0(&spec).get("tools").is_none());
644
641
  }
645
642
 
646
643
  #[test]
647
- fn fix_a6_missing_tools_errors() {
644
+ fn fix_a6_missing_tools_is_allowed() {
648
645
  let role = "---\nname: w\nrole: R\nprovider: codex\nauth_mode: subscription\ndangerously_skip_permissions: false\n---\nb\n";
649
646
  let team = build_team(TM_CODEX, &[("w.md", role)], &[]);
650
- let err = compile_team(&team).unwrap_err();
651
- assert!(
652
- err.to_string().contains("missing front matter field tools"),
653
- "got: {err}"
654
- );
647
+ compile_team(&team).expect("omitted tools metadata must compile");
655
648
  }
656
649
 
657
650
  #[test]
658
- fn fix_a6_tools_not_a_list_errors() {
651
+ fn fix_a6_tools_non_list_is_transparent() {
659
652
  let role = "---\nname: w\nrole: R\nprovider: codex\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools: justastring\n---\nb\n";
660
653
  let team = build_team(TM_CODEX, &[("w.md", role)], &[]);
661
- let err = compile_team(&team).unwrap_err();
662
- assert!(
663
- err.to_string().contains("tools must be a list"),
664
- "got: {err}"
665
- );
654
+ compile_team(&team).expect("any YAML tools metadata must compile");
666
655
  }
667
656
 
668
657
  // ── A7 system_prompt inline ──
@@ -30,11 +30,10 @@ use std::path::Path;
30
30
  use crate::communication_mode::CommunicationMode;
31
31
  use crate::model::enums::{Provider, ProviderEffort};
32
32
  use crate::model::yaml::Value;
33
- use crate::model::{paths, permissions, spec, yaml, ModelError};
34
- use crate::provider::adapters::pi::first_unsupported_pi_tool_category;
33
+ use crate::model::{paths, spec, yaml, ModelError};
35
34
  use crate::provider::wire::{
36
- builtin_provider_model as wire_builtin_provider_model, is_claude_family,
37
- parse_canonical_provider, provider_model_keys,
35
+ builtin_provider_model as wire_builtin_provider_model, parse_canonical_provider,
36
+ provider_model_keys,
38
37
  };
39
38
 
40
39
  pub const IGNORED_OWNER_TEAM_ID_FIELD: &str = "owner_team_id";
@@ -233,7 +232,6 @@ pub fn compile_team(team_dir: &Path) -> Result<Value, ModelError> {
233
232
  ("role", Value::Str(leader_role)),
234
233
  ("provider", Value::Str(leader_provider)),
235
234
  ("model", leader_model),
236
- ("tools", list_str(vec!["fs_read", "fs_list", "mcp_team"])),
237
235
  (
238
236
  "context_policy",
239
237
  map(vec![
@@ -526,7 +524,6 @@ fn compile_role_agent_with_mode(
526
524
  role_path.display(),
527
525
  )));
528
526
  }
529
- let tools = required_tools(&meta, role_path)?;
530
527
  let prompt_inline = non_empty_trimmed(&body).unwrap_or_else(|| role.clone());
531
528
  let mut agent_items = vec![
532
529
  ("id", Value::Str(id.clone())),
@@ -542,7 +539,6 @@ fn compile_role_agent_with_mode(
542
539
  ("file", Value::Null),
543
540
  ]),
544
541
  ),
545
- ("tools", list_str(tools)),
546
542
  // 0.5.66 bypass 单源:compiler 透传角色 md 的 `dangerously_skip_permissions`
547
543
  // (必填 bool,spec 校验保证存在)。取代旧"恒发 permission_mode: restricted"。
548
544
  (
@@ -606,12 +602,9 @@ fn compile_role_agent_with_mode(
606
602
  })
607
603
  .unwrap_or("");
608
604
  let provider_enum = parse_canonical_provider(provider_str).unwrap_or(Provider::Codex);
609
- if effort.is_claude_only()
610
- && !is_claude_family(provider_enum)
611
- && provider_enum != Provider::Pi
612
- {
605
+ if let Err(reason) = effort.resolve_for_provider(provider_enum) {
613
606
  return Err(ModelError::Validation(format!(
614
- "{}: effort '{}' is only supported by claude/claude_code (provider: {provider_str})",
607
+ "{}: {reason} (effort: {}; provider: {provider_str})",
615
608
  role_path.display(),
616
609
  effort.as_str()
617
610
  )));
@@ -758,25 +751,6 @@ fn validate_pi_role_fields(meta: &Value, path: &Path, provider: &str) -> Result<
758
751
  )));
759
752
  }
760
753
  }
761
- let tools = required_tools(meta, path)?;
762
- if !tools.iter().any(|tool| tool == "mcp_team") {
763
- return Err(ModelError::Validation(format!(
764
- "{}: Pi roles require mcp_team",
765
- path.display()
766
- )));
767
- }
768
- let expanded = permissions::expand_tool_strings(tools.iter().map(String::as_str));
769
- if let Some(category) = first_unsupported_pi_tool_category(
770
- expanded
771
- .iter()
772
- .filter(|category| permissions::is_canonical_tool(category))
773
- .map(String::as_str),
774
- ) {
775
- return Err(ModelError::Validation(format!(
776
- "{}: Pi does not support Team Agent tool category {category:?}; remove it from the role's tools",
777
- path.display()
778
- )));
779
- }
780
754
  Ok(())
781
755
  }
782
756
 
@@ -876,32 +850,6 @@ fn py_int_value(value: &Value) -> Option<i64> {
876
850
  }
877
851
  }
878
852
 
879
- fn required_tools(meta: &Value, path: &Path) -> Result<Vec<String>, ModelError> {
880
- let Some(value) = meta.get("tools") else {
881
- return Err(ModelError::Validation(format!(
882
- "{}: missing front matter field tools",
883
- path.display()
884
- )));
885
- };
886
- let Some(items) = value.as_list() else {
887
- return Err(ModelError::Validation(format!(
888
- "{}: tools must be a list",
889
- path.display()
890
- )));
891
- };
892
- Ok(items
893
- .iter()
894
- .filter_map(Value::as_str)
895
- .map(|tool| {
896
- if tool == "shell" {
897
- "execute_bash".to_string()
898
- } else {
899
- tool.to_string()
900
- }
901
- })
902
- .collect())
903
- }
904
-
905
853
  fn resolve_model(role_meta: &Value, team_meta: &Value, provider: &str) -> Value {
906
854
  if let Some(model) = string_field(role_meta, "model") {
907
855
  return Value::Str(model);
@@ -304,7 +304,6 @@ fn leader_start_plan_with_ambient_authority(
304
304
  identity.team_id.as_str(),
305
305
  );
306
306
  let prompt = crate::lifecycle::worker_command_context::compile_pi_leader_system_prompt();
307
- let tools = ["mcp_team", "fs_read", "fs_list", "fs_write", "execute_bash"];
308
307
  let team_mcp_tools = [
309
308
  "assign_task",
310
309
  "send_message",
@@ -327,7 +326,6 @@ fn leader_start_plan_with_ambient_authority(
327
326
  model: parsed.model.as_deref(),
328
327
  effort: parsed.effort,
329
328
  system_prompt: &prompt,
330
- tool_categories: &tools,
331
329
  team_mcp_tools: &team_mcp_tools,
332
330
  mcp_config: &mcp_config,
333
331
  session_scope: crate::lifecycle::launch::pi_mcp::pi_leader_session_scope(
@@ -31,7 +31,6 @@ use std::time::{SystemTime, UNIX_EPOCH};
31
31
  use crate::lifecycle::*;
32
32
  use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
33
33
  use crate::model::ids::AgentId;
34
- use crate::model::permissions::{self, AgentPermissionInput};
35
34
  use crate::model::yaml::{self, Value};
36
35
  use crate::state::persist::load_runtime_state;
37
36
  use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};
@@ -28,7 +28,6 @@ use std::process::Command;
28
28
  use crate::lifecycle::*;
29
29
  use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
30
30
  use crate::model::ids::AgentId;
31
- use crate::model::permissions::{self, AgentPermissionInput};
32
31
  use crate::model::yaml::{self, Value};
33
32
  use crate::state::persist::load_runtime_state;
34
33
  use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};
@@ -24,7 +24,6 @@ use std::process::Command;
24
24
  use crate::lifecycle::*;
25
25
  use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
26
26
  use crate::model::ids::AgentId;
27
- use crate::model::permissions::{self, AgentPermissionInput};
28
27
  use crate::model::yaml::{self, Value};
29
28
  use crate::state::persist::load_runtime_state;
30
29
  use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};
@@ -106,11 +105,6 @@ pub(super) fn running_agent_state(
106
105
  "mcp_config".to_string(),
107
106
  serde_json::json!(mcp_config_path.to_string_lossy().to_string()),
108
107
  );
109
- state.insert(
110
- "permissions".to_string(),
111
- permissions_json(agent, id, provider)
112
- .map_err(|e| LifecycleError::Compile(e.to_string()))?,
113
- );
114
108
  // 0.5.66 bypass 单源:policy 从 agent 字段派生,不再用全队 `DangerousApproval`。
115
109
  persist_effective_approval_policy_from_yaml_agent(&mut state, agent, provider);
116
110
  // 0.5.66 bypass 单源 §2.6:fresh spawn 记录运行面 bypass 值(供 restart drift 挡板)。
@@ -9,7 +9,7 @@
9
9
  //! - name: transport_has_session
10
10
  //! what: 探测 tmux session 是否存在,探测本身出错一律当不存在
11
11
  //! - name: provider_effort_from_raw
12
- //! what: 解析 effort 并要求该 provider 支持,否则 None
12
+ //! what: 解析 effort,保留兼容忽略项并拒绝非法 provider 组合
13
13
  //! - name: quick_start_depth_guard
14
14
  //! what: 定出本次 quick-start 的父团队与层级,推不出父团队时拒绝
15
15
  //! - name: annotate_persisted_team_depth
@@ -33,7 +33,6 @@ use std::process::Command;
33
33
  use crate::lifecycle::*;
34
34
  use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
35
35
  use crate::model::ids::AgentId;
36
- use crate::model::permissions::{self, AgentPermissionInput};
37
36
  use crate::model::yaml::{self, Value};
38
37
  use crate::state::persist::load_runtime_state;
39
38
  use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};
@@ -136,24 +135,24 @@ pub(super) fn parse_auth_mode(raw: &str) -> Option<AuthMode> {
136
135
  /// purpose: 从原始字符串解析 effort,并要求当前 provider 支持它
137
136
  /// params:
138
137
  /// raw: effort 原文,None 或空白返回 None
139
- /// returns: 解析成功且该 provider 支持时返回该档位,否则 None
138
+ /// returns: 支持的档位或兼容忽略的 None;已知非法 provider/effort 组合返回错误
140
139
  /// ---
141
140
  /// 0.4.x provider effort MVP step 4: low-level from a raw string. Returns
142
- /// `Some(effort)` when the level parses AND the provider supports it.
141
+ /// `Some(effort)` for supported levels, None for legacy ignored values,
142
+ /// and an error for provider/effort combinations rejected by configuration.
143
143
  pub(crate) fn provider_effort_from_raw(
144
144
  raw: Option<&str>,
145
145
  provider: Provider,
146
- ) -> Option<ProviderEffort> {
147
- let raw = raw?.trim();
148
- if raw.is_empty() {
149
- return None;
150
- }
151
- let effort = ProviderEffort::parse(raw)?;
152
- if effort.is_supported_by(provider) {
153
- Some(effort)
154
- } else {
155
- None
156
- }
146
+ ) -> Result<Option<ProviderEffort>, LifecycleError> {
147
+ let Some(effort) = raw.and_then(ProviderEffort::parse) else {
148
+ return Ok(None);
149
+ };
150
+ effort.resolve_for_provider(provider).map_err(|reason| {
151
+ LifecycleError::RequirementUnmet(format!(
152
+ "{reason} (effort: {}; provider: {provider:?})",
153
+ effort.as_str()
154
+ ))
155
+ })
157
156
  }
158
157
 
159
158
  /// ---
@@ -172,7 +171,7 @@ pub(crate) fn provider_effort_event_payload(
172
171
  return None;
173
172
  }
174
173
  let effort = ProviderEffort::parse(raw)?;
175
- if effort.is_supported_by(provider) {
174
+ if !matches!(effort.resolve_for_provider(provider), Ok(None)) {
176
175
  return None;
177
176
  }
178
177
  Some(serde_json::json!({
@@ -222,7 +221,7 @@ pub(crate) fn extend_worker_env_unset_for_effort(
222
221
  pub(crate) fn provider_effort_for_spawn(
223
222
  agent: &crate::model::yaml::Value,
224
223
  provider: Provider,
225
- ) -> Option<ProviderEffort> {
224
+ ) -> Result<Option<ProviderEffort>, LifecycleError> {
226
225
  provider_effort_from_raw(agent.get("effort").and_then(|v| v.as_str()), provider)
227
226
  }
228
227
 
@@ -253,7 +252,7 @@ pub(crate) fn provider_effort_event_if_dropped(
253
252
  pub(crate) fn provider_effort_for_spawn_json(
254
253
  agent: &serde_json::Value,
255
254
  provider: Provider,
256
- ) -> Option<ProviderEffort> {
255
+ ) -> Result<Option<ProviderEffort>, LifecycleError> {
257
256
  provider_effort_from_raw(
258
257
  agent.get("effort").and_then(serde_json::Value::as_str),
259
258
  provider,
@@ -308,10 +307,16 @@ pub(super) fn quick_start_depth_guard(
308
307
  requested_team: Option<&str>,
309
308
  _strict_real_runtime: bool,
310
309
  ) -> Result<QuickStartDepth, LifecycleError> {
311
- let env_parent = std::env::var("TEAM_AGENT_OWNER_TEAM_ID")
312
- .ok()
313
- .map(|value| value.trim().to_string())
314
- .filter(|value| !value.is_empty());
310
+ // OWNER_TEAM_ID is injected into every worker process. It is parent
311
+ // context only when the target quick-start workspace is the same workspace
312
+ // that produced the environment; otherwise a worker launching a fresh
313
+ // external workspace would inherit a false nesting parent.
314
+ let env_parent = owner_context_matches_workspace(workspace).then(|| {
315
+ std::env::var("TEAM_AGENT_OWNER_TEAM_ID")
316
+ .ok()
317
+ .map(|value| value.trim().to_string())
318
+ .filter(|value| !value.is_empty())
319
+ }).flatten();
315
320
  let parent = env_parent;
316
321
  let Some(parent) = parent else {
317
322
  let state = crate::state::persist::load_runtime_state(workspace)
@@ -366,6 +371,18 @@ pub(super) fn quick_start_depth_guard(
366
371
  /// purpose: 从 runtime state 推断父团队键
367
372
  /// returns: 活跃团队键,且该团队确有 running 席位时返回它,否则 None
368
373
  /// ---
374
+ fn owner_context_matches_workspace(workspace: &Path) -> bool {
375
+ let Some(env_workspace) = std::env::var_os("TEAM_AGENT_WORKSPACE") else {
376
+ return false;
377
+ };
378
+ let env_workspace = PathBuf::from(env_workspace);
379
+ env_workspace == workspace
380
+ || std::fs::canonicalize(&env_workspace)
381
+ .ok()
382
+ .zip(std::fs::canonicalize(workspace).ok())
383
+ .is_some_and(|(env_path, target_path)| env_path == target_path)
384
+ }
385
+
369
386
  pub(super) fn infer_parent_team_from_active_state(state: &serde_json::Value) -> Option<String> {
370
387
  let active = explicit_active_team_key(state)?;
371
388
  let team = state
@@ -25,7 +25,6 @@ use std::process::Command;
25
25
  use crate::lifecycle::*;
26
26
  use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
27
27
  use crate::model::ids::AgentId;
28
- use crate::model::permissions::{self, AgentPermissionInput};
29
28
  use crate::model::yaml::{self, Value};
30
29
  use crate::state::persist::load_runtime_state;
31
30
  use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};
@@ -31,7 +31,6 @@ use crate::lifecycle::profile_launch::parse_provider;
31
31
  use crate::lifecycle::*;
32
32
  use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
33
33
  use crate::model::ids::AgentId;
34
- use crate::model::permissions::{self, AgentPermissionInput};
35
34
  use crate::model::yaml::{self, Value};
36
35
  use crate::state::persist::load_runtime_state;
37
36
  use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};
@@ -16,7 +16,6 @@
16
16
  //! what: 把 argv 里的 MCP 配置参数改指到已落盘的文件
17
17
  //! depends:
18
18
  //! - crate::provider::McpConfig
19
- //! - crate::model::permissions
20
19
  //! - crate::event_log::EventLog
21
20
  //! - crate::lifecycle::profile_launch
22
21
  //! - std::fs
@@ -33,8 +32,6 @@ use std::process::Command;
33
32
 
34
33
  use crate::lifecycle::*;
35
34
  use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
36
- use crate::model::ids::AgentId;
37
- use crate::model::permissions::{self, AgentPermissionInput};
38
35
  use crate::model::yaml::{self, Value};
39
36
  use crate::state::persist::load_runtime_state;
40
37
  use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};
@@ -644,58 +641,3 @@ pub(crate) fn point_native_mcp_config_at_file(
644
641
  _ => {}
645
642
  }
646
643
  }
647
-
648
- /// ---
649
- /// purpose: 解析该席位的权限并序列化成 JSON
650
- /// returns: 含 agent_id、provider、排序后的工具串、逐工具的执行强度与是否有仅提示项
651
- /// errors: 权限解析失败时返回 ModelError
652
- /// ---
653
- pub(super) fn permissions_json(
654
- agent: &Value,
655
- id: &str,
656
- provider: Provider,
657
- ) -> Result<serde_json::Value, crate::model::ModelError> {
658
- let tools = agent.get("tools").and_then(Value::as_list).map(|items| {
659
- items
660
- .iter()
661
- .filter_map(Value::as_str)
662
- .map(str::to_string)
663
- .collect::<Vec<_>>()
664
- });
665
- let resolved = permissions::resolve_permissions(&AgentPermissionInput {
666
- id: Some(AgentId::new(id)),
667
- provider,
668
- role: agent
669
- .get("role")
670
- .and_then(Value::as_str)
671
- .map(str::to_string),
672
- tools,
673
- })?;
674
- let mut out = serde_json::Map::new();
675
- out.insert("agent_id".to_string(), serde_json::json!(id));
676
- out.insert("provider".to_string(), serde_json::json!(provider));
677
- out.insert(
678
- "tools".to_string(),
679
- serde_json::json!(resolved.sorted_tool_strings()),
680
- );
681
- out.insert(
682
- "resolved_tools".to_string(),
683
- serde_json::Value::Array(
684
- resolved
685
- .resolved_tools
686
- .iter()
687
- .map(|tool| {
688
- serde_json::json!({
689
- "tool": tool.tool,
690
- "enforcement": tool.enforcement,
691
- })
692
- })
693
- .collect(),
694
- ),
695
- );
696
- out.insert(
697
- "has_prompt_only".to_string(),
698
- serde_json::json!(resolved.has_prompt_only),
699
- );
700
- Ok(serde_json::Value::Object(out))
701
- }
@@ -25,7 +25,6 @@ use std::process::Command;
25
25
  use crate::lifecycle::*;
26
26
  use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
27
27
  use crate::model::ids::AgentId;
28
- use crate::model::permissions::{self, AgentPermissionInput};
29
28
  use crate::model::yaml::{self, Value};
30
29
  use crate::state::persist::load_runtime_state;
31
30
  use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};
@@ -995,7 +995,6 @@ pub(crate) struct PiMaterializeRequest<'a> {
995
995
  pub model: Option<&'a str>,
996
996
  pub effort: Option<ProviderEffort>,
997
997
  pub system_prompt: &'a str,
998
- pub tool_categories: &'a [&'a str],
999
998
  pub team_mcp_tools: &'a [&'a str],
1000
999
  pub mcp_config: &'a McpConfig,
1001
1000
  pub session_scope: PiSessionScope,
@@ -1093,7 +1092,6 @@ fn materialize_pi_plan_with_session(
1093
1092
  model: model.as_deref(),
1094
1093
  effort: request.effort,
1095
1094
  system_prompt: request.system_prompt,
1096
- tool_categories: request.tool_categories,
1097
1095
  session_dir,
1098
1096
  session: PiSessionSelector::Resume { path: &exact_path },
1099
1097
  agent_id: request.agent_id,
@@ -1106,7 +1104,6 @@ fn materialize_pi_plan_with_session(
1106
1104
  model: model.as_deref(),
1107
1105
  effort: request.effort,
1108
1106
  system_prompt: request.system_prompt,
1109
- tool_categories: request.tool_categories,
1110
1107
  session_dir,
1111
1108
  session: PiSessionSelector::Fresh {
1112
1109
  session_id: session_id.as_str(),
@@ -31,7 +31,6 @@ use std::process::Command;
31
31
  use crate::lifecycle::*;
32
32
  use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
33
33
  use crate::model::ids::AgentId;
34
- use crate::model::permissions::{self, AgentPermissionInput};
35
34
  use crate::model::yaml::{self, Value};
36
35
  use crate::state::persist::load_runtime_state;
37
36
  use crate::transport::{PaneField, PaneId, SessionName, Target, Transport, WindowName};
@@ -4434,7 +4433,7 @@ mod fresh_quick_start_leader_binding_tests {
4434
4433
 
4435
4434
 
4436
4435
  #[test]
4437
- fn invalid_pi_tool_category_is_rejected_before_runtime_persistence() {
4436
+ fn pi_tool_category_is_transparently_accepted_by_compiler() {
4438
4437
  let root = std::env::temp_dir().join(format!(
4439
4438
  "ta-quick-start-pi-tools-{}-{}",
4440
4439
  std::process::id(),
@@ -4453,29 +4452,20 @@ mod fresh_quick_start_leader_binding_tests {
4453
4452
  )
4454
4453
  .unwrap();
4455
4454
 
4456
- let transport = crate::transport::test_support::OfflineTransport::new();
4457
- let mut discover = |_requested: &str| -> Result<Vec<String>, ()> { Err(()) };
4458
- let error = quick_start_with_transport_in_workspace_with_display_pi_preflight(
4459
- &root,
4460
- &team,
4461
- None,
4462
- true,
4463
- None,
4464
- &transport,
4465
- false,
4466
- &mut discover,
4467
- )
4468
- .expect_err("invalid Pi category must fail before quick-start persistence");
4469
- let error = error.to_string();
4470
- assert!(error.contains("Pi does not support Team Agent tool category \"provider_builtin\""));
4471
- assert!(error.contains("remove it from the role's tools"));
4472
- assert!(
4473
- !crate::state::persist::runtime_state_path(&root).exists(),
4474
- "quick-start validation failure must not persist runtime state"
4475
- );
4455
+ let spec = crate::compiler::compile_team(&team)
4456
+ .expect("tools metadata must be transparently accepted by the compiler");
4457
+ let worker = spec
4458
+ .get("agents")
4459
+ .and_then(Value::as_list)
4460
+ .and_then(|agents| {
4461
+ agents.iter().find(|agent| {
4462
+ agent.get("id").and_then(Value::as_str) == Some("worker")
4463
+ })
4464
+ })
4465
+ .expect("compiled spec must retain the worker role");
4476
4466
  assert!(
4477
- !root.join(".team/runtime").exists(),
4478
- "quick-start validation failure must not create runtime artifacts"
4467
+ worker.get("tools").is_none(),
4468
+ "tools metadata must not acquire runtime semantics"
4479
4469
  );
4480
4470
  let _ = std::fs::remove_dir_all(root);
4481
4471
  }
@@ -28,7 +28,6 @@ use std::process::Command;
28
28
  use crate::lifecycle::*;
29
29
  use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
30
30
  use crate::model::ids::AgentId;
31
- use crate::model::permissions::{self, AgentPermissionInput};
32
31
  use crate::model::yaml::{self, Value};
33
32
  use crate::state::persist::load_runtime_state;
34
33
  use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};