@team-agent/installer 0.3.9 → 0.3.11
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/send.rs +9 -2
- package/crates/team-agent/src/coordinator/backoff.rs +83 -2
- package/crates/team-agent/src/coordinator/tests/spine.rs +6 -0
- package/crates/team-agent/src/coordinator/tick.rs +410 -168
- package/crates/team-agent/src/leader/lease.rs +19 -0
- package/crates/team-agent/src/leader/rediscover/tests.rs +12 -0
- package/crates/team-agent/src/leader/rediscover.rs +2 -0
- package/crates/team-agent/src/lifecycle/launch.rs +35 -0
- package/crates/team-agent/src/lifecycle/restart/agent.rs +17 -3
- package/crates/team-agent/src/lifecycle/restart/common.rs +75 -0
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +201 -3
- package/crates/team-agent/src/lifecycle/restart/selection.rs +51 -14
- package/crates/team-agent/src/lifecycle/restart.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/core.rs +89 -15
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +68 -3
- package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +3 -1
- package/crates/team-agent/src/mcp_server/helpers.rs +24 -5
- package/crates/team-agent/src/mcp_server/normalize.rs +13 -6
- package/crates/team-agent/src/mcp_server/tests/send.rs +310 -212
- package/crates/team-agent/src/messaging/delivery.rs +83 -2
- package/crates/team-agent/src/messaging/helpers.rs +30 -10
- package/crates/team-agent/src/messaging/send.rs +71 -14
- package/crates/team-agent/src/messaging/tests/basic.rs +25 -7
- package/crates/team-agent/src/messaging/tests/runtime.rs +565 -111
- package/crates/team-agent/src/messaging/types.rs +19 -4
- package/crates/team-agent/src/provider/approvals/parsing.rs +43 -14
- package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +12 -9
- package/crates/team-agent/src/transport/test_support.rs +12 -1
- package/package.json +4 -4
|
@@ -17,7 +17,7 @@ use crate::transport::{
|
|
|
17
17
|
use super::helpers::{message_exists, MessageStatusShadow};
|
|
18
18
|
use super::{
|
|
19
19
|
DeliveryOutcome, DeliveryRefusal, DeliveryStage, DeliveryStatus, MessagingError,
|
|
20
|
-
PaneWidthQuery, TrustRetryPayload,
|
|
20
|
+
PaneWidthQuery, TrustRetryPayload, SEND_RETRY_MAX_ATTEMPTS,
|
|
21
21
|
};
|
|
22
22
|
use crate::state::projection::OwnerTeamResolution;
|
|
23
23
|
|
|
@@ -286,7 +286,6 @@ pub fn deliver_pending_message(
|
|
|
286
286
|
"submit_unverified:{}",
|
|
287
287
|
submit_verification_wire(inject_report.submit_verification)
|
|
288
288
|
);
|
|
289
|
-
store.mark(message_id, "submitted_unverified", Some(&reason))?;
|
|
290
289
|
event_log.write(
|
|
291
290
|
"send.unverified",
|
|
292
291
|
serde_json::json!({
|
|
@@ -296,6 +295,29 @@ pub fn deliver_pending_message(
|
|
|
296
295
|
"attempts": inject_report.attempts,
|
|
297
296
|
}),
|
|
298
297
|
)?;
|
|
298
|
+
if inject_report.attempts >= u32::from(SEND_RETRY_MAX_ATTEMPTS) {
|
|
299
|
+
store.mark(message_id, "failed", Some("send_unverified_exhausted"))?;
|
|
300
|
+
emit_send_failed_exhausted(
|
|
301
|
+
workspace,
|
|
302
|
+
state,
|
|
303
|
+
event_log,
|
|
304
|
+
message_id,
|
|
305
|
+
&message.recipient,
|
|
306
|
+
inject_report.attempts,
|
|
307
|
+
&reason,
|
|
308
|
+
)?;
|
|
309
|
+
return Ok(DeliveryOutcome {
|
|
310
|
+
ok: false,
|
|
311
|
+
status: DeliveryStatus::Failed,
|
|
312
|
+
message_status: MessageStatusShadow("failed".to_string()),
|
|
313
|
+
message_id: Some(message_id.to_string()),
|
|
314
|
+
verification: Some(reason),
|
|
315
|
+
stage: Some(DeliveryStage::Submit),
|
|
316
|
+
reason: None,
|
|
317
|
+
channel: None,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
store.mark(message_id, "submitted_unverified", Some(&reason))?;
|
|
299
321
|
return Ok(DeliveryOutcome {
|
|
300
322
|
ok: false,
|
|
301
323
|
status: DeliveryStatus::Failed,
|
|
@@ -538,6 +560,65 @@ fn leader_receiver_field_in_state<'a>(
|
|
|
538
560
|
.filter(|value| !value.is_empty())
|
|
539
561
|
}
|
|
540
562
|
|
|
563
|
+
fn emit_send_failed_exhausted(
|
|
564
|
+
workspace: &Path,
|
|
565
|
+
state: &serde_json::Value,
|
|
566
|
+
event_log: &EventLog,
|
|
567
|
+
message_id: &str,
|
|
568
|
+
recipient: &str,
|
|
569
|
+
attempts: u32,
|
|
570
|
+
verification: &str,
|
|
571
|
+
) -> Result<(), MessagingError> {
|
|
572
|
+
event_log.write(
|
|
573
|
+
"send.failed",
|
|
574
|
+
serde_json::json!({
|
|
575
|
+
"message_id": message_id,
|
|
576
|
+
"recipient": recipient,
|
|
577
|
+
"attempts": attempts,
|
|
578
|
+
"max_attempts": SEND_RETRY_MAX_ATTEMPTS,
|
|
579
|
+
"reason": "send_unverified_exhausted",
|
|
580
|
+
"verification": verification,
|
|
581
|
+
}),
|
|
582
|
+
)?;
|
|
583
|
+
let content = format!(
|
|
584
|
+
"send.failed\nerror: send to {recipient} remained unverified after {attempts}/{SEND_RETRY_MAX_ATTEMPTS} attempts\naction: inspect the target pane and retry the send\nlog: .team/logs/events.jsonl"
|
|
585
|
+
);
|
|
586
|
+
match crate::messaging::send_to_leader_receiver(
|
|
587
|
+
workspace,
|
|
588
|
+
state,
|
|
589
|
+
"leader",
|
|
590
|
+
&content,
|
|
591
|
+
None,
|
|
592
|
+
"coordinator",
|
|
593
|
+
false,
|
|
594
|
+
Some(&format!("send.failed:{message_id}")),
|
|
595
|
+
event_log,
|
|
596
|
+
) {
|
|
597
|
+
Ok(outcome) => {
|
|
598
|
+
event_log.write(
|
|
599
|
+
"send.failed_notification",
|
|
600
|
+
serde_json::json!({
|
|
601
|
+
"message_id": message_id,
|
|
602
|
+
"recipient": recipient,
|
|
603
|
+
"leader_notification_status": super::helpers::status_wire(outcome.status),
|
|
604
|
+
"leader_message_id": outcome.message_id,
|
|
605
|
+
}),
|
|
606
|
+
)?;
|
|
607
|
+
}
|
|
608
|
+
Err(error) => {
|
|
609
|
+
event_log.write(
|
|
610
|
+
"send.failed_notification_failed",
|
|
611
|
+
serde_json::json!({
|
|
612
|
+
"message_id": message_id,
|
|
613
|
+
"recipient": recipient,
|
|
614
|
+
"error": error.to_string(),
|
|
615
|
+
}),
|
|
616
|
+
)?;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
Ok(())
|
|
620
|
+
}
|
|
621
|
+
|
|
541
622
|
fn active_team_entry(state: &serde_json::Value) -> Option<&serde_json::Value> {
|
|
542
623
|
let team = state.get("active_team_key").and_then(serde_json::Value::as_str)?;
|
|
543
624
|
state
|
|
@@ -29,6 +29,7 @@ pub(crate) fn status_wire(status: DeliveryStatus) -> &'static str {
|
|
|
29
29
|
DeliveryStatus::Queued => "queued",
|
|
30
30
|
DeliveryStatus::Blocked => "blocked",
|
|
31
31
|
DeliveryStatus::Refused => "refused",
|
|
32
|
+
DeliveryStatus::Degraded => "degraded",
|
|
32
33
|
DeliveryStatus::RetryScheduled => "retry_scheduled",
|
|
33
34
|
DeliveryStatus::TrustAutoAnswerExhausted => "trust_auto_answer_exhausted",
|
|
34
35
|
DeliveryStatus::AlreadyDelivered => "already_delivered",
|
|
@@ -40,7 +41,10 @@ pub(crate) fn status_wire(status: DeliveryStatus) -> &'static str {
|
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
pub(crate) fn message_exists(
|
|
44
|
+
pub(crate) fn message_exists(
|
|
45
|
+
store: &MessageStore,
|
|
46
|
+
message_id: &str,
|
|
47
|
+
) -> Result<bool, MessagingError> {
|
|
44
48
|
let conn = crate::db::schema::open_db(store.db_path())?;
|
|
45
49
|
let found: Option<String> = conn
|
|
46
50
|
.query_row(
|
|
@@ -65,7 +69,10 @@ pub(crate) fn next_run_id() -> String {
|
|
|
65
69
|
id.chars().filter(|c| *c != '_').take(12).collect()
|
|
66
70
|
}
|
|
67
71
|
|
|
68
|
-
pub(crate) fn required_str<'a>(
|
|
72
|
+
pub(crate) fn required_str<'a>(
|
|
73
|
+
value: &'a serde_json::Value,
|
|
74
|
+
key: &str,
|
|
75
|
+
) -> Result<&'a str, MessagingError> {
|
|
69
76
|
value
|
|
70
77
|
.get(key)
|
|
71
78
|
.and_then(|v| v.as_str())
|
|
@@ -85,7 +92,9 @@ pub(crate) fn validate_result_envelope(envelope: &serde_json::Value) -> Result<(
|
|
|
85
92
|
}
|
|
86
93
|
for key in ["changes", "tests", "risks", "artifacts", "next_actions"] {
|
|
87
94
|
if !envelope.get(key).is_some_and(serde_json::Value::is_array) {
|
|
88
|
-
return Err(MessagingError::Validation(format!(
|
|
95
|
+
return Err(MessagingError::Validation(format!(
|
|
96
|
+
"missing required array field: {key}"
|
|
97
|
+
)));
|
|
89
98
|
}
|
|
90
99
|
}
|
|
91
100
|
Ok(())
|
|
@@ -122,10 +131,12 @@ pub(crate) fn non_provider_command(command: &str) -> Option<&str> {
|
|
|
122
131
|
pub(crate) fn latest_prompt_signal(scrollback: &str) -> Option<AgentActivity> {
|
|
123
132
|
let lower = scrollback.to_ascii_lowercase();
|
|
124
133
|
let idle_pos = latest_idle_prompt_pos(scrollback);
|
|
125
|
-
let working_pos = [
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
134
|
+
let working_pos = [
|
|
135
|
+
"working", "thinking", "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏",
|
|
136
|
+
]
|
|
137
|
+
.iter()
|
|
138
|
+
.filter_map(|needle| lower.rfind(needle))
|
|
139
|
+
.max();
|
|
129
140
|
match (idle_pos, working_pos) {
|
|
130
141
|
(Some(i), Some(w)) if i > w => Some(idle_activity()),
|
|
131
142
|
(Some(_), None) => Some(idle_activity()),
|
|
@@ -168,10 +179,19 @@ pub fn fail_leader_delivery(
|
|
|
168
179
|
error: Option<&str>,
|
|
169
180
|
) -> Result<DeliveryOutcome, MessagingError> {
|
|
170
181
|
let store = MessageStore::open(workspace)?;
|
|
171
|
-
let sender = payload
|
|
172
|
-
|
|
182
|
+
let sender = payload
|
|
183
|
+
.get("sender")
|
|
184
|
+
.and_then(serde_json::Value::as_str)
|
|
185
|
+
.unwrap_or("system");
|
|
186
|
+
let content = payload
|
|
187
|
+
.get("content")
|
|
188
|
+
.and_then(serde_json::Value::as_str)
|
|
189
|
+
.unwrap_or("");
|
|
173
190
|
let task_id = payload.get("task_id").and_then(serde_json::Value::as_str);
|
|
174
|
-
let message_id = match payload
|
|
191
|
+
let message_id = match payload
|
|
192
|
+
.get("message_id")
|
|
193
|
+
.and_then(serde_json::Value::as_str)
|
|
194
|
+
{
|
|
175
195
|
Some(existing) => existing.to_string(),
|
|
176
196
|
None => store.create_message(task_id, sender, "leader", content, None, false, None)?,
|
|
177
197
|
};
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
use std::path::Path;
|
|
4
4
|
|
|
5
|
+
use crate::coordinator::{CoordinatorHealthStatus, WorkspacePath};
|
|
5
6
|
use crate::event_log::EventLog;
|
|
6
|
-
use crate::model::ids::{TaskId, TeamKey};
|
|
7
7
|
use crate::model::enums::PaneLiveness;
|
|
8
|
+
use crate::model::ids::{TaskId, TeamKey};
|
|
8
9
|
use crate::transport::{PaneId, Transport};
|
|
9
10
|
|
|
10
11
|
use super::helpers::{status_wire, MessageStatusShadow};
|
|
@@ -118,7 +119,15 @@ pub fn send_message(
|
|
|
118
119
|
MessageTarget::Single(target) => target,
|
|
119
120
|
MessageTarget::Broadcast => {
|
|
120
121
|
let recipients = broadcast_recipients(&state, &opts.sender, opts.team.as_ref());
|
|
121
|
-
return fanout_send(
|
|
122
|
+
return fanout_send(
|
|
123
|
+
workspace,
|
|
124
|
+
&state,
|
|
125
|
+
&recipients,
|
|
126
|
+
content,
|
|
127
|
+
opts,
|
|
128
|
+
&event_log,
|
|
129
|
+
"*",
|
|
130
|
+
);
|
|
122
131
|
}
|
|
123
132
|
MessageTarget::Fanout(recipients) if recipients.is_empty() => {
|
|
124
133
|
// swallow batch 3 ②: a failed send carries its reason (Python send error
|
|
@@ -135,7 +144,9 @@ pub fn send_message(
|
|
|
135
144
|
});
|
|
136
145
|
}
|
|
137
146
|
MessageTarget::Fanout(recipients) => {
|
|
138
|
-
return fanout_send(
|
|
147
|
+
return fanout_send(
|
|
148
|
+
workspace, &state, recipients, content, opts, &event_log, "fanout",
|
|
149
|
+
);
|
|
139
150
|
}
|
|
140
151
|
};
|
|
141
152
|
// send.py:259-261 — a non-leader target that is NOT a known team agent is refused
|
|
@@ -170,6 +181,10 @@ pub fn send_message(
|
|
|
170
181
|
}
|
|
171
182
|
}
|
|
172
183
|
}
|
|
184
|
+
if let Some(outcome) = coordinator_unavailable_outcome(workspace, recipient, opts, &event_log)?
|
|
185
|
+
{
|
|
186
|
+
return Ok(outcome);
|
|
187
|
+
}
|
|
173
188
|
let store = crate::message_store::MessageStore::open(workspace)?;
|
|
174
189
|
let task_id = opts.task_id.as_ref().map(|t| t.as_str());
|
|
175
190
|
let owner_team_id = opts.team.as_ref().map(|t| t.as_str());
|
|
@@ -220,9 +235,9 @@ fn task_exists(state: &serde_json::Value, task_id: &TaskId) -> bool {
|
|
|
220
235
|
.get("tasks")
|
|
221
236
|
.and_then(serde_json::Value::as_array)
|
|
222
237
|
.is_some_and(|tasks| {
|
|
223
|
-
tasks
|
|
224
|
-
.
|
|
225
|
-
|
|
238
|
+
tasks.iter().any(|task| {
|
|
239
|
+
task.get("id").and_then(serde_json::Value::as_str) == Some(task_id.as_str())
|
|
240
|
+
})
|
|
226
241
|
})
|
|
227
242
|
}
|
|
228
243
|
|
|
@@ -259,6 +274,46 @@ fn refused_outcome_with_verification(
|
|
|
259
274
|
}
|
|
260
275
|
}
|
|
261
276
|
|
|
277
|
+
fn coordinator_unavailable_outcome(
|
|
278
|
+
workspace: &Path,
|
|
279
|
+
recipient: &str,
|
|
280
|
+
opts: &SendOptions,
|
|
281
|
+
event_log: &EventLog,
|
|
282
|
+
) -> Result<Option<DeliveryOutcome>, MessagingError> {
|
|
283
|
+
let coordinator_workspace = WorkspacePath::new(workspace.to_path_buf());
|
|
284
|
+
let health = crate::coordinator::coordinator_health(&coordinator_workspace);
|
|
285
|
+
if health.ok || matches!(health.status, CoordinatorHealthStatus::Missing) {
|
|
286
|
+
return Ok(None);
|
|
287
|
+
}
|
|
288
|
+
let warning = format!(
|
|
289
|
+
"coordinator is not running; message was not queued for {recipient}. Run `team-agent diagnose` or restart the team before sending again."
|
|
290
|
+
);
|
|
291
|
+
event_log.write(
|
|
292
|
+
"send.coordinator_unavailable",
|
|
293
|
+
serde_json::json!({
|
|
294
|
+
"recipient": recipient,
|
|
295
|
+
"sender": opts.sender,
|
|
296
|
+
"coordinator_status": health.status,
|
|
297
|
+
"coordinator_pid": health.pid.map(|pid| pid.get()),
|
|
298
|
+
"message_queued": false,
|
|
299
|
+
"warning": warning,
|
|
300
|
+
"coordinator_log": crate::coordinator::coordinator_log_path(&coordinator_workspace)
|
|
301
|
+
.display()
|
|
302
|
+
.to_string(),
|
|
303
|
+
}),
|
|
304
|
+
)?;
|
|
305
|
+
Ok(Some(DeliveryOutcome {
|
|
306
|
+
ok: false,
|
|
307
|
+
status: DeliveryStatus::Degraded,
|
|
308
|
+
message_status: MessageStatusShadow("degraded".to_string()),
|
|
309
|
+
message_id: None,
|
|
310
|
+
verification: Some(warning),
|
|
311
|
+
stage: None,
|
|
312
|
+
reason: Some(DeliveryRefusal::CoordinatorUnavailable),
|
|
313
|
+
channel: Some("coordinator_unavailable".to_string()),
|
|
314
|
+
}))
|
|
315
|
+
}
|
|
316
|
+
|
|
262
317
|
fn rebind_required_outcome(message_id: Option<String>) -> DeliveryOutcome {
|
|
263
318
|
rebind_required_outcome_with_verification(
|
|
264
319
|
message_id,
|
|
@@ -291,7 +346,10 @@ fn sender_is_leader(state: &serde_json::Value, sender: &str) -> bool {
|
|
|
291
346
|
sender == leader_id || sender == "leader" || sender == "Leader"
|
|
292
347
|
}
|
|
293
348
|
|
|
294
|
-
fn backfill_leader_binding_for_delivery_view(
|
|
349
|
+
fn backfill_leader_binding_for_delivery_view(
|
|
350
|
+
state: &mut serde_json::Value,
|
|
351
|
+
raw_state: &serde_json::Value,
|
|
352
|
+
) {
|
|
295
353
|
let Some(obj) = state.as_object_mut() else {
|
|
296
354
|
return;
|
|
297
355
|
};
|
|
@@ -329,7 +387,9 @@ fn send_owner_gate_refusal(
|
|
|
329
387
|
None,
|
|
330
388
|
)
|
|
331
389
|
.map_err(|e| MessagingError::Routing(e.to_string()))?;
|
|
332
|
-
if let Some(refusal) =
|
|
390
|
+
if let Some(refusal) =
|
|
391
|
+
crate::state::owner_gate::check_team_owner(state, &caller, false, &LiveLiveness)
|
|
392
|
+
{
|
|
333
393
|
if caller.pane_id.is_empty() {
|
|
334
394
|
return Ok(Some(refused_outcome(DeliveryRefusal::NoCallerPane)));
|
|
335
395
|
}
|
|
@@ -365,7 +425,8 @@ fn explicit_claim_applied(workspace: &Path, _team_key: &str, _caller_pane: &str)
|
|
|
365
425
|
.iter()
|
|
366
426
|
.rev()
|
|
367
427
|
.any(|event| {
|
|
368
|
-
event.get("event").and_then(serde_json::Value::as_str)
|
|
428
|
+
event.get("event").and_then(serde_json::Value::as_str)
|
|
429
|
+
== Some("leader_receiver.rebind_applied")
|
|
369
430
|
})
|
|
370
431
|
}
|
|
371
432
|
|
|
@@ -514,11 +575,7 @@ fn broadcast_recipients(
|
|
|
514
575
|
.and_then(|t| t.get("agents"))
|
|
515
576
|
.and_then(serde_json::Value::as_object)
|
|
516
577
|
})
|
|
517
|
-
.or_else(||
|
|
518
|
-
state
|
|
519
|
-
.get("agents")
|
|
520
|
-
.and_then(serde_json::Value::as_object)
|
|
521
|
-
});
|
|
578
|
+
.or_else(|| state.get("agents").and_then(serde_json::Value::as_object));
|
|
522
579
|
if let Some(agents) = agents_obj {
|
|
523
580
|
for (agent_id, _) in agents {
|
|
524
581
|
if agent_id == sender {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
use super::*;
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
// ════════════════════════════════════════════════════════════════════════
|
|
5
4
|
// GROUP A — serde byte-locks (audit/event wire values; changing one byte
|
|
6
5
|
// breaks downstream recognizers/event consumers). delivery.py / send.py /
|
|
@@ -15,6 +14,7 @@ fn delivery_status_serde_snake_case_byte_locked() {
|
|
|
15
14
|
(DeliveryStatus::Queued, "\"queued\""),
|
|
16
15
|
(DeliveryStatus::Blocked, "\"blocked\""),
|
|
17
16
|
(DeliveryStatus::Refused, "\"refused\""),
|
|
17
|
+
(DeliveryStatus::Degraded, "\"degraded\""),
|
|
18
18
|
(DeliveryStatus::RetryScheduled, "\"retry_scheduled\""),
|
|
19
19
|
(
|
|
20
20
|
DeliveryStatus::TrustAutoAnswerExhausted,
|
|
@@ -22,7 +22,10 @@ fn delivery_status_serde_snake_case_byte_locked() {
|
|
|
22
22
|
),
|
|
23
23
|
(DeliveryStatus::AlreadyDelivered, "\"already_delivered\""),
|
|
24
24
|
(DeliveryStatus::FallbackLog, "\"fallback_log\""),
|
|
25
|
-
(
|
|
25
|
+
(
|
|
26
|
+
DeliveryStatus::BroadcastDelivered,
|
|
27
|
+
"\"broadcast_delivered\"",
|
|
28
|
+
),
|
|
26
29
|
(DeliveryStatus::BroadcastPartial, "\"broadcast_partial\""),
|
|
27
30
|
(DeliveryStatus::FanoutDelivered, "\"fanout_delivered\""),
|
|
28
31
|
(DeliveryStatus::FanoutPartial, "\"fanout_partial\""),
|
|
@@ -40,16 +43,32 @@ fn delivery_refusal_serde_snake_case_byte_locked() {
|
|
|
40
43
|
DeliveryRefusal::HumanConfirmationRequired,
|
|
41
44
|
"\"human_confirmation_required\"",
|
|
42
45
|
),
|
|
43
|
-
(
|
|
46
|
+
(
|
|
47
|
+
DeliveryRefusal::MissingPermissions,
|
|
48
|
+
"\"missing_permissions\"",
|
|
49
|
+
),
|
|
44
50
|
(DeliveryRefusal::RecipientBusy, "\"recipient_busy\""),
|
|
45
51
|
(DeliveryRefusal::UnknownRecipient, "\"unknown_recipient\""),
|
|
46
|
-
(
|
|
52
|
+
(
|
|
53
|
+
DeliveryRefusal::TmuxTargetMissing,
|
|
54
|
+
"\"tmux_target_missing\"",
|
|
55
|
+
),
|
|
47
56
|
(
|
|
48
57
|
DeliveryRefusal::MessageAlreadyClaimed,
|
|
49
58
|
"\"message_already_claimed\"",
|
|
50
59
|
),
|
|
51
|
-
(
|
|
52
|
-
|
|
60
|
+
(
|
|
61
|
+
DeliveryRefusal::LeaderNotAttached,
|
|
62
|
+
"\"leader_not_attached\"",
|
|
63
|
+
),
|
|
64
|
+
(
|
|
65
|
+
DeliveryRefusal::CoordinatorUnavailable,
|
|
66
|
+
"\"coordinator_unavailable\"",
|
|
67
|
+
),
|
|
68
|
+
(
|
|
69
|
+
DeliveryRefusal::TeamOwnerMismatch,
|
|
70
|
+
"\"team_owner_mismatch\"",
|
|
71
|
+
),
|
|
53
72
|
(DeliveryRefusal::Ambiguous, "\"ambiguous\""),
|
|
54
73
|
(
|
|
55
74
|
DeliveryRefusal::RecipientPaneInNonInputMode,
|
|
@@ -354,4 +373,3 @@ fn result_id_from_text_strips_trailing_whitespace() {
|
|
|
354
373
|
Some("abc".to_string())
|
|
355
374
|
);
|
|
356
375
|
}
|
|
357
|
-
|