@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.
- package/Cargo.lock +8 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/Cargo.toml +6 -0
- package/crates/team-agent/src/cli/adapters.rs +6 -1
- package/crates/team-agent/src/cli/diagnose.rs +82 -0
- package/crates/team-agent/src/cli/emit.rs +3 -3
- package/crates/team-agent/src/cli/grok_slot.rs +299 -0
- package/crates/team-agent/src/cli/leader.rs +21 -7
- package/crates/team-agent/src/cli/leaders.rs +125 -1
- package/crates/team-agent/src/cli/mod.rs +20 -0
- package/crates/team-agent/src/cli/send/presentation.rs +7 -1
- package/crates/team-agent/src/cli/spec.rs +2 -0
- package/crates/team-agent/src/cli/status_port/compact.rs +28 -0
- package/crates/team-agent/src/cli/status_port/snapshot.rs +25 -1
- package/crates/team-agent/src/cli/tests/base.rs +26 -3
- package/crates/team-agent/src/cli/tests/compile.rs +1 -1
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +129 -19
- package/crates/team-agent/src/cli/tests/mod.rs +2 -2
- package/crates/team-agent/src/cli/tests/run_delegation.rs +1 -1
- package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +387 -6
- package/crates/team-agent/src/cli/tests/status_send.rs +79 -10
- package/crates/team-agent/src/cli/tests/verb_validate.rs +1 -1
- package/crates/team-agent/src/communication_mode/mod.rs +6 -4
- package/crates/team-agent/src/compiler/tests.rs +23 -20
- package/crates/team-agent/src/compiler.rs +84 -5
- package/crates/team-agent/src/conpty/backend.rs +16 -0
- package/crates/team-agent/src/coordinator/backoff.rs +55 -0
- package/crates/team-agent/src/coordinator/conpty_shim.rs +82 -0
- package/crates/team-agent/src/coordinator/health.rs +173 -0
- package/crates/team-agent/src/coordinator/mod.rs +31 -0
- package/crates/team-agent/src/coordinator/orphan.rs +31 -0
- package/crates/team-agent/src/coordinator/runtime_detectors.rs +30 -0
- package/crates/team-agent/src/coordinator/runtime_observation.rs +25 -0
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +59 -0
- package/crates/team-agent/src/coordinator/steps/delivery.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/health_sync.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/mod.rs +22 -0
- package/crates/team-agent/src/coordinator/steps/persist.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/session_gate.rs +10 -0
- package/crates/team-agent/src/coordinator/tick.rs +133 -22
- package/crates/team-agent/src/coordinator/types.rs +49 -0
- package/crates/team-agent/src/db/message_store.rs +39 -5
- package/crates/team-agent/src/layout/worker_env.rs +20 -3
- package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
- package/crates/team-agent/src/leader/provider_attribution.rs +53 -0
- package/crates/team-agent/src/leader/registry.rs +65 -0
- package/crates/team-agent/src/leader/start.rs +1080 -58
- package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/display.rs +56 -0
- package/crates/team-agent/src/lifecycle/helpers.rs +57 -0
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +332 -14
- package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +69 -2
- package/crates/team-agent/src/lifecycle/launch/agent_state.rs +196 -2
- package/crates/team-agent/src/lifecycle/launch/clone_agent.rs +101 -11
- package/crates/team-agent/src/lifecycle/launch/cursor_create_chat.rs +229 -0
- package/crates/team-agent/src/lifecycle/launch/cursor_mcp.rs +332 -0
- package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +204 -457
- package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +24 -0
- package/crates/team-agent/src/lifecycle/launch/grok_per_seat.rs +260 -0
- package/crates/team-agent/src/lifecycle/launch/identity.rs +146 -0
- package/crates/team-agent/src/lifecycle/launch/layout.rs +60 -0
- package/crates/team-agent/src/lifecycle/launch/leader_context.rs +112 -0
- package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +530 -0
- package/crates/team-agent/src/lifecycle/launch/ownership.rs +40 -0
- package/crates/team-agent/src/lifecycle/launch/plan.rs +31 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +66 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +78 -0
- package/crates/team-agent/src/lifecycle/launch/readiness.rs +85 -38
- package/crates/team-agent/src/lifecycle/launch/role_source.rs +44 -44
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +101 -4
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +244 -48
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +109 -2
- package/crates/team-agent/src/lifecycle/launch/worker_env.rs +214 -1
- package/crates/team-agent/src/lifecycle/launch.rs +209 -43
- package/crates/team-agent/src/lifecycle/lock.rs +42 -1
- package/crates/team-agent/src/lifecycle/mod.rs +11 -0
- package/crates/team-agent/src/lifecycle/pane_input_lock.rs +161 -0
- package/crates/team-agent/src/lifecycle/profile_launch.rs +98 -0
- package/crates/team-agent/src/lifecycle/profile_smoke.rs +51 -1
- package/crates/team-agent/src/lifecycle/restart/agent.rs +213 -3
- package/crates/team-agent/src/lifecycle/restart/common.rs +373 -18
- package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +27 -0
- package/crates/team-agent/src/lifecycle/restart/preflight.rs +25 -0
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +165 -15
- package/crates/team-agent/src/lifecycle/restart/remove.rs +196 -5
- package/crates/team-agent/src/lifecycle/restart/selection.rs +72 -0
- package/crates/team-agent/src/lifecycle/restart/team_state.rs +22 -0
- package/crates/team-agent/src/lifecycle/restart.rs +29 -0
- package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +3 -4
- package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +4 -4
- package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +309 -0
- package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +159 -174
- package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +15 -13
- package/crates/team-agent/src/lifecycle/tests/core.rs +4 -18
- package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/cursor_mcp_overlay.rs +287 -0
- package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +229 -0
- package/crates/team-agent/src/lifecycle/tests/cursor_restart_resume_red.rs +353 -0
- package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/g1_silent_faces.rs +784 -0
- package/crates/team-agent/src/lifecycle/tests/gate_fixtures.rs +562 -0
- package/crates/team-agent/src/lifecycle/tests/grok_effort_argv_red.rs +239 -0
- package/crates/team-agent/src/lifecycle/tests/grok_mcp_overlay_red.rs +498 -0
- package/crates/team-agent/src/lifecycle/tests/grok_require_explicit_model_red.rs +250 -0
- package/crates/team-agent/src/lifecycle/tests/grok_restart_resume_red.rs +293 -0
- package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +3 -0
- package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +3 -3
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +65 -40
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +20 -32
- package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +17 -9
- package/crates/team-agent/src/lifecycle/tests/mcp_tool_name_format_red.rs +69 -0
- package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +24 -23
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +98 -62
- package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +3 -1
- package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +41 -8
- package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +3 -3
- package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +60 -2
- package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +69 -26
- package/crates/team-agent/src/lifecycle/tests.rs +23 -15
- package/crates/team-agent/src/lifecycle/types.rs +61 -2
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +200 -33
- package/crates/team-agent/src/mcp_server/tests/scoped.rs +100 -1
- package/crates/team-agent/src/mcp_server/tests.rs +196 -2
- package/crates/team-agent/src/messaging/delivery.rs +405 -40
- package/crates/team-agent/src/messaging/helpers.rs +2 -0
- package/crates/team-agent/src/messaging/leader_receiver.rs +53 -1
- package/crates/team-agent/src/messaging/results.rs +4 -0
- package/crates/team-agent/src/messaging/send.rs +34 -0
- package/crates/team-agent/src/messaging/tests/dup_inject.rs +406 -0
- package/crates/team-agent/src/messaging/tests/e23.rs +9 -0
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +66 -0
- package/crates/team-agent/src/messaging/tests/main_preserved.rs +1 -1
- package/crates/team-agent/src/messaging/tests/mod.rs +1 -0
- package/crates/team-agent/src/messaging/types.rs +4 -1
- package/crates/team-agent/src/model/enums.rs +14 -5
- package/crates/team-agent/src/model/permissions.rs +3 -0
- package/crates/team-agent/src/model/spec.rs +129 -6
- package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +3 -1
- package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +3 -1
- package/crates/team-agent/src/model/testdata/team.spec.yaml +3 -1
- package/crates/team-agent/src/os_probe.rs +73 -2
- package/crates/team-agent/src/provider/adapter.rs +231 -4
- package/crates/team-agent/src/provider/adapters/claude.rs +5 -2
- package/crates/team-agent/src/provider/adapters/codex.rs +5 -2
- package/crates/team-agent/src/provider/adapters/copilot.rs +5 -2
- package/crates/team-agent/src/provider/adapters/cursor_agent.rs +86 -0
- package/crates/team-agent/src/provider/adapters/grok.rs +157 -0
- package/crates/team-agent/src/provider/adapters/mod.rs +2 -0
- package/crates/team-agent/src/provider/bypass_flags.rs +124 -0
- package/crates/team-agent/src/provider/classify.rs +4 -2
- package/crates/team-agent/src/provider/faults.rs +2 -1
- package/crates/team-agent/src/provider/mod.rs +13 -0
- package/crates/team-agent/src/provider/session/capture.rs +158 -20
- package/crates/team-agent/src/provider/session/context_fork/claude.rs +38 -1
- package/crates/team-agent/src/provider/session/context_fork/codex.rs +72 -1
- package/crates/team-agent/src/provider/session/context_fork/outcome.rs +57 -1
- package/crates/team-agent/src/provider/session/context_fork.rs +77 -3
- package/crates/team-agent/src/provider/session/mod.rs +18 -0
- package/crates/team-agent/src/provider/session/resume.rs +57 -0
- package/crates/team-agent/src/provider/session_scan/claude.rs +125 -1
- package/crates/team-agent/src/provider/session_scan/codex.rs +94 -1
- package/crates/team-agent/src/provider/session_scan/common.rs +204 -2
- package/crates/team-agent/src/provider/session_scan/copilot.rs +33 -1
- package/crates/team-agent/src/provider/session_scan/cursor.rs +538 -0
- package/crates/team-agent/src/provider/session_scan/grok.rs +288 -0
- package/crates/team-agent/src/provider/session_scan.rs +8 -0
- package/crates/team-agent/src/provider/submit_now.rs +94 -0
- package/crates/team-agent/src/provider/tests/adapter.rs +304 -0
- package/crates/team-agent/src/provider/types.rs +4 -2
- package/crates/team-agent/src/provider/wire.rs +23 -1
- package/crates/team-agent/src/state/persist.rs +301 -8
- package/crates/team-agent/src/state/repository.rs +5 -0
- package/crates/team-agent/src/tmux_backend/tests.rs +1737 -42
- package/crates/team-agent/src/tmux_backend.rs +1083 -75
- package/crates/team-agent/src/transport/test_support.rs +20 -0
- package/crates/team-agent/src/transport/tests/wire.rs +28 -2
- package/crates/team-agent/src/transport.rs +265 -1
- package/npm/install.mjs +129 -73
- package/package.json +4 -4
- package/skills/team-agent/SKILL.md +33 -238
- package/skills/team-agent/command-coverage.json +31 -0
- package/crates/team-agent/src/lifecycle/launch/approval.rs +0 -134
- package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +0 -59
- package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +0 -483
- package/crates/team-agent/src/lifecycle/launch/fork_pending.rs +0 -109
- package/crates/team-agent/src/lifecycle/launch/fork_state.rs +0 -447
- package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +0 -1187
- package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +0 -327
package/Cargo.lock
CHANGED
|
@@ -325,6 +325,12 @@ version = "0.4.29"
|
|
|
325
325
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
326
326
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
|
327
327
|
|
|
328
|
+
[[package]]
|
|
329
|
+
name = "md5"
|
|
330
|
+
version = "0.7.0"
|
|
331
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
332
|
+
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
|
|
333
|
+
|
|
328
334
|
[[package]]
|
|
329
335
|
name = "memchr"
|
|
330
336
|
version = "2.8.0"
|
|
@@ -575,12 +581,13 @@ dependencies = [
|
|
|
575
581
|
|
|
576
582
|
[[package]]
|
|
577
583
|
name = "team-agent"
|
|
578
|
-
version = "0.5.
|
|
584
|
+
version = "0.5.68"
|
|
579
585
|
dependencies = [
|
|
580
586
|
"anyhow",
|
|
581
587
|
"chrono",
|
|
582
588
|
"conpty-transport",
|
|
583
589
|
"libc",
|
|
590
|
+
"md5",
|
|
584
591
|
"regex",
|
|
585
592
|
"rusqlite",
|
|
586
593
|
"serde",
|
package/Cargo.toml
CHANGED
|
@@ -22,6 +22,7 @@ thiserror.workspace = true
|
|
|
22
22
|
serde.workspace = true
|
|
23
23
|
serde_json.workspace = true
|
|
24
24
|
sha2.workspace = true
|
|
25
|
+
md5 = "0.7"
|
|
25
26
|
regex.workspace = true
|
|
26
27
|
chrono.workspace = true
|
|
27
28
|
libc.workspace = true
|
|
@@ -57,5 +58,10 @@ serial_test = { version = "3", features = ["file_locks"] }
|
|
|
57
58
|
name = "e2e"
|
|
58
59
|
path = "tests/e2e/main.rs"
|
|
59
60
|
|
|
61
|
+
# 0.5.66 bypass 单源:E2E 验证角色字段 → 运行面 → drift 挡板 → force fresh spawn。
|
|
62
|
+
[[test]]
|
|
63
|
+
name = "e2e_bypass_single_source"
|
|
64
|
+
path = "tests/e2e_bypass_single_source.rs"
|
|
65
|
+
|
|
60
66
|
[lints]
|
|
61
67
|
workspace = true
|
|
@@ -75,7 +75,7 @@ pub fn cmd_init(args: &InitArgs) -> Result<CmdResult, CliError> {
|
|
|
75
75
|
if args.force || !default_agent_path.exists() {
|
|
76
76
|
std::fs::write(
|
|
77
77
|
&default_agent_path,
|
|
78
|
-
"---\nname: worker\nrole: Worker\nprovider: fake\ntools:\n - mcp_team\n---\n\nWait for instructions.\n",
|
|
78
|
+
"---\nname: worker\nrole: Worker\nprovider: fake\ndangerously_skip_permissions: false\ntools:\n - mcp_team\n---\n\nWait for instructions.\n",
|
|
79
79
|
)?;
|
|
80
80
|
}
|
|
81
81
|
if args.force || !state_path.exists() {
|
|
@@ -1536,6 +1536,11 @@ pub fn cmd_doctor(args: &DoctorArgs) -> Result<CmdResult, CliError> {
|
|
|
1536
1536
|
args.team.as_deref(),
|
|
1537
1537
|
&mut value,
|
|
1538
1538
|
);
|
|
1539
|
+
crate::cli::diagnose::append_registry_channel_unbound_to_report(
|
|
1540
|
+
&args.workspace,
|
|
1541
|
+
args.team.as_deref(),
|
|
1542
|
+
&mut value,
|
|
1543
|
+
);
|
|
1539
1544
|
value
|
|
1540
1545
|
};
|
|
1541
1546
|
Ok(CmdResult::from_json(value, args.json))
|
|
@@ -208,12 +208,45 @@ pub(crate) fn diagnose_runtime_for_workspace(
|
|
|
208
208
|
&mut issues,
|
|
209
209
|
&mut repairs,
|
|
210
210
|
);
|
|
211
|
+
append_registry_channel_unbound_issue(workspace, state, &mut issues, &mut repairs);
|
|
211
212
|
append_legacy_snapshot_issue(workspace, state, &mut issues);
|
|
212
213
|
append_coordinator_health_issue(workspace, state, &mut issues, &mut repairs);
|
|
213
214
|
append_runtime_bindings_stale_after_boot_issue(workspace, state, &mut issues, &mut repairs);
|
|
214
215
|
(issues, repairs)
|
|
215
216
|
}
|
|
216
217
|
|
|
218
|
+
fn append_registry_channel_unbound_issue(
|
|
219
|
+
workspace: &std::path::Path,
|
|
220
|
+
state: &Value,
|
|
221
|
+
issues: &mut Value,
|
|
222
|
+
repairs: &mut Value,
|
|
223
|
+
) {
|
|
224
|
+
let team_key = crate::state::projection::team_state_key(state);
|
|
225
|
+
if team_key.is_empty()
|
|
226
|
+
|| crate::lifecycle::launch::launched_team_receiver_is_attached(workspace, &team_key)
|
|
227
|
+
{
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if let Some(items) = issues.as_array_mut() {
|
|
231
|
+
if !items.iter().any(|item| {
|
|
232
|
+
item.as_str() == Some("leader_not_attached")
|
|
233
|
+
|| item.get("id").and_then(Value::as_str) == Some("leader_channel_unbound")
|
|
234
|
+
}) {
|
|
235
|
+
items.push(json!("leader_not_attached"));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if let Some(items) = repairs.as_array_mut() {
|
|
239
|
+
items.push(recovery_hint(
|
|
240
|
+
state
|
|
241
|
+
.get("session_name")
|
|
242
|
+
.and_then(Value::as_str)
|
|
243
|
+
.unwrap_or(team_key.as_str()),
|
|
244
|
+
"leader_not_attached",
|
|
245
|
+
"team-agent claim-leader",
|
|
246
|
+
));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
217
250
|
fn append_live_leader_workspace_mismatch_issue(
|
|
218
251
|
workspace: &std::path::Path,
|
|
219
252
|
state: &Value,
|
|
@@ -339,6 +372,53 @@ fn live_leader_workspace_mismatch(
|
|
|
339
372
|
Some((issue, repair))
|
|
340
373
|
}
|
|
341
374
|
|
|
375
|
+
pub(crate) fn append_registry_channel_unbound_to_report(
|
|
376
|
+
workspace: &std::path::Path,
|
|
377
|
+
team: Option<&str>,
|
|
378
|
+
report: &mut Value,
|
|
379
|
+
) {
|
|
380
|
+
let team_key = team
|
|
381
|
+
.filter(|team| !team.is_empty())
|
|
382
|
+
.map(str::to_string)
|
|
383
|
+
.or_else(|| {
|
|
384
|
+
crate::state::persist::load_runtime_state(workspace)
|
|
385
|
+
.ok()
|
|
386
|
+
.map(|state| crate::state::projection::team_state_key(&state))
|
|
387
|
+
})
|
|
388
|
+
.unwrap_or_default();
|
|
389
|
+
if team_key.is_empty()
|
|
390
|
+
|| crate::lifecycle::launch::launched_team_receiver_is_attached(workspace, &team_key)
|
|
391
|
+
{
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
let Some(object) = report.as_object_mut() else {
|
|
395
|
+
return;
|
|
396
|
+
};
|
|
397
|
+
let mut issues = object
|
|
398
|
+
.get("issues")
|
|
399
|
+
.and_then(Value::as_array)
|
|
400
|
+
.cloned()
|
|
401
|
+
.unwrap_or_default();
|
|
402
|
+
if !issues.iter().any(|item| {
|
|
403
|
+
item.as_str() == Some("leader_not_attached")
|
|
404
|
+
|| item.get("id").and_then(Value::as_str) == Some("leader_channel_unbound")
|
|
405
|
+
}) {
|
|
406
|
+
issues.push(json!("leader_not_attached"));
|
|
407
|
+
}
|
|
408
|
+
object.insert("issues".to_string(), Value::Array(issues));
|
|
409
|
+
let mut repairs = object
|
|
410
|
+
.get("suggested_repairs")
|
|
411
|
+
.and_then(Value::as_array)
|
|
412
|
+
.cloned()
|
|
413
|
+
.unwrap_or_default();
|
|
414
|
+
repairs.push(recovery_hint(
|
|
415
|
+
&team_key,
|
|
416
|
+
"leader_not_attached",
|
|
417
|
+
"team-agent claim-leader",
|
|
418
|
+
));
|
|
419
|
+
object.insert("suggested_repairs".to_string(), Value::Array(repairs));
|
|
420
|
+
}
|
|
421
|
+
|
|
342
422
|
pub(crate) fn append_selected_live_leader_workspace_mismatch(
|
|
343
423
|
workspace: &std::path::Path,
|
|
344
424
|
team: Option<&str>,
|
|
@@ -1485,6 +1565,8 @@ pub(crate) fn provider_doctor_checks() -> Value {
|
|
|
1485
1565
|
crate::provider::Provider::ClaudeCode,
|
|
1486
1566
|
crate::provider::Provider::Codex,
|
|
1487
1567
|
crate::provider::Provider::GeminiCli,
|
|
1568
|
+
crate::provider::Provider::Grok,
|
|
1569
|
+
crate::provider::Provider::CursorAgent,
|
|
1488
1570
|
crate::provider::Provider::Fake,
|
|
1489
1571
|
] {
|
|
1490
1572
|
let adapter = crate::provider::get_adapter(provider);
|
|
@@ -45,7 +45,7 @@ fn emit_with_json_order(
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
///
|
|
48
|
-
/// `main(argv)`(`parser.py:84`):**CLI 唯一进程入口**。codex/claude/copilot passthrough 早返回 →
|
|
48
|
+
/// `main(argv)`(`parser.py:84`):**CLI 唯一进程入口**。codex/claude/copilot/grok/cursor passthrough 早返回 →
|
|
49
49
|
/// 解析 argv 到 subcommand → 调对应 handler → 异常落盘 + 信封 + `ExitCode::Error` →
|
|
50
50
|
/// `consume_leader_inbox_summary` → `emit` → `result.ok is False ? Error : Ok`。
|
|
51
51
|
/// **行为入口**:契约可端到端跑 argv→(stdout, exit code)。
|
|
@@ -262,7 +262,7 @@ const DISPATCH_COMMANDS: &[&str] = &[
|
|
|
262
262
|
const SPEC_ONLY_HELP_COMMANDS: &[&str] = &["start"];
|
|
263
263
|
// Command grammar, not provider identity parsing: these are top-level CLI
|
|
264
264
|
// passthrough verbs for starting a leader under a provider executable.
|
|
265
|
-
const LEADER_PASSTHROUGH_COMMANDS: &[&str] = &["codex", "claude", "copilot"];
|
|
265
|
+
const LEADER_PASSTHROUGH_COMMANDS: &[&str] = &["codex", "claude", "copilot", "grok", "cursor"];
|
|
266
266
|
|
|
267
267
|
fn is_leader_passthrough_command(command: &str) -> bool {
|
|
268
268
|
LEADER_PASSTHROUGH_COMMANDS.contains(&command)
|
|
@@ -310,7 +310,7 @@ fn default_help() -> String {
|
|
|
310
310
|
"Guided recovery",
|
|
311
311
|
&["claim-leader", "takeover", "attach-leader"],
|
|
312
312
|
);
|
|
313
|
-
out.push_str("\nProvider launchers:\n team-agent codex|claude|copilot ...\n");
|
|
313
|
+
out.push_str("\nProvider launchers:\n team-agent codex|claude|copilot|grok|cursor ...\n");
|
|
314
314
|
out.push_str("\nRun `team-agent <command> --help` for command flags.");
|
|
315
315
|
out
|
|
316
316
|
}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: 对账 .grok/config.toml 残余共享槽与在役 grok 席,含未首 turn 危险窗
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: grok_slot_report
|
|
6
|
+
//! what: 盘上 ID/OWNER/AUTH、在役 grok、pre_first_turn;读不出为 unjudgeable
|
|
7
|
+
//! boundary:
|
|
8
|
+
//! - 读不出不报一致
|
|
9
|
+
//! - 不写盘、不改 overlay
|
|
10
|
+
//! maturity: wired
|
|
11
|
+
//! ---
|
|
12
|
+
|
|
13
|
+
use serde_json::{json, Value};
|
|
14
|
+
use std::path::Path;
|
|
15
|
+
|
|
16
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
17
|
+
pub(crate) struct GrokSlotReport {
|
|
18
|
+
pub readable: bool,
|
|
19
|
+
pub consistent: bool,
|
|
20
|
+
pub disk_team_agent_id: Option<String>,
|
|
21
|
+
pub disk_owner_team_id: Option<String>,
|
|
22
|
+
pub disk_auth_mode: Option<String>,
|
|
23
|
+
pub expected_owner_team_id: Option<String>,
|
|
24
|
+
pub expected_auth_mode: Option<String>,
|
|
25
|
+
pub live_seats: Vec<String>,
|
|
26
|
+
pub pre_first_turn: Vec<String>,
|
|
27
|
+
pub reason: String,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
impl GrokSlotReport {
|
|
31
|
+
pub(crate) fn to_json(&self) -> Value {
|
|
32
|
+
json!({
|
|
33
|
+
"readable": self.readable,
|
|
34
|
+
"consistent": self.consistent,
|
|
35
|
+
"disk_team_agent_id": self.disk_team_agent_id,
|
|
36
|
+
"disk_owner_team_id": self.disk_owner_team_id,
|
|
37
|
+
"disk_auth_mode": self.disk_auth_mode,
|
|
38
|
+
"expected_owner_team_id": self.expected_owner_team_id,
|
|
39
|
+
"expected_auth_mode": self.expected_auth_mode,
|
|
40
|
+
"live_seats": self.live_seats,
|
|
41
|
+
"pre_first_turn": self.pre_first_turn,
|
|
42
|
+
"reason": self.reason,
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
pub(crate) fn grok_slot_report(workspace: &Path, state: &Value) -> GrokSlotReport {
|
|
48
|
+
if let Some(reason) = unreadable_state_reason(workspace, state) {
|
|
49
|
+
return unjudgeable(reason, Vec::new(), Vec::new());
|
|
50
|
+
}
|
|
51
|
+
let live = live_grok_from_state(state);
|
|
52
|
+
let pre_first_turn = live
|
|
53
|
+
.iter()
|
|
54
|
+
.filter(|seat| seat.pre_first_turn)
|
|
55
|
+
.map(|seat| seat.id.clone())
|
|
56
|
+
.collect::<Vec<_>>();
|
|
57
|
+
let live_ids = live.iter().map(|seat| seat.id.clone()).collect::<Vec<_>>();
|
|
58
|
+
let expected_owner = expected_owner_team_id(state);
|
|
59
|
+
let expected_auth = expected_auth_mode(&live);
|
|
60
|
+
|
|
61
|
+
let toml_path = workspace.join(".grok").join("config.toml");
|
|
62
|
+
if toml_path.exists() {
|
|
63
|
+
match std::fs::read_to_string(&toml_path) {
|
|
64
|
+
Err(error) => {
|
|
65
|
+
return unjudgeable(
|
|
66
|
+
format!("unjudgeable: cannot read {} ({error})", toml_path.display()),
|
|
67
|
+
live_ids,
|
|
68
|
+
pre_first_turn,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
Ok(text) => {
|
|
72
|
+
let disk = parse_disk_slot(&text);
|
|
73
|
+
return classify(disk, live_ids, pre_first_turn, expected_owner, expected_auth);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
classify(
|
|
78
|
+
DiskSlot::default(),
|
|
79
|
+
live_ids,
|
|
80
|
+
pre_first_turn,
|
|
81
|
+
expected_owner,
|
|
82
|
+
expected_auth,
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
|
87
|
+
struct DiskSlot {
|
|
88
|
+
team_agent_id: Option<String>,
|
|
89
|
+
owner_team_id: Option<String>,
|
|
90
|
+
auth_mode: Option<String>,
|
|
91
|
+
extras: Vec<(String, String)>,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
fn parse_disk_slot(text: &str) -> DiskSlot {
|
|
95
|
+
let keys = crate::lifecycle::launch::per_seat_keys_in_toml(text);
|
|
96
|
+
DiskSlot {
|
|
97
|
+
team_agent_id: find_key(&keys, "TEAM_AGENT_ID"),
|
|
98
|
+
owner_team_id: find_key(&keys, "TEAM_AGENT_OWNER_TEAM_ID"),
|
|
99
|
+
auth_mode: find_key(&keys, "TEAM_AGENT_AUTH_MODE"),
|
|
100
|
+
extras: keys
|
|
101
|
+
.into_iter()
|
|
102
|
+
.filter(|(key, _)| {
|
|
103
|
+
key != "TEAM_AGENT_ID"
|
|
104
|
+
&& key != "TEAM_AGENT_OWNER_TEAM_ID"
|
|
105
|
+
&& key != "TEAM_AGENT_AUTH_MODE"
|
|
106
|
+
})
|
|
107
|
+
.collect(),
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
fn find_key(keys: &[(String, String)], want: &str) -> Option<String> {
|
|
112
|
+
keys.iter()
|
|
113
|
+
.find(|(key, _)| key == want)
|
|
114
|
+
.map(|(_, value)| value.clone())
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
fn unjudgeable(
|
|
118
|
+
reason: String,
|
|
119
|
+
live_seats: Vec<String>,
|
|
120
|
+
pre_first_turn: Vec<String>,
|
|
121
|
+
) -> GrokSlotReport {
|
|
122
|
+
GrokSlotReport {
|
|
123
|
+
readable: false,
|
|
124
|
+
consistent: false,
|
|
125
|
+
disk_team_agent_id: None,
|
|
126
|
+
disk_owner_team_id: None,
|
|
127
|
+
disk_auth_mode: None,
|
|
128
|
+
expected_owner_team_id: None,
|
|
129
|
+
expected_auth_mode: None,
|
|
130
|
+
live_seats,
|
|
131
|
+
pre_first_turn,
|
|
132
|
+
reason,
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
fn unreadable_state_reason(workspace: &Path, state: &Value) -> Option<String> {
|
|
137
|
+
let path = workspace.join(".team").join("runtime").join("state.json");
|
|
138
|
+
if !path.exists() {
|
|
139
|
+
return None;
|
|
140
|
+
}
|
|
141
|
+
match std::fs::read_to_string(&path) {
|
|
142
|
+
Err(error) => Some(format!(
|
|
143
|
+
"unjudgeable: cannot read {} ({error})",
|
|
144
|
+
path.display()
|
|
145
|
+
)),
|
|
146
|
+
Ok(text) => match serde_json::from_str::<Value>(&text) {
|
|
147
|
+
Err(error) => Some(format!(
|
|
148
|
+
"unjudgeable: cannot parse {} ({error})",
|
|
149
|
+
path.display()
|
|
150
|
+
)),
|
|
151
|
+
Ok(_) if state.is_null() => {
|
|
152
|
+
Some("unjudgeable: runtime state exists but could not be loaded".to_string())
|
|
153
|
+
}
|
|
154
|
+
Ok(_) => None,
|
|
155
|
+
},
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
fn classify(
|
|
160
|
+
disk: DiskSlot,
|
|
161
|
+
live_ids: Vec<String>,
|
|
162
|
+
pre_first_turn: Vec<String>,
|
|
163
|
+
expected_owner: Option<String>,
|
|
164
|
+
expected_auth: Option<String>,
|
|
165
|
+
) -> GrokSlotReport {
|
|
166
|
+
let mut mismatches = Vec::new();
|
|
167
|
+
if let Some(disk_id) = disk.team_agent_id.as_deref() {
|
|
168
|
+
mismatches.push(format!("per-seat key TEAM_AGENT_ID={disk_id}"));
|
|
169
|
+
}
|
|
170
|
+
if let Some(disk_owner) = disk.owner_team_id.as_deref() {
|
|
171
|
+
mismatches.push(format!("per-seat key TEAM_AGENT_OWNER_TEAM_ID={disk_owner}"));
|
|
172
|
+
}
|
|
173
|
+
if let Some(disk_auth) = disk.auth_mode.as_deref() {
|
|
174
|
+
mismatches.push(format!("per-seat key TEAM_AGENT_AUTH_MODE={disk_auth}"));
|
|
175
|
+
}
|
|
176
|
+
for (key, value) in &disk.extras {
|
|
177
|
+
mismatches.push(format!("per-seat key {key}={value}"));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if mismatches.is_empty() {
|
|
181
|
+
let reason =
|
|
182
|
+
"toml carries no per-seat identity keys; identity inherits from pane env".to_string();
|
|
183
|
+
return GrokSlotReport {
|
|
184
|
+
readable: true,
|
|
185
|
+
consistent: true,
|
|
186
|
+
disk_team_agent_id: disk.team_agent_id,
|
|
187
|
+
disk_owner_team_id: disk.owner_team_id,
|
|
188
|
+
disk_auth_mode: disk.auth_mode,
|
|
189
|
+
expected_owner_team_id: expected_owner,
|
|
190
|
+
expected_auth_mode: expected_auth,
|
|
191
|
+
live_seats: live_ids,
|
|
192
|
+
pre_first_turn,
|
|
193
|
+
reason,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
GrokSlotReport {
|
|
198
|
+
readable: true,
|
|
199
|
+
consistent: false,
|
|
200
|
+
disk_team_agent_id: disk.team_agent_id,
|
|
201
|
+
disk_owner_team_id: disk.owner_team_id,
|
|
202
|
+
disk_auth_mode: disk.auth_mode,
|
|
203
|
+
expected_owner_team_id: expected_owner,
|
|
204
|
+
expected_auth_mode: expected_auth,
|
|
205
|
+
live_seats: live_ids,
|
|
206
|
+
pre_first_turn,
|
|
207
|
+
reason: format!("grok slot mismatch: {}", mismatches.join("; ")),
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
struct LiveGrok {
|
|
212
|
+
id: String,
|
|
213
|
+
pre_first_turn: bool,
|
|
214
|
+
auth_mode: Option<String>,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
fn live_grok_from_state(state: &Value) -> Vec<LiveGrok> {
|
|
218
|
+
let mut out = Vec::new();
|
|
219
|
+
push_live_from_map(state.get("agents").and_then(Value::as_object), &mut out);
|
|
220
|
+
if let Some(teams) = state.get("teams").and_then(Value::as_object) {
|
|
221
|
+
for team in teams.values() {
|
|
222
|
+
push_live_from_map(team.get("agents").and_then(Value::as_object), &mut out);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
out
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
fn push_live_from_map(agents: Option<&serde_json::Map<String, Value>>, out: &mut Vec<LiveGrok>) {
|
|
229
|
+
let Some(agents) = agents else {
|
|
230
|
+
return;
|
|
231
|
+
};
|
|
232
|
+
for (id, agent) in agents {
|
|
233
|
+
if out.iter().any(|row| row.id == *id) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
let provider = agent.get("provider").and_then(Value::as_str).unwrap_or("");
|
|
237
|
+
if provider != "grok" {
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
let status = agent
|
|
241
|
+
.get("status")
|
|
242
|
+
.and_then(Value::as_str)
|
|
243
|
+
.unwrap_or("running");
|
|
244
|
+
if matches!(
|
|
245
|
+
status,
|
|
246
|
+
"stopped" | "stopping" | "removed" | "spawn_failed" | "failed"
|
|
247
|
+
) {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
let first = agent
|
|
251
|
+
.get("first_send_at")
|
|
252
|
+
.and_then(Value::as_str)
|
|
253
|
+
.filter(|s| !s.is_empty());
|
|
254
|
+
out.push(LiveGrok {
|
|
255
|
+
id: id.clone(),
|
|
256
|
+
pre_first_turn: first.is_none(),
|
|
257
|
+
auth_mode: agent_auth_mode(agent),
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
fn agent_auth_mode(agent: &Value) -> Option<String> {
|
|
263
|
+
match agent.get("auth_mode") {
|
|
264
|
+
Some(Value::String(s)) if !s.trim().is_empty() => Some(s.clone()),
|
|
265
|
+
Some(other) if !other.is_null() => Some(other.to_string().trim_matches('"').to_string()),
|
|
266
|
+
_ => None,
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
fn expected_owner_team_id(state: &Value) -> Option<String> {
|
|
271
|
+
for key in ["active_team_key", "team_key", "TEAM_AGENT_OWNER_TEAM_ID"] {
|
|
272
|
+
if let Some(value) = state
|
|
273
|
+
.get(key)
|
|
274
|
+
.and_then(Value::as_str)
|
|
275
|
+
.map(str::trim)
|
|
276
|
+
.filter(|s| !s.is_empty())
|
|
277
|
+
{
|
|
278
|
+
return Some(value.to_string());
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
None
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
fn expected_auth_mode(live: &[LiveGrok]) -> Option<String> {
|
|
285
|
+
let mut found = None;
|
|
286
|
+
for seat in live {
|
|
287
|
+
let Some(auth) = seat.auth_mode.as_deref() else {
|
|
288
|
+
continue;
|
|
289
|
+
};
|
|
290
|
+
match found {
|
|
291
|
+
None => found = Some(auth.to_string()),
|
|
292
|
+
Some(ref existing) if existing != auth => return None,
|
|
293
|
+
Some(_) => {}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
found
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//!
|
|
2
|
-
//! cli · leader — `codex`/`claude`/`copilot` passthrough(`cmd_leader_passthrough` + `_provider_args` /
|
|
2
|
+
//! cli · leader — `codex`/`claude`/`copilot`/`grok`/`cursor` passthrough(`cmd_leader_passthrough` + `_provider_args` /
|
|
3
3
|
//! `_leader_launcher_args`)+ leader fallback inbox 摘要(`consume_leader_inbox_summary` 及
|
|
4
4
|
//! `_leader_inbox_entries` / `_leader_inbox_summary` / `_leader_inbox_entry_title`)。
|
|
5
5
|
|
|
@@ -96,9 +96,9 @@ fn without_leader_json(values: &[String]) -> Vec<String> {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
///
|
|
99
|
-
/// `codex`/`claude`/`copilot` passthrough(`parser.py:86`/`_run_leader_passthrough`):leader 早返回,
|
|
99
|
+
/// `codex`/`claude`/`copilot`/`grok`/`cursor` passthrough(`parser.py:86`/`_run_leader_passthrough`):leader 早返回,
|
|
100
100
|
/// **不**进 subparser。`-h`/`--help` 打 usage 直接返回 [`CmdResult::none`]。否则解析 attach
|
|
101
|
-
/// 旗标 + `lifecycle_port::start_leader`。`command`
|
|
101
|
+
/// 旗标 + `lifecycle_port::start_leader`。`command` 必须是闭集动词,禁止 command-text 回落到 ClaudeCode。
|
|
102
102
|
pub fn cmd_leader_passthrough(
|
|
103
103
|
command: &str,
|
|
104
104
|
provider_args: &[String],
|
|
@@ -110,14 +110,28 @@ pub fn cmd_leader_passthrough(
|
|
|
110
110
|
let as_json = leader_launcher_json(provider_args);
|
|
111
111
|
let launcher_args = without_leader_json(provider_args);
|
|
112
112
|
let attach = leader_launcher_args(&launcher_args)?;
|
|
113
|
-
let provider = leader_passthrough_provider(command)
|
|
113
|
+
let provider = leader_passthrough_provider(command).ok_or_else(|| {
|
|
114
|
+
CliError::Usage(format!(
|
|
115
|
+
"unknown leader passthrough command: {command}"
|
|
116
|
+
))
|
|
117
|
+
})?;
|
|
114
118
|
let value = lifecycle_port::start_leader(provider, &attach.provider_args, cwd, &attach)?;
|
|
115
119
|
Ok(CmdResult::from_json(value, as_json))
|
|
116
120
|
}
|
|
117
121
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
122
|
+
/// Closed-set verb → Provider. `None` = not a passthrough verb (must not become ClaudeCode).
|
|
123
|
+
pub(crate) fn leader_passthrough_provider(
|
|
124
|
+
command: &str,
|
|
125
|
+
) -> Option<crate::model::enums::Provider> {
|
|
126
|
+
use crate::model::enums::Provider;
|
|
127
|
+
match command {
|
|
128
|
+
"codex" => Some(Provider::Codex),
|
|
129
|
+
"claude" => Some(Provider::ClaudeCode),
|
|
130
|
+
"copilot" => Some(Provider::Copilot),
|
|
131
|
+
"grok" => Some(Provider::Grok),
|
|
132
|
+
"cursor" => Some(Provider::CursorAgent),
|
|
133
|
+
_ => None,
|
|
134
|
+
}
|
|
121
135
|
}
|
|
122
136
|
|
|
123
137
|
// =============================================================================
|