@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
@@ -18,6 +18,15 @@ fn e23_fallback_pane_has_single_shared_noisy_surface() {
18
18
  .unwrap(),
19
19
  "primary-ok guard must run before any fallback audit/inject attempt"
20
20
  );
21
+ let already = leader_receiver
22
+ .split("fn message_already_delivered")
23
+ .nth(1)
24
+ .unwrap_or("");
25
+ assert!(
26
+ already.contains("submitted_pending_acceptance")
27
+ && already.contains("injected_awaiting_receipt"),
28
+ "fallback already-delivered set must include the live post-submit statuses, not only the dead string submitted"
29
+ );
21
30
  }
22
31
 
23
32
  #[test]
@@ -99,6 +99,72 @@ fn attached_leader_mailbox_is_rechecked_by_the_normal_delivery_tick() {
99
99
  assert!(case.events().contains("leader_receiver.mailbox_requeued"));
100
100
  }
101
101
 
102
+ #[test]
103
+ fn claimed_leader_delivery_revalidates_a_stale_unattached_snapshot() {
104
+ let workspace = tmp_ws("leader-inject-acceptance-stale-attach");
105
+ let rollout = workspace.join("leader.jsonl");
106
+ std::fs::write(&rollout, "").unwrap();
107
+ let current_state = serde_json::json!({
108
+ "active_team_key": "acceptance-team",
109
+ "session_name": "team-acceptance",
110
+ "leader_receiver": {
111
+ "pane_id": "%leader",
112
+ "status": "attached",
113
+ "provider": "claude",
114
+ "rollout_path": rollout,
115
+ },
116
+ "teams": {
117
+ "acceptance-team": {
118
+ "session_name": "team-acceptance",
119
+ }
120
+ }
121
+ });
122
+ crate::state::persist::save_runtime_state(&workspace, &current_state).unwrap();
123
+ let store = MessageStore::open(&workspace).unwrap();
124
+ let event_log = EventLog::new(&workspace);
125
+ let message_id = store
126
+ .create_message(
127
+ None,
128
+ "worker",
129
+ "leader",
130
+ "stale attach canary",
131
+ None,
132
+ false,
133
+ Some("acceptance-team"),
134
+ )
135
+ .unwrap();
136
+ let stale_state = serde_json::json!({
137
+ "active_team_key": "acceptance-team",
138
+ "teams": {
139
+ "acceptance-team": {
140
+ "session_name": "team-acceptance"
141
+ }
142
+ }
143
+ });
144
+ let transport = RecordingTransport::new("");
145
+
146
+ let outcome = deliver_pending_message(
147
+ &workspace,
148
+ &store,
149
+ &transport,
150
+ &message_id,
151
+ &event_log,
152
+ &stale_state,
153
+ )
154
+ .expect("delivery from stale coordinator snapshot");
155
+
156
+ assert_ne!(
157
+ outcome.reason,
158
+ Some(DeliveryRefusal::LeaderNotAttached),
159
+ "a claim made after attach must revalidate durable state before writing leader_not_attached"
160
+ );
161
+ assert_ne!(
162
+ outcome.message_status.0, "failed",
163
+ "a stale pre-attach snapshot must not strand the requeued row"
164
+ );
165
+ assert_eq!(transport.inject_count(), 1);
166
+ }
167
+
102
168
  #[test]
