@team-agent/installer 0.5.6 → 0.5.8
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 +46 -17
- package/crates/team-agent/src/cli/mod.rs +130 -1
- package/crates/team-agent/src/cli/named_address.rs +69 -9
- package/crates/team-agent/src/cli/send.rs +95 -5
- package/crates/team-agent/src/cli/status_port.rs +97 -8
- package/crates/team-agent/src/cli/tests/main_preserved.rs +54 -0
- package/crates/team-agent/src/cli/tests/status_send.rs +109 -2
- package/crates/team-agent/src/coordinator/tick.rs +7 -1
- package/crates/team-agent/src/db/agent_health_capture.rs +96 -0
- package/crates/team-agent/src/db/mod.rs +1 -0
- package/crates/team-agent/src/lifecycle/restart/remove.rs +107 -76
- package/crates/team-agent/src/lifecycle/restart.rs +1 -1
- package/crates/team-agent/src/lifecycle/types.rs +15 -0
- package/crates/team-agent/src/mcp_server/helpers.rs +18 -1
- package/crates/team-agent/src/mcp_server/tests/send.rs +125 -13
- package/crates/team-agent/src/mcp_server/tools.rs +16 -1
- package/crates/team-agent/src/messaging/delivery.rs +12 -5
- package/crates/team-agent/src/messaging/tests/runtime.rs +7 -2
- package/package.json +4 -4
|
@@ -112,29 +112,43 @@ fn send_outcome_direct_renders_compact_body() {
|
|
|
112
112
|
// ════════════════════════════════════════════════════════════════════════
|
|
113
113
|
// CONTROL-PLANE: send_message worker recipient → WorkerAccepted (tools.py:135-183)
|
|
114
114
|
// ════════════════════════════════════════════════════════════════════════
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
// A worker recipient w/ a delivered message_id → async accepted carrying the
|
|
118
|
-
// byte-stable poll hint. Identity anchored on injected env (no candidate scan).
|
|
119
|
-
// golden: a leader WITH owner_team_id on an unseeded ws would hit the C23 cross-team
|
|
120
|
-
// refusal first (worker-1 not in visible peers) -> PeerNotInScope. owner_team_id=None
|
|
121
|
-
// (legacy single-team) bypasses that, isolating the worker-recipient accepted path.
|
|
122
|
-
// The cross-team refusal has its own tests (refuse_cross_team_peer_* / send_message_cross_team_*).
|
|
123
|
-
// A-7: the accepted path needs a REAL stored message_id (tools.py:175-181 —
|
|
124
|
-
// fabricated ids are gone), so the workspace seeds a running worker-1 the
|
|
125
|
-
// delivery layer can actually queue for.
|
|
126
|
-
let ws = unique_ws("send-worker");
|
|
115
|
+
fn seed_current_worker_state(tag: &str) -> std::path::PathBuf {
|
|
116
|
+
let ws = unique_ws(tag);
|
|
127
117
|
crate::state::persist::save_runtime_state(
|
|
128
118
|
&ws,
|
|
129
119
|
&serde_json::json!({
|
|
130
120
|
"session_name": "team-x",
|
|
121
|
+
"active_team_key": "current",
|
|
131
122
|
"agents": {
|
|
132
123
|
"worker-1": {"status": "running", "agent_id": "worker-1", "window": "worker-1"},
|
|
133
124
|
},
|
|
125
|
+
"teams": {
|
|
126
|
+
"current": {
|
|
127
|
+
"status": "alive",
|
|
128
|
+
"agents": {
|
|
129
|
+
"worker-1": {"status": "running", "agent_id": "worker-1", "window": "worker-1"},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
134
133
|
}),
|
|
135
134
|
)
|
|
136
135
|
.unwrap();
|
|
137
|
-
|
|
136
|
+
ws
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
#[test]
|
|
140
|
+
fn send_message_worker_recipient_returns_accepted_with_poll_hint() {
|
|
141
|
+
// A worker recipient w/ a delivered message_id → async accepted carrying the
|
|
142
|
+
// byte-stable poll hint. Identity anchored on injected env (no candidate scan).
|
|
143
|
+
// A-7: the accepted path needs a REAL stored message_id (tools.py:175-181 —
|
|
144
|
+
// fabricated ids are gone), so the workspace seeds a running worker-1 the
|
|
145
|
+
// delivery layer can actually queue for.
|
|
146
|
+
let ws = seed_current_worker_state("send-worker");
|
|
147
|
+
let tools = TeamOrchestratorTools::with_identity(
|
|
148
|
+
&ws,
|
|
149
|
+
Some(AgentId::new("leader")),
|
|
150
|
+
Some(TeamKey::new("current")),
|
|
151
|
+
);
|
|
138
152
|
let outcome = tools.send_message(
|
|
139
153
|
&MessageTarget::Single("worker-1".to_string()),
|
|
140
154
|
"do the thing",
|
|
@@ -155,6 +169,104 @@ fn send_message_worker_recipient_returns_accepted_with_poll_hint() {
|
|
|
155
169
|
}
|
|
156
170
|
}
|
|
157
171
|
|
|
172
|
+
#[test]
|
|
173
|
+
fn ordinary_send_assign_shape_has_no_recovery_marker() {
|
|
174
|
+
let ws = seed_current_worker_state("ordinary-no-recovery");
|
|
175
|
+
let tools = TeamOrchestratorTools::with_identity(
|
|
176
|
+
&ws,
|
|
177
|
+
Some(AgentId::new("leader")),
|
|
178
|
+
Some(TeamKey::new("current")),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
let sent = tools
|
|
182
|
+
.send_message(
|
|
183
|
+
&MessageTarget::Single("worker-1".to_string()),
|
|
184
|
+
"ordinary send",
|
|
185
|
+
None,
|
|
186
|
+
None,
|
|
187
|
+
None,
|
|
188
|
+
None,
|
|
189
|
+
)
|
|
190
|
+
.expect("ordinary send ok")
|
|
191
|
+
.to_value();
|
|
192
|
+
assert!(
|
|
193
|
+
!sent.as_object().unwrap().contains_key("recovery"),
|
|
194
|
+
"ordinary send must not carry recovery marker: {sent}"
|
|
195
|
+
);
|
|
196
|
+
assert!(
|
|
197
|
+
sent.get("acceptance_marker").is_none(),
|
|
198
|
+
"ordinary send must not carry acceptance marker: {sent}"
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
let assigned = tools
|
|
202
|
+
.assign_task(
|
|
203
|
+
&json!({"id": "ordinary-task", "assignee": "worker-1", "title": "ordinary"}),
|
|
204
|
+
None,
|
|
205
|
+
)
|
|
206
|
+
.expect("ordinary assign ok");
|
|
207
|
+
let assigned_value = serde_json::to_value(&assigned).expect("serialize ordinary assign");
|
|
208
|
+
assert!(
|
|
209
|
+
!assigned_value.as_object().unwrap().contains_key("recovery"),
|
|
210
|
+
"ordinary assign must not carry recovery marker: {assigned_value}"
|
|
211
|
+
);
|
|
212
|
+
assert!(
|
|
213
|
+
assigned_value.get("acceptance_marker").is_none(),
|
|
214
|
+
"ordinary assign must not carry acceptance marker: {assigned_value}"
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
let recovery_false_assigned = tools
|
|
218
|
+
.assign_task(
|
|
219
|
+
&json!({
|
|
220
|
+
"id": "ordinary-recovery-false-task",
|
|
221
|
+
"assignee": "worker-1",
|
|
222
|
+
"title": "ordinary recovery false",
|
|
223
|
+
"recovery": false,
|
|
224
|
+
}),
|
|
225
|
+
None,
|
|
226
|
+
)
|
|
227
|
+
.expect("recovery=false assign ok");
|
|
228
|
+
let recovery_false_value =
|
|
229
|
+
serde_json::to_value(&recovery_false_assigned).expect("serialize recovery=false assign");
|
|
230
|
+
assert!(
|
|
231
|
+
!recovery_false_value
|
|
232
|
+
.as_object()
|
|
233
|
+
.unwrap()
|
|
234
|
+
.contains_key("recovery"),
|
|
235
|
+
"recovery=false assign must not carry recovery marker: {recovery_false_value}"
|
|
236
|
+
);
|
|
237
|
+
assert!(
|
|
238
|
+
recovery_false_value.get("acceptance_marker").is_none(),
|
|
239
|
+
"recovery=false assign must not carry acceptance marker: {recovery_false_value}"
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
#[test]
|
|
244
|
+
fn recovery_assign_shape_has_structured_marker() {
|
|
245
|
+
let ws = seed_current_worker_state("recovery-marker");
|
|
246
|
+
let tools = TeamOrchestratorTools::with_identity(
|
|
247
|
+
&ws,
|
|
248
|
+
Some(AgentId::new("leader")),
|
|
249
|
+
Some(TeamKey::new("current")),
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
let assigned = tools
|
|
253
|
+
.assign_task(
|
|
254
|
+
&json!({
|
|
255
|
+
"id": "recovery-task",
|
|
256
|
+
"assignee": "worker-1",
|
|
257
|
+
"title": "recover",
|
|
258
|
+
"recovery": true,
|
|
259
|
+
}),
|
|
260
|
+
None,
|
|
261
|
+
)
|
|
262
|
+
.expect("recovery assign ok");
|
|
263
|
+
assert_eq!(assigned.fields.get("recovery"), Some(&json!(true)));
|
|
264
|
+
assert_eq!(
|
|
265
|
+
assigned.fields.get("acceptance_marker"),
|
|
266
|
+
Some(&json!("recovery"))
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
158
270
|
#[test]
|
|
159
271
|
fn send_message_worker_recipient_surfaces_dead_coordinator_warning() {
|
|
160
272
|
let ws = unique_ws("send-worker-dead-coord");
|
|
@@ -116,6 +116,7 @@ impl TeamOrchestratorTools {
|
|
|
116
116
|
};
|
|
117
117
|
|
|
118
118
|
let task_value = Value::Object(task_obj.clone());
|
|
119
|
+
let recovery = task_recovery_marker(&task_value);
|
|
119
120
|
let mut state = load_runtime_state(&self.workspace).map_err(tool_runtime_error)?;
|
|
120
121
|
ensure_object(&mut state);
|
|
121
122
|
let team_key = self
|
|
@@ -139,7 +140,16 @@ impl TeamOrchestratorTools {
|
|
|
139
140
|
None,
|
|
140
141
|
None,
|
|
141
142
|
)?;
|
|
142
|
-
compact_tool_result(&out.to_value())
|
|
143
|
+
let mut ok = compact_tool_result(&out.to_value())?;
|
|
144
|
+
if recovery {
|
|
145
|
+
ok.fields
|
|
146
|
+
.insert("recovery".to_string(), serde_json::json!(true));
|
|
147
|
+
ok.fields.insert(
|
|
148
|
+
"acceptance_marker".to_string(),
|
|
149
|
+
Value::String("recovery".to_string()),
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
Ok(ok)
|
|
143
153
|
}
|
|
144
154
|
|
|
145
155
|
/// `send_message` (`tools.py:135-183`): C14/C15/C17 scope resolution.
|
|
@@ -989,6 +999,11 @@ fn assignment_message(task: &Value, explicit: Option<&str>) -> String {
|
|
|
989
999
|
json_dumps_default(task)
|
|
990
1000
|
}
|
|
991
1001
|
|
|
1002
|
+
fn task_recovery_marker(task: &Value) -> bool {
|
|
1003
|
+
task.get("recovery").and_then(Value::as_bool) == Some(true)
|
|
1004
|
+
|| task.get("acceptance_marker").and_then(Value::as_str) == Some("recovery")
|
|
1005
|
+
}
|
|
1006
|
+
|
|
992
1007
|
fn scope_override_name(scope: Scope) -> Option<&'static str> {
|
|
993
1008
|
match scope {
|
|
994
1009
|
Scope::Team => Some("team"),
|
|
@@ -1981,12 +1981,19 @@ fn arm_turn_open(state: &mut serde_json::Value, recipient: &str, message_id: &Op
|
|
|
1981
1981
|
.and_then(|agents| agents.get_mut(recipient))
|
|
1982
1982
|
.and_then(serde_json::Value::as_object_mut)
|
|
1983
1983
|
{
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1984
|
+
// Phase-DX E2 (CR P0 red line #5): the leader→worker turn message id.
|
|
1985
|
+
// Renamed from a misleading legacy field name (which was never a task
|
|
1986
|
+
// FSM id — task FSM is A1) to `current_turn_message_id`, matching its
|
|
1987
|
+
// actual semantic. Reader compatibility lives in `coordinator/tick.rs`
|
|
1988
|
+
// (whitelisted for the transition) and `mcp_server/helpers.rs`; other
|
|
1989
|
+
// messaging/lifecycle/mcp_server code MUST NOT treat this as
|
|
1990
|
+
// authoritative task state.
|
|
1991
|
+
let field = message_id
|
|
1992
|
+
.as_ref()
|
|
1993
|
+
.map_or(serde_json::Value::Null, |id| {
|
|
1987
1994
|
serde_json::Value::String(id.clone())
|
|
1988
|
-
})
|
|
1989
|
-
);
|
|
1995
|
+
});
|
|
1996
|
+
agent.insert("current_turn_message_id".to_string(), field);
|
|
1990
1997
|
}
|
|
1991
1998
|
}
|
|
1992
1999
|
|
|
@@ -1581,10 +1581,15 @@ fn deliver_pending_submit_unverified_is_not_saved_by_readback() {
|
|
|
1581
1581
|
Some(&serde_json::json!(message_id)),
|
|
1582
1582
|
"current-turn fact is armed immediately after physical inject succeeds"
|
|
1583
1583
|
);
|
|
1584
|
+
// Phase-DX E2 rename: the leader→worker turn message id lives under
|
|
1585
|
+
// `current_turn_message_id` (the previous name misleadingly implied a task
|
|
1586
|
+
// FSM id — that is A1 territory). Same value written by
|
|
1587
|
+
// `delivery::arm_turn_open`; the invariant of arming-without-delivered is
|
|
1588
|
+
// unchanged.
|
|
1584
1589
|
assert_eq!(
|
|
1585
|
-
updated.pointer("/agents/w1/
|
|
1590
|
+
updated.pointer("/agents/w1/current_turn_message_id"),
|
|
1586
1591
|
Some(&serde_json::json!(message_id)),
|
|
1587
|
-
"agent
|
|
1592
|
+
"agent current-turn message id is armed without marking the message delivered"
|
|
1588
1593
|
);
|
|
1589
1594
|
}
|
|
1590
1595
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-agent/installer",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.8",
|
|
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.8",
|
|
24
|
+
"@team-agent/cli-darwin-x64": "0.5.8",
|
|
25
|
+
"@team-agent/cli-linux-x64": "0.5.8"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node npm/bincheck.mjs",
|