@team-agent/installer 0.5.53 → 0.5.54
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/emit.rs +94 -3
- package/crates/team-agent/src/cli/send/persist.rs +1 -0
- package/crates/team-agent/src/cli/send/presentation.rs +1 -0
- package/crates/team-agent/src/cli/send.rs +1 -0
- package/crates/team-agent/src/cli/spec.rs +1 -1
- package/crates/team-agent/src/cli/tests/lane_c.rs +3 -3
- package/crates/team-agent/src/cli/tests/leader_watch.rs +1 -0
- package/crates/team-agent/src/cli/tests/named_address.rs +1 -0
- package/crates/team-agent/src/cli/tests/status_send.rs +1 -0
- package/crates/team-agent/src/cli/types.rs +1 -0
- package/crates/team-agent/src/coordinator/tests/basics.rs +4 -4
- package/crates/team-agent/src/db/message_store.rs +31 -2
- package/crates/team-agent/src/db/migration.rs +7 -6
- package/crates/team-agent/src/db/schema.rs +18 -5
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +1 -0
- package/crates/team-agent/src/mcp_server/normalize.rs +8 -0
- package/crates/team-agent/src/mcp_server/tests/wire.rs +1 -1
- package/crates/team-agent/src/mcp_server/tools.rs +71 -0
- package/crates/team-agent/src/mcp_server/types.rs +4 -0
- package/crates/team-agent/src/mcp_server/wire.rs +42 -4
- package/crates/team-agent/src/messaging/delivery.rs +2 -0
- package/crates/team-agent/src/messaging/helpers.rs +1 -0
- package/crates/team-agent/src/messaging/leader_receiver.rs +32 -0
- package/crates/team-agent/src/messaging/mod.rs +2 -1
- package/crates/team-agent/src/messaging/persist.rs +68 -2
- package/crates/team-agent/src/messaging/presentation.rs +307 -0
- package/crates/team-agent/src/messaging/results.rs +130 -3
- package/crates/team-agent/src/messaging/selftest.rs +1 -0
- package/crates/team-agent/src/messaging/send.rs +54 -2
- package/crates/team-agent/src/messaging/tests/runtime.rs +51 -1
- package/crates/team-agent/src/messaging/types.rs +2 -0
- package/crates/team-agent/src/messaging/watchers.rs +1 -0
- package/crates/team-agent/src/provider/session/capture.rs +74 -0
- package/crates/team-agent/src/provider/session_scan/common/tests.rs +112 -0
- package/crates/team-agent/src/provider/session_scan/common.rs +14 -86
- package/package.json +4 -4
- package/schemas/result-envelope.schema.json +10 -0
- package/skills/team-agent/SKILL.md +3 -0
|
@@ -968,6 +968,17 @@ fn allocate_session_candidates(
|
|
|
968
968
|
CandidateMatchKind::Any,
|
|
969
969
|
item,
|
|
970
970
|
) {
|
|
971
|
+
Some(candidate)
|
|
972
|
+
if codex_no_expected(item)
|
|
973
|
+
&& candidate_is_visible_to_pending_peer(
|
|
974
|
+
item,
|
|
975
|
+
&candidate,
|
|
976
|
+
pending,
|
|
977
|
+
candidates_by_agent,
|
|
978
|
+
) =>
|
|
979
|
+
{
|
|
980
|
+
ambiguous.insert(item.agent_id.clone());
|
|
981
|
+
}
|
|
971
982
|
Some(candidate) => {
|
|
972
983
|
claimed.extend(captured_provider_session_keys(item, &candidate.captured));
|
|
973
984
|
assignments.insert(item.agent_id.clone(), candidate);
|
|
@@ -985,6 +996,30 @@ fn allocate_session_candidates(
|
|
|
985
996
|
(assignments, ambiguous)
|
|
986
997
|
}
|
|
987
998
|
|
|
999
|
+
fn candidate_is_visible_to_pending_peer(
|
|
1000
|
+
owner: &PendingSessionCapture,
|
|
1001
|
+
candidate: &CapturedSessionCandidate,
|
|
1002
|
+
pending: &[PendingSessionCapture],
|
|
1003
|
+
candidates_by_agent: &BTreeMap<String, Vec<CapturedSessionCandidate>>,
|
|
1004
|
+
) -> bool {
|
|
1005
|
+
let owner_keys = captured_provider_session_keys(owner, &candidate.captured);
|
|
1006
|
+
pending.iter().any(|peer| {
|
|
1007
|
+
peer.agent_id != owner.agent_id
|
|
1008
|
+
&& peer.provider == owner.provider
|
|
1009
|
+
&& peer.team_key == owner.team_key
|
|
1010
|
+
&& candidates_by_agent
|
|
1011
|
+
.get(&peer.agent_id)
|
|
1012
|
+
.is_some_and(|candidates| {
|
|
1013
|
+
candidates.iter().any(|peer_candidate| {
|
|
1014
|
+
!owner_keys.is_disjoint(&captured_provider_session_keys(
|
|
1015
|
+
peer,
|
|
1016
|
+
&peer_candidate.captured,
|
|
1017
|
+
))
|
|
1018
|
+
})
|
|
1019
|
+
})
|
|
1020
|
+
})
|
|
1021
|
+
}
|
|
1022
|
+
|
|
988
1023
|
fn allocate_global_one_to_one(
|
|
989
1024
|
pending: &[PendingSessionCapture],
|
|
990
1025
|
candidates_by_agent: &BTreeMap<String, Vec<CapturedSessionCandidate>>,
|
|
@@ -1702,6 +1737,45 @@ mod u1_tests {
|
|
|
1702
1737
|
);
|
|
1703
1738
|
}
|
|
1704
1739
|
|
|
1740
|
+
#[test]
|
|
1741
|
+
fn codex_shared_single_candidate_without_identity_is_deferred() {
|
|
1742
|
+
let mut state = serde_json::json!({
|
|
1743
|
+
"agents": {
|
|
1744
|
+
"parent-peer": {
|
|
1745
|
+
"provider": "codex",
|
|
1746
|
+
"status": "running",
|
|
1747
|
+
"spawn_cwd": "/tmp/u1-cwd"
|
|
1748
|
+
},
|
|
1749
|
+
"worker-a": {
|
|
1750
|
+
"provider": "codex",
|
|
1751
|
+
"status": "running",
|
|
1752
|
+
"spawn_cwd": "/tmp/u1-cwd"
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
});
|
|
1756
|
+
let candidate = leader_like_candidate(
|
|
1757
|
+
"019f3327-shared",
|
|
1758
|
+
"/Users/alauda/.codex/sessions/2026/07/06/rollout-shared.jsonl",
|
|
1759
|
+
);
|
|
1760
|
+
let mut canned = BTreeMap::new();
|
|
1761
|
+
canned.insert("parent-peer".to_string(), vec![candidate.clone()]);
|
|
1762
|
+
canned.insert("worker-a".to_string(), vec![candidate]);
|
|
1763
|
+
let canned_for_adapter = canned.clone();
|
|
1764
|
+
let mut adapter_for = move |provider| {
|
|
1765
|
+
Box::new(
|
|
1766
|
+
test_support::CaptureCandidatesAdapter::new(provider, None, "")
|
|
1767
|
+
.with_candidates(canned_for_adapter.clone()),
|
|
1768
|
+
) as Box<dyn ProviderAdapter>
|
|
1769
|
+
};
|
|
1770
|
+
|
|
1771
|
+
let report = capture_missing_provider_sessions_once(&mut state, &mut adapter_for, true, 0)
|
|
1772
|
+
.expect("capture pass succeeds");
|
|
1773
|
+
|
|
1774
|
+
assert!(report.assigned.is_empty(), "report={report:?}");
|
|
1775
|
+
assert!(state["agents"]["parent-peer"].get("session_id").is_none());
|
|
1776
|
+
assert!(state["agents"]["worker-a"].get("session_id").is_none());
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1705
1779
|
#[test]
|
|
1706
1780
|
fn codex_embedded_identity_assigns_each_same_cwd_worker_to_own_rollout() {
|
|
1707
1781
|
let mut state = serde_json::json!({
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
use super::*;
|
|
2
|
+
|
|
3
|
+
fn temp_dir(name: &str) -> PathBuf {
|
|
4
|
+
static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
|
|
5
|
+
let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
|
6
|
+
let dir =
|
|
7
|
+
std::env::temp_dir().join(format!("ta-session-scan-{name}-{}-{n}", std::process::id()));
|
|
8
|
+
std::fs::create_dir_all(&dir).expect("create temp dir");
|
|
9
|
+
dir
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
fn write_codex_rollout(path: &Path, cwd: &Path, session_id: &str, embedded_agent_id: &str) {
|
|
13
|
+
let text = format!(
|
|
14
|
+
"{{\"session_meta\":{{\"payload\":{{\"id\":\"{session_id}\",\"cwd\":\"{}\"}}}}}}\n\
|
|
15
|
+
{{\"type\":\"turn_context\",\"payload\":{{}}}}\n\
|
|
16
|
+
{{\"type\":\"response_item\",\"payload\":{{\"content\":[{{\"type\":\"input_text\",\"text\":\"You are Team Agent worker `{embedded_agent_id}` with role `fixture`.\"}}]}}}}\n",
|
|
17
|
+
cwd.to_string_lossy()
|
|
18
|
+
);
|
|
19
|
+
std::fs::write(path, text).expect("write codex rollout");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#[test]
|
|
23
|
+
fn codex_prompt_worker_identity_is_a_positive_match() {
|
|
24
|
+
let dir = temp_dir("positive");
|
|
25
|
+
let rollout = dir.join("rollout-frontend.jsonl");
|
|
26
|
+
write_codex_rollout(&rollout, &dir, "sess-frontend", "frontend");
|
|
27
|
+
let context = CaptureSessionContext {
|
|
28
|
+
agent_id: "frontend".to_string(),
|
|
29
|
+
spawn_cwd: dir.clone(),
|
|
30
|
+
pane_id: None,
|
|
31
|
+
pane_pid: None,
|
|
32
|
+
spawned_at: None,
|
|
33
|
+
expected_session_id: None,
|
|
34
|
+
provider_projects_root: None,
|
|
35
|
+
};
|
|
36
|
+
let candidates = parse_candidate_files(
|
|
37
|
+
Provider::Codex,
|
|
38
|
+
&context,
|
|
39
|
+
vec![SessionCandidate {
|
|
40
|
+
path: rollout.clone(),
|
|
41
|
+
requires_cwd_match: false,
|
|
42
|
+
}],
|
|
43
|
+
);
|
|
44
|
+
assert_eq!(candidates.len(), 1);
|
|
45
|
+
assert!(candidates[0].positive_agent_id_match);
|
|
46
|
+
let _ = std::fs::remove_dir_all(&dir);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#[test]
|
|
50
|
+
fn codex_identity_in_incomplete_final_head_record_is_positive() {
|
|
51
|
+
let dir = temp_dir("cross-cap-positive");
|
|
52
|
+
let rollout = dir.join("rollout-worker-a.jsonl");
|
|
53
|
+
let meta = format!(
|
|
54
|
+
"{{\"session_meta\":{{\"payload\":{{\"id\":\"sess-worker-a\",\"cwd\":\"{}\"}}}}}}\n",
|
|
55
|
+
dir.to_string_lossy()
|
|
56
|
+
);
|
|
57
|
+
let marker = "You are Team Agent worker `worker-a` with role `fixture`.";
|
|
58
|
+
let body = format!("{meta}{{\"text\":\"{marker}{}\"}}\n", "x".repeat(70_000));
|
|
59
|
+
std::fs::write(&rollout, body).expect("write cross-cap rollout");
|
|
60
|
+
assert!(!read_head_text(&rollout, CAPTURE_HEAD_BYTES)
|
|
61
|
+
.expect("read complete records")
|
|
62
|
+
.contains(marker));
|
|
63
|
+
let context = CaptureSessionContext {
|
|
64
|
+
agent_id: "worker-a".to_string(),
|
|
65
|
+
spawn_cwd: dir.clone(),
|
|
66
|
+
pane_id: None,
|
|
67
|
+
pane_pid: None,
|
|
68
|
+
spawned_at: None,
|
|
69
|
+
expected_session_id: None,
|
|
70
|
+
provider_projects_root: None,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
let candidates = parse_candidate_files(
|
|
74
|
+
Provider::Codex,
|
|
75
|
+
&context,
|
|
76
|
+
vec![SessionCandidate {
|
|
77
|
+
path: rollout,
|
|
78
|
+
requires_cwd_match: false,
|
|
79
|
+
}],
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
assert_eq!(candidates.len(), 1);
|
|
83
|
+
assert_eq!(candidates[0].embedded_agent_id.as_deref(), Some("worker-a"));
|
|
84
|
+
assert!(candidates[0].positive_agent_id_match);
|
|
85
|
+
let _ = std::fs::remove_dir_all(&dir);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
#[test]
|
|
89
|
+
fn codex_prompt_worker_identity_mismatch_is_rejected_for_that_agent() {
|
|
90
|
+
let dir = temp_dir("mismatch");
|
|
91
|
+
let rollout = dir.join("rollout-ios-dev.jsonl");
|
|
92
|
+
write_codex_rollout(&rollout, &dir, "sess-ios-dev", "ios-dev");
|
|
93
|
+
let context = CaptureSessionContext {
|
|
94
|
+
agent_id: "frontend".to_string(),
|
|
95
|
+
spawn_cwd: dir.clone(),
|
|
96
|
+
pane_id: None,
|
|
97
|
+
pane_pid: None,
|
|
98
|
+
spawned_at: None,
|
|
99
|
+
expected_session_id: None,
|
|
100
|
+
provider_projects_root: None,
|
|
101
|
+
};
|
|
102
|
+
let candidates = parse_candidate_files(
|
|
103
|
+
Provider::Codex,
|
|
104
|
+
&context,
|
|
105
|
+
vec![SessionCandidate {
|
|
106
|
+
path: rollout,
|
|
107
|
+
requires_cwd_match: false,
|
|
108
|
+
}],
|
|
109
|
+
);
|
|
110
|
+
assert!(candidates.is_empty());
|
|
111
|
+
let _ = std::fs::remove_dir_all(&dir);
|
|
112
|
+
}
|
|
@@ -69,9 +69,11 @@ pub(super) fn parse_candidate_files(
|
|
|
69
69
|
let mut out = Vec::new();
|
|
70
70
|
for candidate in candidates {
|
|
71
71
|
let path = candidate.path;
|
|
72
|
-
let Ok(
|
|
72
|
+
let Ok(head_bytes) = read_head_bytes(&path, CAPTURE_HEAD_BYTES) else {
|
|
73
73
|
continue;
|
|
74
74
|
};
|
|
75
|
+
let text = complete_head_text(&head_bytes);
|
|
76
|
+
let identity_text = String::from_utf8_lossy(&head_bytes);
|
|
75
77
|
let records = parse_session_records(&text);
|
|
76
78
|
if records.is_empty() {
|
|
77
79
|
continue;
|
|
@@ -98,7 +100,7 @@ pub(super) fn parse_candidate_files(
|
|
|
98
100
|
} else {
|
|
99
101
|
Confidence::Low
|
|
100
102
|
};
|
|
101
|
-
let embedded_agent_id = embedded_team_agent_worker_id_from_text(&
|
|
103
|
+
let embedded_agent_id = embedded_team_agent_worker_id_from_text(&identity_text);
|
|
102
104
|
if embedded_agent_id
|
|
103
105
|
.as_deref()
|
|
104
106
|
.is_some_and(|id| id != context.agent_id.as_str())
|
|
@@ -241,15 +243,23 @@ fn cap_candidates_by_mtime(out: &mut Vec<SessionCandidate>, cap: usize) {
|
|
|
241
243
|
}
|
|
242
244
|
|
|
243
245
|
pub(super) fn read_head_text(path: &Path, max_bytes: u64) -> std::io::Result<String> {
|
|
246
|
+
read_head_bytes(path, max_bytes).map(|bytes| complete_head_text(&bytes))
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
fn read_head_bytes(path: &Path, max_bytes: u64) -> std::io::Result<Vec<u8>> {
|
|
244
250
|
use std::io::Read;
|
|
245
251
|
let file = std::fs::File::open(path)?;
|
|
246
252
|
let mut bytes = Vec::new();
|
|
247
253
|
file.take(max_bytes).read_to_end(&mut bytes)?;
|
|
254
|
+
Ok(bytes)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
fn complete_head_text(bytes: &[u8]) -> String {
|
|
248
258
|
let complete = match bytes.iter().rposition(|byte| *byte == b'\n') {
|
|
249
259
|
Some(last_newline) => &bytes[..=last_newline],
|
|
250
260
|
None => &bytes[..],
|
|
251
261
|
};
|
|
252
|
-
|
|
262
|
+
String::from_utf8_lossy(complete).into_owned()
|
|
253
263
|
}
|
|
254
264
|
|
|
255
265
|
fn collect_optional_candidate_files(
|
|
@@ -414,86 +424,4 @@ fn path_is_under_team_runtime(path: &Path) -> bool {
|
|
|
414
424
|
}
|
|
415
425
|
|
|
416
426
|
#[cfg(test)]
|
|
417
|
-
mod tests
|
|
418
|
-
use super::*;
|
|
419
|
-
|
|
420
|
-
fn temp_dir(name: &str) -> PathBuf {
|
|
421
|
-
static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
|
|
422
|
-
let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
|
423
|
-
let dir =
|
|
424
|
-
std::env::temp_dir().join(format!("ta-session-scan-{name}-{}-{n}", std::process::id()));
|
|
425
|
-
std::fs::create_dir_all(&dir).expect("create temp dir");
|
|
426
|
-
dir
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
fn write_codex_rollout(path: &Path, cwd: &Path, session_id: &str, embedded_agent_id: &str) {
|
|
430
|
-
let text = format!(
|
|
431
|
-
"{{\"session_meta\":{{\"payload\":{{\"id\":\"{session_id}\",\"cwd\":\"{}\"}}}}}}\n\
|
|
432
|
-
{{\"type\":\"turn_context\",\"payload\":{{}}}}\n\
|
|
433
|
-
{{\"type\":\"response_item\",\"payload\":{{\"content\":[{{\"type\":\"input_text\",\"text\":\"You are Team Agent worker `{embedded_agent_id}` with role `fixture`.\"}}]}}}}\n",
|
|
434
|
-
cwd.to_string_lossy()
|
|
435
|
-
);
|
|
436
|
-
std::fs::write(path, text).expect("write codex rollout");
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
#[test]
|
|
440
|
-
fn codex_prompt_worker_identity_is_a_positive_match() {
|
|
441
|
-
let dir = temp_dir("positive");
|
|
442
|
-
let rollout = dir.join("rollout-frontend.jsonl");
|
|
443
|
-
write_codex_rollout(&rollout, &dir, "sess-frontend", "frontend");
|
|
444
|
-
let context = CaptureSessionContext {
|
|
445
|
-
agent_id: "frontend".to_string(),
|
|
446
|
-
spawn_cwd: dir.clone(),
|
|
447
|
-
pane_id: None,
|
|
448
|
-
pane_pid: None,
|
|
449
|
-
spawned_at: None,
|
|
450
|
-
expected_session_id: None,
|
|
451
|
-
provider_projects_root: None,
|
|
452
|
-
};
|
|
453
|
-
let candidates = parse_candidate_files(
|
|
454
|
-
Provider::Codex,
|
|
455
|
-
&context,
|
|
456
|
-
vec![SessionCandidate {
|
|
457
|
-
path: rollout.clone(),
|
|
458
|
-
requires_cwd_match: false,
|
|
459
|
-
}],
|
|
460
|
-
);
|
|
461
|
-
assert_eq!(candidates.len(), 1);
|
|
462
|
-
assert!(
|
|
463
|
-
candidates[0].positive_agent_id_match,
|
|
464
|
-
"Codex transcript prompt identity must be treated as a positive worker id source"
|
|
465
|
-
);
|
|
466
|
-
let _ = std::fs::remove_file(&rollout);
|
|
467
|
-
let _ = std::fs::remove_dir_all(&dir);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
#[test]
|
|
471
|
-
fn codex_prompt_worker_identity_mismatch_is_rejected_for_that_agent() {
|
|
472
|
-
let dir = temp_dir("mismatch");
|
|
473
|
-
let rollout = dir.join("rollout-ios-dev.jsonl");
|
|
474
|
-
write_codex_rollout(&rollout, &dir, "sess-ios-dev", "ios-dev");
|
|
475
|
-
let context = CaptureSessionContext {
|
|
476
|
-
agent_id: "frontend".to_string(),
|
|
477
|
-
spawn_cwd: dir.clone(),
|
|
478
|
-
pane_id: None,
|
|
479
|
-
pane_pid: None,
|
|
480
|
-
spawned_at: None,
|
|
481
|
-
expected_session_id: None,
|
|
482
|
-
provider_projects_root: None,
|
|
483
|
-
};
|
|
484
|
-
let candidates = parse_candidate_files(
|
|
485
|
-
Provider::Codex,
|
|
486
|
-
&context,
|
|
487
|
-
vec![SessionCandidate {
|
|
488
|
-
path: rollout.clone(),
|
|
489
|
-
requires_cwd_match: false,
|
|
490
|
-
}],
|
|
491
|
-
);
|
|
492
|
-
assert!(
|
|
493
|
-
candidates.is_empty(),
|
|
494
|
-
"state agent=frontend must not accept a Codex rollout whose prompt says ios-dev"
|
|
495
|
-
);
|
|
496
|
-
let _ = std::fs::remove_file(&rollout);
|
|
497
|
-
let _ = std::fs::remove_dir_all(&dir);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
427
|
+
mod tests;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-agent/installer",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.54",
|
|
4
4
|
"description": "npx installer for Team Agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"codex",
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"team-agent-installer": "npm/install.mjs"
|
|
21
21
|
},
|
|
22
22
|
"optionalDependencies": {
|
|
23
|
-
"@team-agent/cli-darwin-arm64": "0.5.
|
|
24
|
-
"@team-agent/cli-darwin-x64": "0.5.
|
|
25
|
-
"@team-agent/cli-linux-x64": "0.5.
|
|
23
|
+
"@team-agent/cli-darwin-arm64": "0.5.54",
|
|
24
|
+
"@team-agent/cli-darwin-x64": "0.5.54",
|
|
25
|
+
"@team-agent/cli-linux-x64": "0.5.54"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node npm/bincheck.mjs",
|
|
@@ -11,6 +11,16 @@
|
|
|
11
11
|
"agent_id": { "type": "string", "minLength": 1 },
|
|
12
12
|
"status": { "enum": ["success", "blocked", "failed", "partial"] },
|
|
13
13
|
"summary": { "type": "string" },
|
|
14
|
+
"presentation": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"required": ["sink", "class"],
|
|
17
|
+
"additionalProperties": false,
|
|
18
|
+
"properties": {
|
|
19
|
+
"sink": { "enum": ["leader", "casefile", "silent"] },
|
|
20
|
+
"class": { "enum": ["message", "progress", "stage_result", "stage_pass", "bounce", "blocking", "final_review", "timeout"] },
|
|
21
|
+
"case_id": { "type": "string", "minLength": 1 }
|
|
22
|
+
}
|
|
23
|
+
},
|
|
14
24
|
"changes": {
|
|
15
25
|
"type": "array",
|
|
16
26
|
"items": {
|
|
@@ -166,6 +166,7 @@ For diagnosis, run `team-agent profile show deepseek --workspace . --json`; neve
|
|
|
166
166
|
- `quick-start` is only for first-time team creation from role docs. If that team already has runtime state, use `team-agent restart . --team <session_name_or_team_name>` to resume it. If restart cannot recover context, explain the loss and wait for explicit user consent before using `team-agent restart . --allow-fresh`; never reset context through quick-start.
|
|
167
167
|
- If the user explicitly asks a worker to create or operate a nested child team, first read `references/team-in-team.md`. Child teams must use an independent child workspace, never the parent `.team/current`.
|
|
168
168
|
- `team-agent send --watch-result coder "Do the bounded task"` sends a direct worker message, returns after delivery, and lets the coordinator collect/report completion asynchronously.
|
|
169
|
+
- Advanced orchestration callers may add `--presentation-sink leader|casefile|silent --message-class CLASS [--case-id CASE]`. All sinks remain durable and pullable; `casefile`/`silent` suppress only live leader injection. Missing presentation metadata preserves the normal leader-visible behavior.
|
|
169
170
|
- After `send --watch-result` succeeds, do not run `sleep`, `status`, `inbox`, or `collect` polling loops unless the user explicitly asks for diagnosis; the coordinator will notify the leader when the result arrives.
|
|
170
171
|
- `team-agent send --task task_initial "Start"` routes by task.
|
|
171
172
|
- `team-agent status` shows team, worker health, result-store counts, `session_id`, `captured_via`, and attribution confidence. `team-agent status --json` is compact and context-safe by default; use `team-agent status --detail --json` only for raw runtime-state diagnostics.
|
|
@@ -237,6 +238,8 @@ team_orchestrator.send_message(to="*", content="short broadcast")
|
|
|
237
238
|
team_orchestrator.report_result(summary="short completion", status="success", tests=[{"command":"command","status":"passed"}])
|
|
238
239
|
```
|
|
239
240
|
|
|
241
|
+
For typed orchestration traffic, both `send_message` and `report_result` accept `presentation={"sink":"leader|casefile|silent","class":"message|progress|stage_result|stage_pass|bounce|blocking|final_review|timeout","case_id":"optional-case"}`. If the object is present, `sink` and `class` are required and unknown values fail closed. `casefile` and `silent` are durable-only, not deletion. The fixed critical classes `stage_pass`, `bounce`, `blocking`, `final_review`, and `timeout` always appear on the leader screen even when another sink is requested. Routing uses the typed class, never words in the content or summary.
|
|
242
|
+
|
|
240
243
|
Do not pass `sender`, `task_id`, `requires_ack`, `schema_version`, or `agent_id` unless doing a low-level compatibility diagnostic. The MCP runtime fills those fields and keeps delivery metadata in runtime state and event logs. If provider env loses the worker id, MCP infers it from active task/message state and falls back to an explicit `unknown` sender instead of treating the worker as leader.
|
|
241
244
|
|
|
242
245
|
Message targets are team-scoped. Use `leader`, another teammate agent id, or `*` for all other team members. The runtime excludes the sender from `*` broadcasts and never scans unrelated terminal windows for recipients.
|