@team-agent/installer 0.3.22 → 0.3.24
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/leader/lease.rs +34 -13
- package/crates/team-agent/src/leader/tests/lease_api.rs +79 -0
- package/crates/team-agent/src/lifecycle/helpers.rs +11 -3
- package/crates/team-agent/src/lifecycle/launch.rs +2 -16
- package/crates/team-agent/src/lifecycle/restart/common.rs +103 -0
- package/crates/team-agent/src/messaging/delivery.rs +74 -27
- package/crates/team-agent/src/messaging/tests/mod.rs +1 -0
- package/crates/team-agent/src/messaging/tests/wave2.rs +689 -0
- package/crates/team-agent/src/state/projection.rs +5 -0
- package/crates/team-agent/src/tmux_backend/tests.rs +46 -0
- package/crates/team-agent/src/tmux_backend.rs +17 -5
- package/crates/team-agent/src/transport/test_support.rs +79 -6
- package/package.json +4 -4
- package/skills/team-agent/SKILL.md +2 -1
- package/skills/team-agent/references/team-in-team.md +127 -0
|
@@ -298,6 +298,11 @@ pub fn project_top_level_view(state: &Value, team_key: &str) -> Value {
|
|
|
298
298
|
team_entry_from_state(state, team_key).and_then(Value::as_object).cloned().unwrap_or_default();
|
|
299
299
|
let mut p = entry_obj.clone(); // projection = deepcopy(entry)
|
|
300
300
|
// setdefault session_name/team_dir ← entry.get(...)(缺则插,值可为 null)。
|
|
301
|
+
// **0.3.24 excision** (U1-A real-machine RED v2): the ff38ab9 root-state
|
|
302
|
+
// `or_else` fallback was reverted along with the wave-2 U1-A drift fallback,
|
|
303
|
+
// since this projection change had no other consumer. v0.3.25 will re-introduce
|
|
304
|
+
// this together with the writer-shape + rediscover-writer fix — see
|
|
305
|
+
// `.team/artifacts/u1-a-realmachine-v2-fix-or-excise.md`.
|
|
301
306
|
if !p.contains_key("session_name") {
|
|
302
307
|
p.insert("session_name".to_string(), entry_obj.get("session_name").cloned().unwrap_or(Value::Null));
|
|
303
308
|
}
|
|
@@ -526,6 +526,52 @@ CaptureMissingToken, not the static CaptureContainsToken false-positive"
|
|
|
526
526
|
);
|
|
527
527
|
}
|
|
528
528
|
|
|
529
|
+
#[test]
|
|
530
|
+
fn inject_waits_for_token_visibility_before_enter() {
|
|
531
|
+
let text = "hello [team-agent-token:e31]".to_string();
|
|
532
|
+
let marker_visible = format!("{text}\n");
|
|
533
|
+
let (be, rec) = backend_with(
|
|
534
|
+
MockResp::Out(ok("")),
|
|
535
|
+
vec![
|
|
536
|
+
MockResp::Out(ok("")), // set-buffer
|
|
537
|
+
MockResp::Out(ok("")), // paste-buffer
|
|
538
|
+
MockResp::Out(ok("")), // delete-buffer
|
|
539
|
+
MockResp::Out(ok("")), // pasted-content prompt poll 1
|
|
540
|
+
MockResp::Out(ok("")), // pasted-content prompt poll 2
|
|
541
|
+
MockResp::Out(ok("")), // pasted-content prompt poll 3
|
|
542
|
+
MockResp::Out(ok("")), // pasted-content prompt poll 4
|
|
543
|
+
MockResp::Out(ok("")), // pasted-content prompt poll 5
|
|
544
|
+
MockResp::Out(ok("")), // token gate: not visible yet
|
|
545
|
+
MockResp::Out(ok("")), // token gate: still not visible
|
|
546
|
+
MockResp::Out(ok(&marker_visible)), // token gate: visible, Enter may fire
|
|
547
|
+
MockResp::Out(ok("")), // send-keys Enter
|
|
548
|
+
],
|
|
549
|
+
);
|
|
550
|
+
|
|
551
|
+
let report = be
|
|
552
|
+
.inject(&Target::Pane(PaneId::new("%7")), &InjectPayload::Text(text), Key::Enter, true)
|
|
553
|
+
.expect("inject");
|
|
554
|
+
let calls = rec.lock().unwrap().clone();
|
|
555
|
+
let submit_index = calls
|
|
556
|
+
.iter()
|
|
557
|
+
.position(|argv| argv.get(1).map(String::as_str) == Some("send-keys"))
|
|
558
|
+
.expect("inject must eventually send Enter");
|
|
559
|
+
let captures_before_submit = calls[..submit_index]
|
|
560
|
+
.iter()
|
|
561
|
+
.filter(|argv| argv.get(1).map(String::as_str) == Some("capture-pane"))
|
|
562
|
+
.count();
|
|
563
|
+
|
|
564
|
+
assert!(
|
|
565
|
+
captures_before_submit >= super::PASTED_CONTENT_APPEAR_POLLS as usize + 3,
|
|
566
|
+
"Enter must wait until the pasted token is visible; calls={calls:?}"
|
|
567
|
+
);
|
|
568
|
+
assert_eq!(report.inject_verification, InjectVerification::CaptureContainsToken);
|
|
569
|
+
assert_eq!(
|
|
570
|
+
report.submit_verification,
|
|
571
|
+
SubmitVerification::EnterSentWithoutPlaceholderCheck
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
|
|
529
575
|
#[test]
|
|
530
576
|
fn send_keys_cancel_mode_queries_mode_and_dispatches_cancel_argv() {
|
|
531
577
|
let (be, rec) = backend_with(
|
|
@@ -845,16 +845,28 @@ fn token_visible_in_capture(
|
|
|
845
845
|
}
|
|
846
846
|
}
|
|
847
847
|
|
|
848
|
-
/// U1 #7
|
|
849
|
-
///
|
|
850
|
-
///
|
|
851
|
-
/// the static `inject_verification_for_payload` would have reported as success.
|
|
848
|
+
/// U1 #7 / E31: wait briefly for the just-pasted token marker before submitting.
|
|
849
|
+
/// `Ok(None)` for non-token payloads (nothing to check). `Ok(Some(false))` means the
|
|
850
|
+
/// paste did not become visible before the Python-parity fallback delay.
|
|
852
851
|
fn pre_submit_token_visible(
|
|
853
852
|
backend: &TmuxBackend,
|
|
854
853
|
target: &Target,
|
|
855
854
|
payload: &InjectPayload,
|
|
856
855
|
) -> Result<Option<bool>, TransportError> {
|
|
857
|
-
|
|
856
|
+
if payload_token_marker(payload).is_none() {
|
|
857
|
+
return Ok(None);
|
|
858
|
+
}
|
|
859
|
+
for attempt in 0..PASTED_CONTENT_APPEAR_POLLS {
|
|
860
|
+
if let Some(true) = token_visible_in_capture(backend, target, payload)? {
|
|
861
|
+
return Ok(Some(true));
|
|
862
|
+
}
|
|
863
|
+
if attempt + 1 < PASTED_CONTENT_APPEAR_POLLS {
|
|
864
|
+
std::thread::sleep(Duration::from_millis(25));
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
// Python waits 250ms between paste-buffer and Enter to let bracketed paste settle.
|
|
868
|
+
std::thread::sleep(Duration::from_millis(250));
|
|
869
|
+
Ok(Some(false))
|
|
858
870
|
}
|
|
859
871
|
|
|
860
872
|
const TOKEN_POST_SUBMIT_READBACK_POLLS: u32 = 5;
|
|
@@ -37,6 +37,18 @@ struct OfflineState {
|
|
|
37
37
|
inject_targets: Vec<Target>,
|
|
38
38
|
inject_payloads: Vec<String>,
|
|
39
39
|
tmux_endpoint: Option<String>,
|
|
40
|
+
/// U1-B contract: when set, `list_targets()` returns `TransportError::MuxUnavailable`
|
|
41
|
+
/// — used to model tmux server jitter (subprocess-fork-failed level). The whole
|
|
42
|
+
/// resolve must DEFER, not silently coerce to an empty vec.
|
|
43
|
+
list_targets_error: Option<String>,
|
|
44
|
+
/// U1-C / general: pre-staged `capture()` payload, keyed by target stringification.
|
|
45
|
+
/// `Target::Pane(p)` keys as `p.as_str()`; `Target::SessionWindow{session,window}`
|
|
46
|
+
/// keys as `format!("{session}:{window}")`. A miss returns empty text (current
|
|
47
|
+
/// default behaviour).
|
|
48
|
+
capture_text: BTreeMap<String, String>,
|
|
49
|
+
/// U1-C Tail-peek contract: every `capture()` call records its `CaptureRange`,
|
|
50
|
+
/// in order, so a test can prove the delivery peek site narrowed Full → Tail(80).
|
|
51
|
+
capture_ranges: Vec<CaptureRange>,
|
|
40
52
|
}
|
|
41
53
|
|
|
42
54
|
impl Default for OfflineState {
|
|
@@ -57,6 +69,9 @@ impl Default for OfflineState {
|
|
|
57
69
|
inject_targets: Vec::new(),
|
|
58
70
|
inject_payloads: Vec::new(),
|
|
59
71
|
tmux_endpoint: None,
|
|
72
|
+
list_targets_error: None,
|
|
73
|
+
capture_text: BTreeMap::new(),
|
|
74
|
+
capture_ranges: Vec::new(),
|
|
60
75
|
}
|
|
61
76
|
}
|
|
62
77
|
}
|
|
@@ -127,6 +142,40 @@ impl OfflineTransport {
|
|
|
127
142
|
self
|
|
128
143
|
}
|
|
129
144
|
|
|
145
|
+
/// U1-B: stage a `list_targets()` error to model tmux server jitter. While set,
|
|
146
|
+
/// every call returns `Err(TransportError::MuxUnavailable{detail})`. Pass an
|
|
147
|
+
/// empty string to clear via re-builder if needed.
|
|
148
|
+
pub fn with_list_targets_error(self, detail: impl Into<String>) -> Self {
|
|
149
|
+
self.with_state(|state| state.list_targets_error = Some(detail.into()));
|
|
150
|
+
self
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// Pre-stage `capture()` output for a `Target::Pane(pane_id)` key.
|
|
154
|
+
pub fn with_capture_for_pane(
|
|
155
|
+
self,
|
|
156
|
+
pane_id: impl Into<String>,
|
|
157
|
+
text: impl Into<String>,
|
|
158
|
+
) -> Self {
|
|
159
|
+
self.with_state(|state| {
|
|
160
|
+
state.capture_text.insert(pane_id.into(), text.into());
|
|
161
|
+
});
|
|
162
|
+
self
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/// Pre-stage `capture()` output for a `Target::SessionWindow{session,window}` key.
|
|
166
|
+
pub fn with_capture_for_session_window(
|
|
167
|
+
self,
|
|
168
|
+
session: impl Into<String>,
|
|
169
|
+
window: impl Into<String>,
|
|
170
|
+
text: impl Into<String>,
|
|
171
|
+
) -> Self {
|
|
172
|
+
let key = format!("{}:{}", session.into(), window.into());
|
|
173
|
+
self.with_state(|state| {
|
|
174
|
+
state.capture_text.insert(key, text.into());
|
|
175
|
+
});
|
|
176
|
+
self
|
|
177
|
+
}
|
|
178
|
+
|
|
130
179
|
pub fn calls(&self) -> Vec<&'static str> {
|
|
131
180
|
self.with_state(|state| state.calls.clone())
|
|
132
181
|
}
|
|
@@ -163,6 +212,12 @@ impl OfflineTransport {
|
|
|
163
212
|
self.with_state(|state| state.inject_payloads.clone())
|
|
164
213
|
}
|
|
165
214
|
|
|
215
|
+
/// All `capture()` ranges observed, in call order. Used by U1-C Tail-peek
|
|
216
|
+
/// contracts to prove the delivery peek site requested Tail rather than Full.
|
|
217
|
+
pub fn capture_ranges(&self) -> Vec<CaptureRange> {
|
|
218
|
+
self.with_state(|state| state.capture_ranges.clone())
|
|
219
|
+
}
|
|
220
|
+
|
|
166
221
|
fn record(&self, call: &'static str) {
|
|
167
222
|
self.with_state(|state| state.calls.push(call));
|
|
168
223
|
}
|
|
@@ -294,11 +349,21 @@ impl Transport for OfflineTransport {
|
|
|
294
349
|
|
|
295
350
|
fn capture(
|
|
296
351
|
&self,
|
|
297
|
-
|
|
352
|
+
target: &Target,
|
|
298
353
|
range: CaptureRange,
|
|
299
354
|
) -> Result<CapturedText, TransportError> {
|
|
300
|
-
|
|
301
|
-
|
|
355
|
+
let key = match target {
|
|
356
|
+
Target::Pane(p) => p.as_str().to_string(),
|
|
357
|
+
Target::SessionWindow { session, window } => {
|
|
358
|
+
format!("{}:{}", session.as_str(), window.as_str())
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
let text = self.with_state(|state| {
|
|
362
|
+
state.calls.push("capture");
|
|
363
|
+
state.capture_ranges.push(range);
|
|
364
|
+
state.capture_text.get(&key).cloned().unwrap_or_default()
|
|
365
|
+
});
|
|
366
|
+
Ok(CapturedText { text, range })
|
|
302
367
|
}
|
|
303
368
|
|
|
304
369
|
fn query(
|
|
@@ -329,10 +394,18 @@ impl Transport for OfflineTransport {
|
|
|
329
394
|
}
|
|
330
395
|
|
|
331
396
|
fn list_targets(&self) -> Result<Vec<PaneInfo>, TransportError> {
|
|
332
|
-
|
|
397
|
+
// U1-B: when `with_list_targets_error` is set, return a real Err so the
|
|
398
|
+
// caller sees server jitter rather than a coerced empty vec.
|
|
399
|
+
if let Some(detail) = self.with_state(|state| {
|
|
333
400
|
state.calls.push("list_targets");
|
|
334
|
-
state.
|
|
335
|
-
})
|
|
401
|
+
state.list_targets_error.clone()
|
|
402
|
+
}) {
|
|
403
|
+
return Err(TransportError::MuxUnavailable {
|
|
404
|
+
backend: BackendKind::Tmux,
|
|
405
|
+
detail,
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
Ok(self.with_state(|state| state.targets.clone()))
|
|
336
409
|
}
|
|
337
410
|
|
|
338
411
|
fn has_session(&self, _session: &SessionName) -> Result<bool, TransportError> {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-agent/installer",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.24",
|
|
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.3.
|
|
24
|
-
"@team-agent/cli-darwin-x64": "0.3.
|
|
25
|
-
"@team-agent/cli-linux-x64": "0.3.
|
|
23
|
+
"@team-agent/cli-darwin-arm64": "0.3.24",
|
|
24
|
+
"@team-agent/cli-darwin-x64": "0.3.24",
|
|
25
|
+
"@team-agent/cli-linux-x64": "0.3.24"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node npm/bincheck.mjs",
|
|
@@ -158,6 +158,7 @@ For diagnosis, run `team-agent profile show deepseek --workspace . --json`; neve
|
|
|
158
158
|
- Quick-start generated files stay inside the selected team directory, for example `.team/current/` or `.team/alpha/`; do not create or expect root `team.spec.yaml` or `team_state.md`.
|
|
159
159
|
- Use `team-agent quick-start ./roles --team-id alpha` to create a second generated team under `.team/alpha/`, or pass an existing team directory directly such as `team-agent quick-start .team/alpha`.
|
|
160
160
|
- `quick-start` is for fresh startup from role docs. If stored worker context already exists for that runtime session, follow the returned `team-agent restart . --team <session>` action to resume it. Use `--fresh` only when the user explicitly accepts new blank worker contexts.
|
|
161
|
+
- 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`.
|
|
161
162
|
- `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.
|
|
162
163
|
- 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.
|
|
163
164
|
- `team-agent send --task task_initial "Start"` routes by task.
|
|
@@ -214,7 +215,7 @@ Removing a worker at runtime is the symmetric `team-agent remove-agent <agent> -
|
|
|
214
215
|
|
|
215
216
|
## Worker Protocol
|
|
216
217
|
|
|
217
|
-
Workers do not run nested Team Agent teams.
|
|
218
|
+
Workers normally do not run nested Team Agent teams. When the user or leader explicitly asks for a child team, follow `references/team-in-team.md`; otherwise workers only provide the target and content for progress, and a short completion summary at the end:
|
|
218
219
|
|
|
219
220
|
```text
|
|
220
221
|
team_orchestrator.send_message(to="leader", content="short progress or blocker")
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Team-In-Team Reference
|
|
2
|
+
|
|
3
|
+
This reference is for explicitly requested nested Team Agent teams. Use it only when the user or leader asks a worker to create or operate a child team. For normal work, a worker should stay inside its parent team and report through `team_orchestrator`.
|
|
4
|
+
|
|
5
|
+
## Safety Rules
|
|
6
|
+
|
|
7
|
+
- Use the Team Agent framework skill and `team-agent` CLI. Do not use a provider's built-in agent-team feature when the user asked for Team Agent.
|
|
8
|
+
- Never create a child team in the parent team's `.team/current` directory.
|
|
9
|
+
- Never edit, delete, or reuse files under the parent team's `.team/current`.
|
|
10
|
+
- Never shut down a team just because `.team/current` is already occupied. In a nested setup, that is probably the parent team; shutting it down can kill the main node.
|
|
11
|
+
- A child team needs its own workspace directory. The role-doc directory is not the child workspace.
|
|
12
|
+
- MCP tools are team-scoped. A worker must not try to widen `team_orchestrator.send_message` to another team.
|
|
13
|
+
|
|
14
|
+
## Child Workspace
|
|
15
|
+
|
|
16
|
+
Pick an independent directory before writing `TEAM.md` or agent role docs. A safe default is a child workspace under the parent workspace, but outside the parent `.team/current`:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
PARENT_WORKSPACE="$PWD"
|
|
20
|
+
case "$PARENT_WORKSPACE" in
|
|
21
|
+
*/.team/current|*/.team/current/*)
|
|
22
|
+
echo "Refusing to create a child team from parent .team/current; cd to the parent workspace root first." >&2
|
|
23
|
+
exit 1
|
|
24
|
+
;;
|
|
25
|
+
esac
|
|
26
|
+
CHILD_TEAM="child-review-team"
|
|
27
|
+
CHILD_WORKSPACE="$PARENT_WORKSPACE/.team/children/review-$(date +%Y%m%d%H%M%S)"
|
|
28
|
+
mkdir -p "$CHILD_WORKSPACE"
|
|
29
|
+
cd "$CHILD_WORKSPACE"
|
|
30
|
+
mkdir -p .team/current/agents
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Then create the child team's files inside the child workspace:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
cat > .team/current/TEAM.md <<'EOF'
|
|
37
|
+
---
|
|
38
|
+
name: child-review-team
|
|
39
|
+
objective: Run a bounded child-team task and report to the parent worker.
|
|
40
|
+
dangerous_auto_approve: false
|
|
41
|
+
fast: false
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
Child team config only.
|
|
45
|
+
EOF
|
|
46
|
+
|
|
47
|
+
cat > .team/current/agents/reviewer.md <<'EOF'
|
|
48
|
+
---
|
|
49
|
+
name: reviewer
|
|
50
|
+
role: Independent Reviewer
|
|
51
|
+
provider: codex
|
|
52
|
+
tools:
|
|
53
|
+
- fs_read
|
|
54
|
+
- fs_list
|
|
55
|
+
- mcp_team
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
Review only the files and instructions provided by the child-team leader.
|
|
59
|
+
Report findings with team_orchestrator.report_result exactly once.
|
|
60
|
+
EOF
|
|
61
|
+
|
|
62
|
+
team-agent quick-start .team/current
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
If `quick-start` reports that `current` or a tmux session already exists, do not run `shutdown`. Move to a new child workspace or change the child team name.
|
|
66
|
+
|
|
67
|
+
## Parent Protection
|
|
68
|
+
|
|
69
|
+
The parent worker is also the child team's leader. Keep the two scopes separate:
|
|
70
|
+
|
|
71
|
+
- Parent team: the worker talks upward with its parent `team_orchestrator` MCP tools.
|
|
72
|
+
- Child team: the same worker talks downward with `team-agent send --workspace "$CHILD_WORKSPACE" --team "$CHILD_TEAM" ...`.
|
|
73
|
+
|
|
74
|
+
Do not run child lifecycle commands against the parent workspace. Always pass the child workspace and team when operating the child team from the parent worker.
|
|
75
|
+
|
|
76
|
+
## Parent-Child Messaging
|
|
77
|
+
|
|
78
|
+
Use explicit two-hop routing.
|
|
79
|
+
|
|
80
|
+
Parent leader to child worker:
|
|
81
|
+
|
|
82
|
+
1. Parent leader sends the child-task instruction to the parent worker that owns the child team.
|
|
83
|
+
2. Parent worker sends into the child team:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
team-agent send reviewer "Review this patch and report findings" \
|
|
87
|
+
--workspace "$CHILD_WORKSPACE" \
|
|
88
|
+
--team "$CHILD_TEAM" \
|
|
89
|
+
--watch-result
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Child worker to parent leader:
|
|
93
|
+
|
|
94
|
+
1. Child worker reports to its child leader:
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
team_orchestrator.send_message(to="leader", content="short progress")
|
|
98
|
+
team_orchestrator.report_result(summary="short completion", status="success")
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
2. The parent worker, acting as child leader, relays the relevant summary upward to the parent leader with its parent-team MCP tools.
|
|
102
|
+
|
|
103
|
+
Do not ask a child worker to send directly to a parent-team agent id. Current MCP scope is team-local and should refuse out-of-scope peers. Use CLI `--workspace` and `--team` only from the appropriate leader/operator side when crossing team boundaries.
|
|
104
|
+
|
|
105
|
+
## Clean-Context Roles
|
|
106
|
+
|
|
107
|
+
Asking for a clean-context role is a normal feature request when the user wants a blind reviewer, a fresh second opinion, or a role that forgets earlier conversation. It is not the same as failure recovery.
|
|
108
|
+
|
|
109
|
+
For an existing worker that should restart with a blank provider context, the leader can use:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
team-agent reset-agent "$AGENT_ID" --workspace "$WORKSPACE" --team "$TEAM" --discard-session --json
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
or the equivalent restart form:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
team-agent restart-agent "$AGENT_ID" --workspace "$WORKSPACE" --team "$TEAM" --discard-session --json
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Use this only when the user intentionally wants a clean context. For incident recovery, follow `recovery-runbook.md`, where context-preserving repair comes first.
|
|
122
|
+
|
|
123
|
+
## Framework Gaps To Track
|
|
124
|
+
|
|
125
|
+
- Nested mode needs a way to pin the framework Team Agent skill by absolute path, so providers do not choose their built-in agent-team feature.
|
|
126
|
+
- A child-team worker should be prevented from shutting down the parent team by mistake.
|
|
127
|
+
- Child team creation should avoid defaulting to the fragile parent `current` directory.
|