@team-agent/installer 0.3.1 → 0.3.3
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 +34 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/Cargo.toml +1 -1
- package/crates/team-agent/src/cli/adapters.rs +234 -26
- package/crates/team-agent/src/cli/diagnose.rs +144 -10
- package/crates/team-agent/src/cli/emit.rs +289 -54
- package/crates/team-agent/src/cli/leader.rs +37 -8
- package/crates/team-agent/src/cli/mod.rs +1281 -196
- package/crates/team-agent/src/cli/status_port.rs +195 -46
- package/crates/team-agent/src/cli/tests/divergence.rs +1 -2
- package/crates/team-agent/src/cli/tests/lane_c.rs +23 -13
- package/crates/team-agent/src/cli/tests/main_preserved.rs +2 -0
- package/crates/team-agent/src/cli/tests/run_delegation.rs +59 -3
- package/crates/team-agent/src/cli/types.rs +18 -0
- package/crates/team-agent/src/compiler.rs +15 -5
- package/crates/team-agent/src/coordinator/health.rs +95 -17
- package/crates/team-agent/src/coordinator/mod.rs +4 -0
- package/crates/team-agent/src/coordinator/runtime_detectors.rs +500 -0
- package/crates/team-agent/src/coordinator/runtime_observation.rs +58 -0
- package/crates/team-agent/src/coordinator/tick.rs +222 -69
- package/crates/team-agent/src/coordinator/types.rs +15 -3
- package/crates/team-agent/src/db/schema.rs +37 -2
- package/crates/team-agent/src/diagnose/comms.rs +226 -0
- package/crates/team-agent/src/diagnose/mod.rs +45 -0
- package/crates/team-agent/src/diagnose/orphans.rs +658 -0
- package/crates/team-agent/src/fake_worker.rs +146 -3
- package/crates/team-agent/src/leader/start.rs +121 -23
- package/crates/team-agent/src/leader/types.rs +44 -1
- package/crates/team-agent/src/lib.rs +3 -0
- package/crates/team-agent/src/lifecycle/display.rs +645 -47
- package/crates/team-agent/src/lifecycle/launch.rs +1061 -146
- package/crates/team-agent/src/lifecycle/mod.rs +2 -0
- package/crates/team-agent/src/lifecycle/profile_launch.rs +810 -0
- package/crates/team-agent/src/lifecycle/profile_smoke.rs +522 -0
- package/crates/team-agent/src/lifecycle/restart/agent.rs +99 -23
- package/crates/team-agent/src/lifecycle/restart/common.rs +183 -24
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +498 -22
- package/crates/team-agent/src/lifecycle/restart/remove.rs +27 -7
- package/crates/team-agent/src/lifecycle/restart/team_state.rs +19 -0
- package/crates/team-agent/src/lifecycle/restart.rs +24 -1
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +5 -5
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +37 -7
- package/crates/team-agent/src/lifecycle/types.rs +19 -0
- package/crates/team-agent/src/mcp_server/helpers.rs +1 -0
- package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +341 -0
- package/crates/team-agent/src/mcp_server/lifecycle_tools/mod.rs +10 -0
- package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +158 -0
- package/crates/team-agent/src/mcp_server/mod.rs +3 -74
- package/crates/team-agent/src/mcp_server/tests/scoped.rs +1 -1
- package/crates/team-agent/src/mcp_server/tests/send.rs +6 -5
- package/crates/team-agent/src/mcp_server/tools.rs +312 -111
- package/crates/team-agent/src/mcp_server/types.rs +6 -4
- package/crates/team-agent/src/mcp_server/wire.rs +19 -7
- package/crates/team-agent/src/message_store.rs +21 -4
- package/crates/team-agent/src/messaging/delivery.rs +470 -59
- package/crates/team-agent/src/messaging/mod.rs +9 -6
- package/crates/team-agent/src/messaging/results.rs +353 -63
- package/crates/team-agent/src/messaging/selftest.rs +199 -12
- package/crates/team-agent/src/messaging/send.rs +35 -3
- package/crates/team-agent/src/messaging/tests/runtime.rs +19 -4
- package/crates/team-agent/src/messaging/types.rs +11 -3
- package/crates/team-agent/src/os_probe.rs +119 -0
- package/crates/team-agent/src/packaging/migrate.rs +10 -2
- package/crates/team-agent/src/packaging/tests.rs +23 -0
- package/crates/team-agent/src/provider/adapter.rs +564 -63
- package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +1 -7
- package/crates/team-agent/src/provider/classify.rs +51 -4
- package/crates/team-agent/src/provider/helpers.rs +10 -1
- package/crates/team-agent/src/provider/startup_prompt.rs +94 -0
- package/crates/team-agent/src/provider/types.rs +47 -0
- package/crates/team-agent/src/session_capture.rs +616 -0
- package/crates/team-agent/src/state/persist.rs +170 -1
- package/crates/team-agent/src/state/projection.rs +141 -8
- package/crates/team-agent/src/state/selector.rs +5 -2
- package/crates/team-agent/src/tmux_backend.rs +161 -64
- package/crates/team-agent/src/transport/test_support.rs +9 -0
- package/crates/team-agent/src/transport/tests/wire.rs +4 -0
- package/crates/team-agent/src/transport.rs +13 -2
- package/package.json +4 -4
|
@@ -88,13 +88,7 @@ pub fn awaiting_human_confirm_reason(
|
|
|
88
88
|
None
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
ApprovalKind::Command =>
|
|
92
|
-
if leader_auto_approval_allowed {
|
|
93
|
-
None
|
|
94
|
-
} else {
|
|
95
|
-
Some("leader_restricted")
|
|
96
|
-
}
|
|
97
|
-
}
|
|
91
|
+
ApprovalKind::Command => Some("command_approval_requires_human"),
|
|
98
92
|
ApprovalKind::Unknown => Some("approval_requires_human"),
|
|
99
93
|
}
|
|
100
94
|
}
|
|
@@ -229,7 +229,11 @@ fn decide_state(fact: &LifecycleFact, process: ProcessLiveness) -> ClassifyResul
|
|
|
229
229
|
ProcessLiveness::Alive => classify_result(
|
|
230
230
|
TurnState::Working,
|
|
231
231
|
fact.turn_id.clone(),
|
|
232
|
-
"
|
|
232
|
+
if fact.reason == "assistant_in_flight" {
|
|
233
|
+
&fact.reason
|
|
234
|
+
} else {
|
|
235
|
+
"open_turn"
|
|
236
|
+
},
|
|
233
237
|
ClassifySource::SessionFile,
|
|
234
238
|
Vec::new(),
|
|
235
239
|
),
|
|
@@ -281,12 +285,55 @@ fn decide_state(fact: &LifecycleFact, process: ProcessLiveness) -> ClassifyResul
|
|
|
281
285
|
|
|
282
286
|
fn claude_lifecycle_fact(record: &serde_json::Value) -> Option<LifecycleFact> {
|
|
283
287
|
let record_type = record.get("type").and_then(serde_json::Value::as_str);
|
|
288
|
+
if record_type == Some("system")
|
|
289
|
+
&& record.get("subtype").and_then(serde_json::Value::as_str) == Some("api_error")
|
|
290
|
+
&& record.get("level").and_then(serde_json::Value::as_str) == Some("error")
|
|
291
|
+
{
|
|
292
|
+
return Some(lifecycle(
|
|
293
|
+
FactKind::Error,
|
|
294
|
+
record
|
|
295
|
+
.get("sessionId")
|
|
296
|
+
.or_else(|| record.get("parentUuid"))
|
|
297
|
+
.or_else(|| record.get("uuid"))
|
|
298
|
+
.and_then(serde_json::Value::as_str)
|
|
299
|
+
.map(TurnId::new),
|
|
300
|
+
"api_error",
|
|
301
|
+
vec!["api_error".to_string()],
|
|
302
|
+
));
|
|
303
|
+
}
|
|
304
|
+
if record_type == Some("user") && claude_record_has_error_tool_result(record) {
|
|
305
|
+
return Some(lifecycle(
|
|
306
|
+
FactKind::Error,
|
|
307
|
+
record
|
|
308
|
+
.get("parentUuid")
|
|
309
|
+
.or_else(|| record.get("uuid"))
|
|
310
|
+
.and_then(serde_json::Value::as_str)
|
|
311
|
+
.map(TurnId::new),
|
|
312
|
+
"tool_result_is_error",
|
|
313
|
+
vec!["tool_result_is_error".to_string()],
|
|
314
|
+
));
|
|
315
|
+
}
|
|
284
316
|
if record_type == Some("assistant") {
|
|
285
|
-
let
|
|
317
|
+
let turn_id = record.get("requestId").and_then(serde_json::Value::as_str).map(TurnId::new);
|
|
318
|
+
let Some(stop_reason) = record
|
|
286
319
|
.get("message")
|
|
287
320
|
.and_then(|m| m.get("stop_reason"))
|
|
288
|
-
.and_then(serde_json::Value::as_str)
|
|
289
|
-
|
|
321
|
+
.and_then(serde_json::Value::as_str) else {
|
|
322
|
+
if record
|
|
323
|
+
.get("message")
|
|
324
|
+
.and_then(|m| m.get("content"))
|
|
325
|
+
.and_then(serde_json::Value::as_array)
|
|
326
|
+
.is_some()
|
|
327
|
+
{
|
|
328
|
+
return Some(lifecycle(
|
|
329
|
+
FactKind::TurnOpen,
|
|
330
|
+
turn_id,
|
|
331
|
+
"assistant_in_flight",
|
|
332
|
+
vec!["assistant_in_flight".to_string()],
|
|
333
|
+
));
|
|
334
|
+
}
|
|
335
|
+
return None;
|
|
336
|
+
};
|
|
290
337
|
return match stop_reason {
|
|
291
338
|
"tool_use" => Some(lifecycle(FactKind::TurnOpen, turn_id, "open_turn", Vec::new())),
|
|
292
339
|
"end_turn" => Some(lifecycle(FactKind::TurnComplete, turn_id, "end_turn", Vec::new())),
|
|
@@ -22,9 +22,18 @@ pub(crate) fn find_session_id(record: &serde_json::Value) -> Option<String> {
|
|
|
22
22
|
if let Some(s) = record.get("sessionId").and_then(serde_json::Value::as_str) {
|
|
23
23
|
return Some(s.to_string());
|
|
24
24
|
}
|
|
25
|
-
record
|
|
25
|
+
if let Some(s) = record
|
|
26
26
|
.get("session_id")
|
|
27
27
|
.and_then(serde_json::Value::as_str)
|
|
28
|
+
{
|
|
29
|
+
return Some(s.to_string());
|
|
30
|
+
}
|
|
31
|
+
record
|
|
32
|
+
.get("session_meta")
|
|
33
|
+
.and_then(|v| v.get("payload"))
|
|
34
|
+
.or_else(|| record.get("payload"))
|
|
35
|
+
.and_then(|v| v.get("id"))
|
|
36
|
+
.and_then(serde_json::Value::as_str)
|
|
28
37
|
.map(ToString::to_string)
|
|
29
38
|
}
|
|
30
39
|
|
|
@@ -20,6 +20,14 @@ const TRUST_MARKERS: &[&str] = &[
|
|
|
20
20
|
"Do you trust this folder?",
|
|
21
21
|
];
|
|
22
22
|
const UPDATE_MARKERS: &[&str] = &["Update available!", "Update now"];
|
|
23
|
+
const CLAUDE_TRUST_MARKERS: &[&str] = &[
|
|
24
|
+
"Quick safety check",
|
|
25
|
+
"Is this a project you created or one you trust?",
|
|
26
|
+
"Yes, I trust this folder",
|
|
27
|
+
"No, exit",
|
|
28
|
+
"Enter to confirm",
|
|
29
|
+
];
|
|
30
|
+
const CLAUDE_READY_MARKERS: &[&str] = &["Claude Code"];
|
|
23
31
|
/// Plain ready markers (not the bare `›` glyph — that glyph also indicates a
|
|
24
32
|
/// numbered-menu selector and is handled by [`rightmost_input_prompt_glyph`] with
|
|
25
33
|
/// shape gating per N15 / CR-063: detect by SHAPE, not a single Unicode codepoint).
|
|
@@ -87,6 +95,17 @@ pub fn classify_codex_startup_screen(output: &str) -> StartupScreenDecision {
|
|
|
87
95
|
}
|
|
88
96
|
}
|
|
89
97
|
|
|
98
|
+
pub fn classify_claude_startup_screen(output: &str) -> StartupScreenDecision {
|
|
99
|
+
if has_active_claude_yes_trust_shape(output) {
|
|
100
|
+
return StartupScreenDecision::AnswerWorkspaceTrust;
|
|
101
|
+
}
|
|
102
|
+
if has_claude_ready_shape(output) {
|
|
103
|
+
StartupScreenDecision::Ready
|
|
104
|
+
} else {
|
|
105
|
+
StartupScreenDecision::KeepPolling
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
90
109
|
/// Actionable trust shape (N15): the captured text contains a trust phrase AND a
|
|
91
110
|
/// numbered-menu selector line `› <digit>. `. This is the modal-still-active signal
|
|
92
111
|
/// that survives Codex's pre-rendering of the banner/input prompt below the menu.
|
|
@@ -121,6 +140,51 @@ fn contains_numbered_menu_glyph(output: &str) -> bool {
|
|
|
121
140
|
false
|
|
122
141
|
}
|
|
123
142
|
|
|
143
|
+
fn has_active_claude_yes_trust_shape(output: &str) -> bool {
|
|
144
|
+
if !CLAUDE_TRUST_MARKERS
|
|
145
|
+
.iter()
|
|
146
|
+
.all(|marker| output.contains(marker))
|
|
147
|
+
{
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
output
|
|
151
|
+
.lines()
|
|
152
|
+
.map(str::trim_start)
|
|
153
|
+
.any(|line| {
|
|
154
|
+
line == "❯ 1. Yes, I trust this folder"
|
|
155
|
+
|| line == "> 1. Yes, I trust this folder"
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
fn has_claude_ready_shape(output: &str) -> bool {
|
|
160
|
+
max_rfind(output, CLAUDE_READY_MARKERS).is_some()
|
|
161
|
+
&& rightmost_claude_input_prompt_glyph(output).is_some()
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
fn rightmost_claude_input_prompt_glyph(output: &str) -> Option<usize> {
|
|
165
|
+
let mut best = rightmost_input_prompt_for_glyph(output, '❯');
|
|
166
|
+
best = max_two(best, rightmost_input_prompt_for_glyph(output, '>'));
|
|
167
|
+
best
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
fn rightmost_input_prompt_for_glyph(output: &str, glyph: char) -> Option<usize> {
|
|
171
|
+
let glyph_len = glyph.len_utf8();
|
|
172
|
+
let mut best = None;
|
|
173
|
+
let mut start = 0;
|
|
174
|
+
while let Some(rel) = output[start..].find(glyph) {
|
|
175
|
+
let abs = start + rel;
|
|
176
|
+
let tail_start = abs + glyph_len;
|
|
177
|
+
if tail_start <= output.len() && !is_numbered_menu_tail(&output[tail_start..]) {
|
|
178
|
+
best = Some(abs);
|
|
179
|
+
}
|
|
180
|
+
start = tail_start;
|
|
181
|
+
if start > output.len() {
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
best
|
|
186
|
+
}
|
|
187
|
+
|
|
124
188
|
/// Rightmost `›` whose tail is NOT a numbered-menu selector (` <digit>. `). A bare
|
|
125
189
|
/// `›` followed by free text or whitespace is the Codex main-input prompt indicator;
|
|
126
190
|
/// a `›` followed by `1. Yes, continue` is part of the trust/update menu and is NOT
|
|
@@ -204,6 +268,36 @@ pub fn codex_handle_startup_prompts(
|
|
|
204
268
|
handled
|
|
205
269
|
}
|
|
206
270
|
|
|
271
|
+
pub fn claude_handle_startup_prompts(
|
|
272
|
+
transport: &dyn Transport,
|
|
273
|
+
target: &Target,
|
|
274
|
+
checks: usize,
|
|
275
|
+
sleep_s: f64,
|
|
276
|
+
) -> Vec<HandledPrompt> {
|
|
277
|
+
let mut handled = Vec::new();
|
|
278
|
+
for _ in 0..checks {
|
|
279
|
+
let screen = match transport.capture(target, CaptureRange::Full) {
|
|
280
|
+
Ok(captured) => captured.text,
|
|
281
|
+
Err(_) => String::new(),
|
|
282
|
+
};
|
|
283
|
+
match classify_claude_startup_screen(&screen) {
|
|
284
|
+
StartupScreenDecision::AnswerWorkspaceTrust => {
|
|
285
|
+
let _ = transport.send_keys(target, &[Key::Enter]);
|
|
286
|
+
handled.push(HandledPrompt {
|
|
287
|
+
prompt: "claude_workspace_trust".to_string(),
|
|
288
|
+
action: "sent_enter".to_string(),
|
|
289
|
+
});
|
|
290
|
+
sleep_between_polls(sleep_s);
|
|
291
|
+
}
|
|
292
|
+
StartupScreenDecision::Ready => break,
|
|
293
|
+
StartupScreenDecision::SkipUpdatePrompt | StartupScreenDecision::KeepPolling => {
|
|
294
|
+
sleep_between_polls(sleep_s);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
handled
|
|
299
|
+
}
|
|
300
|
+
|
|
207
301
|
fn max_rfind(output: &str, needles: &[&str]) -> Option<usize> {
|
|
208
302
|
needles.iter().filter_map(|needle| output.rfind(needle)).max()
|
|
209
303
|
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
//! provider 共享类型:enums / newtypes / ProviderCaps / ProviderError / 捕获+classify payload / 占位结构。
|
|
2
2
|
|
|
3
|
+
use std::collections::{BTreeMap, BTreeSet};
|
|
3
4
|
use std::path::PathBuf;
|
|
4
5
|
|
|
5
6
|
use serde::{Deserialize, Serialize};
|
|
6
7
|
use thiserror::Error;
|
|
7
8
|
|
|
9
|
+
use super::AuthMode;
|
|
10
|
+
|
|
8
11
|
// ===========================================================================
|
|
9
12
|
// ENUMS (穷尽 + serde rename 到精确 Python 字符串)
|
|
10
13
|
// ===========================================================================
|
|
@@ -305,6 +308,50 @@ pub struct ProviderCaps {
|
|
|
305
308
|
pub writes_global_settings: bool,
|
|
306
309
|
}
|
|
307
310
|
|
|
311
|
+
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
|
|
312
|
+
pub struct ProviderCommandOverrides {
|
|
313
|
+
pub model: Option<String>,
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
|
|
317
|
+
pub struct ProviderProfileLaunch {
|
|
318
|
+
pub env_overlay: BTreeMap<String, String>,
|
|
319
|
+
pub env_unset: BTreeSet<String>,
|
|
320
|
+
pub command_overrides: ProviderCommandOverrides,
|
|
321
|
+
pub claude_config_dir: Option<PathBuf>,
|
|
322
|
+
pub claude_projects_root: Option<PathBuf>,
|
|
323
|
+
pub managed_mcp_config: bool,
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
#[derive(Debug, Clone, Copy)]
|
|
327
|
+
pub struct ProviderCommandContext<'a> {
|
|
328
|
+
pub auth_mode: AuthMode,
|
|
329
|
+
pub mcp_config: Option<&'a McpConfig>,
|
|
330
|
+
pub system_prompt: Option<&'a str>,
|
|
331
|
+
pub model: Option<&'a str>,
|
|
332
|
+
pub tools: &'a [&'a str],
|
|
333
|
+
pub profile_launch: Option<&'a ProviderProfileLaunch>,
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
337
|
+
pub struct CommandPlan {
|
|
338
|
+
pub argv: Vec<String>,
|
|
339
|
+
pub expected_session_id: Option<SessionId>,
|
|
340
|
+
pub provider_projects_root: Option<PathBuf>,
|
|
341
|
+
pub managed_mcp_config: bool,
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
impl CommandPlan {
|
|
345
|
+
pub fn argv_only(argv: Vec<String>) -> Self {
|
|
346
|
+
Self {
|
|
347
|
+
argv,
|
|
348
|
+
expected_session_id: None,
|
|
349
|
+
provider_projects_root: None,
|
|
350
|
+
managed_mcp_config: false,
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
308
355
|
// ===========================================================================
|
|
309
356
|
// ERROR
|
|
310
357
|
// ===========================================================================
|