@team-agent/installer 0.5.66 → 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 +1 -0
- package/crates/team-agent/src/cli/adapters.rs +5 -0
- 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/missing_subcommands.rs +129 -19
- 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/communication_mode/mod.rs +6 -4
- package/crates/team-agent/src/compiler.rs +59 -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 +920 -63
- 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 -12
- package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +62 -0
- package/crates/team-agent/src/lifecycle/launch/agent_state.rs +59 -0
- 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 -456
- 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 +62 -2
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +139 -0
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +109 -0
- package/crates/team-agent/src/lifecycle/launch/worker_env.rs +214 -1
- package/crates/team-agent/src/lifecycle/launch.rs +99 -29
- 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 +124 -0
- package/crates/team-agent/src/lifecycle/restart/common.rs +369 -11
- 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 +106 -0
- 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/agent_ops.rs +2 -3
- 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 +150 -157
- package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/core.rs +4 -0
- 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/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/lane_ops.rs +63 -38
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +3 -2
- package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +16 -8
- 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 +20 -19
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +97 -61
- package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +2 -0
- package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +40 -7
- package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +59 -1
- package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +69 -26
- package/crates/team-agent/src/lifecycle/tests.rs +23 -13
- package/crates/team-agent/src/lifecycle/types.rs +61 -0
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +85 -11
- 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/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/os_probe.rs +73 -2
- package/crates/team-agent/src/provider/adapter.rs +231 -4
- 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 +46 -2
- 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 +11 -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 +1064 -75
- package/crates/team-agent/src/transport/tests/wire.rs +28 -2
- package/crates/team-agent/src/transport.rs +248 -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/fork_agent/completion.rs +0 -59
- package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +0 -488
- package/crates/team-agent/src/lifecycle/launch/fork_pending.rs +0 -109
- package/crates/team-agent/src/lifecycle/launch/fork_state.rs +0 -447
|
@@ -4081,6 +4081,9 @@ pub mod lifecycle_port {
|
|
|
4081
4081
|
/// `run_comms_selftest`、`diagnose/orphan_cleanup.py` `orphan_gate`/`cleanup_orphan_coordinators`、
|
|
4082
4082
|
/// `message_store/schema_migration.py` `schema_diagnosis`/`fix_schema_layout`)。
|
|
4083
4083
|
/// `cmd_doctor` 的所有分支委派点。返回 `Value`(稳定 JSON 形状由 diagnose lane 拥有)。
|
|
4084
|
+
pub mod grok_slot;
|
|
4085
|
+
pub(crate) use grok_slot::grok_slot_report;
|
|
4086
|
+
|
|
4084
4087
|
pub mod diagnose_port {
|
|
4085
4088
|
use super::*;
|
|
4086
4089
|
|
|
@@ -4134,6 +4137,13 @@ pub mod diagnose_port {
|
|
|
4134
4137
|
let health = crate::coordinator::coordinator_health(
|
|
4135
4138
|
&crate::coordinator::WorkspacePath::new(workspace.to_path_buf()),
|
|
4136
4139
|
);
|
|
4140
|
+
let coordinator_ok = health.ok;
|
|
4141
|
+
let coordinator_error = health.metadata_mismatch_reason.clone();
|
|
4142
|
+
let state = crate::state::persist::load_runtime_state(workspace).unwrap_or(Value::Null);
|
|
4143
|
+
let grok_slot = crate::cli::grok_slot_report(workspace, &state).to_json();
|
|
4144
|
+
let grok_slot_ok = grok_slot.get("consistent").and_then(Value::as_bool) == Some(true)
|
|
4145
|
+
&& grok_slot.get("readable").and_then(Value::as_bool) == Some(true);
|
|
4146
|
+
let ok = ok && grok_slot_ok && coordinator_ok;
|
|
4137
4147
|
Ok(json!({
|
|
4138
4148
|
"tmux": {
|
|
4139
4149
|
"installed": tmux_installed,
|
|
@@ -4149,11 +4159,21 @@ pub mod diagnose_port {
|
|
|
4149
4159
|
"secret_scan": secret_scan(workspace),
|
|
4150
4160
|
"profile_smoke": profile_smoke_value,
|
|
4151
4161
|
"coordinator": coordinator_health_value(health),
|
|
4162
|
+
"grok_slot": grok_slot,
|
|
4152
4163
|
"ok": ok,
|
|
4153
4164
|
"error": if ok {
|
|
4154
4165
|
Value::Null
|
|
4166
|
+
} else if !grok_slot_ok {
|
|
4167
|
+
grok_slot
|
|
4168
|
+
.get("reason")
|
|
4169
|
+
.cloned()
|
|
4170
|
+
.unwrap_or_else(|| json!("grok_slot_mismatch"))
|
|
4155
4171
|
} else if !profile_smoke_ok && !legacy_only_failure {
|
|
4156
4172
|
json!("profile_smoke_failed")
|
|
4173
|
+
} else if !coordinator_ok {
|
|
4174
|
+
coordinator_error
|
|
4175
|
+
.map(Value::String)
|
|
4176
|
+
.unwrap_or_else(|| json!("coordinator_unavailable"))
|
|
4157
4177
|
} else if workspace_valid {
|
|
4158
4178
|
json!("workspace has no Team Agent spec or runtime context")
|
|
4159
4179
|
} else {
|
|
@@ -3,6 +3,7 @@ use crate::cli::{CliError, CmdOutput, CmdResult, ExitCode};
|
|
|
3
3
|
use crate::messaging::{
|
|
4
4
|
DeliveryOutcome, DeliveryRefusal, DeliveryStage, DeliveryStatus, MessageTarget, SendOptions,
|
|
5
5
|
};
|
|
6
|
+
use crate::transport::{turn_verification_wire, TurnVerification};
|
|
6
7
|
use serde_json::{json, Value};
|
|
7
8
|
|
|
8
9
|
pub(super) fn watch_notice_json(target: &MessageTarget, opts: &SendOptions) -> Value {
|
|
@@ -113,6 +114,11 @@ pub(super) fn delivery_outcome_json(
|
|
|
113
114
|
"stage": outcome.stage.map(delivery_stage_wire),
|
|
114
115
|
"reason": outcome.reason.map(delivery_refusal_wire),
|
|
115
116
|
"channel": outcome.channel,
|
|
117
|
+
"turn_verification": turn_verification_wire(
|
|
118
|
+
outcome
|
|
119
|
+
.turn_verification
|
|
120
|
+
.unwrap_or(TurnVerification::NotYetObserved),
|
|
121
|
+
),
|
|
116
122
|
})
|
|
117
123
|
}
|
|
118
124
|
|
|
@@ -183,7 +189,7 @@ pub(super) fn send_human_output(value: &Value) -> String {
|
|
|
183
189
|
send_human_field(value, "message_id"),
|
|
184
190
|
format!("target: {}", send_human_target(value)),
|
|
185
191
|
];
|
|
186
|
-
for key in ["verification", "stage", "reason", "channel"] {
|
|
192
|
+
for key in ["verification", "stage", "reason", "channel", "turn_verification"] {
|
|
187
193
|
if !value.get(key).is_none_or(Value::is_null) {
|
|
188
194
|
parts.push(send_human_field(value, key));
|
|
189
195
|
}
|
|
@@ -179,6 +179,8 @@ pub(crate) const COMMAND_SPECS: &[CommandSpec] = &[
|
|
|
179
179
|
CommandSpec { name: "codex", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "codex" }, summary: "launch a Codex leader", usage: "usage: team-agent codex [provider args...]", default_help: false, command_help: true, suggestion_index: true, token_usage: TokenUsage::Yes, alias_of: None, sunset: None, action: None, governance: None },
|
|
180
180
|
CommandSpec { name: "claude", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "claude" }, summary: "launch a Claude leader", usage: "usage: team-agent claude [provider args...]", default_help: false, command_help: true, suggestion_index: true, token_usage: TokenUsage::Yes, alias_of: None, sunset: None, action: None, governance: None },
|
|
181
181
|
CommandSpec { name: "copilot", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "copilot" }, summary: "launch a Copilot leader", usage: "usage: team-agent copilot [provider args...]", default_help: false, command_help: true, suggestion_index: true, token_usage: TokenUsage::Yes, alias_of: None, sunset: None, action: None, governance: None },
|
|
182
|
+
CommandSpec { name: "grok", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "grok" }, summary: "launch a Grok leader", usage: "usage: team-agent grok [provider args...]", default_help: false, command_help: true, suggestion_index: true, token_usage: TokenUsage::Yes, alias_of: None, sunset: None, action: None, governance: None },
|
|
183
|
+
CommandSpec { name: "cursor", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::LeaderPassthrough { provider: "cursor" }, summary: "launch a Cursor leader", usage: "usage: team-agent cursor [provider args...]", default_help: false, command_help: true, suggestion_index: true, token_usage: TokenUsage::Yes, alias_of: None, sunset: None, action: None, governance: None },
|
|
182
184
|
|
|
183
185
|
CommandSpec { name: "init", tier: CommandTier::CompatHidden, category: CommandCategory::Compat, kind: CommandKind::Dispatch(DispatchKind::Init), summary: "legacy bootstrap command", usage: "usage: team-agent init [--workspace WORKSPACE] [--force] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::No, alias_of: Some("quick-start"), sunset: Some("C2"), action: Some("use `team-agent quick-start`"), governance: None },
|
|
184
186
|
CommandSpec { name: "start", tier: CommandTier::CompatHidden, category: CommandCategory::Compat, kind: CommandKind::SpecOnlyAlias { alias_of: "quick-start" }, summary: "legacy start alias", usage: "usage: team-agent start [TEAMDIR] [--yes] [--fresh] [--json]", default_help: false, command_help: true, suggestion_index: false, token_usage: TokenUsage::Conditional, alias_of: Some("quick-start"), sunset: Some("C2"), action: Some("use `team-agent quick-start` or `team-agent restart`"), governance: None },
|
|
@@ -4,6 +4,9 @@ use super::*;
|
|
|
4
4
|
pub(super) fn compact_status(full: Value) -> Value {
|
|
5
5
|
let not_ready = compact_not_ready(&full);
|
|
6
6
|
let ready = compact_ready(&full, ¬_ready);
|
|
7
|
+
let grok_slot = full.get("grok_slot").cloned().unwrap_or(Value::Null);
|
|
8
|
+
let grok_identity_slot_ok = grok_slot.get("consistent").and_then(Value::as_bool) == Some(true)
|
|
9
|
+
&& grok_slot.get("readable").and_then(Value::as_bool) == Some(true);
|
|
7
10
|
json!({
|
|
8
11
|
"ok": true,
|
|
9
12
|
"team": full.get("team").cloned().unwrap_or(Value::Null),
|
|
@@ -11,6 +14,8 @@ pub(super) fn compact_status(full: Value) -> Value {
|
|
|
11
14
|
"leader_attach_command": full.get("leader_attach_command").cloned().unwrap_or(Value::Null),
|
|
12
15
|
"ready": ready,
|
|
13
16
|
"not_ready": not_ready,
|
|
17
|
+
"grok_slot": grok_slot,
|
|
18
|
+
"grok_identity_slot_ok": grok_identity_slot_ok,
|
|
14
19
|
"agents": compact_agents(full.get("agents")),
|
|
15
20
|
})
|
|
16
21
|
}
|
|
@@ -55,11 +60,20 @@ pub(super) fn compact_not_ready(full: &Value) -> Value {
|
|
|
55
60
|
})
|
|
56
61
|
.unwrap_or_default();
|
|
57
62
|
let mut obj = Map::new();
|
|
63
|
+
let channel_unbound = reasons
|
|
64
|
+
.iter()
|
|
65
|
+
.any(|reason| reason == "leader_receiver_unbound");
|
|
58
66
|
obj.insert(
|
|
59
67
|
"reasons".to_string(),
|
|
60
68
|
Value::Array(reasons.into_iter().map(Value::String).collect()),
|
|
61
69
|
);
|
|
62
70
|
obj.insert("agents".to_string(), Value::Array(agents));
|
|
71
|
+
if channel_unbound {
|
|
72
|
+
obj.insert(
|
|
73
|
+
"next_action".to_string(),
|
|
74
|
+
Value::String("claim-leader".to_string()),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
63
77
|
Value::Object(obj)
|
|
64
78
|
}
|
|
65
79
|
|
|
@@ -112,6 +126,20 @@ pub(super) fn not_ready_reasons(full: &Value) -> Vec<String> {
|
|
|
112
126
|
{
|
|
113
127
|
reasons.push("awaiting_trust_prompt".to_string());
|
|
114
128
|
}
|
|
129
|
+
let grok_slot = full.get("grok_slot");
|
|
130
|
+
let grok_readable = grok_slot
|
|
131
|
+
.and_then(|slot| slot.get("readable"))
|
|
132
|
+
.and_then(Value::as_bool);
|
|
133
|
+
let grok_consistent = grok_slot
|
|
134
|
+
.and_then(|slot| slot.get("consistent"))
|
|
135
|
+
.and_then(Value::as_bool);
|
|
136
|
+
if grok_readable != Some(true) || grok_consistent != Some(true) {
|
|
137
|
+
if grok_readable == Some(false) || grok_slot.is_none() || grok_slot == Some(&Value::Null) {
|
|
138
|
+
reasons.push("grok_slot_unjudgeable".to_string());
|
|
139
|
+
} else {
|
|
140
|
+
reasons.push("grok_slot_mismatch".to_string());
|
|
141
|
+
}
|
|
142
|
+
}
|
|
115
143
|
reasons
|
|
116
144
|
}
|
|
117
145
|
|
|
@@ -90,7 +90,29 @@ impl RuntimeSnapshot {
|
|
|
90
90
|
serde_json::json!(tmux_present),
|
|
91
91
|
);
|
|
92
92
|
}
|
|
93
|
-
let readiness = crate::cli::diagnose::wait_readiness(&readiness_state);
|
|
93
|
+
let mut readiness = crate::cli::diagnose::wait_readiness(&readiness_state);
|
|
94
|
+
let team_key = owner_team_id
|
|
95
|
+
.filter(|team| !team.is_empty())
|
|
96
|
+
.map(str::to_string)
|
|
97
|
+
.unwrap_or_else(|| crate::state::projection::team_state_key(state));
|
|
98
|
+
if !team_key.is_empty()
|
|
99
|
+
&& !crate::lifecycle::launch::launched_team_receiver_is_attached(workspace, &team_key)
|
|
100
|
+
{
|
|
101
|
+
if let Some(object) = readiness.as_object_mut() {
|
|
102
|
+
object.insert("all_attached_receiver".to_string(), json!(false));
|
|
103
|
+
object.insert("ready".to_string(), json!(false));
|
|
104
|
+
object.insert("state".to_string(), json!("leader_receiver_unbound"));
|
|
105
|
+
object.insert("next_action".to_string(), json!("claim-leader"));
|
|
106
|
+
object.insert(
|
|
107
|
+
"reason".to_string(),
|
|
108
|
+
json!("leader registry does not authorize this workspace"),
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
let grok_slot = crate::cli::grok_slot_report(workspace, state).to_json();
|
|
113
|
+
let grok_identity_slot_ok = grok_slot.get("consistent").and_then(Value::as_bool)
|
|
114
|
+
== Some(true)
|
|
115
|
+
&& grok_slot.get("readable").and_then(Value::as_bool) == Some(true);
|
|
94
116
|
let full = crate::redaction::redact_external_value(&json!({
|
|
95
117
|
"ok": true,
|
|
96
118
|
"team": state.pointer("/leader/id").cloned().unwrap_or_else(|| json!("leader")),
|
|
@@ -120,6 +142,8 @@ impl RuntimeSnapshot {
|
|
|
120
142
|
"readiness": readiness,
|
|
121
143
|
"coordinator": coordinator_health_value(health),
|
|
122
144
|
"runtime": runtime_block,
|
|
145
|
+
"grok_slot": grok_slot,
|
|
146
|
+
"grok_identity_slot_ok": grok_identity_slot_ok,
|
|
123
147
|
"reminder": crate::cli::STATUS_REMINDER,
|
|
124
148
|
"last_events": Value::Array(
|
|
125
149
|
crate::event_log::EventLog::new(workspace)
|
|
@@ -753,20 +753,43 @@ fn cmd_leader_passthrough_help_returns_none() {
|
|
|
753
753
|
assert_eq!(r2.output, CmdOutput::None);
|
|
754
754
|
let r3 = cmd_leader_passthrough("copilot", &["--help".into()], Path::new(".")).unwrap();
|
|
755
755
|
assert_eq!(r3.output, CmdOutput::None);
|
|
756
|
+
let r4 = cmd_leader_passthrough("grok", &["-h".into()], Path::new(".")).unwrap();
|
|
757
|
+
assert_eq!(r4.output, CmdOutput::None);
|
|
758
|
+
let r5 = cmd_leader_passthrough("cursor", &["--help".into()], Path::new(".")).unwrap();
|
|
759
|
+
assert_eq!(r5.output, CmdOutput::None);
|
|
756
760
|
}
|
|
757
761
|
|
|
758
762
|
#[test]
|
|
759
763
|
fn cmd_leader_passthrough_maps_copilot_provider() {
|
|
760
764
|
assert_eq!(
|
|
761
765
|
leader_passthrough_provider("codex"),
|
|
762
|
-
crate::model::enums::Provider::Codex
|
|
766
|
+
Some(crate::model::enums::Provider::Codex)
|
|
763
767
|
);
|
|
764
768
|
assert_eq!(
|
|
765
769
|
leader_passthrough_provider("claude"),
|
|
766
|
-
crate::model::enums::Provider::ClaudeCode
|
|
770
|
+
Some(crate::model::enums::Provider::ClaudeCode)
|
|
767
771
|
);
|
|
768
772
|
assert_eq!(
|
|
769
773
|
leader_passthrough_provider("copilot"),
|
|
770
|
-
crate::model::enums::Provider::Copilot
|
|
774
|
+
Some(crate::model::enums::Provider::Copilot)
|
|
771
775
|
);
|
|
772
776
|
}
|
|
777
|
+
|
|
778
|
+
#[test]
|
|
779
|
+
fn cmd_leader_passthrough_maps_grok_and_cursor_not_claude() {
|
|
780
|
+
assert_eq!(
|
|
781
|
+
leader_passthrough_provider("grok"),
|
|
782
|
+
Some(crate::model::enums::Provider::Grok)
|
|
783
|
+
);
|
|
784
|
+
assert_eq!(
|
|
785
|
+
leader_passthrough_provider("cursor"),
|
|
786
|
+
Some(crate::model::enums::Provider::CursorAgent)
|
|
787
|
+
);
|
|
788
|
+
assert_ne!(
|
|
789
|
+
leader_passthrough_provider("cursor"),
|
|
790
|
+
Some(crate::model::enums::Provider::ClaudeCode)
|
|
791
|
+
);
|
|
792
|
+
assert_eq!(leader_passthrough_provider("agent"), None);
|
|
793
|
+
assert_eq!(leader_passthrough_provider("cursor_agent"), None);
|
|
794
|
+
assert_eq!(leader_passthrough_provider("not-a-passthrough"), None);
|
|
795
|
+
}
|
|
@@ -37,6 +37,75 @@ impl Drop for WorkspaceCleanup {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
struct IsolatedHome {
|
|
41
|
+
prev: Option<String>,
|
|
42
|
+
dir: std::path::PathBuf,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
impl IsolatedHome {
|
|
46
|
+
fn enter(tag: &str) -> Self {
|
|
47
|
+
let base = std::env::var_os("TEAM_AGENT_TEST_TMP")
|
|
48
|
+
.map(std::path::PathBuf::from)
|
|
49
|
+
.unwrap_or_else(std::env::temp_dir);
|
|
50
|
+
let dir = base.join(format!(
|
|
51
|
+
"ta-cli-home-{tag}-{}-{}",
|
|
52
|
+
std::process::id(),
|
|
53
|
+
std::time::SystemTime::now()
|
|
54
|
+
.duration_since(std::time::UNIX_EPOCH)
|
|
55
|
+
.unwrap()
|
|
56
|
+
.as_nanos()
|
|
57
|
+
));
|
|
58
|
+
std::fs::create_dir_all(dir.join(".team-agent").join("leaders")).unwrap();
|
|
59
|
+
let prev = std::env::var("HOME").ok();
|
|
60
|
+
std::env::set_var("HOME", &dir);
|
|
61
|
+
Self { prev, dir }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
impl Drop for IsolatedHome {
|
|
66
|
+
fn drop(&mut self) {
|
|
67
|
+
match &self.prev {
|
|
68
|
+
Some(value) => std::env::set_var("HOME", value),
|
|
69
|
+
None => std::env::remove_var("HOME"),
|
|
70
|
+
}
|
|
71
|
+
let _ = std::fs::remove_dir_all(&self.dir);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
fn seed_self_signed_attached_leader(ws: &std::path::Path) {
|
|
76
|
+
std::fs::write(
|
|
77
|
+
ws.join(".team").join("runtime").join("state.json"),
|
|
78
|
+
serde_json::to_vec(&json!({
|
|
79
|
+
"team_key": "current",
|
|
80
|
+
"leader": {"id": "leader"},
|
|
81
|
+
"leader_receiver": {
|
|
82
|
+
"mode": "direct_tmux",
|
|
83
|
+
"status": "attached",
|
|
84
|
+
"pane_id": "%1",
|
|
85
|
+
"provider": "codex"
|
|
86
|
+
},
|
|
87
|
+
}))
|
|
88
|
+
.unwrap(),
|
|
89
|
+
)
|
|
90
|
+
.unwrap();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
fn seed_leader_registry_entry(ws: &std::path::Path) {
|
|
94
|
+
let entry = crate::leader::registry::build_entry(
|
|
95
|
+
ws,
|
|
96
|
+
"current",
|
|
97
|
+
"direct_tmux",
|
|
98
|
+
json!({"status": "attached", "pane_id": "%1"}),
|
|
99
|
+
1,
|
|
100
|
+
"diagnose-healthy-fixture",
|
|
101
|
+
"2026-08-18T00:00:00Z".to_string(),
|
|
102
|
+
);
|
|
103
|
+
assert!(
|
|
104
|
+
crate::leader::registry::write_entry_best_effort(&entry).is_some(),
|
|
105
|
+
"hermetic HOME must accept a leaders/ registry write"
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
40
109
|
// =========================================================================
|
|
41
110
|
// WAVE-2 NON-SUB CHECKPOINT — missing CLI subcommands (ABSENT from cli/emit.rs dispatch).
|
|
42
111
|
// emit.rs `dispatch` has NO arm for sessions, peek, collect, e2e, diagnose,
|
|
@@ -255,33 +324,74 @@ fn dispatch_routes_collect_with_spec() {
|
|
|
255
324
|
);
|
|
256
325
|
}
|
|
257
326
|
|
|
258
|
-
// ── diagnose ──
|
|
259
|
-
//
|
|
260
|
-
// agents -> issues=[] -> EXIT 0
|
|
261
|
-
// RED: unrouted -> Error.
|
|
327
|
+
// ── diagnose ── attached is host-registry authority, not state.json self-sign.
|
|
328
|
+
// Seed leader_receiver=attached + a ~/.team-agent/leaders/ row for this workspace
|
|
329
|
+
// + NO session_name + NO agents -> issues=[] -> EXIT 0.
|
|
262
330
|
#[test]
|
|
331
|
+
#[serial(env)]
|
|
263
332
|
fn dispatch_routes_diagnose_healthy_leader() {
|
|
333
|
+
let _env = EnvUnsetGuard::unset(&[
|
|
334
|
+
"TMUX",
|
|
335
|
+
"TMUX_PANE",
|
|
336
|
+
"TEAM_AGENT_WORKSPACE",
|
|
337
|
+
"TEAM_AGENT_TEAM_ID",
|
|
338
|
+
"TEAM_AGENT_OWNER_TEAM_ID",
|
|
339
|
+
"TEAM_AGENT_ACTIVE_TEAM",
|
|
340
|
+
"TEAM_AGENT_ID",
|
|
341
|
+
"TEAM_AGENT_LEADER_PANE_ID",
|
|
342
|
+
"TEAM_AGENT_LEADER_SESSION_UUID",
|
|
343
|
+
"TEAM_AGENT_LEADER_SESSION_UUID_OVERRIDE",
|
|
344
|
+
"TEAM_AGENT_LEADER_PROVIDER",
|
|
345
|
+
]);
|
|
346
|
+
let _home = IsolatedHome::enter("diagnose-healthy");
|
|
264
347
|
let ws = tmp_workspace();
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
serde_json::to_vec(&json!({
|
|
268
|
-
"leader": {"id": "leader"},
|
|
269
|
-
"leader_receiver": {"mode": "direct_tmux", "status": "attached", "pane_id": "%1", "provider": "codex"},
|
|
270
|
-
}))
|
|
271
|
-
.unwrap(),
|
|
272
|
-
)
|
|
273
|
-
.unwrap();
|
|
348
|
+
seed_self_signed_attached_leader(&ws);
|
|
349
|
+
seed_leader_registry_entry(&ws);
|
|
274
350
|
let code = run(
|
|
275
351
|
&cli_argv(&["diagnose", "--workspace", &ws.to_string_lossy(), "--json"]),
|
|
276
352
|
&ws,
|
|
277
353
|
);
|
|
278
354
|
assert_eq!(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
355
|
+
code,
|
|
356
|
+
ExitCode::Ok,
|
|
357
|
+
"`diagnose` must be Ok when state.json looks attached AND ~/.team-agent/leaders/ \
|
|
358
|
+
has a matching attached row; got {code:?}"
|
|
359
|
+
);
|
|
360
|
+
let _ = std::fs::remove_dir_all(&ws);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
#[test]
|
|
364
|
+
#[serial(env)]
|
|
365
|
+
fn dispatch_routes_diagnose_self_signed_attached_without_registry_is_not_ok() {
|
|
366
|
+
// 已废除的行为:旧实现只信 `state.json` 的自签 attached(F2 谎话本体),此断言证明它确实没了。
|
|
367
|
+
// 夹具只种 leader_receiver.status=attached,注册表无该 workspace 条目。
|
|
368
|
+
// 旧 diagnose 会因此报 Ok;新 diagnose 必须以注册表为可投递权威,判非 Ok。
|
|
369
|
+
let _env = EnvUnsetGuard::unset(&[
|
|
370
|
+
"TMUX",
|
|
371
|
+
"TMUX_PANE",
|
|
372
|
+
"TEAM_AGENT_WORKSPACE",
|
|
373
|
+
"TEAM_AGENT_TEAM_ID",
|
|
374
|
+
"TEAM_AGENT_OWNER_TEAM_ID",
|
|
375
|
+
"TEAM_AGENT_ACTIVE_TEAM",
|
|
376
|
+
"TEAM_AGENT_ID",
|
|
377
|
+
"TEAM_AGENT_LEADER_PANE_ID",
|
|
378
|
+
"TEAM_AGENT_LEADER_SESSION_UUID",
|
|
379
|
+
"TEAM_AGENT_LEADER_SESSION_UUID_OVERRIDE",
|
|
380
|
+
"TEAM_AGENT_LEADER_PROVIDER",
|
|
381
|
+
]);
|
|
382
|
+
let _home = IsolatedHome::enter("diagnose-f2-tombstone");
|
|
383
|
+
let ws = tmp_workspace();
|
|
384
|
+
seed_self_signed_attached_leader(&ws);
|
|
385
|
+
let code = run(
|
|
386
|
+
&cli_argv(&["diagnose", "--workspace", &ws.to_string_lossy(), "--json"]),
|
|
387
|
+
&ws,
|
|
388
|
+
);
|
|
389
|
+
assert_ne!(
|
|
390
|
+
code,
|
|
391
|
+
ExitCode::Ok,
|
|
392
|
+
"self-signed state.json attached with no leaders/ row must not diagnose Ok (F2 lie is dead); \
|
|
393
|
+
got {code:?}"
|
|
394
|
+
);
|
|
285
395
|
let _ = std::fs::remove_dir_all(&ws);
|
|
286
396
|
}
|
|
287
397
|
|