@team-agent/installer 0.5.3 → 0.5.4
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/diagnose.rs +191 -31
- package/crates/team-agent/src/cli/mod.rs +93 -34
- package/crates/team-agent/src/coordinator/tick.rs +13 -0
- package/crates/team-agent/src/lifecycle/launch.rs +14 -6
- package/crates/team-agent/src/lifecycle/restart/agent.rs +15 -1
- package/crates/team-agent/src/lifecycle/restart/common.rs +53 -2
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +108 -6
- package/crates/team-agent/src/lifecycle/restart/selection.rs +47 -18
- package/crates/team-agent/src/lifecycle/restart.rs +16 -6
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +287 -53
- package/crates/team-agent/src/lifecycle/tests/restart.rs +144 -5
- package/crates/team-agent/src/lifecycle/types.rs +1 -0
- package/crates/team-agent/src/provider/adapter.rs +103 -41
- package/crates/team-agent/src/provider/session/capture.rs +328 -66
- package/crates/team-agent/src/provider/session/resume.rs +30 -28
- package/crates/team-agent/src/provider/session_scan/common.rs +117 -1
- package/crates/team-agent/src/provider/session_scan/copilot.rs +1 -0
- package/crates/team-agent/src/provider/session_scan.rs +1 -0
- package/crates/team-agent/src/state/persist.rs +17 -0
- package/crates/team-agent/src/state/projection.rs +59 -4
- package/package.json +4 -4
|
@@ -95,9 +95,14 @@ pub enum ResumeRefusalReason {
|
|
|
95
95
|
SessionCaptureIncomplete,
|
|
96
96
|
/// State session_id differs from the provider's observed session
|
|
97
97
|
/// (T6 L5.5 drift). Caller should reconcile before resuming.
|
|
98
|
-
SessionDrift {
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
SessionDrift { expected: String, actual: String },
|
|
99
|
+
/// The persisted provider backing exists, but the transcript itself
|
|
100
|
+
/// declares a different Team Agent worker identity.
|
|
101
|
+
SessionIdentityMismatch {
|
|
102
|
+
expected_agent_id: String,
|
|
103
|
+
embedded_agent_id: String,
|
|
104
|
+
session_id: String,
|
|
105
|
+
rollout_path: Option<PathBuf>,
|
|
101
106
|
},
|
|
102
107
|
/// Catch-all for refusals the structured shape hasn't taxonomized
|
|
103
108
|
/// yet. Carries the legacy free-form string. This variant exists so
|
|
@@ -118,11 +123,10 @@ impl ResumeRefusalReason {
|
|
|
118
123
|
ResumeRefusalReason::SessionBackingStoreMissing { .. } => {
|
|
119
124
|
"session_backing_store_missing"
|
|
120
125
|
}
|
|
121
|
-
ResumeRefusalReason::ProviderResumeUnsupported { .. } =>
|
|
122
|
-
"provider_resume_unsupported"
|
|
123
|
-
}
|
|
126
|
+
ResumeRefusalReason::ProviderResumeUnsupported { .. } => "provider_resume_unsupported",
|
|
124
127
|
ResumeRefusalReason::SessionCaptureIncomplete => "session_capture_incomplete",
|
|
125
128
|
ResumeRefusalReason::SessionDrift { .. } => "session_drift",
|
|
129
|
+
ResumeRefusalReason::SessionIdentityMismatch { .. } => "session_identity_mismatch",
|
|
126
130
|
// For Other we still report the legacy wire so the existing
|
|
127
131
|
// `session_unresumable` JSON shape is preserved end-to-end.
|
|
128
132
|
ResumeRefusalReason::Other { .. } => "session_unresumable",
|
|
@@ -134,16 +138,20 @@ impl ResumeRefusalReason {
|
|
|
134
138
|
pub fn from_legacy(reason: &str) -> Self {
|
|
135
139
|
match reason {
|
|
136
140
|
"no_persisted_session_id" => ResumeRefusalReason::NoSessionId,
|
|
137
|
-
"session_backing_store_missing" => {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
}
|
|
141
|
+
"session_backing_store_missing" => ResumeRefusalReason::SessionBackingStoreMissing {
|
|
142
|
+
checked_paths: Vec::new(),
|
|
143
|
+
recovery_hint: None,
|
|
144
|
+
},
|
|
143
145
|
"provider_resume_unsupported" => ResumeRefusalReason::ProviderResumeUnsupported {
|
|
144
146
|
provider: String::new(),
|
|
145
147
|
},
|
|
146
148
|
"session_capture_incomplete" => ResumeRefusalReason::SessionCaptureIncomplete,
|
|
149
|
+
"session_identity_mismatch" => ResumeRefusalReason::SessionIdentityMismatch {
|
|
150
|
+
expected_agent_id: String::new(),
|
|
151
|
+
embedded_agent_id: String::new(),
|
|
152
|
+
session_id: String::new(),
|
|
153
|
+
rollout_path: None,
|
|
154
|
+
},
|
|
147
155
|
other => ResumeRefusalReason::Other {
|
|
148
156
|
legacy_reason: other.to_string(),
|
|
149
157
|
},
|
|
@@ -155,15 +163,11 @@ impl ResumeRefusalReason {
|
|
|
155
163
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
156
164
|
pub enum ResumePreflightOutcome {
|
|
157
165
|
/// Worker can resume from the named session.
|
|
158
|
-
Resume {
|
|
159
|
-
session_id: String,
|
|
160
|
-
},
|
|
166
|
+
Resume { session_id: String },
|
|
161
167
|
/// `--allow-fresh` was set; worker will start fresh.
|
|
162
168
|
FreshStart,
|
|
163
169
|
/// Resume refused. Caller MUST NOT proceed with teardown/spawn.
|
|
164
|
-
Refuse {
|
|
165
|
-
reason: ResumeRefusalReason,
|
|
166
|
-
},
|
|
170
|
+
Refuse { reason: ResumeRefusalReason },
|
|
167
171
|
}
|
|
168
172
|
|
|
169
173
|
/// Information about whether a provider backing file is present on disk.
|
|
@@ -239,9 +243,7 @@ impl ResumePreflight {
|
|
|
239
243
|
} else {
|
|
240
244
|
ResumePreflightOutcome::Refuse {
|
|
241
245
|
reason: ResumeRefusalReason::SessionBackingStoreMissing {
|
|
242
|
-
checked_paths: backing
|
|
243
|
-
.map(|b| b.paths.clone())
|
|
244
|
-
.unwrap_or_default(),
|
|
246
|
+
checked_paths: backing.map(|b| b.paths.clone()).unwrap_or_default(),
|
|
245
247
|
recovery_hint,
|
|
246
248
|
},
|
|
247
249
|
}
|
|
@@ -272,6 +274,7 @@ mod tests {
|
|
|
272
274
|
"session_backing_store_missing",
|
|
273
275
|
"provider_resume_unsupported",
|
|
274
276
|
"session_capture_incomplete",
|
|
277
|
+
"session_identity_mismatch",
|
|
275
278
|
] {
|
|
276
279
|
assert_eq!(ResumeRefusalReason::from_legacy(wire).wire(), wire);
|
|
277
280
|
}
|
|
@@ -383,10 +386,11 @@ mod tests {
|
|
|
383
386
|
);
|
|
384
387
|
match out {
|
|
385
388
|
ResumePreflightOutcome::Refuse {
|
|
386
|
-
reason:
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
389
|
+
reason:
|
|
390
|
+
ResumeRefusalReason::SessionBackingStoreMissing {
|
|
391
|
+
recovery_hint: Some(h),
|
|
392
|
+
..
|
|
393
|
+
},
|
|
390
394
|
} => {
|
|
391
395
|
assert_eq!(h, hint);
|
|
392
396
|
}
|
|
@@ -405,9 +409,7 @@ mod tests {
|
|
|
405
409
|
let out = ResumePreflight::check(Some("sess-x"), true, Some(&backing), "codex", false);
|
|
406
410
|
match out {
|
|
407
411
|
ResumePreflightOutcome::Refuse {
|
|
408
|
-
reason: ResumeRefusalReason::SessionBackingStoreMissing {
|
|
409
|
-
recovery_hint, ..
|
|
410
|
-
},
|
|
412
|
+
reason: ResumeRefusalReason::SessionBackingStoreMissing { recovery_hint, .. },
|
|
411
413
|
} => {
|
|
412
414
|
assert!(recovery_hint.is_none());
|
|
413
415
|
}
|
|
@@ -98,7 +98,15 @@ pub(super) fn parse_candidate_files(
|
|
|
98
98
|
} else {
|
|
99
99
|
Confidence::Low
|
|
100
100
|
};
|
|
101
|
-
let
|
|
101
|
+
let embedded_agent_id = embedded_team_agent_worker_id_from_text(&text);
|
|
102
|
+
if embedded_agent_id
|
|
103
|
+
.as_deref()
|
|
104
|
+
.is_some_and(|id| id != context.agent_id.as_str())
|
|
105
|
+
{
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
let positive_agent_id_match = candidate_text_has_team_agent_id(&text, context)
|
|
109
|
+
|| embedded_agent_id.as_deref() == Some(context.agent_id.as_str());
|
|
102
110
|
let agent_path_match = candidate_path_matches_agent_id(&path, context);
|
|
103
111
|
if matches!(provider, Provider::Claude | Provider::ClaudeCode)
|
|
104
112
|
&& super::claude::records_have_leader_marker(&records)
|
|
@@ -113,6 +121,7 @@ pub(super) fn parse_candidate_files(
|
|
|
113
121
|
attribution_confidence,
|
|
114
122
|
spawn_cwd: context.spawn_cwd.clone(),
|
|
115
123
|
},
|
|
124
|
+
embedded_agent_id,
|
|
116
125
|
positive_agent_id_match,
|
|
117
126
|
agent_path_match,
|
|
118
127
|
});
|
|
@@ -308,6 +317,28 @@ fn candidate_text_has_team_agent_id(text: &str, context: &CaptureSessionContext)
|
|
|
308
317
|
.any(|needle| text.contains(needle))
|
|
309
318
|
}
|
|
310
319
|
|
|
320
|
+
pub(crate) fn embedded_team_agent_worker_id_from_text(text: &str) -> Option<String> {
|
|
321
|
+
const PREFIX: &str = "You are Team Agent worker `";
|
|
322
|
+
let start = text.find(PREFIX)? + PREFIX.len();
|
|
323
|
+
let rest = &text[start..];
|
|
324
|
+
let end = rest.find('`')?;
|
|
325
|
+
let id = &rest[..end];
|
|
326
|
+
if id.is_empty()
|
|
327
|
+
|| !id
|
|
328
|
+
.chars()
|
|
329
|
+
.all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '_' | '-' | '.'))
|
|
330
|
+
{
|
|
331
|
+
return None;
|
|
332
|
+
}
|
|
333
|
+
Some(id.to_string())
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
pub(crate) fn rollout_path_embedded_team_agent_worker_id(path: &Path) -> Option<String> {
|
|
337
|
+
read_head_text(path, CAPTURE_HEAD_BYTES)
|
|
338
|
+
.ok()
|
|
339
|
+
.and_then(|text| embedded_team_agent_worker_id_from_text(&text))
|
|
340
|
+
}
|
|
341
|
+
|
|
311
342
|
fn candidate_path_matches_agent_id(path: &Path, context: &CaptureSessionContext) -> bool {
|
|
312
343
|
let id = context.agent_id.as_str();
|
|
313
344
|
if id.is_empty() {
|
|
@@ -348,3 +379,88 @@ fn path_is_under_team_runtime(path: &Path) -> bool {
|
|
|
348
379
|
path.components()
|
|
349
380
|
.any(|c| c.as_os_str() == std::ffi::OsStr::new(".team"))
|
|
350
381
|
}
|
|
382
|
+
|
|
383
|
+
#[cfg(test)]
|
|
384
|
+
mod tests {
|
|
385
|
+
use super::*;
|
|
386
|
+
|
|
387
|
+
fn temp_dir(name: &str) -> PathBuf {
|
|
388
|
+
static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
|
|
389
|
+
let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
|
390
|
+
let dir =
|
|
391
|
+
std::env::temp_dir().join(format!("ta-session-scan-{name}-{}-{n}", std::process::id()));
|
|
392
|
+
std::fs::create_dir_all(&dir).expect("create temp dir");
|
|
393
|
+
dir
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
fn write_codex_rollout(path: &Path, cwd: &Path, session_id: &str, embedded_agent_id: &str) {
|
|
397
|
+
let text = format!(
|
|
398
|
+
"{{\"session_meta\":{{\"payload\":{{\"id\":\"{session_id}\",\"cwd\":\"{}\"}}}}}}\n\
|
|
399
|
+
{{\"type\":\"turn_context\",\"payload\":{{}}}}\n\
|
|
400
|
+
{{\"type\":\"response_item\",\"payload\":{{\"content\":[{{\"type\":\"input_text\",\"text\":\"You are Team Agent worker `{embedded_agent_id}` with role `fixture`.\"}}]}}}}\n",
|
|
401
|
+
cwd.to_string_lossy()
|
|
402
|
+
);
|
|
403
|
+
std::fs::write(path, text).expect("write codex rollout");
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
#[test]
|
|
407
|
+
fn codex_prompt_worker_identity_is_a_positive_match() {
|
|
408
|
+
let dir = temp_dir("positive");
|
|
409
|
+
let rollout = dir.join("rollout-frontend.jsonl");
|
|
410
|
+
write_codex_rollout(&rollout, &dir, "sess-frontend", "frontend");
|
|
411
|
+
let context = CaptureSessionContext {
|
|
412
|
+
agent_id: "frontend".to_string(),
|
|
413
|
+
spawn_cwd: dir.clone(),
|
|
414
|
+
pane_id: None,
|
|
415
|
+
pane_pid: None,
|
|
416
|
+
spawned_at: None,
|
|
417
|
+
expected_session_id: None,
|
|
418
|
+
provider_projects_root: None,
|
|
419
|
+
};
|
|
420
|
+
let candidates = parse_candidate_files(
|
|
421
|
+
Provider::Codex,
|
|
422
|
+
&context,
|
|
423
|
+
vec![SessionCandidate {
|
|
424
|
+
path: rollout.clone(),
|
|
425
|
+
requires_cwd_match: false,
|
|
426
|
+
}],
|
|
427
|
+
);
|
|
428
|
+
assert_eq!(candidates.len(), 1);
|
|
429
|
+
assert!(
|
|
430
|
+
candidates[0].positive_agent_id_match,
|
|
431
|
+
"Codex transcript prompt identity must be treated as a positive worker id source"
|
|
432
|
+
);
|
|
433
|
+
let _ = std::fs::remove_file(&rollout);
|
|
434
|
+
let _ = std::fs::remove_dir_all(&dir);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
#[test]
|
|
438
|
+
fn codex_prompt_worker_identity_mismatch_is_rejected_for_that_agent() {
|
|
439
|
+
let dir = temp_dir("mismatch");
|
|
440
|
+
let rollout = dir.join("rollout-ios-dev.jsonl");
|
|
441
|
+
write_codex_rollout(&rollout, &dir, "sess-ios-dev", "ios-dev");
|
|
442
|
+
let context = CaptureSessionContext {
|
|
443
|
+
agent_id: "frontend".to_string(),
|
|
444
|
+
spawn_cwd: dir.clone(),
|
|
445
|
+
pane_id: None,
|
|
446
|
+
pane_pid: None,
|
|
447
|
+
spawned_at: None,
|
|
448
|
+
expected_session_id: None,
|
|
449
|
+
provider_projects_root: None,
|
|
450
|
+
};
|
|
451
|
+
let candidates = parse_candidate_files(
|
|
452
|
+
Provider::Codex,
|
|
453
|
+
&context,
|
|
454
|
+
vec![SessionCandidate {
|
|
455
|
+
path: rollout.clone(),
|
|
456
|
+
requires_cwd_match: false,
|
|
457
|
+
}],
|
|
458
|
+
);
|
|
459
|
+
assert!(
|
|
460
|
+
candidates.is_empty(),
|
|
461
|
+
"state agent=frontend must not accept a Codex rollout whose prompt says ios-dev"
|
|
462
|
+
);
|
|
463
|
+
let _ = std::fs::remove_file(&rollout);
|
|
464
|
+
let _ = std::fs::remove_dir_all(&dir);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
@@ -49,6 +49,7 @@ pub struct CaptureSessionContext {
|
|
|
49
49
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
50
50
|
pub struct CapturedSessionCandidate {
|
|
51
51
|
pub captured: CapturedSession,
|
|
52
|
+
pub embedded_agent_id: Option<String>,
|
|
52
53
|
pub positive_agent_id_match: bool,
|
|
53
54
|
pub agent_path_match: bool,
|
|
54
55
|
}
|
|
@@ -234,6 +234,23 @@ pub(crate) fn save_runtime_state_with_lifecycle_topology_authority(
|
|
|
234
234
|
save_runtime_state_with_merge_options(workspace, state, &[], None, &[], agent_ids)
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
pub(crate) fn save_runtime_state_with_lifecycle_topology_authority_and_capture_backfill_skip(
|
|
238
|
+
workspace: &Path,
|
|
239
|
+
state: &Value,
|
|
240
|
+
skip_capture_backfill_team_key: &str,
|
|
241
|
+
skip_capture_backfill_agent_ids: &[&str],
|
|
242
|
+
topology_agent_ids: &[&str],
|
|
243
|
+
) -> Result<(), StateError> {
|
|
244
|
+
save_runtime_state_with_merge_options(
|
|
245
|
+
workspace,
|
|
246
|
+
state,
|
|
247
|
+
&[],
|
|
248
|
+
Some(skip_capture_backfill_team_key),
|
|
249
|
+
skip_capture_backfill_agent_ids,
|
|
250
|
+
topology_agent_ids,
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
|
|
237
254
|
pub(crate) fn save_runtime_state_with_deleted_agents(
|
|
238
255
|
workspace: &Path,
|
|
239
256
|
state: &Value,
|
|
@@ -17,6 +17,7 @@ use super::StateError;
|
|
|
17
17
|
use crate::state::persist::{
|
|
18
18
|
load_runtime_state, save_runtime_state_with_deleted_agents,
|
|
19
19
|
save_runtime_state_with_lifecycle_topology_authority,
|
|
20
|
+
save_runtime_state_with_lifecycle_topology_authority_and_capture_backfill_skip,
|
|
20
21
|
save_runtime_state_with_team_tombstone_lifecycle_topology_authority,
|
|
21
22
|
save_runtime_state_with_team_tombstoned_agents,
|
|
22
23
|
};
|
|
@@ -595,7 +596,7 @@ pub(crate) fn save_team_scoped_state_with_tombstone_lifecycle_topology_authority
|
|
|
595
596
|
team_state: &Value,
|
|
596
597
|
agent_ids: &[&str],
|
|
597
598
|
) -> Result<(), StateError> {
|
|
598
|
-
save_team_scoped_state_with_merge_options(workspace, team_state, &[], agent_ids, agent_ids)
|
|
599
|
+
save_team_scoped_state_with_merge_options(workspace, team_state, &[], agent_ids, &[], agent_ids)
|
|
599
600
|
}
|
|
600
601
|
|
|
601
602
|
pub(crate) fn save_team_scoped_state_with_lifecycle_topology_authority(
|
|
@@ -603,7 +604,23 @@ pub(crate) fn save_team_scoped_state_with_lifecycle_topology_authority(
|
|
|
603
604
|
team_state: &Value,
|
|
604
605
|
agent_ids: &[&str],
|
|
605
606
|
) -> Result<(), StateError> {
|
|
606
|
-
save_team_scoped_state_with_merge_options(workspace, team_state, &[], &[], agent_ids)
|
|
607
|
+
save_team_scoped_state_with_merge_options(workspace, team_state, &[], &[], &[], agent_ids)
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
pub(crate) fn save_team_scoped_state_with_lifecycle_topology_authority_and_capture_backfill_skip(
|
|
611
|
+
workspace: &Path,
|
|
612
|
+
team_state: &Value,
|
|
613
|
+
skip_capture_backfill_agent_ids: &[&str],
|
|
614
|
+
topology_agent_ids: &[&str],
|
|
615
|
+
) -> Result<(), StateError> {
|
|
616
|
+
save_team_scoped_state_with_merge_options(
|
|
617
|
+
workspace,
|
|
618
|
+
team_state,
|
|
619
|
+
&[],
|
|
620
|
+
&[],
|
|
621
|
+
skip_capture_backfill_agent_ids,
|
|
622
|
+
topology_agent_ids,
|
|
623
|
+
)
|
|
607
624
|
}
|
|
608
625
|
|
|
609
626
|
fn save_team_scoped_state_with_merge_exceptions(
|
|
@@ -618,6 +635,7 @@ fn save_team_scoped_state_with_merge_exceptions(
|
|
|
618
635
|
deleted_agent_ids,
|
|
619
636
|
tombstoned_agent_ids,
|
|
620
637
|
&[],
|
|
638
|
+
&[],
|
|
621
639
|
)
|
|
622
640
|
}
|
|
623
641
|
|
|
@@ -626,6 +644,7 @@ fn save_team_scoped_state_with_merge_options(
|
|
|
626
644
|
team_state: &Value,
|
|
627
645
|
deleted_agent_ids: &[&str],
|
|
628
646
|
tombstoned_agent_ids: &[&str],
|
|
647
|
+
skip_capture_backfill_agent_ids: &[&str],
|
|
629
648
|
topology_agent_ids: &[&str],
|
|
630
649
|
) -> Result<(), StateError> {
|
|
631
650
|
let target_key = team_state_key(team_state);
|
|
@@ -669,12 +688,30 @@ fn save_team_scoped_state_with_merge_options(
|
|
|
669
688
|
topology_agent_ids,
|
|
670
689
|
);
|
|
671
690
|
}
|
|
691
|
+
if !skip_capture_backfill_agent_ids.is_empty() {
|
|
692
|
+
return save_runtime_state_with_lifecycle_topology_authority_and_capture_backfill_skip(
|
|
693
|
+
workspace,
|
|
694
|
+
&merged,
|
|
695
|
+
&target_key,
|
|
696
|
+
skip_capture_backfill_agent_ids,
|
|
697
|
+
topology_agent_ids,
|
|
698
|
+
);
|
|
699
|
+
}
|
|
672
700
|
return save_runtime_state_with_lifecycle_topology_authority(
|
|
673
701
|
workspace,
|
|
674
702
|
&merged,
|
|
675
703
|
topology_agent_ids,
|
|
676
704
|
);
|
|
677
705
|
}
|
|
706
|
+
if !skip_capture_backfill_agent_ids.is_empty() {
|
|
707
|
+
return save_runtime_state_with_lifecycle_topology_authority_and_capture_backfill_skip(
|
|
708
|
+
workspace,
|
|
709
|
+
&merged,
|
|
710
|
+
&target_key,
|
|
711
|
+
skip_capture_backfill_agent_ids,
|
|
712
|
+
&[],
|
|
713
|
+
);
|
|
714
|
+
}
|
|
678
715
|
if tombstoned_agent_ids.is_empty() {
|
|
679
716
|
return save_runtime_state_with_deleted_agents(workspace, &merged, deleted_agent_ids);
|
|
680
717
|
}
|
|
@@ -709,10 +746,28 @@ fn save_team_scoped_state_with_merge_options(
|
|
|
709
746
|
}
|
|
710
747
|
if tombstoned_agent_ids.is_empty() {
|
|
711
748
|
if !topology_agent_ids.is_empty() {
|
|
712
|
-
|
|
749
|
+
if !skip_capture_backfill_agent_ids.is_empty() {
|
|
750
|
+
save_runtime_state_with_lifecycle_topology_authority_and_capture_backfill_skip(
|
|
751
|
+
workspace,
|
|
752
|
+
&Value::Object(merged),
|
|
753
|
+
&target_key,
|
|
754
|
+
skip_capture_backfill_agent_ids,
|
|
755
|
+
topology_agent_ids,
|
|
756
|
+
)
|
|
757
|
+
} else {
|
|
758
|
+
save_runtime_state_with_lifecycle_topology_authority(
|
|
759
|
+
workspace,
|
|
760
|
+
&Value::Object(merged),
|
|
761
|
+
topology_agent_ids,
|
|
762
|
+
)
|
|
763
|
+
}
|
|
764
|
+
} else if !skip_capture_backfill_agent_ids.is_empty() {
|
|
765
|
+
save_runtime_state_with_lifecycle_topology_authority_and_capture_backfill_skip(
|
|
713
766
|
workspace,
|
|
714
767
|
&Value::Object(merged),
|
|
715
|
-
|
|
768
|
+
&target_key,
|
|
769
|
+
skip_capture_backfill_agent_ids,
|
|
770
|
+
&[],
|
|
716
771
|
)
|
|
717
772
|
} else {
|
|
718
773
|
save_runtime_state_with_deleted_agents(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-agent/installer",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4",
|
|
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.4",
|
|
24
|
+
"@team-agent/cli-darwin-x64": "0.5.4",
|
|
25
|
+
"@team-agent/cli-linux-x64": "0.5.4"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node npm/bincheck.mjs",
|