@team-agent/installer 0.5.59 → 0.5.61
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 +44 -1
- package/crates/team-agent/src/cli/diagnose.rs +183 -0
- package/crates/team-agent/src/cli/emit.rs +63 -14
- package/crates/team-agent/src/cli/leader.rs +8 -1
- package/crates/team-agent/src/cli/mod.rs +263 -5
- package/crates/team-agent/src/cli/named_address.rs +18 -1
- package/crates/team-agent/src/cli/send/presentation.rs +2 -0
- package/crates/team-agent/src/cli/spec.rs +4 -1
- package/crates/team-agent/src/cli/status_port/store.rs +7 -3
- package/crates/team-agent/src/cli/tests/named_address.rs +65 -0
- package/crates/team-agent/src/cli/types.rs +33 -0
- package/crates/team-agent/src/communication_mode/mod.rs +53 -0
- package/crates/team-agent/src/compiler/tests.rs +5 -5
- package/crates/team-agent/src/compiler.rs +53 -1
- package/crates/team-agent/src/coordinator/tick.rs +21 -8
- package/crates/team-agent/src/db/message_store.rs +45 -1
- package/crates/team-agent/src/fake_worker.rs +21 -1
- package/crates/team-agent/src/leader/start.rs +732 -94
- package/crates/team-agent/src/leader/tests/identity.rs +64 -57
- package/crates/team-agent/src/leader/tests/identity_session_names.rs +42 -0
- package/crates/team-agent/src/lib.rs +4 -0
- package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +58 -0
- package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +57 -51
- package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +148 -8
- package/crates/team-agent/src/lifecycle/launch/fork_state.rs +5 -0
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +1 -1
- package/crates/team-agent/src/lifecycle/launch.rs +1 -1
- package/crates/team-agent/src/lifecycle/restart/agent.rs +1 -1
- package/crates/team-agent/src/lifecycle/restart/common.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +35 -1
- package/crates/team-agent/src/lifecycle/types.rs +1 -0
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +29 -11
- package/crates/team-agent/src/mcp_server/normalize.rs +1 -0
- package/crates/team-agent/src/mcp_server/tests/send.rs +26 -0
- package/crates/team-agent/src/mcp_server/tests/wire.rs +1 -1
- package/crates/team-agent/src/mcp_server/tools.rs +100 -66
- package/crates/team-agent/src/mcp_server/types.rs +5 -0
- package/crates/team-agent/src/mcp_server/wire.rs +27 -21
- package/crates/team-agent/src/messaging/delivery.rs +105 -38
- package/crates/team-agent/src/messaging/leader_channel.rs +33 -10
- package/crates/team-agent/src/messaging/leader_receiver.rs +40 -23
- package/crates/team-agent/src/messaging/presentation.rs +109 -0
- package/crates/team-agent/src/messaging/results.rs +165 -17
- package/crates/team-agent/src/messaging/send.rs +94 -81
- package/crates/team-agent/src/messaging/tests/leader_channel.rs +6 -6
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +187 -0
- package/crates/team-agent/src/messaging/tests/runtime.rs +13 -6
- package/crates/team-agent/src/messaging/types.rs +5 -0
- package/crates/team-agent/src/messaging/watchers.rs +5 -0
- package/crates/team-agent/src/model/mod.rs +1 -0
- package/crates/team-agent/src/model/pane_authority_refusal.rs +358 -0
- package/crates/team-agent/src/model/spec.rs +16 -0
- package/crates/team-agent/src/provider/adapter.rs +57 -0
- package/crates/team-agent/src/provider/adapters/claude_fork.rs +10 -2
- package/crates/team-agent/src/provider/session/capture.rs +69 -34
- package/crates/team-agent/src/provider/session/context_fork/codex.rs +386 -9
- package/crates/team-agent/src/provider/session/context_fork/outcome.rs +3 -14
- package/crates/team-agent/src/provider/session/context_fork.rs +7 -2
- package/crates/team-agent/src/provider/session/mod.rs +3 -2
- package/crates/team-agent/src/provider/session_scan/claude.rs +92 -3
- package/crates/team-agent/src/provider/session_scan/codex.rs +28 -0
- package/crates/team-agent/src/provider/session_scan/common.rs +34 -8
- package/crates/team-agent/src/provider/session_scan.rs +8 -0
- package/crates/team-agent/src/topology.rs +3 -0
- package/package.json +4 -4
- package/skills/team-agent/command-coverage.json +379 -0
|
@@ -12,6 +12,11 @@ use super::{AuthMode, Provider};
|
|
|
12
12
|
|
|
13
13
|
pub use crate::provider::session_scan::{CaptureSessionContext, CapturedSessionCandidate};
|
|
14
14
|
|
|
15
|
+
pub trait ForkBackingMaterialization {
|
|
16
|
+
fn path(&self) -> &Path;
|
|
17
|
+
fn handoff(&mut self);
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
// ===========================================================================
|
|
16
21
|
// TRAIT: ProviderAdapter (method SIGNATURES only — 无 body)
|
|
17
22
|
// ===========================================================================
|
|
@@ -187,6 +192,24 @@ pub trait ProviderAdapter {
|
|
|
187
192
|
.map(CommandPlan::argv_only)
|
|
188
193
|
}
|
|
189
194
|
|
|
195
|
+
fn materialize_fork_backing(
|
|
196
|
+
&self,
|
|
197
|
+
source_path: &Path,
|
|
198
|
+
source_session_id: &SessionId,
|
|
199
|
+
source_agent_id: &str,
|
|
200
|
+
target_agent_id: &str,
|
|
201
|
+
plan: &mut CommandPlan,
|
|
202
|
+
) -> Result<Option<Box<dyn ForkBackingMaterialization>>, ProviderError> {
|
|
203
|
+
let _ = (
|
|
204
|
+
source_path,
|
|
205
|
+
source_session_id,
|
|
206
|
+
source_agent_id,
|
|
207
|
+
target_agent_id,
|
|
208
|
+
plan,
|
|
209
|
+
);
|
|
210
|
+
Ok(None)
|
|
211
|
+
}
|
|
212
|
+
|
|
190
213
|
/// 计算本 provider 该用的 MCP server 配置(`adapter.py` mcp_config;claude
|
|
191
214
|
/// compatible_api 走 `ensure_compatible_claude_mcp_config`)。
|
|
192
215
|
fn mcp_config(&self, auth_mode: AuthMode) -> Result<McpConfig, ProviderError>;
|
|
@@ -549,6 +572,29 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
549
572
|
}
|
|
550
573
|
}
|
|
551
574
|
|
|
575
|
+
fn materialize_fork_backing(
|
|
576
|
+
&self,
|
|
577
|
+
source_path: &Path,
|
|
578
|
+
source_session_id: &SessionId,
|
|
579
|
+
source_agent_id: &str,
|
|
580
|
+
target_agent_id: &str,
|
|
581
|
+
plan: &mut CommandPlan,
|
|
582
|
+
) -> Result<Option<Box<dyn ForkBackingMaterialization>>, ProviderError> {
|
|
583
|
+
match self.provider {
|
|
584
|
+
Provider::Codex => crate::provider::session::materialize_codex_fork(
|
|
585
|
+
source_path,
|
|
586
|
+
source_session_id,
|
|
587
|
+
source_agent_id,
|
|
588
|
+
target_agent_id,
|
|
589
|
+
plan,
|
|
590
|
+
)
|
|
591
|
+
.map(|materialized| {
|
|
592
|
+
Some(Box::new(materialized) as Box<dyn ForkBackingMaterialization>)
|
|
593
|
+
}),
|
|
594
|
+
_ => Ok(None),
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
552
598
|
fn capture_session_id(
|
|
553
599
|
&self,
|
|
554
600
|
agent_id: &str,
|
|
@@ -1198,3 +1244,14 @@ pub(crate) fn next_session_token() -> String {
|
|
|
1198
1244
|
bytes[15],
|
|
1199
1245
|
)
|
|
1200
1246
|
}
|
|
1247
|
+
|
|
1248
|
+
#[cfg(test)]
|
|
1249
|
+
mod fork_materialization_source_guard {
|
|
1250
|
+
#[test]
|
|
1251
|
+
fn lifecycle_uses_provider_neutral_fork_materialization_dispatch() {
|
|
1252
|
+
let source = include_str!("../lifecycle/launch/fork_agent.rs");
|
|
1253
|
+
assert!(source.contains(".materialize_fork_backing("));
|
|
1254
|
+
assert!(!source.contains("materialize_codex_fork"));
|
|
1255
|
+
assert!(!source.contains("codex_fork"));
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
@@ -30,6 +30,8 @@ pub(crate) fn materialize_claude_fork(
|
|
|
30
30
|
source_path: &Path,
|
|
31
31
|
source_session_id: &SessionId,
|
|
32
32
|
target_session_id: &SessionId,
|
|
33
|
+
source_agent_id: &str,
|
|
34
|
+
target_agent_id: &str,
|
|
33
35
|
) -> Result<ClaudeForkMaterialization, ProviderError> {
|
|
34
36
|
let parent = source_path.parent().ok_or_else(|| {
|
|
35
37
|
ProviderError::Io(format!(
|
|
@@ -46,7 +48,12 @@ pub(crate) fn materialize_claude_fork(
|
|
|
46
48
|
}
|
|
47
49
|
let source = std::fs::read_to_string(source_path)
|
|
48
50
|
.map_err(|error| ProviderError::Io(error.to_string()))?;
|
|
49
|
-
let rewritten = source
|
|
51
|
+
let rewritten = source
|
|
52
|
+
.replace(source_session_id.as_str(), target_session_id.as_str())
|
|
53
|
+
.replace(
|
|
54
|
+
&format!("You are Team Agent worker `{source_agent_id}`"),
|
|
55
|
+
&format!("You are Team Agent worker `{target_agent_id}`"),
|
|
56
|
+
);
|
|
50
57
|
validate_jsonl(&rewritten)?;
|
|
51
58
|
let temp = parent.join(format!(
|
|
52
59
|
".{}.tmp-{}",
|
|
@@ -108,7 +115,8 @@ mod tests {
|
|
|
108
115
|
std::fs::write(&source_path, &source).unwrap();
|
|
109
116
|
|
|
110
117
|
let mut materialized =
|
|
111
|
-
materialize_claude_fork(&source_path, &source_id, &target_id)
|
|
118
|
+
materialize_claude_fork(&source_path, &source_id, &target_id, "source", "target")
|
|
119
|
+
.unwrap();
|
|
112
120
|
let target_path = dir.join(format!("{}.jsonl", target_id.as_str()));
|
|
113
121
|
let target = std::fs::read_to_string(&target_path).unwrap();
|
|
114
122
|
assert!(!target.contains(source_id.as_str()));
|
|
@@ -29,6 +29,7 @@ pub struct CapturePassReport {
|
|
|
29
29
|
/// (coordinator tick) emits a throttled
|
|
30
30
|
/// `provider.session.transcript_missing` event for each entry.
|
|
31
31
|
pub transcript_missing: Vec<TranscriptMissing>,
|
|
32
|
+
pub(crate) context_forks: Vec<crate::lifecycle::launch::ContextForkFinalized>,
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
@@ -283,12 +284,22 @@ where
|
|
|
283
284
|
.zip(candidate.captured.session_id.as_ref())
|
|
284
285
|
.is_some_and(|(expected, captured)| expected.as_str() != captured.as_str());
|
|
285
286
|
if mismatch {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
287
|
+
let ambiguity_changed = agent_obj
|
|
288
|
+
.get("attribution_ambiguous")
|
|
289
|
+
.and_then(Value::as_bool)
|
|
290
|
+
!= Some(true);
|
|
291
|
+
if !finalize_ambiguous || ambiguity_changed {
|
|
292
|
+
report.ambiguous.push(AmbiguousSessionCapture {
|
|
293
|
+
agent_id: item.agent_id.clone(),
|
|
294
|
+
spawn_cwd: item.context.spawn_cwd.to_string_lossy().to_string(),
|
|
295
|
+
});
|
|
296
|
+
}
|
|
290
297
|
if finalize_ambiguous {
|
|
291
|
-
|
|
298
|
+
if ambiguity_changed {
|
|
299
|
+
agent_obj
|
|
300
|
+
.insert("attribution_ambiguous".to_string(), serde_json::json!(true));
|
|
301
|
+
report.changed = true;
|
|
302
|
+
}
|
|
292
303
|
// 0.4.6 tuple-atomic contract (audit:93): do NOT write
|
|
293
304
|
// `captured_at` for ambiguity diagnostics. `captured_at`
|
|
294
305
|
// is part of the authoritative tuple and may only be
|
|
@@ -298,7 +309,6 @@ where
|
|
|
298
309
|
// "looks captured" rows. Ambiguity diagnostics live in
|
|
299
310
|
// the `attribution_ambiguous` flag + events; persist
|
|
300
311
|
// event log for the timestamp.
|
|
301
|
-
report.changed = true;
|
|
302
312
|
}
|
|
303
313
|
continue;
|
|
304
314
|
}
|
|
@@ -306,26 +316,39 @@ where
|
|
|
306
316
|
// partial candidates (no session_id or no rollout_path). When
|
|
307
317
|
// it returns false, the row stays unchanged; capture re-runs
|
|
308
318
|
// on the next coordinator tick.
|
|
309
|
-
if apply_captured_session(agent_obj, &candidate.captured) {
|
|
319
|
+
if let Some(applied) = apply_captured_session(agent_obj, &candidate.captured) {
|
|
310
320
|
report.changed = true;
|
|
311
321
|
report.assigned.push(item.agent_id);
|
|
322
|
+
if let AppliedCapture::ContextFork(context_fork) = applied {
|
|
323
|
+
report.context_forks.push(context_fork);
|
|
324
|
+
}
|
|
312
325
|
}
|
|
313
326
|
continue;
|
|
314
327
|
}
|
|
315
328
|
if ambiguous_ids.contains(&item.agent_id) {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
329
|
+
let ambiguity_changed = agent_obj
|
|
330
|
+
.get("attribution_ambiguous")
|
|
331
|
+
.and_then(Value::as_bool)
|
|
332
|
+
!= Some(true)
|
|
333
|
+
|| agent_obj.get("capture_state").and_then(Value::as_str)
|
|
334
|
+
!= Some("attribution_ambiguous");
|
|
335
|
+
if !finalize_ambiguous || ambiguity_changed {
|
|
336
|
+
report.ambiguous.push(AmbiguousSessionCapture {
|
|
337
|
+
agent_id: item.agent_id.clone(),
|
|
338
|
+
spawn_cwd: item.context.spawn_cwd.to_string_lossy().to_string(),
|
|
339
|
+
});
|
|
340
|
+
}
|
|
320
341
|
if finalize_ambiguous {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
342
|
+
if ambiguity_changed {
|
|
343
|
+
agent_obj.insert("attribution_ambiguous".to_string(), serde_json::json!(true));
|
|
344
|
+
agent_obj.insert(
|
|
345
|
+
"capture_state".to_string(),
|
|
346
|
+
serde_json::json!("attribution_ambiguous"),
|
|
347
|
+
);
|
|
348
|
+
// 0.4.6 tuple-atomic contract: see comment above. Removed
|
|
349
|
+
// `captured_at` ambiguity write.
|
|
350
|
+
report.changed = true;
|
|
351
|
+
}
|
|
329
352
|
}
|
|
330
353
|
continue;
|
|
331
354
|
}
|
|
@@ -662,6 +685,9 @@ fn pending_session_capture<F>(
|
|
|
662
685
|
where
|
|
663
686
|
F: FnMut(Provider) -> Box<dyn ProviderAdapter>,
|
|
664
687
|
{
|
|
688
|
+
if agent.get("capture_state").and_then(Value::as_str) == Some("attribution_ambiguous") {
|
|
689
|
+
return None;
|
|
690
|
+
}
|
|
665
691
|
let provider = incomplete_resumable_provider(agent, adapter_for)?;
|
|
666
692
|
let spawn_cwd = agent
|
|
667
693
|
.get("spawn_cwd")
|
|
@@ -676,6 +702,17 @@ where
|
|
|
676
702
|
.and_then(Value::as_str)
|
|
677
703
|
.filter(|value| !value.is_empty())
|
|
678
704
|
.map(str::to_string);
|
|
705
|
+
let expected_session_id = agent
|
|
706
|
+
.get("_pending_session_id")
|
|
707
|
+
.and_then(Value::as_str)
|
|
708
|
+
.filter(|value| !value.is_empty())
|
|
709
|
+
.map(SessionId::new);
|
|
710
|
+
if matches!(provider, Provider::Codex)
|
|
711
|
+
&& agent.get("capture_state").and_then(Value::as_str) == Some("pending_context_fork")
|
|
712
|
+
&& expected_session_id.is_none()
|
|
713
|
+
{
|
|
714
|
+
return None;
|
|
715
|
+
}
|
|
679
716
|
Some(PendingSessionCapture {
|
|
680
717
|
agent_id: agent_id.to_string(),
|
|
681
718
|
provider,
|
|
@@ -699,15 +736,7 @@ where
|
|
|
699
736
|
.and_then(Value::as_u64)
|
|
700
737
|
.and_then(|pid| u32::try_from(pid).ok()),
|
|
701
738
|
spawned_at,
|
|
702
|
-
expected_session_id
|
|
703
|
-
None
|
|
704
|
-
} else {
|
|
705
|
-
agent
|
|
706
|
-
.get("_pending_session_id")
|
|
707
|
-
.and_then(Value::as_str)
|
|
708
|
-
.filter(|value| !value.is_empty())
|
|
709
|
-
.map(SessionId::new)
|
|
710
|
-
},
|
|
739
|
+
expected_session_id,
|
|
711
740
|
provider_projects_root: agent
|
|
712
741
|
.get("claude_projects_root")
|
|
713
742
|
.and_then(Value::as_str)
|
|
@@ -1144,6 +1173,11 @@ fn candidate_key(owner: &PendingSessionCapture, candidate: &CapturedSessionCandi
|
|
|
1144
1173
|
.join("|")
|
|
1145
1174
|
}
|
|
1146
1175
|
|
|
1176
|
+
enum AppliedCapture {
|
|
1177
|
+
Session,
|
|
1178
|
+
ContextFork(crate::lifecycle::launch::ContextForkFinalized),
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1147
1181
|
/// 0.4.6 tuple-atomic contract (audit §Capture 修改清单, line 111): writes
|
|
1148
1182
|
/// the authoritative session tuple if and only if the candidate carries
|
|
1149
1183
|
/// BOTH `session_id` and `rollout_path`. A partial candidate (e.g.
|
|
@@ -1154,15 +1188,16 @@ fn candidate_key(owner: &PendingSessionCapture, candidate: &CapturedSessionCandi
|
|
|
1154
1188
|
fn apply_captured_session(
|
|
1155
1189
|
agent_obj: &mut serde_json::Map<String, Value>,
|
|
1156
1190
|
captured: &CapturedSession,
|
|
1157
|
-
) ->
|
|
1191
|
+
) -> Option<AppliedCapture> {
|
|
1158
1192
|
if agent_obj.get("capture_state").and_then(Value::as_str) == Some("pending_context_fork") {
|
|
1159
|
-
return crate::lifecycle::finalize_pending_fork_capture(agent_obj, captured)
|
|
1193
|
+
return crate::lifecycle::finalize_pending_fork_capture(agent_obj, captured)
|
|
1194
|
+
.map(AppliedCapture::ContextFork);
|
|
1160
1195
|
}
|
|
1161
1196
|
let Some(session_id) = captured.session_id.as_ref() else {
|
|
1162
|
-
return
|
|
1197
|
+
return None;
|
|
1163
1198
|
};
|
|
1164
1199
|
let Some(rollout_path) = captured.rollout_path.as_ref() else {
|
|
1165
|
-
return
|
|
1200
|
+
return None;
|
|
1166
1201
|
};
|
|
1167
1202
|
agent_obj.insert(
|
|
1168
1203
|
"session_id".to_string(),
|
|
@@ -1192,7 +1227,7 @@ fn apply_captured_session(
|
|
|
1192
1227
|
// diagnose/status observability.
|
|
1193
1228
|
agent_obj.remove("_pending_session_id");
|
|
1194
1229
|
agent_obj.insert("capture_state".to_string(), serde_json::json!("captured"));
|
|
1195
|
-
|
|
1230
|
+
Some(AppliedCapture::Session)
|
|
1196
1231
|
}
|
|
1197
1232
|
|
|
1198
1233
|
fn claimed_provider_session_keys(
|
|
@@ -2621,7 +2656,7 @@ mod u1_tests {
|
|
|
2621
2656
|
spawn_cwd: PathBuf::from("/tmp/cwd"),
|
|
2622
2657
|
};
|
|
2623
2658
|
let written = super::apply_captured_session(&mut agent, &captured);
|
|
2624
|
-
assert!(written, "apply must
|
|
2659
|
+
assert!(written.is_some(), "apply must accept valid captured");
|
|
2625
2660
|
assert!(
|
|
2626
2661
|
agent.get("_pending_session_id").is_none(),
|
|
2627
2662
|
"S1-CAPTURE-001: _pending_session_id must be removed after capture \
|