@team-agent/installer 0.5.111 → 0.5.113
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 +1 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/src/cli/adapters.rs +93 -779
- package/crates/team-agent/src/cli/diagnose.rs +2 -424
- package/crates/team-agent/src/cli/emit.rs +158 -373
- package/crates/team-agent/src/cli/leaders.rs +32 -32
- package/crates/team-agent/src/cli/mod.rs +8 -15
- package/crates/team-agent/src/cli/named_address.rs +17 -16
- package/crates/team-agent/src/cli/route.rs +4 -4
- package/crates/team-agent/src/cli/send/presentation.rs +19 -18
- package/crates/team-agent/src/cli/send/resolve.rs +11 -11
- package/crates/team-agent/src/cli/send.rs +4 -4
- package/crates/team-agent/src/cli/spec.rs +29 -32
- package/crates/team-agent/src/cli/status.rs +7 -7
- package/crates/team-agent/src/cli/tests/base.rs +6 -6
- package/crates/team-agent/src/cli/tests/divergence.rs +3 -2
- package/crates/team-agent/src/cli/tests/lane_c.rs +1 -1
- package/crates/team-agent/src/cli/tests/leader_watch.rs +2 -86
- package/crates/team-agent/src/cli/tests/main_preserved.rs +15 -140
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +1 -258
- package/crates/team-agent/src/cli/tests/mod.rs +0 -2
- package/crates/team-agent/src/cli/tests/named_address.rs +3 -3
- package/crates/team-agent/src/cli/tests/status_send.rs +7 -7
- package/crates/team-agent/src/cli/triage.rs +53 -2
- package/crates/team-agent/src/cli/types.rs +12 -116
- package/crates/team-agent/src/compiler/tests.rs +12 -0
- package/crates/team-agent/src/compiler.rs +0 -9
- package/crates/team-agent/src/coordinator/health.rs +3 -283
- package/crates/team-agent/src/coordinator/mod.rs +0 -5
- package/crates/team-agent/src/coordinator/tests/basics.rs +0 -9
- package/crates/team-agent/src/coordinator/tests/mod.rs +0 -1
- package/crates/team-agent/src/coordinator/tests/tick_core.rs +0 -17
- package/crates/team-agent/src/coordinator/types.rs +1 -21
- package/crates/team-agent/src/db/message_store.rs +0 -42
- package/crates/team-agent/src/db/migration.rs +49 -9
- package/crates/team-agent/src/db/schema.rs +27 -20
- package/crates/team-agent/src/leader/owner_bind.rs +0 -24
- package/crates/team-agent/src/leader/start.rs +5 -0
- package/crates/team-agent/src/leader/tests/byte_findings.rs +0 -20
- package/crates/team-agent/src/leader/tests/identity.rs +21 -32
- package/crates/team-agent/src/leader/tests/lease_claim.rs +0 -55
- package/crates/team-agent/src/lifecycle/launch/identity.rs +172 -13
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +325 -14
- package/crates/team-agent/src/lifecycle/launch.rs +2 -2
- package/crates/team-agent/src/lifecycle/profile_launch.rs +58 -46
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +97 -52
- package/crates/team-agent/src/lifecycle/restart.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -51
- package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +3 -2
- package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +23 -23
- package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +29 -55
- package/crates/team-agent/src/messaging/mod.rs +0 -2
- package/crates/team-agent/src/messaging/results.rs +0 -58
- package/crates/team-agent/src/messaging/tests/runtime.rs +0 -46
- package/crates/team-agent/src/provider/adapter.rs +8 -59
- package/crates/team-agent/src/provider/adapters/claude.rs +6 -14
- package/crates/team-agent/src/provider/adapters/codex.rs +8 -18
- package/crates/team-agent/src/provider/adapters/copilot.rs +5 -22
- package/crates/team-agent/src/provider/adapters/cursor_agent.rs +8 -45
- package/crates/team-agent/src/provider/adapters/grok.rs +7 -31
- package/crates/team-agent/src/provider/adapters/pi.rs +7 -18
- package/crates/team-agent/src/provider/command_helpers.rs +45 -0
- package/crates/team-agent/src/provider/mod.rs +1 -0
- package/crates/team-agent/src/provider/model_catalog.rs +21 -28
- package/crates/team-agent/src/provider/tests/adapter.rs +70 -0
- package/crates/team-agent/src/provider/tests/command_helpers.rs +55 -0
- package/crates/team-agent/src/state/repository.rs +1 -5
- package/package.json +4 -4
- package/templates/team_state.md +1 -1
- package/crates/team-agent/src/cli/tests/peer_allow.rs +0 -164
- package/crates/team-agent/src/cli/tests/verb_validate.rs +0 -195
- package/crates/team-agent/src/coordinator/tests/watch.rs +0 -223
- package/crates/team-agent/src/messaging/peers.rs +0 -34
|
@@ -124,7 +124,7 @@ fn send_hint(entry: &LeaderRegistryEntry) -> String {
|
|
|
124
124
|
format!(
|
|
125
125
|
"team-agent send {} {}",
|
|
126
126
|
quote_cli_arg(&to),
|
|
127
|
-
quote_cli_arg("
|
|
127
|
+
quote_cli_arg("message")
|
|
128
128
|
)
|
|
129
129
|
}
|
|
130
130
|
|
|
@@ -183,19 +183,19 @@ fn format_leaders_human(rows: &[LeaderRow], view: LeadersView) -> String {
|
|
|
183
183
|
if rows.is_empty() {
|
|
184
184
|
return match view {
|
|
185
185
|
LeadersView::Live => {
|
|
186
|
-
"
|
|
186
|
+
"No live leaders. Run team-agent doctor --workspace . for the current team.\n".to_string()
|
|
187
187
|
}
|
|
188
|
-
LeadersView::All => "
|
|
189
|
-
LeadersView::Stale => "
|
|
188
|
+
LeadersView::All => "No registered leaders. Open a leader with team-agent pi for first-time collaboration.\n".to_string(),
|
|
189
|
+
LeadersView::Stale => "No stale leader registrations.\n".to_string(),
|
|
190
190
|
};
|
|
191
191
|
}
|
|
192
|
-
let mut out = String::from("
|
|
192
|
+
let mut out = String::from("leader\tstatus\tworkspace\tteam\tsend command\n");
|
|
193
193
|
for row in rows {
|
|
194
194
|
out.push_str("leader\t");
|
|
195
195
|
out.push_str(match row.status {
|
|
196
|
-
"LIVE" => "
|
|
197
|
-
"STALE" => "
|
|
198
|
-
"AMBIGUOUS" => "
|
|
196
|
+
"LIVE" => "LIVE",
|
|
197
|
+
"STALE" => "STALE",
|
|
198
|
+
"AMBIGUOUS" => "AMBIGUOUS",
|
|
199
199
|
other => other,
|
|
200
200
|
});
|
|
201
201
|
out.push('\t');
|
|
@@ -212,7 +212,7 @@ fn format_leaders_human(rows: &[LeaderRow], view: LeadersView) -> String {
|
|
|
212
212
|
&row.entry.workspace,
|
|
213
213
|
Some(&row.entry.team_key),
|
|
214
214
|
)
|
|
215
|
-
.unwrap_or_else(|| "
|
|
215
|
+
.unwrap_or_else(|| "Verify the workspace path".to_string()),
|
|
216
216
|
);
|
|
217
217
|
}
|
|
218
218
|
out.push('\n');
|
|
@@ -240,33 +240,33 @@ fn prune_json(report: &RegistryPruneReport) -> Value {
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
fn format_prune_human(report: &RegistryPruneReport) -> String {
|
|
243
|
-
let mut out = format!("
|
|
243
|
+
let mut out = format!("dry_run: {}\n", report.dry_run);
|
|
244
244
|
for (label, items) in [
|
|
245
|
-
("
|
|
246
|
-
("
|
|
247
|
-
("
|
|
248
|
-
("
|
|
249
|
-
("
|
|
245
|
+
("candidates", &report.candidates),
|
|
246
|
+
("removed", &report.removed),
|
|
247
|
+
("kept", &report.kept),
|
|
248
|
+
("skipped", &report.skipped),
|
|
249
|
+
("errors", &report.errors),
|
|
250
250
|
] {
|
|
251
251
|
out.push_str(&format!("{label}: {}\n", items.len()));
|
|
252
252
|
for item in items {
|
|
253
253
|
out.push_str(" ");
|
|
254
254
|
out.push_str(&item.workspace.display().to_string());
|
|
255
|
-
out.push_str("
|
|
255
|
+
out.push_str(", team: ");
|
|
256
256
|
out.push_str(&item.team_key);
|
|
257
257
|
out.push_str(" [");
|
|
258
258
|
out.push_str(match item.reason.as_str() {
|
|
259
|
-
"LIVE" => "
|
|
260
|
-
"team_key_not_found" => "
|
|
261
|
-
"team_not_alive" => "
|
|
262
|
-
"registry_entry_missing" => "
|
|
259
|
+
"LIVE" => "still live; registration retained",
|
|
260
|
+
"team_key_not_found" => "team no longer exists in the workspace",
|
|
261
|
+
"team_not_alive" => "team is stopped",
|
|
262
|
+
"registry_entry_missing" => "registration no longer exists",
|
|
263
263
|
"registry_entry_changed" | "canonical_state_changed" => {
|
|
264
|
-
"
|
|
264
|
+
"registration or team changed; cleanup refused"
|
|
265
265
|
}
|
|
266
|
-
"canonical_state_unavailable" => "
|
|
267
|
-
"registry_lock_unavailable" => "
|
|
268
|
-
"remove_failed" => "
|
|
269
|
-
_ => "
|
|
266
|
+
"canonical_state_unavailable" => "team state unavailable; registration retained",
|
|
267
|
+
"registry_lock_unavailable" => "registry lock unavailable; cleanup refused",
|
|
268
|
+
"remove_failed" => "registration removal failed; inspect file permissions",
|
|
269
|
+
_ => "registration could not be verified safely; diagnose the selected workspace",
|
|
270
270
|
});
|
|
271
271
|
out.push_str("]\n");
|
|
272
272
|
}
|
|
@@ -337,13 +337,13 @@ mod tests {
|
|
|
337
337
|
assert_eq!(lines.len(), 3, "one header and both selected rows: {out}");
|
|
338
338
|
let header = lines[0];
|
|
339
339
|
assert_eq!(header.split('\t').count(), 5);
|
|
340
|
-
assert!(header.contains("
|
|
340
|
+
assert!(header.contains("status") && header.contains("workspace") && header.contains("team"));
|
|
341
341
|
let live: Vec<_> = lines[1].split('\t').collect();
|
|
342
342
|
assert_eq!(
|
|
343
343
|
&live[..4],
|
|
344
344
|
&[
|
|
345
345
|
"leader",
|
|
346
|
-
"
|
|
346
|
+
"LIVE",
|
|
347
347
|
"/Volumes/nvme/Projects/讨论team-agent",
|
|
348
348
|
"wiki-team"
|
|
349
349
|
]
|
|
@@ -354,7 +354,7 @@ mod tests {
|
|
|
354
354
|
"team-agent",
|
|
355
355
|
"send",
|
|
356
356
|
"leader",
|
|
357
|
-
"
|
|
357
|
+
"Complete the task and reply to the leader.",
|
|
358
358
|
"--workspace",
|
|
359
359
|
"/Volumes/nvme/Projects/讨论team-agent",
|
|
360
360
|
"--team",
|
|
@@ -364,7 +364,7 @@ mod tests {
|
|
|
364
364
|
let stale: Vec<_> = lines[2].split('\t').collect();
|
|
365
365
|
assert_eq!(
|
|
366
366
|
&stale[..4],
|
|
367
|
-
&["leader", "
|
|
367
|
+
&["leader", "STALE", "/Users/alauda/stale", "old-team"]
|
|
368
368
|
);
|
|
369
369
|
assert_eq!(stale[4], "-", "stale rows must never suggest a send: {out}");
|
|
370
370
|
}
|
|
@@ -404,9 +404,9 @@ mod tests {
|
|
|
404
404
|
#[test]
|
|
405
405
|
fn empty_view_names_are_explicit() {
|
|
406
406
|
for (view, empty, next) in [
|
|
407
|
-
(LeadersView::Live, "
|
|
408
|
-
(LeadersView::Stale, "
|
|
409
|
-
(LeadersView::All, "
|
|
407
|
+
(LeadersView::Live, "No live leaders", Some("team-agent doctor")),
|
|
408
|
+
(LeadersView::Stale, "No stale leader registrations", None),
|
|
409
|
+
(LeadersView::All, "No registered leaders", Some("team-agent pi")),
|
|
410
410
|
] {
|
|
411
411
|
let out = format_leaders_human(&[], view);
|
|
412
412
|
assert!(out.contains(empty) && out.ends_with('\n'), "{out}");
|
|
@@ -49,12 +49,12 @@ use crate::messaging::{self, MessageTarget, SendOptions, TrustedSender};
|
|
|
49
49
|
use crate::model::ids::{TaskId, TeamKey};
|
|
50
50
|
|
|
51
51
|
pub(crate) const COMMS_BOUNDARY_TEXT: &str =
|
|
52
|
-
"
|
|
53
|
-
pub(crate) const QUICK_START_REMINDER: &str = "
|
|
52
|
+
"This checks terminal connection consistency only; it does not send a message or prove that a worker replied.";
|
|
53
|
+
pub(crate) const QUICK_START_REMINDER: &str = "Next: send a task. Use team-agent status for status and team-agent inbox leader -n 3 for replies; do not inspect worker terminal content.";
|
|
54
54
|
pub(crate) const SEND_REMINDER: &str =
|
|
55
|
-
"
|
|
55
|
+
"Wait for an actual reply; use team-agent inbox leader -n 3. Do not inspect worker terminal content.";
|
|
56
56
|
pub(crate) const STATUS_REMINDER: &str =
|
|
57
|
-
"
|
|
57
|
+
"Next: use team-agent inbox leader -n 3 for replies. Status is not proof of task completion; do not inspect worker terminal content.";
|
|
58
58
|
|
|
59
59
|
pub mod adapters;
|
|
60
60
|
pub mod attach_app_server_leader;
|
|
@@ -4176,7 +4176,7 @@ pub mod lifecycle_port {
|
|
|
4176
4176
|
"team-agent".to_string(),
|
|
4177
4177
|
"send".to_string(),
|
|
4178
4178
|
"implementer".to_string(),
|
|
4179
|
-
"
|
|
4179
|
+
"Complete the task and reply to the leader.".to_string(),
|
|
4180
4180
|
"--workspace".to_string(),
|
|
4181
4181
|
team_dir.to_string_lossy().into_owned(),
|
|
4182
4182
|
"--team".to_string(),
|
|
@@ -4213,7 +4213,7 @@ pub mod lifecycle_port {
|
|
|
4213
4213
|
"team-agent".to_string(),
|
|
4214
4214
|
"send".to_string(),
|
|
4215
4215
|
"implementer".to_string(),
|
|
4216
|
-
"
|
|
4216
|
+
"Complete the task and reply to the leader.".to_string(),
|
|
4217
4217
|
"--workspace".to_string(),
|
|
4218
4218
|
team_dir.to_string_lossy().into_owned(),
|
|
4219
4219
|
"--team".to_string(),
|
|
@@ -5629,8 +5629,8 @@ pub mod diagnose_port {
|
|
|
5629
5629
|
}
|
|
5630
5630
|
}
|
|
5631
5631
|
|
|
5632
|
-
/// PLACEHOLDER → leader lane(`runtime.{takeover,claim_leader
|
|
5633
|
-
/// leader.rs 已有 `claim_leader
|
|
5632
|
+
/// PLACEHOLDER → leader lane(`runtime.{takeover,claim_leader}` 的 CLI 视图)。
|
|
5633
|
+
/// leader.rs 已有 `claim_leader`(返 `LeaseResult`);CLI 需 `takeover` +
|
|
5634
5634
|
/// 把 `LeaseResult` 投影成稳定 `--json` 形状。这两步由 leader 集成收口,本层仅声明 CLI 委派面。
|
|
5635
5635
|
pub mod leader_port {
|
|
5636
5636
|
use super::*;
|
|
@@ -5769,13 +5769,6 @@ pub mod leader_port {
|
|
|
5769
5769
|
Ok(value)
|
|
5770
5770
|
}
|
|
5771
5771
|
|
|
5772
|
-
///
|
|
5773
|
-
/// `runtime.leader_identity(workspace, team)`(`cmd_identity`)。
|
|
5774
|
-
pub fn leader_identity(workspace: &Path, team: Option<&str>) -> Result<Value, CliError> {
|
|
5775
|
-
crate::leader::leader_identity(workspace, team)
|
|
5776
|
-
.map_err(|e| CliError::Runtime(e.to_string()))
|
|
5777
|
-
}
|
|
5778
|
-
|
|
5779
5772
|
/// E7 (0.5.9 host-leader-registry-design §14 step 3): after a
|
|
5780
5773
|
/// canonical binding command succeeds, write a derived discovery
|
|
5781
5774
|
/// entry to `~/.team-agent/leaders`. Registry write failure is
|
|
@@ -78,16 +78,16 @@ pub(crate) struct NamedAddressError {
|
|
|
78
78
|
|
|
79
79
|
pub(crate) fn human_address_reason(reason: &str) -> Option<&'static str> {
|
|
80
80
|
Some(match reason {
|
|
81
|
-
"name_invalid" => "
|
|
82
|
-
"workspace_not_found" => "
|
|
81
|
+
"name_invalid" => "Invalid recipient name; use a name from the agent list.",
|
|
82
|
+
"workspace_not_found" => "The selected workspace directory does not exist; verify the path.",
|
|
83
83
|
"state_not_found" | "workspace_no_state" => {
|
|
84
|
-
"
|
|
84
|
+
"No usable team exists in the selected workspace; use quick-start for initial creation or restart for an initialized team."
|
|
85
85
|
}
|
|
86
|
-
"team_key_not_found" => "
|
|
87
|
-
"leader_not_attached" => "
|
|
88
|
-
"name_not_resolvable" => "
|
|
89
|
-
"name_not_live" => "
|
|
90
|
-
"name_ambiguous" => "
|
|
86
|
+
"team_key_not_found" => "The selected workspace has no such team; verify --workspace and --team.",
|
|
87
|
+
"leader_not_attached" => "No leader is attached to this team; diagnose it and follow the recovery action.",
|
|
88
|
+
"name_not_resolvable" => "This team has no such agent; check the agent list.",
|
|
89
|
+
"name_not_live" => "The agent is not running or its channel is stale; check status before recovery.",
|
|
90
|
+
"name_ambiguous" => "Multiple targets share this name; select an explicit workspace and team.",
|
|
91
91
|
_ => return None,
|
|
92
92
|
})
|
|
93
93
|
}
|
|
@@ -121,23 +121,24 @@ impl NamedAddressError {
|
|
|
121
121
|
pub(crate) fn human_guidance(&self, requested: &str) -> (String, String) {
|
|
122
122
|
let workspace = self.human_scope.as_deref();
|
|
123
123
|
let mut message = format!(
|
|
124
|
-
"{}\
|
|
125
|
-
human_address_reason(self.kind.as_str()).unwrap_or("
|
|
124
|
+
"{}\nRequested recipient: {requested:?}\nReason: {}",
|
|
125
|
+
human_address_reason(self.kind.as_str()).unwrap_or("No usable recipient could be resolved."),
|
|
126
|
+
self.message
|
|
126
127
|
);
|
|
127
128
|
if let Some(workspace) = workspace {
|
|
128
|
-
message.push_str(&format!("\
|
|
129
|
+
message.push_str(&format!("\nSelected workspace: {:?}", workspace.to_string_lossy()));
|
|
129
130
|
}
|
|
130
131
|
if let Some(team) = self.human_team.as_deref() {
|
|
131
|
-
message.push_str(&format!("\
|
|
132
|
+
message.push_str(&format!("\nSelected team: {team:?}"));
|
|
132
133
|
}
|
|
133
|
-
let mut action = "
|
|
134
|
+
let mut action = "No reliable spelling suggestion is available; verify the workspace and team before choosing a target.".to_string();
|
|
134
135
|
for candidate in &self.candidates {
|
|
135
136
|
if let (Some(agent), Some(team)) = (
|
|
136
137
|
candidate.get("agent_id").and_then(Value::as_str),
|
|
137
138
|
candidate.get("team_key").and_then(Value::as_str),
|
|
138
139
|
) {
|
|
139
140
|
message.push_str(&format!(
|
|
140
|
-
"\
|
|
141
|
+
"\nCandidate team {team:?}, agent {agent:?}; confirm the intended target."
|
|
141
142
|
));
|
|
142
143
|
}
|
|
143
144
|
}
|
|
@@ -162,7 +163,7 @@ impl NamedAddressError {
|
|
|
162
163
|
if let Some(command) =
|
|
163
164
|
super::adapters::send_command(agent, workspace, Some(team))
|
|
164
165
|
{
|
|
165
|
-
action = format!("
|
|
166
|
+
action = format!("Did you mean agent {agent:?} in team {team:?}? After confirmation, replace the example message below. Nothing was sent:\n{command}");
|
|
166
167
|
}
|
|
167
168
|
}
|
|
168
169
|
}
|
|
@@ -171,7 +172,7 @@ impl NamedAddressError {
|
|
|
171
172
|
workspace.filter(|path| !path.to_string_lossy().chars().any(char::is_control))
|
|
172
173
|
{
|
|
173
174
|
action.push_str(&format!(
|
|
174
|
-
"\
|
|
175
|
+
"\nList available agents with:\nteam-agent status --workspace {}",
|
|
175
176
|
super::adapters::shell_quote(&workspace.to_string_lossy())
|
|
176
177
|
));
|
|
177
178
|
}
|
|
@@ -5,7 +5,7 @@ use crate::provider::argv_route::{self, Config, Mutation, Override, RouteError};
|
|
|
5
5
|
use crate::provider::Provider;
|
|
6
6
|
use serde_json::{json, Value};
|
|
7
7
|
|
|
8
|
-
pub(crate) const HELP: &str = "
|
|
8
|
+
pub(crate) const HELP: &str = "Purpose:\nConfigure additional provider launch arguments. Disabled by default.\nUsage: team-agent route [status|enable|disable|show [TOOL]|set TOOL -- ARG...|add TOOL -- ARG...|clear TOOL] [--json]\n\nOptions:\nstatus reports the switch; enable/disable change it; show lists all or one provider's arguments.\nset replaces arguments; add appends; clear removes a provider's arguments. Use --json for structured output.\nTOOL is a supported provider name, such as pi/codex/claude/copilot/grok/cursor_agent.\nEverything after the first -- is a literal provider argument, including --help and --json.\nConfiguration: ~/.team-agent/argv-routing.json. This is host-global, not isolated by workspace or team.\nTEAM_AGENT_CLI_ARGV_ROUTING overrides the persisted switch.\nExamples:\nteam-agent route status\nteam-agent route set pi -- --mode text\nteam-agent route enable\n\nNext Action:\nVerify arguments with route show pi. Changes apply to the next launch, not an existing session.\nWithout an enabled configuration, providers retain their original arguments. Leave routing disabled unless the arguments are understood.";
|
|
9
9
|
|
|
10
10
|
struct Request {
|
|
11
11
|
operation: String,
|
|
@@ -16,9 +16,9 @@ struct Request {
|
|
|
16
16
|
|
|
17
17
|
fn usage() -> RouteError {
|
|
18
18
|
RouteError {
|
|
19
|
-
error: "
|
|
19
|
+
error: "Invalid or incomplete arguments; set/add require a provider name, -- and at least one provider argument".to_string(),
|
|
20
20
|
reason: "argv_route_usage",
|
|
21
|
-
action: "
|
|
21
|
+
action: "Follow the Examples in team-agent route --help, such as team-agent route set pi -- --mode text".to_string(),
|
|
22
22
|
config_path: None,
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -118,7 +118,7 @@ fn execute(request: &Request) -> Result<Value, RouteError> {
|
|
|
118
118
|
}
|
|
119
119
|
if override_state != Override::Unset {
|
|
120
120
|
value["notice"] = json!(format!(
|
|
121
|
-
"
|
|
121
|
+
"Switch overridden by environment variable {} ({})",
|
|
122
122
|
argv_route::ENV_NAME,
|
|
123
123
|
override_state.status()
|
|
124
124
|
));
|
|
@@ -20,7 +20,7 @@ pub(super) fn watch_notice_json(target: &MessageTarget, opts: &SendOptions) -> V
|
|
|
20
20
|
"watcher_id": format!("watch-{agent_id}"),
|
|
21
21
|
"task_id": opts.task_id.as_ref().map(|t| t.as_str().to_string()),
|
|
22
22
|
"agent_id": agent_id,
|
|
23
|
-
"notice": "
|
|
23
|
+
"notice": "The leader is notified when the agent replies; use team-agent inbox leader -n 3."
|
|
24
24
|
})
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -188,50 +188,51 @@ pub(super) fn send_human_output(value: &Value) -> String {
|
|
|
188
188
|
let reason = value.get("reason").and_then(Value::as_str).unwrap_or("");
|
|
189
189
|
let explanation =
|
|
190
190
|
crate::cli::named_address::human_address_reason(reason).unwrap_or(match reason {
|
|
191
|
-
"target_not_in_team" | "unknown_recipient" => "
|
|
191
|
+
"target_not_in_team" | "unknown_recipient" => "The selected team has no such agent.",
|
|
192
192
|
"missing_permissions" | "human_confirmation_required" => {
|
|
193
|
-
"
|
|
193
|
+
"Send refused: communication permission or human confirmation is required."
|
|
194
194
|
}
|
|
195
195
|
"recipient_busy" | "recipient_pane_in_non_input_mode" => {
|
|
196
|
-
"
|
|
196
|
+
"The agent cannot receive a task now; check its status."
|
|
197
197
|
}
|
|
198
198
|
"routing_ambiguous" | "empty_target_list" | "ambiguous" => {
|
|
199
|
-
"
|
|
199
|
+
"No recipient was selected unambiguously; provide an agent name and task message."
|
|
200
200
|
}
|
|
201
201
|
"team_owner_mismatch" | "PaneWorkspaceMismatch" | "session_drift" => {
|
|
202
|
-
"
|
|
202
|
+
"Team or pane ownership mismatch; sending was refused."
|
|
203
203
|
}
|
|
204
204
|
"coordinator_unavailable" | "tmux_target_missing" => {
|
|
205
|
-
"
|
|
205
|
+
"The team channel is unavailable; delivery was not confirmed."
|
|
206
206
|
}
|
|
207
207
|
"message_already_claimed" | "duplicate" => {
|
|
208
|
-
"
|
|
208
|
+
"This message is already claimed or duplicated; do not resend."
|
|
209
209
|
}
|
|
210
|
-
"no_caller_pane" => "
|
|
210
|
+
"no_caller_pane" => "The current terminal has no usable team channel; send from the leader's tool context.",
|
|
211
211
|
_ => value
|
|
212
212
|
.get("error")
|
|
213
213
|
.and_then(Value::as_str)
|
|
214
|
-
.unwrap_or("
|
|
214
|
+
.unwrap_or("Send was unsuccessful; task delivery was not confirmed."),
|
|
215
215
|
});
|
|
216
|
-
let
|
|
216
|
+
let details = crate::cli::triage::diagnostic_details(value);
|
|
217
|
+
let mut out = format!("{explanation}\nReport: {details}\nAdditional diagnosis: use team-agent status for agent names or team-agent doctor --workspace . for channel problems. Do not resend repeatedly.");
|
|
217
218
|
if let Some(suggested) = value
|
|
218
219
|
.get("suggested_name")
|
|
219
220
|
.and_then(Value::as_str)
|
|
220
221
|
.filter(|name| !name.is_empty() && !name.contains(['/', ':', ',', '\n', '\r']))
|
|
221
222
|
{
|
|
222
223
|
out.push_str(&format!(
|
|
223
|
-
"\
|
|
224
|
+
"\nDid you mean {suggested}? Confirm before using that agent name."
|
|
224
225
|
));
|
|
225
226
|
}
|
|
226
227
|
return out;
|
|
227
228
|
}
|
|
228
229
|
if value.get("status").and_then(Value::as_str) == Some("stored_only") {
|
|
229
|
-
return format!("
|
|
230
|
+
return format!("Message stored for {agent}; it was not injected into the current conversation.\nNext: use team-agent inbox leader -n 3 for replies. Mailbox storage does not guarantee execution.");
|
|
230
231
|
}
|
|
231
232
|
if value.get("delivered").and_then(Value::as_bool) == Some(true) {
|
|
232
|
-
format!("
|
|
233
|
+
format!("Delivered to {agent}. {}", crate::cli::SEND_REMINDER)
|
|
233
234
|
} else {
|
|
234
|
-
"
|
|
235
|
+
"Task accepted, but delivery to the agent's conversation is still pending.\nNext: check team-agent status or team-agent doctor --workspace .; wait for an actual reply without repeated sends.".to_string()
|
|
235
236
|
}
|
|
236
237
|
}
|
|
237
238
|
|
|
@@ -251,7 +252,7 @@ pub(super) fn send_human_target(value: &Value) -> String {
|
|
|
251
252
|
!name.is_empty() && *name != "*" && !name.contains(['/', ':', ',', '\n', '\r'])
|
|
252
253
|
})
|
|
253
254
|
.map(str::to_string)
|
|
254
|
-
.unwrap_or_else(|| "
|
|
255
|
+
.unwrap_or_else(|| "the recipient".to_string())
|
|
255
256
|
}
|
|
256
257
|
|
|
257
258
|
pub(super) fn send_human_status(value: &Value) -> String {
|
|
@@ -286,11 +287,11 @@ pub(super) fn send_reminder_for_value(value: &Value) -> &'static str {
|
|
|
286
287
|
let status = value.get("status").and_then(Value::as_str);
|
|
287
288
|
let delivery_status = value.get("delivery_status").and_then(Value::as_str);
|
|
288
289
|
if status == Some("stored_only") {
|
|
289
|
-
"
|
|
290
|
+
"Mailbox-only: not injected into the current conversation; storage does not guarantee execution."
|
|
290
291
|
} else if delivered == Some(true) && delivery_status != Some("pending") {
|
|
291
292
|
crate::cli::SEND_REMINDER
|
|
292
293
|
} else {
|
|
293
|
-
"
|
|
294
|
+
"Task accepted, but delivery is unconfirmed. Check team-agent status/doctor; wait for an actual reply without repeated sends."
|
|
294
295
|
}
|
|
295
296
|
}
|
|
296
297
|
|
|
@@ -7,11 +7,11 @@ use serde_json::{json, Value};
|
|
|
7
7
|
|
|
8
8
|
pub(super) fn warn_send_alias(flag: &str) {
|
|
9
9
|
let spec = crate::cli::spec::command_spec("send");
|
|
10
|
-
let sunset = spec.and_then(|spec| spec.sunset).unwrap_or("
|
|
10
|
+
let sunset = spec.and_then(|spec| spec.sunset).unwrap_or("a future compatibility release");
|
|
11
11
|
let action = spec.and_then(|spec| spec.action).unwrap_or(
|
|
12
|
-
"
|
|
12
|
+
"Use team-agent send <agent> 'task message'; select the workspace and team with --workspace/--team",
|
|
13
13
|
);
|
|
14
|
-
eprintln!("
|
|
14
|
+
eprintln!("Warning: {flag} is deprecated and will be removed in {sunset}. Next: {action}");
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
pub(super) fn logical_to_from_args(
|
|
@@ -22,7 +22,7 @@ pub(super) fn logical_to_from_args(
|
|
|
22
22
|
&& (args.target.is_some() || args.targets.is_some() || args.to_leader.is_some())
|
|
23
23
|
{
|
|
24
24
|
return Err(CliError::Usage(
|
|
25
|
-
"
|
|
25
|
+
"An agent name cannot be combined with --to-name/--pane/--to; choose one addressing mode".to_string(),
|
|
26
26
|
));
|
|
27
27
|
}
|
|
28
28
|
let supplied = [
|
|
@@ -36,7 +36,7 @@ pub(super) fn logical_to_from_args(
|
|
|
36
36
|
.count();
|
|
37
37
|
if supplied > 1 {
|
|
38
38
|
return Err(CliError::Usage(
|
|
39
|
-
"
|
|
39
|
+
"An agent name, --targets, --to-name and --to-leader are mutually exclusive; choose one addressing mode"
|
|
40
40
|
.to_string(),
|
|
41
41
|
));
|
|
42
42
|
}
|
|
@@ -52,11 +52,11 @@ pub(super) fn logical_to_from_args(
|
|
|
52
52
|
if args.target.is_none() && supplied > 0 && args.message.is_empty() {
|
|
53
53
|
if args.to_name.is_some() {
|
|
54
54
|
return Err(CliError::Usage(
|
|
55
|
-
"
|
|
55
|
+
"Provide a task message; --to-name cannot send an empty message".to_string(),
|
|
56
56
|
));
|
|
57
57
|
}
|
|
58
58
|
return Err(CliError::Usage(
|
|
59
|
-
"
|
|
59
|
+
"Provide a nonempty task message after the agent name, such as team-agent send worker 'Review the changes'".to_string(),
|
|
60
60
|
));
|
|
61
61
|
}
|
|
62
62
|
Ok(logical_to)
|
|
@@ -182,7 +182,7 @@ pub(super) fn send_to_logical_to(
|
|
|
182
182
|
.collect::<Vec<_>>();
|
|
183
183
|
if names.is_empty() || names.len() != logical_to.split(',').count() {
|
|
184
184
|
return Err(CliError::Usage(
|
|
185
|
-
"
|
|
185
|
+
"The recipient list contains an empty agent name; use names returned by status".to_string(),
|
|
186
186
|
));
|
|
187
187
|
}
|
|
188
188
|
|
|
@@ -322,7 +322,7 @@ pub(super) fn adapt_positional_bare_error(
|
|
|
322
322
|
.and_then(|suggested| suggested.rsplit('/').next())
|
|
323
323
|
.map(str::to_string);
|
|
324
324
|
if let Some(suggested) = error.suggested_name.as_deref() {
|
|
325
|
-
error.action = format!("
|
|
325
|
+
error.action = format!("Did you mean {suggested}? Confirm the agent name; select the workspace and team with --workspace/--team.");
|
|
326
326
|
}
|
|
327
327
|
}
|
|
328
328
|
|
|
@@ -336,7 +336,7 @@ pub(super) fn logical_recipient_id(
|
|
|
336
336
|
.ok_or_else(|| CliError::Runtime("resolved worker is missing agent id".to_string())),
|
|
337
337
|
crate::cli::named_address::NamedTargetKind::Leader => Ok("leader".to_string()),
|
|
338
338
|
crate::cli::named_address::NamedTargetKind::SessionWindow => Err(CliError::Usage(
|
|
339
|
-
"
|
|
339
|
+
"Terminal-window addressing is no longer supported; use an agent name or leader, with --workspace/--team to select scope"
|
|
340
340
|
.to_string(),
|
|
341
341
|
)),
|
|
342
342
|
}
|
|
@@ -350,7 +350,7 @@ pub(super) fn send_to_resolved_name(
|
|
|
350
350
|
let recipient = logical_recipient_id(resolved)?;
|
|
351
351
|
if resolved.warning.is_some() {
|
|
352
352
|
eprintln!(
|
|
353
|
-
"
|
|
353
|
+
"Warning: legacy addressing is deprecated; send by agent name and select scope with --workspace/--team."
|
|
354
354
|
);
|
|
355
355
|
}
|
|
356
356
|
let target = MessageTarget::Single(recipient);
|
|
@@ -41,20 +41,20 @@ pub fn cmd_send(args: &SendArgs) -> Result<CmdResult, CliError> {
|
|
|
41
41
|
|| args.to_leader.is_some()
|
|
42
42
|
{
|
|
43
43
|
let message = if args.to_name.is_some() {
|
|
44
|
-
"
|
|
44
|
+
"An agent name cannot be combined with --to-name/--pane/--to; choose one addressing mode"
|
|
45
45
|
} else {
|
|
46
|
-
"--pane
|
|
46
|
+
"--pane cannot be combined with an agent name, --to or --to-leader"
|
|
47
47
|
};
|
|
48
48
|
return Err(CliError::Usage(message.to_string()));
|
|
49
49
|
}
|
|
50
50
|
let content = args.message.join(" ");
|
|
51
51
|
if content.is_empty() {
|
|
52
52
|
return Err(CliError::Usage(
|
|
53
|
-
"
|
|
53
|
+
"Provide a task message; --pane cannot send an empty message".to_string(),
|
|
54
54
|
));
|
|
55
55
|
}
|
|
56
56
|
return Err(CliError::Usage(format!(
|
|
57
|
-
"--pane {pane_id}
|
|
57
|
+
"--pane {pane_id} is no longer supported; use team-agent send <agent> 'task message' with --workspace/--team to select scope"
|
|
58
58
|
)));
|
|
59
59
|
}
|
|
60
60
|
if args.targets.is_some() {
|