@team-agent/installer 0.5.49 → 0.5.51
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 +6 -4
- package/crates/team-agent/src/cli/emit.rs +91 -39
- package/crates/team-agent/src/cli/mod.rs +33 -15
- package/crates/team-agent/src/cli/named_address.rs +82 -53
- package/crates/team-agent/src/cli/send/coordinator.rs +163 -0
- package/crates/team-agent/src/cli/send/mailbox.rs +99 -0
- package/crates/team-agent/src/cli/send/persist.rs +154 -0
- package/crates/team-agent/src/cli/send/presentation.rs +333 -0
- package/crates/team-agent/src/cli/send/resolve.rs +361 -0
- package/crates/team-agent/src/cli/send.rs +103 -1308
- package/crates/team-agent/src/cli/spec.rs +2 -2
- package/crates/team-agent/src/cli/status_port/agents.rs +358 -0
- package/crates/team-agent/src/cli/status_port/approvals.rs +79 -0
- package/crates/team-agent/src/cli/status_port/compact.rs +207 -0
- package/crates/team-agent/src/cli/status_port/format.rs +145 -0
- package/crates/team-agent/src/cli/status_port/inbox.rs +36 -0
- package/crates/team-agent/src/cli/status_port/runtime.rs +195 -0
- package/crates/team-agent/src/cli/status_port/snapshot.rs +181 -0
- package/crates/team-agent/src/cli/status_port/store.rs +412 -0
- package/crates/team-agent/src/cli/status_port/tests.rs +54 -0
- package/crates/team-agent/src/cli/status_port.rs +47 -1548
- package/crates/team-agent/src/cli/tests/leader_watch.rs +1 -1
- package/crates/team-agent/src/cli/tests/named_address.rs +9 -7
- package/crates/team-agent/src/cli/tests/run_delegation.rs +2 -3
- package/crates/team-agent/src/cli/tests/status_send.rs +17 -33
- package/crates/team-agent/src/cli/types.rs +5 -8
- package/crates/team-agent/src/coordinator/conpty_shim.rs +34 -30
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +135 -13
- package/crates/team-agent/src/coordinator/tick.rs +37 -0
- package/crates/team-agent/src/db/agent_health_capture.rs +18 -13
- package/crates/team-agent/src/db/message_store.rs +154 -44
- package/crates/team-agent/src/event_log.rs +73 -0
- package/crates/team-agent/src/leader/start.rs +28 -4
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +424 -0
- package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +297 -0
- package/crates/team-agent/src/lifecycle/launch/agent_state.rs +160 -0
- package/crates/team-agent/src/lifecycle/launch/approval.rs +134 -0
- package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +492 -0
- package/crates/team-agent/src/lifecycle/launch/fork_state.rs +297 -0
- package/crates/team-agent/src/lifecycle/launch/identity.rs +372 -0
- package/crates/team-agent/src/lifecycle/launch/layout.rs +313 -0
- package/crates/team-agent/src/lifecycle/launch/leader_context.rs +478 -0
- package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +201 -0
- package/crates/team-agent/src/lifecycle/launch/ownership.rs +66 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +477 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +278 -0
- package/crates/team-agent/src/lifecycle/launch/readiness.rs +123 -0
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +377 -0
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +434 -0
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +499 -0
- package/crates/team-agent/src/lifecycle/launch/worker_env.rs +438 -0
- package/crates/team-agent/src/lifecycle/launch.rs +119 -5351
- package/crates/team-agent/src/lifecycle/restart/agent.rs +44 -26
- package/crates/team-agent/src/lifecycle/restart/common.rs +53 -27
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +67 -26
- package/crates/team-agent/src/lifecycle/restart/remove.rs +435 -72
- package/crates/team-agent/src/lifecycle/restart.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +575 -17
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +55 -2
- package/crates/team-agent/src/lifecycle/tests/lifecycle_lock.rs +24 -1
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +1 -1
- package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +30 -7
- package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +8 -4
- package/crates/team-agent/src/mcp_server/mod.rs +2 -2
- package/crates/team-agent/src/mcp_server/tests/send.rs +22 -15
- package/crates/team-agent/src/mcp_server/tests/wire.rs +6 -0
- package/crates/team-agent/src/mcp_server/tools.rs +26 -15
- package/crates/team-agent/src/mcp_server/wire.rs +2 -18
- package/crates/team-agent/src/messaging/activity.rs +4 -2
- package/crates/team-agent/src/messaging/address.rs +86 -0
- package/crates/team-agent/src/messaging/delivery.rs +165 -39
- package/crates/team-agent/src/messaging/helpers.rs +17 -13
- package/crates/team-agent/src/messaging/leader_receiver.rs +60 -35
- package/crates/team-agent/src/messaging/mod.rs +11 -2
- package/crates/team-agent/src/messaging/persist.rs +309 -0
- package/crates/team-agent/src/messaging/results.rs +16 -24
- package/crates/team-agent/src/messaging/scheduler.rs +4 -2
- package/crates/team-agent/src/messaging/selftest.rs +19 -12
- package/crates/team-agent/src/messaging/send.rs +133 -58
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +305 -0
- package/crates/team-agent/src/messaging/tests/mod.rs +1 -0
- package/crates/team-agent/src/messaging/tests/runtime.rs +38 -17
- package/crates/team-agent/src/messaging/watchers.rs +13 -3
- package/crates/team-agent/src/redaction.rs +72 -2
- package/crates/team-agent/src/state/persist.rs +2 -1
- package/crates/team-agent/src/state/repository/tests.rs +47 -0
- package/crates/team-agent/src/state/repository.rs +59 -16
- package/package.json +4 -4
|
@@ -45,20 +45,28 @@ pub fn deliver_stored_message(
|
|
|
45
45
|
) -> Result<DeliveryOutcome, MessagingError> {
|
|
46
46
|
let _ = (wait_visible, timeout);
|
|
47
47
|
let recipient = target.unwrap_or("leader");
|
|
48
|
-
let
|
|
49
|
-
|
|
48
|
+
let super::PersistResolution::Persisted(persisted) = super::persist::persist_internal_send(
|
|
49
|
+
workspace,
|
|
50
|
+
super::InternalSendKind::Delivery,
|
|
51
|
+
team.map(TeamKey::as_str),
|
|
50
52
|
task_id.map(crate::model::ids::TaskId::as_str),
|
|
51
53
|
sender,
|
|
52
54
|
recipient,
|
|
53
55
|
content,
|
|
54
56
|
None,
|
|
55
57
|
requires_ack,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
+
None,
|
|
59
|
+
super::InitialDisposition::Accepted,
|
|
60
|
+
)?
|
|
61
|
+
else {
|
|
62
|
+
unreachable!("internal delivery does not accept caller-supplied ids")
|
|
63
|
+
};
|
|
64
|
+
let message_status = persisted.row_status.as_str().to_string();
|
|
65
|
+
let message_id = persisted.message_id;
|
|
58
66
|
Ok(DeliveryOutcome {
|
|
59
67
|
ok: true,
|
|
60
68
|
status: DeliveryStatus::Queued,
|
|
61
|
-
message_status: MessageStatusShadow(
|
|
69
|
+
message_status: MessageStatusShadow(message_status),
|
|
62
70
|
message_id: Some(message_id),
|
|
63
71
|
verification: None,
|
|
64
72
|
stage: None,
|
|
@@ -67,6 +75,17 @@ pub fn deliver_stored_message(
|
|
|
67
75
|
})
|
|
68
76
|
}
|
|
69
77
|
|
|
78
|
+
pub fn deliver_persisted_message(
|
|
79
|
+
workspace: &Path,
|
|
80
|
+
transport: &dyn Transport,
|
|
81
|
+
message_id: &str,
|
|
82
|
+
event_log: &EventLog,
|
|
83
|
+
state: &serde_json::Value,
|
|
84
|
+
) -> Result<DeliveryOutcome, MessagingError> {
|
|
85
|
+
let store = MessageStore::open(workspace)?;
|
|
86
|
+
deliver_pending_message(workspace, &store, transport, message_id, event_log, state)
|
|
87
|
+
}
|
|
88
|
+
|
|
70
89
|
// ===========================================================================
|
|
71
90
|
// delivery.py — 单条 tmux 注入投递 + trust 有界重试 + turn-open arm (card §16)
|
|
72
91
|
// ===========================================================================
|
|
@@ -200,6 +219,22 @@ pub fn deliver_pending_message(
|
|
|
200
219
|
}
|
|
201
220
|
_ => state,
|
|
202
221
|
};
|
|
222
|
+
if message.recipient == "leader" && message.status == "submitted_pending_acceptance" {
|
|
223
|
+
return observe_pending_leader_acceptance(store, event_log, state, message_id);
|
|
224
|
+
}
|
|
225
|
+
if message.recipient == "leader" && message.status == "queued_until_leader_attach" {
|
|
226
|
+
if leader_receiver_status(state) != Some("attached") {
|
|
227
|
+
return Ok(leader_acceptance_pending_outcome(
|
|
228
|
+
message_id,
|
|
229
|
+
"queued_until_leader_attach",
|
|
230
|
+
));
|
|
231
|
+
}
|
|
232
|
+
store.mark(message_id, "accepted", None)?;
|
|
233
|
+
event_log.write(
|
|
234
|
+
"leader_receiver.mailbox_requeued",
|
|
235
|
+
serde_json::json!({"message_id": message_id}),
|
|
236
|
+
)?;
|
|
237
|
+
}
|
|
203
238
|
let attempt = if store.claim_for_delivery(message_id)? {
|
|
204
239
|
message.delivery_attempts.saturating_add(1)
|
|
205
240
|
} else if message.status == "target_resolved" && message.error.is_some() {
|
|
@@ -524,14 +559,10 @@ pub fn deliver_pending_message(
|
|
|
524
559
|
}
|
|
525
560
|
let submit_verified = inject_submit_verified(&inject_report);
|
|
526
561
|
let readback_verified = pane_readback_verified(&inject_report);
|
|
527
|
-
//
|
|
528
|
-
//
|
|
529
|
-
//
|
|
530
|
-
let verified =
|
|
531
|
-
true
|
|
532
|
-
} else {
|
|
533
|
-
submit_verified
|
|
534
|
-
};
|
|
562
|
+
// A successful tmux command is never provider-acceptance proof. Leader and
|
|
563
|
+
// worker delivery both require post-submit evidence; leader additionally
|
|
564
|
+
// waits for its authoritative provider transcript below.
|
|
565
|
+
let verified = submit_verified;
|
|
535
566
|
if !verified {
|
|
536
567
|
let reason = if !readback_verified {
|
|
537
568
|
"pane_readback_unverified:capture_missing_token".to_string()
|
|
@@ -658,7 +689,26 @@ pub fn deliver_pending_message(
|
|
|
658
689
|
});
|
|
659
690
|
}
|
|
660
691
|
}
|
|
661
|
-
|
|
692
|
+
if is_leader_recipient {
|
|
693
|
+
store.record_delivery_submission(message_id, readback_verified)?;
|
|
694
|
+
if !leader_transcript_has_token(state, message_id) {
|
|
695
|
+
store.mark(message_id, "submitted_pending_acceptance", None)?;
|
|
696
|
+
event_log.write(
|
|
697
|
+
"leader_receiver.acceptance_pending",
|
|
698
|
+
serde_json::json!({
|
|
699
|
+
"message_id": message_id,
|
|
700
|
+
"reason": "provider_receipt_not_observed",
|
|
701
|
+
}),
|
|
702
|
+
)?;
|
|
703
|
+
return Ok(leader_acceptance_pending_outcome(
|
|
704
|
+
message_id,
|
|
705
|
+
"submitted_pending_acceptance",
|
|
706
|
+
));
|
|
707
|
+
}
|
|
708
|
+
store.mark_delivered_with_receipt(message_id)?;
|
|
709
|
+
} else {
|
|
710
|
+
store.mark(message_id, "delivered", None)?;
|
|
711
|
+
}
|
|
662
712
|
let mut delivered_event = serde_json::json!({"message_id": message_id});
|
|
663
713
|
if let Some(metadata) = resolved.metadata.as_ref() {
|
|
664
714
|
append_target_metadata(&mut delivered_event, metadata);
|
|
@@ -908,7 +958,8 @@ fn deliver_leader_via_app_server(
|
|
|
908
958
|
);
|
|
909
959
|
match crate::codex_app_server::submit_to_bound_thread(&binding, message_id, &rendered) {
|
|
910
960
|
Ok(submit) => {
|
|
911
|
-
store.
|
|
961
|
+
store.record_delivery_submission(message_id, true)?;
|
|
962
|
+
store.mark_delivered_with_receipt(message_id)?;
|
|
912
963
|
event_log.write(
|
|
913
964
|
"message.delivered",
|
|
914
965
|
serde_json::json!({"message_id": message_id}),
|
|
@@ -1800,10 +1851,16 @@ pub fn deliver_pending_messages(
|
|
|
1800
1851
|
let conn = crate::db::schema::open_db(store.db_path())?;
|
|
1801
1852
|
let mut stmt = conn.prepare(
|
|
1802
1853
|
"select message_id from messages
|
|
1803
|
-
where status in (
|
|
1854
|
+
where status in (
|
|
1855
|
+
'pending', 'accepted', 'target_resolved',
|
|
1856
|
+
'submitted_pending_acceptance', 'queued_until_leader_attach', ?1
|
|
1857
|
+
)
|
|
1804
1858
|
order by created_at, message_id",
|
|
1805
1859
|
)?;
|
|
1806
|
-
let rows = stmt.query_map(
|
|
1860
|
+
let rows = stmt.query_map(
|
|
1861
|
+
[crate::message_store::MessageRowStatus::QueuedCoordinatorUnavailable.as_str()],
|
|
1862
|
+
|row| row.get::<_, String>(0),
|
|
1863
|
+
)?;
|
|
1807
1864
|
rows.collect::<Result<Vec<_>, _>>()?
|
|
1808
1865
|
};
|
|
1809
1866
|
let mut delivered = Vec::new();
|
|
@@ -1874,7 +1931,7 @@ pub fn deliver_pending_messages(
|
|
|
1874
1931
|
continue;
|
|
1875
1932
|
}
|
|
1876
1933
|
};
|
|
1877
|
-
if outcome.
|
|
1934
|
+
if matches!(outcome.status, DeliveryStatus::Delivered) {
|
|
1878
1935
|
delivered.push(message_id);
|
|
1879
1936
|
}
|
|
1880
1937
|
}
|
|
@@ -2017,6 +2074,73 @@ fn recipient_is_busy(state: &serde_json::Value, recipient: &str) -> bool {
|
|
|
2017
2074
|
== Some("busy")
|
|
2018
2075
|
}
|
|
2019
2076
|
|
|
2077
|
+
fn leader_rollout_path(state: &serde_json::Value) -> Option<std::path::PathBuf> {
|
|
2078
|
+
state
|
|
2079
|
+
.get("leader")
|
|
2080
|
+
.and_then(|leader| leader.get("rollout_path"))
|
|
2081
|
+
.and_then(serde_json::Value::as_str)
|
|
2082
|
+
.or_else(|| {
|
|
2083
|
+
leader_receiver_value(state)
|
|
2084
|
+
.and_then(|receiver| receiver.get("rollout_path"))
|
|
2085
|
+
.and_then(serde_json::Value::as_str)
|
|
2086
|
+
})
|
|
2087
|
+
.filter(|path| !path.is_empty())
|
|
2088
|
+
.map(std::path::PathBuf::from)
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
pub(crate) fn leader_transcript_has_token(state: &serde_json::Value, message_id: &str) -> bool {
|
|
2092
|
+
let Some(path) = leader_rollout_path(state) else {
|
|
2093
|
+
return false;
|
|
2094
|
+
};
|
|
2095
|
+
rollout_tail_contains(
|
|
2096
|
+
&path,
|
|
2097
|
+
&format!("[team-agent-token:{message_id}]"),
|
|
2098
|
+
64 * 1024,
|
|
2099
|
+
)
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
fn leader_acceptance_pending_outcome(message_id: &str, status: &str) -> DeliveryOutcome {
|
|
2103
|
+
DeliveryOutcome {
|
|
2104
|
+
ok: true,
|
|
2105
|
+
status: DeliveryStatus::RetryScheduled,
|
|
2106
|
+
message_status: MessageStatusShadow(status.to_string()),
|
|
2107
|
+
message_id: Some(message_id.to_string()),
|
|
2108
|
+
verification: Some("provider_receipt_not_observed".to_string()),
|
|
2109
|
+
stage: Some(DeliveryStage::Submit),
|
|
2110
|
+
reason: None,
|
|
2111
|
+
channel: Some("leader_acceptance_pending".to_string()),
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
fn observe_pending_leader_acceptance(
|
|
2116
|
+
store: &MessageStore,
|
|
2117
|
+
event_log: &EventLog,
|
|
2118
|
+
state: &serde_json::Value,
|
|
2119
|
+
message_id: &str,
|
|
2120
|
+
) -> Result<DeliveryOutcome, MessagingError> {
|
|
2121
|
+
if !leader_transcript_has_token(state, message_id) {
|
|
2122
|
+
return Ok(leader_acceptance_pending_outcome(
|
|
2123
|
+
message_id,
|
|
2124
|
+
"submitted_pending_acceptance",
|
|
2125
|
+
));
|
|
2126
|
+
}
|
|
2127
|
+
store.mark_delivered_with_receipt(message_id)?;
|
|
2128
|
+
event_log.write(
|
|
2129
|
+
"message.delivered",
|
|
2130
|
+
serde_json::json!({"message_id": message_id}),
|
|
2131
|
+
)?;
|
|
2132
|
+
Ok(DeliveryOutcome {
|
|
2133
|
+
ok: true,
|
|
2134
|
+
status: DeliveryStatus::Delivered,
|
|
2135
|
+
message_status: MessageStatusShadow("delivered".to_string()),
|
|
2136
|
+
message_id: Some(message_id.to_string()),
|
|
2137
|
+
verification: Some("provider_transcript_receipt".to_string()),
|
|
2138
|
+
stage: None,
|
|
2139
|
+
reason: None,
|
|
2140
|
+
channel: Some("leader_receiver".to_string()),
|
|
2141
|
+
})
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2020
2144
|
/// `_handle_trust_retry_needed` (`delivery.py:221`):trust 应答失败时调度有界退避重试
|
|
2021
2145
|
/// (`attempt < MAX` → schedule;`>= MAX` → 终态 mark failed + `trust_auto_answer_exhausted`)。
|
|
2022
2146
|
pub fn handle_trust_retry_needed(
|
|
@@ -2341,8 +2465,10 @@ fn save_scoped_state(
|
|
|
2341
2465
|
state: &serde_json::Value,
|
|
2342
2466
|
owner_team_id: Option<&str>,
|
|
2343
2467
|
) -> Result<(), MessagingError> {
|
|
2344
|
-
|
|
2345
|
-
|
|
2468
|
+
let scoped_owner_team_id = owner_team_id
|
|
2469
|
+
.filter(|team| !team.is_empty())
|
|
2470
|
+
.filter(|_| {
|
|
2471
|
+
state
|
|
2346
2472
|
.get("teams")
|
|
2347
2473
|
.and_then(serde_json::Value::as_object)
|
|
2348
2474
|
.is_some_and(|teams| {
|
|
@@ -2354,14 +2480,13 @@ fn save_scoped_state(
|
|
|
2354
2480
|
})
|
|
2355
2481
|
.is_some_and(|team| teams.contains_key(&team))
|
|
2356
2482
|
})
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
}
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
}
|
|
2483
|
+
});
|
|
2484
|
+
crate::state::repository::StateRepository::new(workspace).save(
|
|
2485
|
+
crate::state::repository::StateWriteIntent::MessagingDeliveryState {
|
|
2486
|
+
owner_team_id: scoped_owner_team_id,
|
|
2487
|
+
},
|
|
2488
|
+
state,
|
|
2489
|
+
)?;
|
|
2365
2490
|
Ok(())
|
|
2366
2491
|
}
|
|
2367
2492
|
|
|
@@ -2374,8 +2499,10 @@ fn save_scoped_state_reapplying_after_conflict<F>(
|
|
|
2374
2499
|
where
|
|
2375
2500
|
F: FnOnce(&mut serde_json::Value),
|
|
2376
2501
|
{
|
|
2377
|
-
|
|
2378
|
-
|
|
2502
|
+
let scoped_owner_team_id = owner_team_id
|
|
2503
|
+
.filter(|team| !team.is_empty())
|
|
2504
|
+
.filter(|_| {
|
|
2505
|
+
state
|
|
2379
2506
|
.get("teams")
|
|
2380
2507
|
.and_then(serde_json::Value::as_object)
|
|
2381
2508
|
.is_some_and(|teams| {
|
|
@@ -2387,15 +2514,14 @@ where
|
|
|
2387
2514
|
})
|
|
2388
2515
|
.is_some_and(|team| teams.contains_key(&team))
|
|
2389
2516
|
})
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
}
|
|
2517
|
+
});
|
|
2518
|
+
crate::state::repository::StateRepository::new(workspace).save_reapplying(
|
|
2519
|
+
crate::state::repository::StateWriteIntent::MessagingDeliveryState {
|
|
2520
|
+
owner_team_id: scoped_owner_team_id,
|
|
2521
|
+
},
|
|
2522
|
+
state,
|
|
2523
|
+
reapply,
|
|
2524
|
+
)?;
|
|
2399
2525
|
Ok(())
|
|
2400
2526
|
}
|
|
2401
2527
|
|
|
@@ -274,21 +274,25 @@ pub fn fail_leader_delivery(
|
|
|
274
274
|
error: Option<&str>,
|
|
275
275
|
) -> Result<DeliveryOutcome, MessagingError> {
|
|
276
276
|
let store = MessageStore::open(workspace)?;
|
|
277
|
-
let
|
|
278
|
-
.get("sender")
|
|
279
|
-
.and_then(serde_json::Value::as_str)
|
|
280
|
-
.unwrap_or("system");
|
|
281
|
-
let content = payload
|
|
282
|
-
.get("content")
|
|
283
|
-
.and_then(serde_json::Value::as_str)
|
|
284
|
-
.unwrap_or("");
|
|
285
|
-
let task_id = payload.get("task_id").and_then(serde_json::Value::as_str);
|
|
286
|
-
let message_id = match payload
|
|
277
|
+
let Some(message_id) = payload
|
|
287
278
|
.get("message_id")
|
|
288
279
|
.and_then(serde_json::Value::as_str)
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
280
|
+
.map(ToOwned::to_owned)
|
|
281
|
+
else {
|
|
282
|
+
crate::event_log::EventLog::new(workspace).write(
|
|
283
|
+
"leader_receiver.delivery_failed_without_message",
|
|
284
|
+
serde_json::json!({"reason": serde_json::to_value(reason).ok(), "error": error}),
|
|
285
|
+
)?;
|
|
286
|
+
return Ok(DeliveryOutcome {
|
|
287
|
+
ok: false,
|
|
288
|
+
status: DeliveryStatus::FallbackLog,
|
|
289
|
+
message_status: MessageStatusShadow("failed".to_string()),
|
|
290
|
+
message_id: None,
|
|
291
|
+
verification: None,
|
|
292
|
+
stage: None,
|
|
293
|
+
reason: Some(reason),
|
|
294
|
+
channel: Some("fallback_log".to_string()),
|
|
295
|
+
});
|
|
292
296
|
};
|
|
293
297
|
store.mark(&message_id, "failed", error)?;
|
|
294
298
|
crate::event_log::EventLog::new(workspace).write(
|
|
@@ -13,7 +13,11 @@ use crate::transport::{
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
use super::helpers::MessageStatusShadow;
|
|
16
|
-
use super::
|
|
16
|
+
use super::persist::persist_internal_send;
|
|
17
|
+
use super::{
|
|
18
|
+
DeliveryOutcome, DeliveryRefusal, DeliveryStage, DeliveryStatus, InitialDisposition,
|
|
19
|
+
InternalSendKind, MessagingError, PersistResolution,
|
|
20
|
+
};
|
|
17
21
|
|
|
18
22
|
/// `_send_to_leader_receiver` (`leader.py:69`) — **N31/N32 funnel primitive**:所有 leader-bound
|
|
19
23
|
/// caller(send_message(to=leader) / report_result / request_human / idle reminder /
|
|
@@ -74,39 +78,32 @@ pub fn send_to_leader_receiver_with_message_id(
|
|
|
74
78
|
serde_json::json!({"sender": sender, "leader_id": leader_id, "result_id": result_id}),
|
|
75
79
|
)?;
|
|
76
80
|
}
|
|
77
|
-
let message_id =
|
|
78
|
-
|
|
81
|
+
let message_id = match persist_internal_send(
|
|
82
|
+
workspace,
|
|
83
|
+
InternalSendKind::LeaderNotification,
|
|
84
|
+
Some(&owner_team),
|
|
85
|
+
task_id.map(TaskId::as_str),
|
|
86
|
+
sender,
|
|
87
|
+
leader_id,
|
|
88
|
+
content,
|
|
89
|
+
None,
|
|
90
|
+
false,
|
|
91
|
+
requested_message_id,
|
|
92
|
+
InitialDisposition::Accepted,
|
|
93
|
+
)? {
|
|
94
|
+
PersistResolution::Duplicate(requested) => {
|
|
79
95
|
return Ok(DeliveryOutcome {
|
|
80
96
|
ok: false,
|
|
81
97
|
status: DeliveryStatus::Refused,
|
|
82
98
|
message_status: MessageStatusShadow("refused".to_string()),
|
|
83
|
-
message_id: Some(requested
|
|
99
|
+
message_id: Some(requested),
|
|
84
100
|
verification: None,
|
|
85
101
|
stage: None,
|
|
86
102
|
reason: Some(DeliveryRefusal::Duplicate),
|
|
87
103
|
channel: Some("leader_receiver".to_string()),
|
|
88
104
|
});
|
|
89
105
|
}
|
|
90
|
-
|
|
91
|
-
requested,
|
|
92
|
-
task_id.map(TaskId::as_str),
|
|
93
|
-
sender,
|
|
94
|
-
leader_id,
|
|
95
|
-
content,
|
|
96
|
-
None,
|
|
97
|
-
false,
|
|
98
|
-
Some(&owner_team),
|
|
99
|
-
)?
|
|
100
|
-
} else {
|
|
101
|
-
store.create_message(
|
|
102
|
-
task_id.map(TaskId::as_str),
|
|
103
|
-
sender,
|
|
104
|
-
leader_id,
|
|
105
|
-
content,
|
|
106
|
-
None,
|
|
107
|
-
false,
|
|
108
|
-
Some(&owner_team),
|
|
109
|
-
)?
|
|
106
|
+
PersistResolution::Persisted(persisted) => persisted.message_id,
|
|
110
107
|
};
|
|
111
108
|
// #231 exactly-once across rebind: insert the leader_notification_log PK BEFORE the
|
|
112
109
|
// unbound-pane check. That way, if the result row gets blocked (I-4) and later the
|
|
@@ -332,7 +329,32 @@ pub fn deliver_to_leader_fallback_pane(
|
|
|
332
329
|
let submit_ok = super::delivery::inject_submit_verified(&report);
|
|
333
330
|
let readback_ok = super::delivery::pane_readback_verified(&report);
|
|
334
331
|
if submit_ok {
|
|
335
|
-
store.
|
|
332
|
+
store.record_delivery_submission(message_id, readback_ok)?;
|
|
333
|
+
if !super::delivery::leader_transcript_has_token(state, message_id) {
|
|
334
|
+
store.mark(message_id, "submitted_pending_acceptance", None)?;
|
|
335
|
+
event_log.write(
|
|
336
|
+
"leader_receiver.acceptance_pending",
|
|
337
|
+
serde_json::json!({
|
|
338
|
+
"message_id": message_id,
|
|
339
|
+
"result_id": result_id,
|
|
340
|
+
"reason": "provider_receipt_not_observed",
|
|
341
|
+
"channel": "fallback_pane",
|
|
342
|
+
}),
|
|
343
|
+
)?;
|
|
344
|
+
return Ok(DeliveryOutcome {
|
|
345
|
+
ok: true,
|
|
346
|
+
status: DeliveryStatus::RetryScheduled,
|
|
347
|
+
message_status: MessageStatusShadow(
|
|
348
|
+
"submitted_pending_acceptance".to_string(),
|
|
349
|
+
),
|
|
350
|
+
message_id: Some(message_id.to_string()),
|
|
351
|
+
verification: Some("provider_receipt_not_observed".to_string()),
|
|
352
|
+
stage: Some(DeliveryStage::Submit),
|
|
353
|
+
reason: None,
|
|
354
|
+
channel: Some("leader_acceptance_pending".to_string()),
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
store.mark_delivered_with_receipt(message_id)?;
|
|
336
358
|
event_log.write(
|
|
337
359
|
"leader_receiver.fallback_pane_submitted",
|
|
338
360
|
serde_json::json!({
|
|
@@ -562,21 +584,24 @@ pub fn enqueue_leader_mailbox_until_attach(
|
|
|
562
584
|
sender: &str,
|
|
563
585
|
event_log: &EventLog,
|
|
564
586
|
) -> Result<DeliveryOutcome, MessagingError> {
|
|
565
|
-
let
|
|
566
|
-
|
|
587
|
+
let PersistResolution::Persisted(persisted) = persist_internal_send(
|
|
588
|
+
target_workspace,
|
|
589
|
+
InternalSendKind::OfflineMailbox,
|
|
590
|
+
Some(canonical_team_key),
|
|
567
591
|
task_id.map(TaskId::as_str),
|
|
568
592
|
sender,
|
|
569
593
|
"leader",
|
|
570
594
|
content,
|
|
571
595
|
None,
|
|
572
596
|
false,
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
597
|
+
None,
|
|
598
|
+
InitialDisposition::QueuedUntilLeaderAttach,
|
|
599
|
+
)?
|
|
600
|
+
else {
|
|
601
|
+
unreachable!("offline mailbox does not accept caller-supplied ids")
|
|
602
|
+
};
|
|
603
|
+
let message_status = persisted.row_status.as_str().to_string();
|
|
604
|
+
let message_id = persisted.message_id;
|
|
580
605
|
event_log.write(
|
|
581
606
|
"leader_mailbox.queued_until_attach",
|
|
582
607
|
serde_json::json!({
|
|
@@ -590,7 +615,7 @@ pub fn enqueue_leader_mailbox_until_attach(
|
|
|
590
615
|
Ok(DeliveryOutcome {
|
|
591
616
|
ok: true,
|
|
592
617
|
status: DeliveryStatus::Queued,
|
|
593
|
-
message_status: MessageStatusShadow(
|
|
618
|
+
message_status: MessageStatusShadow(message_status),
|
|
594
619
|
message_id: Some(message_id),
|
|
595
620
|
verification: None,
|
|
596
621
|
stage: None,
|
|
@@ -61,10 +61,12 @@ use crate::message_store::MessageStore;
|
|
|
61
61
|
use crate::transport::{PaneId, Target, Transport};
|
|
62
62
|
|
|
63
63
|
pub mod activity;
|
|
64
|
+
pub mod address;
|
|
64
65
|
pub mod delivery;
|
|
65
66
|
pub mod helpers;
|
|
66
67
|
pub mod leader_receiver;
|
|
67
68
|
pub mod peers;
|
|
69
|
+
pub mod persist;
|
|
68
70
|
pub mod results;
|
|
69
71
|
pub mod scheduler;
|
|
70
72
|
pub mod selftest;
|
|
@@ -75,9 +77,11 @@ pub mod watchers;
|
|
|
75
77
|
|
|
76
78
|
// ── re-export: 保持 `crate::messaging::X` 与 test `super::X` 解析不变 ──────────
|
|
77
79
|
pub use activity::{classify_agent_activity, detect_cross_worker_deadlocks, detect_idle_fallbacks};
|
|
80
|
+
pub use address::{parse_logical_address, LogicalAddress, LogicalAddressTarget};
|
|
78
81
|
pub use delivery::{
|
|
79
|
-
deliver_pending_message, deliver_pending_messages,
|
|
80
|
-
|
|
82
|
+
deliver_pending_message, deliver_pending_messages, deliver_persisted_message,
|
|
83
|
+
deliver_stored_message, execute_trust_retry, handle_trust_retry_needed,
|
|
84
|
+
record_turn_open_if_leader_to_worker,
|
|
81
85
|
retry_injection_after_trust_auto_answer, stamp_first_send_at_if_leader_to_worker,
|
|
82
86
|
tmux_pane_width,
|
|
83
87
|
};
|
|
@@ -88,6 +92,10 @@ pub use leader_receiver::{
|
|
|
88
92
|
send_to_leader_receiver_with_message_id,
|
|
89
93
|
};
|
|
90
94
|
pub use peers::allow_peer_talk;
|
|
95
|
+
pub use persist::{
|
|
96
|
+
persist_resolved_send, DeliveryBlocker, InitialDisposition, InternalSendKind,
|
|
97
|
+
LogicalRecipient, PersistResolution, PersistedSend, ResolvedSendIntent, SendOrigin,
|
|
98
|
+
};
|
|
91
99
|
pub use results::{
|
|
92
100
|
collect, collect_for_team, collect_results_and_notify_watchers, report_result,
|
|
93
101
|
report_result_for_owner_team, report_result_for_owner_team_with_primary_error,
|
|
@@ -96,6 +104,7 @@ pub use scheduler::{detect_stuck_agents, fire_due_scheduled_events, stuck_cancel
|
|
|
96
104
|
pub use selftest::{evaluate_idle_behavior, run_comms_selftest, CommsSelftestDriver};
|
|
97
105
|
pub use send::{
|
|
98
106
|
apply_worker_sender_bypass, send_message, session_drift_refusal, MessageTarget, SendOptions,
|
|
107
|
+
TrustedSender,
|
|
99
108
|
};
|
|
100
109
|
pub use trust::{attempt_trust_auto_answer, TrustAnswerOutcome};
|
|
101
110
|
pub use types::{
|