103
169
  fn leader_submit_without_receipt_source_cannot_park_forever_without_a_completion_writer() {
104
170
  let case = Case::new_without_rollout("missing-receipt-source");
@@ -94,7 +94,7 @@ fn collect_result_file_ingests_valid_known_task_envelope_into_results() {
94
94
  .unwrap();
95
95
  std::fs::write(
96
96
  team.join("agents").join("w1.md"),
97
- "---\nname: w1\nrole: Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ntools:\n - mcp_team\n---\n\nW1.\n",
97
+ "---\nname: w1\nrole: Worker\nprovider: codex\nmodel: gpt-5.5\nauth_mode: subscription\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nW1.\n",
98
98
  )
99
99
  .unwrap();
100
100
  let spec = crate::compiler::compile_team(&team).expect("compile collect team");
@@ -296,6 +296,7 @@ impl Drop for EnvGuardMsg {
296
296
  }
297
297
 
298
298
  mod basic;
299
+ mod dup_inject;
299
300
  mod e23;
300
301
  mod leader_channel;
301
302
  mod leader_inject_acceptance;
@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
5
5
 
6
6
  use crate::model::enums::Provider;
7
7
  use crate::model::ids::{LeaderSessionUuid, OwnerEpoch, TaskId, TeamKey};
8
- use crate::transport::PaneId;
8
+ use crate::transport::{PaneId, TurnVerification};
9
9
 
10
10
  use super::helpers::MessageStatusShadow;
11
11
 
@@ -308,6 +308,9 @@ pub struct DeliveryOutcome {
308
308
  pub reason: Option<DeliveryRefusal>,
309
309
  /// fallback / broadcast / fanout 等通道标注 (`channel` 字段)。
310
310
  pub channel: Option<String>,
311
+ /// 入箱 vs 开跑。None = 本路径没观测(JSON 必须落成 not_yet_observed,不得报开跑)。
312
+ /// Gap42:只是 metadata,不是投递闸门。
313
+ pub turn_verification: Option<TurnVerification>,
311
314
  }
312
315
 
313
316
  /// trust retry payload (card §45;`delivery.py:273` scheduled_events.payload_json 的 typed 视图)。
@@ -21,6 +21,12 @@ pub enum Provider {
21
21
  /// AGENTS.md + `COPILOT_CUSTOM_INSTRUCTIONS_DIRS` env(B2 灵魂件降级,§C2)。
22
22
  Copilot,
23
23
  GeminiCli,
24
+ /// Grok CLI (0.5.67 provider-adapter). Append system prompt via `--rules`,
25
+ /// bypass `--always-approve`, session `--session-id`.
26
+ Grok,
27
+ /// Cursor `agent` CLI (0.5.67 provider-adapter). Append system prompt via
28
+ /// workspace `.cursor/rules/*.mdc` (方案 1 变体), bypass `--force`.
29
+ CursorAgent,
24
30
  Fake,
25
31
  }
26
32
 
@@ -33,7 +39,8 @@ pub enum Provider {
33
39
  /// Provider support:
34
40
  /// - claude / claude_code: low|medium|high|xhigh|max → `--effort <level>`
35
41
  /// - codex: low|medium|high|xhigh (NOT max) → `-c model_reasoning_effort=<level>`
36
- /// - copilot / gemini_cli / fake: unsupported warning event, no flag
42
+ /// - grok: low|medium|high|xhigh (NOT max) `--effort <level>`
43
+ /// - copilot / gemini_cli / cursor_agent / fake: unsupported — warning event, no flag
37
44
  #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
38
45
  pub enum ProviderEffort {
39
46
  #[serde(rename = "low")]
@@ -78,13 +85,15 @@ impl ProviderEffort {
78
85
  }
79
86
 
80
87
  /// True when the given provider supports this effort level. `max` is
81
- /// Claude-only; other levels are supported by Claude and Codex, ignored
82
- /// by Copilot/Gemini/Fake (warning emitted at runtime).
88
+ /// Claude-only; other levels are supported by Claude, Codex, and Grok.
89
+ /// Copilot/Gemini/CursorAgent/Fake ignore the field (warning at runtime).
83
90
  pub fn is_supported_by(self, provider: Provider) -> bool {
84
91
  match provider {
85
92
  Provider::Claude | Provider::ClaudeCode => true,
86
- Provider::Codex => !self.is_claude_only(),
87
- Provider::Copilot | Provider::GeminiCli | Provider::Fake => false,
93
+ Provider::Codex | Provider::Grok => !self.is_claude_only(),
94
+ Provider::Copilot | Provider::GeminiCli | Provider::CursorAgent | Provider::Fake => {
95
+ false
96
+ }
88
97
  }
89
98
  }
90
99
  }
@@ -189,6 +189,9 @@ fn provider_enforcement(provider: Provider, tool: Tool) -> Enforcement {
189
189
  FsRead | FsWrite | FsList | ExecuteBash | GitDiff | ProviderBuiltin => Hard,
190
190
  },
191
191
  Provider::Fake => Hard,
192
+ // 0.5.67: grok/cursor_agent 无 deny-kind 概念 (同 codex 精神), 诚实
193
+ // prompt_only, 不替 provider 决。
194
+ Provider::Grok | Provider::CursorAgent => PromptOnly,
192
195
  // Copilot(C-2-1 cr verdict):execute_bash/fs_write/network/mcp_team = hard,
193
196
  // fs_read/fs_list/git_diff/provider_builtin = prompt_only(诚实:copilot 无
194
197
  // 对应 deny kind,framework 不替决,留给 provider prompt 控制;MUST-NOT-13)。
@@ -354,8 +354,31 @@ fn basic_schema_errors(spec: &Yaml) -> Vec<String> {
354
354
  );
355
355
  match spec.get("agents") {
356
356
  Some(Yaml::List(agents)) if !agents.is_empty() => {
357
+ // 0.5.66 bypass 单源:收集缺/错 `dangerously_skip_permissions` 的角色清单,
358
+ // 循环后一次性报出,满足"缺字段错误消息含每个缺字段的角色 id 清单"硬要求。
359
+ let mut missing_bypass: Vec<String> = Vec::new();
360
+ let mut bad_type_bypass: Vec<String> = Vec::new();
357
361
  for (idx, agent) in agents.iter().enumerate() {
358
362
  check_agent(agent, &format!("/agents/{idx}"), &mut e);
363
+ match agent.get("dangerously_skip_permissions") {
364
+ None => missing_bypass.push(agent_label(agent, idx)),
365
+ Some(value) if !matches!(value, Yaml::Bool(_)) => {
366
+ bad_type_bypass.push(agent_label(agent, idx))
367
+ }
368
+ Some(_) => {}
369
+ }
370
+ }
371
+ if !missing_bypass.is_empty() {
372
+ e.push(format!(
373
+ "/agents/dangerously_skip_permissions: missing required field (role id(s): {}). This field must be declared explicitly; it controls whether the agent launches with permission prompts bypassed.",
374
+ missing_bypass.join(", ")
375
+ ));
376
+ }
377
+ if !bad_type_bypass.is_empty() {
378
+ e.push(format!(
379
+ "/agents/dangerously_skip_permissions: must be a boolean (role id(s): {}). This field must be declared explicitly; it controls whether the agent launches with permission prompts bypassed.",
380
+ bad_type_bypass.join(", ")
381
+ ));
359
382
  }
360
383
  }
361
384
  _ => e.push("/agents: must be a non-empty list".to_string()),
@@ -384,7 +407,7 @@ fn check_agent(agent: &Yaml, path: &str, errors: &mut Vec<String>) {
384
407
  "working_directory",
385
408
  "system_prompt",
386
409
  "tools",
387
- "permission_mode",
410
+ "dangerously_skip_permissions",
388
411
  "preferred_for",
389
412
  "avoid_for",
390
413
  "output_contract",
@@ -397,7 +420,7 @@ fn check_agent(agent: &Yaml, path: &str, errors: &mut Vec<String>) {
397
420
  "working_directory",
398
421
  "system_prompt",
399
422
  "tools",
400
- "permission_mode",
423
+ "dangerously_skip_permissions",
401
424
  "preferred_for",
402
425
  "avoid_for",
403
426
  "output_contract",
@@ -407,6 +430,10 @@ fn check_agent(agent: &Yaml, path: &str, errors: &mut Vec<String>) {
407
430
  "profile",
408
431
  "credential_ref",
409
432
  "forked_from",
433
+ // 存量角色 md 的 `permission_mode` 是历史无效值(compiler 曾恒发 restricted)。
434
+ // 0.5.66 起不再被 compiler 消费;保留在 allowed 以免存量文件 fail-loud
435
+ // (§3.1 迁移时顺手删除,0.6.0 可收紧)。
436
+ "permission_mode",
410
437
  // 0.4.x provider effort MVP step 3: per-agent effort override (resolved at compile).
411
438
  "effort",
412
439
  ];
@@ -481,6 +508,15 @@ fn check_agent(agent: &Yaml, path: &str, errors: &mut Vec<String>) {
481
508
  }
482
509
  }
483
510
 
511
+ /// 汇总错误里引用 agent 的可读标签:`<id>` 优先,退回 `<index> (role: <role>)`。
512
+ fn agent_label(agent: &Yaml, idx: usize) -> String {
513
+ agent
514
+ .get("id")
515
+ .and_then(Yaml::as_str)
516
+ .map(|id| format!("`{id}`"))
517
+ .unwrap_or_else(|| format!("agents[{idx}]"))
518
+ }
519
+
484
520
  fn check_routing(routing: Option<&Yaml>, errors: &mut Vec<String>) {
485
521
  check_keys_y(
486
522
  routing,
@@ -564,7 +600,6 @@ fn check_runtime(runtime: Option<&Yaml>, errors: &mut Vec<String>) {
564
600
  "max_active_agents",
565
601
  "startup_order",
566
602
  "display_backend",
567
- "dangerous_auto_approve",
568
603
  "auto_attach_leader",
569
604
  "fast",
570
605
  "tick_interval_sec",
@@ -588,9 +623,6 @@ fn check_runtime(runtime: Option<&Yaml>, errors: &mut Vec<String>) {
588
623
  errors.push("/runtime/display_backend: invalid display backend".to_string());
589
624
  }
590
625
  }
591
- if get("dangerous_auto_approve").is_some_and(|v| !matches!(v, Yaml::Bool(_))) {
592
- errors.push("/runtime/dangerous_auto_approve: must be a boolean".to_string());
593
- }
594
626
  if get("auto_trust_own_workspace").is_some_and(|v| !matches!(v, Yaml::Bool(_))) {
595
627
  errors.push("/runtime/auto_trust_own_workspace: must be a boolean".to_string());
596
628
  }
@@ -1020,6 +1052,58 @@ mod tests {
1020
1052
  );
1021
1053
  }
1022
1054
 
1055
+ // 0.5.66 bypass 单源 §4.1:缺 `dangerously_skip_permissions` 字段 → 错误消息
1056
+ // 必须列出每个缺字段的角色 id + 用户可读的必填说明。
1057
+ #[test]
1058
+ fn test_missing_dangerously_field_reports_role_list() {
1059
+ // 从 fixture 文本里删掉第一个 agent 的字段,模拟该角色缺字段。
1060
+ let text = include_str!("testdata/team.spec.yaml");
1061
+ let without_first = text.replacen(" dangerously_skip_permissions: false\n", "", 1);
1062
+ assert_ne!(text, without_first, "fixture must contain the field");
1063
+ let spec = yaml::loads(&without_first).unwrap();
1064
+ let errors = all_spec_errors(&spec);
1065
+ let missing: Vec<&String> = errors
1066
+ .iter()
1067
+ .filter(|m| m.starts_with("/agents/dangerously_skip_permissions: missing"))
1068
+ .collect();
1069
+ assert_eq!(missing.len(), 1, "exactly one missing-field error, got: {errors:#?}");
1070
+ assert!(
1071
+ missing[0].contains("`codex_implementer`"),
1072
+ "must name the role id, got: {}",
1073
+ missing[0]
1074
+ );
1075
+ assert!(
1076
+ missing[0].contains("This field must be declared explicitly; it controls whether the agent launches with permission prompts bypassed."),
1077
+ "must carry the user-facing required-field message, got: {}",
1078
+ missing[0]
1079
+ );
1080
+ }
1081
+
1082
+ // 0.5.66 bypass 单源 §4.1:非 bool 值 → fail-loud 类型错,同样带角色清单 + 硬串。
1083
+ #[test]
1084
+ fn test_invalid_dangerously_type_rejected() {
1085
+ let text = include_str!("testdata/team.spec.yaml");
1086
+ let bad = text.replacen(" dangerously_skip_permissions: false\n", " dangerously_skip_permissions: \"yes\"\n", 1);
1087
+ assert_ne!(text, bad, "fixture must contain the field");
1088
+ let spec = yaml::loads(&bad).unwrap();
1089
+ let errors = all_spec_errors(&spec);
1090
+ let type_errs: Vec<&String> = errors
1091
+ .iter()
1092
+ .filter(|m| m.starts_with("/agents/dangerously_skip_permissions: must be a boolean"))
1093
+ .collect();
1094
+ assert_eq!(type_errs.len(), 1, "exactly one type error, got: {errors:#?}");
1095
+ assert!(
1096
+ type_errs[0].contains("`codex_implementer`"),
1097
+ "must name the role id, got: {}",
1098
+ type_errs[0]
1099
+ );
1100
+ assert!(
1101
+ type_errs[0].contains("This field must be declared explicitly; it controls whether the agent launches with permission prompts bypassed."),
1102
+ "must carry the user-facing required-field message, got: {}",
1103
+ type_errs[0]
1104
+ );
1105
+ }
1106
+
1023
1107
  #[test]
1024
1108
  fn empty_spec_matches_python_golden() {
1025
1109
  let spec = yaml::loads("{}").unwrap();
@@ -1050,4 +1134,43 @@ mod tests {
1050
1134
  ]
1051
1135
  );
1052
1136
  }
1137
+
1138
+ // 0.5.66 bypass 单源 §4.1:7 触发面共用同一 spec 校验层——compile_team 是它们的共同
1139
+ // 必经点(quick-start/restart/add/fork 都调用),缺字段一律 fail-loud。此处验证
1140
+ // compile 级校验确实 fail-loud,即各触发面(经 compile_team)都被覆盖。
1141
+ #[test]
1142
+ fn test_all_7_triggers_run_field_check() {
1143
+ // compile_team 是 7 触发面的 spec 校验必经点:
1144
+ // quick-start(quick_start.rs:233 compile_team)
1145
+ // restart(rebuild.rs:3245 rebuild_runtime_spec_from_roles → compile_team)
1146
+ // add-agent(add_agent.rs:331 compile_role_agent)
1147
+ // clone-agent(clone_agent.rs:62 → add_agent)
1148
+ // fork-agent(fork_agent.rs:96 validate_spec)
1149
+ // start-agent / reset-agent(操作已编译 spec,团队创建时经 compile_team 校验)
1150
+ // 验证 compile_team 对缺字段的角色文档 fail-loud。
1151
+ let team = std::env::temp_dir().join(format!(
1152
+ "ta-spec-7trigger-{}",
1153
+ std::process::id()
1154
+ ));
1155
+ let _ = std::fs::remove_dir_all(&team);
1156
+ std::fs::create_dir_all(team.join("agents")).unwrap();
1157
+ std::fs::write(
1158
+ team.join("TEAM.md"),
1159
+ "---\nname: t7\nobjective: 7 trigger.\nprovider: codex\n---\n\nteam.\n",
1160
+ )
1161
+ .unwrap();
1162
+ // 角色文档故意缺 dangerously_skip_permissions
1163
+ std::fs::write(
1164
+ team.join("agents").join("w.md"),
1165
+ "---\nname: w\nrole: Worker\nprovider: codex\ntools:\n - mcp_team\n---\n\nw.\n",
1166
+ )
1167
+ .unwrap();
1168
+ let err = crate::compiler::compile_team(&team).unwrap_err().to_string();
1169
+ assert!(
1170
+ err.contains("missing front matter field dangerously_skip_permissions")
1171
+ && err.contains("This field must be declared explicitly; it controls whether the agent launches with permission prompts bypassed."),
1172
+ "compile_team must fail-loud on missing field (shared by all 7 triggers); got {err}"
1173
+ );
1174
+ let _ = std::fs::remove_dir_all(&team);
1175
+ }
1053
1176
  }
@@ -39,6 +39,7 @@ agents:
39
39
  - "provider_builtin"
40
40
  - "banana"
41
41
  permission_mode: "restricted"
42
+ dangerously_skip_permissions: false
42
43
  preferred_for:
43
44
  - "implementation"
44
45
  - "bug_fix"
@@ -69,6 +70,7 @@ agents:
69
70
  - "mcp_team"
70
71
  - "provider_builtin"
71
72
  permission_mode: "restricted"
73
+ dangerously_skip_permissions: false
72
74
  preferred_for:
73
75
  - "research"
74
76
  - "architecture"
@@ -99,6 +101,7 @@ agents:
99
101
  - "mcp_team"
100
102
  - "provider_builtin"
101
103
  permission_mode: "restricted"
104
+ dangerously_skip_permissions: false
102
105
  preferred_for:
103
106
  - "review"
104
107
  - "risk_check"
@@ -141,7 +144,6 @@ runtime:
141
144
  session_name: "teamspec-full-example"
142
145
  auto_launch: true
143
146
  require_user_approval_before_launch: true
144
- dangerous_auto_approve: false
145
147
  max_active_agents: 3
146
148
  startup_order:
147
149
  - "codex_implementer"
@@ -38,6 +38,7 @@ agents:
38
38
  - "mcp_team"
39
39
  - "provider_builtin"
40
40
  permission_mode: "restricted"
41
+ dangerously_skip_permissions: false
41
42
  preferred_for:
42
43
  - "implementation"
43
44
  - "bug_fix"
@@ -68,6 +69,7 @@ agents:
68
69
  - "mcp_team"
69
70
  - "provider_builtin"
70
71
  permission_mode: "restricted"
72
+ dangerously_skip_permissions: false
71
73
  preferred_for:
72
74
  - "research"
73
75
  - "architecture"
@@ -98,6 +100,7 @@ agents:
98
100
  - "mcp_team"
99
101
  - "provider_builtin"
100
102
  permission_mode: "restricted"
103
+ dangerously_skip_permissions: false
101
104
  preferred_for:
102
105
  - "review"
103
106
  - "risk_check"
@@ -140,7 +143,6 @@ runtime:
140
143
  session_name: "teamspec-full-example"
141
144
  auto_launch: true
142
145
  require_user_approval_before_launch: true
143
- dangerous_auto_approve: false
144
146
  max_active_agents: 3
145
147
  startup_order:
146
148
  - "codex_implementer"
@@ -38,6 +38,7 @@ agents:
38
38
  - mcp_team
39
39
  - provider_builtin
40
40
  permission_mode: restricted
41
+ dangerously_skip_permissions: false
41
42
  preferred_for:
42
43
  - implementation
43
44
  - bug_fix
@@ -68,6 +69,7 @@ agents:
68
69
  - mcp_team
69
70
  - provider_builtin
70
71
  permission_mode: restricted
72
+ dangerously_skip_permissions: false
71
73
  preferred_for:
72
74
  - research
73
75
  - architecture
@@ -98,6 +100,7 @@ agents:
98
100
  - mcp_team
99
101
  - provider_builtin
100
102
  permission_mode: restricted
103
+ dangerously_skip_permissions: false
101
104
  preferred_for:
102
105
  - review
103
106
  - risk_check
@@ -140,7 +143,6 @@ runtime:
140
143
  session_name: teamspec-full-example
141
144
  auto_launch: true
142
145
  require_user_approval_before_launch: true
143
- dangerous_auto_approve: false
144
146
  max_active_agents: 3
145
147
  startup_order:
146
148
  - codex_implementer
@@ -1,10 +1,31 @@
1
+ //! ---
2
+ //! purpose: 为运行时提供有界的进程输出读取与前台进程组探测
3
+ //! contract:
4
+ //! provides:
5
+ //! - name: bounded_command_output_with_probe
6
+ //! what: 在固定超时内执行命令并读取 stdout,超时后终止子进程
7
+ //! - name: pane_foreground_and_root_pgrp
8
+ //! what: 读取 pane PID 的前台与根进程组 ID,无法判定时返回 None
9
+ //! depends:
10
+ //! - std
11
+ //! boundary:
12
+ //! - 只负责有界探测、临时 stdout 文件的独占创建与清理
13
+ //! - 不决定 shutdown 状态,不重试探测,不修改调用方行为
14
+ //! arch:
15
+ //! allowed_dependencies:
16
+ //! - std
17
+ //! maturity: wired
18
+ //! ---
19
+
1
20
  use std::cell::RefCell;
2
21
  use std::fs::OpenOptions;
3
22
  use std::io::{self, Read};
4
23
  use std::process::{Command, ExitStatus, Stdio};
24
+ use std::sync::atomic::{AtomicU64, Ordering};
5
25
  use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
6
26
 
7
27
  const DEFAULT_TIMEOUT: Duration = Duration::from_millis(900);
28
+ static NEXT_TEMP_OUTPUT_ID: AtomicU64 = AtomicU64::new(0);
8
29
 
9
30
  thread_local! {
10
31
  static PROBE_TIMEOUT: RefCell<Option<ProbeTimeout>> = const { RefCell::new(None) };
@@ -23,19 +44,31 @@ pub(crate) struct BoundedCommandOutput {
23
44
  pub(crate) stdout: Vec<u8>,
24
45
  }
25
46
 
47
+ /// ---
48
+ /// purpose: 清除当前线程记录的探测超时
49
+ /// ---
26
50
  pub(crate) fn clear_probe_timeout() {
27
51
  PROBE_TIMEOUT.with(|timeout| *timeout.borrow_mut() = None);
28
52
  }
29
53
 
54
+ /// ---
55
+ /// purpose: 判断当前线程是否记录了探测超时
56
+ /// ---
30
57
  pub(crate) fn probe_timed_out() -> bool {
31
58
  PROBE_TIMEOUT.with(|timeout| timeout.borrow().is_some())
32
59
  }
33
60
 
61
+ /// ---
62
+ /// purpose: 获取当前线程记录的探测超时详情
63
+ /// ---
34
64
  pub(crate) fn probe_timeout() -> Option<ProbeTimeout> {
35
65
  PROBE_TIMEOUT.with(|timeout| timeout.borrow().clone())
36
66
  }
37
67
 
38
68
  #[cfg(test)]
69
+ /// ---
70
+ /// purpose: 为单元测试设置探测超时详情
71
+ /// ---
39
72
  pub(crate) fn set_probe_timeout_for_test(probe: &'static str, pid: Option<u32>, timeout_ms: u64) {
40
73
  PROBE_TIMEOUT.with(|current| {
41
74
  *current.borrow_mut() = Some(ProbeTimeout {
@@ -46,6 +79,9 @@ pub(crate) fn set_probe_timeout_for_test(probe: &'static str, pid: Option<u32>,
46
79
  });
47
80
  }
48
81
 
82
+ /// ---
83
+ /// purpose: 执行一个带默认超时与探测标识的命令并读取 stdout
84
+ /// ---
49
85
  pub(crate) fn bounded_command_output_with_probe(
50
86
  command: &mut Command,
51
87
  probe: &'static str,
@@ -114,9 +150,14 @@ fn temp_output_path(kind: &str) -> std::path::PathBuf {
114
150
  .duration_since(UNIX_EPOCH)
115
151
  .map(|duration| duration.as_nanos())
116
152
  .unwrap_or(0);
153
+ temp_output_path_with_nanos(kind, nanos)
154
+ }
155
+
156
+ fn temp_output_path_with_nanos(kind: &str, nanos: u128) -> std::path::PathBuf {
157
+ let unique_id = NEXT_TEMP_OUTPUT_ID.fetch_add(1, Ordering::Relaxed);
117
158
  std::env::temp_dir().join(format!(
118
- "team-agent-os-probe-{}-{nanos}.{kind}",
119
- std::process::id()
159
+ "team-agent-os-probe-{}-{nanos}-{unique_id}.{kind}",
160
+ std::process::id(),
120
161
  ))
121
162
  }
122
163
 
@@ -146,6 +187,9 @@ fn read_and_remove(path: &std::path::Path) -> Vec<u8> {
146
187
  ///
147
188
  /// Bounded via [`run_bounded_command`] so a wedged ps cannot stall the
148
189
  /// coordinator tick.
190
+ /// ---
191
+ /// purpose: 读取 pane PID 的前台与根进程组 ID,无法判定时返回 None
192
+ /// ---
149
193
  pub(crate) fn pane_foreground_and_root_pgrp(pane_pid: u32) -> io::Result<Option<(u32, u32)>> {
150
194
  let mut command = Command::new("ps");
151
195
  command.args(["-o", "tpgid=,pgid=", "-p", &pane_pid.to_string()]);
@@ -206,4 +250,31 @@ mod fg_pgrp_tests {
206
250
  pane_foreground_and_root_pgrp(0xFFFF_FFFE).expect("must not error on missing pid");
207
251
  assert!(result.is_none(), "missing pid → None");
208
252
  }
253
+
254
+ #[test]
255
+ fn temp_output_create_new_requires_atomic_discriminator() {
256
+ let fixed_nanos = 0x5eed_u128;
257
+ let first_path = temp_output_path_with_nanos("stdout", fixed_nanos);
258
+ let second_path = temp_output_path_with_nanos("stdout", fixed_nanos);
259
+ let _ = std::fs::remove_file(&first_path);
260
+ let _ = std::fs::remove_file(&second_path);
261
+
262
+ let first = OpenOptions::new()
263
+ .create_new(true)
264
+ .read(true)
265
+ .write(true)
266
+ .open(&first_path)
267
+ .expect("first atomic path create");
268
+ let second = OpenOptions::new()
269
+ .create_new(true)
270
+ .read(true)
271
+ .write(true)
272
+ .open(&second_path)
273
+ .expect("second atomic path create");
274
+ assert_ne!(first_path, second_path, "atomic discriminator must advance");
275
+ drop(first);
276
+ drop(second);
277
+ let _ = std::fs::remove_file(first_path);
278
+ let _ = std::fs::remove_file(second_path);
279
+ }
209
280
  }