@team-agent/installer 0.5.50 → 0.5.52
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 +5 -3
- package/crates/team-agent/src/cli/emit.rs +2 -1
- package/crates/team-agent/src/cli/mod.rs +32 -14
- package/crates/team-agent/src/cli/named_address.rs +46 -115
- package/crates/team-agent/src/cli/send/coordinator.rs +163 -0
- package/crates/team-agent/src/cli/send/mailbox.rs +99 -0
- package/crates/team-agent/src/cli/send/persist.rs +154 -0
- package/crates/team-agent/src/cli/send/presentation.rs +333 -0
- package/crates/team-agent/src/cli/send/resolve.rs +361 -0
- package/crates/team-agent/src/cli/send.rs +44 -1169
- package/crates/team-agent/src/cli/spec.rs +1 -1
- package/crates/team-agent/src/cli/status_port/agents.rs +358 -0
- package/crates/team-agent/src/cli/status_port/approvals.rs +79 -0
- package/crates/team-agent/src/cli/status_port/compact.rs +207 -0
- package/crates/team-agent/src/cli/status_port/format.rs +145 -0
- package/crates/team-agent/src/cli/status_port/inbox.rs +36 -0
- package/crates/team-agent/src/cli/status_port/runtime.rs +195 -0
- package/crates/team-agent/src/cli/status_port/snapshot.rs +181 -0
- package/crates/team-agent/src/cli/status_port/store.rs +412 -0
- package/crates/team-agent/src/cli/status_port/tests.rs +54 -0
- package/crates/team-agent/src/cli/status_port.rs +47 -1548
- package/crates/team-agent/src/cli/tests/main_preserved.rs +1 -0
- package/crates/team-agent/src/cli/tests/run_delegation.rs +1 -0
- package/crates/team-agent/src/cli/types.rs +1 -0
- package/crates/team-agent/src/coordinator/conpty_shim.rs +34 -30
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +135 -13
- package/crates/team-agent/src/coordinator/tick.rs +37 -0
- package/crates/team-agent/src/db/agent_health_capture.rs +18 -13
- package/crates/team-agent/src/db/message_store.rs +154 -44
- package/crates/team-agent/src/event_log.rs +73 -0
- package/crates/team-agent/src/leader/lease.rs +202 -16
- package/crates/team-agent/src/leader/start.rs +28 -4
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +424 -0
- package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +303 -0
- package/crates/team-agent/src/lifecycle/launch/agent_state.rs +160 -0
- package/crates/team-agent/src/lifecycle/launch/approval.rs +134 -0
- package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +492 -0
- package/crates/team-agent/src/lifecycle/launch/fork_state.rs +297 -0
- package/crates/team-agent/src/lifecycle/launch/identity.rs +372 -0
- package/crates/team-agent/src/lifecycle/launch/layout.rs +313 -0
- package/crates/team-agent/src/lifecycle/launch/leader_context.rs +478 -0
- package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +201 -0
- package/crates/team-agent/src/lifecycle/launch/ownership.rs +66 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +477 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +278 -0
- package/crates/team-agent/src/lifecycle/launch/readiness.rs +123 -0
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +377 -0
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +434 -0
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +499 -0
- package/crates/team-agent/src/lifecycle/launch/worker_env.rs +438 -0
- package/crates/team-agent/src/lifecycle/launch.rs +119 -5351
- package/crates/team-agent/src/lifecycle/restart/agent.rs +63 -29
- package/crates/team-agent/src/lifecycle/restart/common.rs +53 -27
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +167 -27
- package/crates/team-agent/src/lifecycle/restart/remove.rs +503 -77
- package/crates/team-agent/src/lifecycle/restart.rs +26 -2
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +906 -17
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +67 -2
- package/crates/team-agent/src/lifecycle/tests/lifecycle_lock.rs +24 -1
- package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +30 -7
- package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +8 -4
- package/crates/team-agent/src/mcp_server/mod.rs +1 -1
- package/crates/team-agent/src/mcp_server/tests/send.rs +6 -10
- package/crates/team-agent/src/mcp_server/tests/tools.rs +18 -1
- package/crates/team-agent/src/mcp_server/tests.rs +11 -4
- package/crates/team-agent/src/mcp_server/tools.rs +14 -5
- package/crates/team-agent/src/messaging/activity.rs +4 -2
- package/crates/team-agent/src/messaging/address.rs +86 -0
- package/crates/team-agent/src/messaging/delivery.rs +288 -183
- package/crates/team-agent/src/messaging/helpers.rs +17 -13
- package/crates/team-agent/src/messaging/leader_channel.rs +171 -0
- package/crates/team-agent/src/messaging/leader_receiver.rs +60 -35
- package/crates/team-agent/src/messaging/mod.rs +15 -2
- package/crates/team-agent/src/messaging/persist.rs +309 -0
- package/crates/team-agent/src/messaging/results.rs +16 -24
- package/crates/team-agent/src/messaging/scheduler.rs +4 -2
- package/crates/team-agent/src/messaging/selftest.rs +19 -12
- package/crates/team-agent/src/messaging/send.rs +101 -49
- package/crates/team-agent/src/messaging/tests/leader_channel.rs +169 -0
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +305 -0
- package/crates/team-agent/src/messaging/tests/mod.rs +2 -0
- package/crates/team-agent/src/messaging/tests/runtime.rs +38 -17
- package/crates/team-agent/src/messaging/watchers.rs +13 -3
- package/crates/team-agent/src/redaction.rs +72 -2
- package/crates/team-agent/src/state/persist.rs +284 -21
- package/crates/team-agent/src/state/projection.rs +245 -30
- package/crates/team-agent/src/state/repository/intent.rs +13 -0
- package/crates/team-agent/src/state/repository/tests.rs +47 -0
- package/crates/team-agent/src/state/repository.rs +94 -27
- package/crates/team-agent/src/state/selector.rs +9 -18
- package/package.json +4 -4
|
@@ -4,6 +4,23 @@
|
|
|
4
4
|
//! `spawn_first_with_env_unset` and `spawn_into_with_env_unset` from
|
|
5
5
|
//! launch.rs:376-405 here.
|
|
6
6
|
|
|
7
|
+
use std::collections::{BTreeMap, BTreeSet};
|
|
8
|
+
use std::path::{Path, PathBuf};
|
|
9
|
+
use std::process::Command;
|
|
10
|
+
|
|
11
|
+
use crate::lifecycle::profile_launch::parse_provider;
|
|
12
|
+
use crate::lifecycle::*;
|
|
13
|
+
use crate::model::enums::{AuthMode, DisplayBackend, PaneLiveness, Provider, ProviderEffort};
|
|
14
|
+
use crate::model::ids::AgentId;
|
|
15
|
+
use crate::model::permissions::{self, AgentPermissionInput};
|
|
16
|
+
use crate::model::yaml::{self, Value};
|
|
17
|
+
use crate::state::persist::load_runtime_state;
|
|
18
|
+
use crate::transport::{PaneId, SessionName, Target, Transport, WindowName};
|
|
19
|
+
|
|
20
|
+
use crate::lifecycle::lock::{acquire_agent_lifecycle_lock, LifecycleLockRequest};
|
|
21
|
+
|
|
22
|
+
use super::*;
|
|
23
|
+
|
|
7
24
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
8
25
|
pub enum SpawnPhase {
|
|
9
26
|
SpawnFirstWithEnvUnset,
|
|
@@ -30,3 +47,363 @@ mod tests {
|
|
|
30
47
|
.starts_with("launch.spawn."));
|
|
31
48
|
}
|
|
32
49
|
}
|
|
50
|
+
|
|
51
|
+
pub(super) fn spawn_agents(
|
|
52
|
+
workspace: &Path,
|
|
53
|
+
spec_path: &Path,
|
|
54
|
+
spec: &Value,
|
|
55
|
+
session_name: &SessionName,
|
|
56
|
+
safety: &DangerousApproval,
|
|
57
|
+
transport: &dyn Transport,
|
|
58
|
+
) -> Result<Vec<StartedAgent>, LifecycleError> {
|
|
59
|
+
// E5 解耦:team_dir(角色定义 + profiles 所在)≠ spec_path.parent()(spec 已迁出到 .team/runtime)。
|
|
60
|
+
// 优先取 state.team_dir(角色目录),回落 spec_path.parent()(legacy 同目录布局)。
|
|
61
|
+
let team_dir_buf = crate::state::persist::load_runtime_state(workspace)
|
|
62
|
+
.ok()
|
|
63
|
+
.and_then(|state| {
|
|
64
|
+
state
|
|
65
|
+
.get("team_dir")
|
|
66
|
+
.and_then(serde_json::Value::as_str)
|
|
67
|
+
.filter(|s| !s.is_empty())
|
|
68
|
+
.map(PathBuf::from)
|
|
69
|
+
});
|
|
70
|
+
let team_dir = team_dir_buf
|
|
71
|
+
.as_deref()
|
|
72
|
+
.unwrap_or_else(|| spec_path.parent().unwrap_or_else(|| Path::new(".")));
|
|
73
|
+
let runtime_fast = matches!(
|
|
74
|
+
spec.get("runtime").and_then(|v| v.get("fast")),
|
|
75
|
+
Some(Value::Bool(true))
|
|
76
|
+
);
|
|
77
|
+
let display_backend = spec_display_backend(spec);
|
|
78
|
+
let active_agent_ids = spec_agent_values(spec)
|
|
79
|
+
.into_iter()
|
|
80
|
+
.filter_map(|agent| {
|
|
81
|
+
if agent_is_paused(agent) {
|
|
82
|
+
None
|
|
83
|
+
} else {
|
|
84
|
+
agent.get("id").and_then(Value::as_str).map(AgentId::new)
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
.collect::<Vec<_>>();
|
|
88
|
+
let layout_plan = if display_backend == DisplayBackend::Adaptive {
|
|
89
|
+
adaptive_layout_plan(&active_agent_ids, ADAPTIVE_LAYOUT_MAX_PER_WINDOW)
|
|
90
|
+
} else {
|
|
91
|
+
Vec::new()
|
|
92
|
+
};
|
|
93
|
+
let mut started = Vec::new();
|
|
94
|
+
for agent in spec_agent_values(spec) {
|
|
95
|
+
let Some(agent_id_raw) = agent.get("id").and_then(Value::as_str) else {
|
|
96
|
+
continue;
|
|
97
|
+
};
|
|
98
|
+
if agent_is_paused(agent) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
let agent_id = AgentId::new(agent_id_raw);
|
|
102
|
+
let provider = agent
|
|
103
|
+
.get("provider")
|
|
104
|
+
.and_then(Value::as_str)
|
|
105
|
+
.and_then(parse_provider)
|
|
106
|
+
.unwrap_or(Provider::Codex);
|
|
107
|
+
let auth_mode = agent
|
|
108
|
+
.get("auth_mode")
|
|
109
|
+
.and_then(Value::as_str)
|
|
110
|
+
.and_then(parse_auth_mode)
|
|
111
|
+
.unwrap_or(AuthMode::Subscription);
|
|
112
|
+
let model = agent.get("model").and_then(Value::as_str);
|
|
113
|
+
let adapter = crate::provider::get_adapter(provider);
|
|
114
|
+
// Contract C / F6.4: pass the COMPILED agent context (resolved role/system prompt,
|
|
115
|
+
// tools list, per-worker MCP config) into command construction so a real worker
|
|
116
|
+
// has both the role instruction AND the callable Team Agent MCP capability.
|
|
117
|
+
// probe5 RED proved that `build_command(.., None, None, ..)` left the worker
|
|
118
|
+
// without `report_result`; placeholders are substituted at spawn time.
|
|
119
|
+
let command_agent = crate::lifecycle::worker_command_context::WorkerCommandAgent::from_yaml(
|
|
120
|
+
agent,
|
|
121
|
+
Some(agent_id_raw),
|
|
122
|
+
provider,
|
|
123
|
+
);
|
|
124
|
+
let system_prompt =
|
|
125
|
+
crate::lifecycle::worker_command_context::compile_worker_system_prompt(&command_agent)?;
|
|
126
|
+
let tools = crate::lifecycle::worker_command_context::resolved_tool_strings_for_command(
|
|
127
|
+
&command_agent,
|
|
128
|
+
provider,
|
|
129
|
+
safety,
|
|
130
|
+
)?;
|
|
131
|
+
let resolved_tool_refs: Vec<&str> = tools.iter().map(String::as_str).collect();
|
|
132
|
+
let mcp_team_id = runtime_team_key_for_spec(spec_path, spec, session_name);
|
|
133
|
+
let mcp_config = adapter
|
|
134
|
+
.mcp_config(auth_mode)
|
|
135
|
+
.map_err(|e| LifecycleError::Provider(e.to_string()))?;
|
|
136
|
+
let mcp_config = resolve_mcp_config(mcp_config, workspace, agent_id_raw, &mcp_team_id);
|
|
137
|
+
let mcp_config_path = write_worker_mcp_config_for_provider(
|
|
138
|
+
workspace,
|
|
139
|
+
agent_id_raw,
|
|
140
|
+
&mcp_config,
|
|
141
|
+
Some(provider),
|
|
142
|
+
)?;
|
|
143
|
+
let profile_dir = team_dir.join("profiles");
|
|
144
|
+
let profile_launch =
|
|
145
|
+
crate::lifecycle::profile_launch::prepare_provider_profile_launch_with_profile_dir(
|
|
146
|
+
workspace,
|
|
147
|
+
agent_id_raw,
|
|
148
|
+
agent,
|
|
149
|
+
Some(&profile_dir),
|
|
150
|
+
Some(&mcp_config),
|
|
151
|
+
)?;
|
|
152
|
+
let command_model = profile_launch.command_overrides.model.as_deref().or(model);
|
|
153
|
+
// 0.4.x provider effort MVP step 4 + 7: resolve effort and emit
|
|
154
|
+
// unsupported warning event when the spec asked for an effort the
|
|
155
|
+
// provider can't satisfy.
|
|
156
|
+
let agent_effort = provider_effort_for_spawn(agent, provider);
|
|
157
|
+
if let Some(event_value) = provider_effort_event_if_dropped(agent, provider, agent_id_raw) {
|
|
158
|
+
let _ = crate::event_log::EventLog::new(workspace)
|
|
159
|
+
.write("provider.effort_unsupported", event_value);
|
|
160
|
+
}
|
|
161
|
+
let mut plan = adapter
|
|
162
|
+
.build_command_plan(crate::provider::ProviderCommandContext {
|
|
163
|
+
auth_mode,
|
|
164
|
+
mcp_config: Some(&mcp_config),
|
|
165
|
+
system_prompt: Some(system_prompt.as_str()),
|
|
166
|
+
model: command_model,
|
|
167
|
+
tools: &resolved_tool_refs,
|
|
168
|
+
profile_launch: Some(&profile_launch),
|
|
169
|
+
// Layer 1 self-healing (architect probe 2026-06-22): expose
|
|
170
|
+
// agent_id as a display-name hint so Claude / Copilot
|
|
171
|
+
// adapters can pass `--name <agent_id>`. Codex has no
|
|
172
|
+
// equivalent flag and ignores the hint.
|
|
173
|
+
agent_id_hint: Some(agent_id_raw),
|
|
174
|
+
effort: agent_effort,
|
|
175
|
+
})
|
|
176
|
+
.map_err(|e| LifecycleError::Provider(e.to_string()))?;
|
|
177
|
+
if !plan.managed_mcp_config && !profile_launch.managed_mcp_config {
|
|
178
|
+
point_native_mcp_config_at_file(&mut plan.argv, provider, &mcp_config_path);
|
|
179
|
+
}
|
|
180
|
+
// C-A-4 cr verdict v2 — Copilot BYOK(compatible_api)硬性校验:
|
|
181
|
+
// "A model is required for BYOK"(help-providers 原文)。检查 agent
|
|
182
|
+
// 的 model 来源:角色 spec.model > profile COPILOT_MODEL(经 env_overlay)
|
|
183
|
+
// > --model 旗(本 worker 路径不在 argv 后追加用户 --model)。三者全空 → 报错
|
|
184
|
+
// 含 "model" 字面,失败信息透传给 leader。
|
|
185
|
+
if matches!(provider, Provider::Copilot) && auth_mode == AuthMode::CompatibleApi {
|
|
186
|
+
let has_model = model.is_some_and(|s| !s.is_empty())
|
|
187
|
+
|| profile_launch
|
|
188
|
+
.command_overrides
|
|
189
|
+
.model
|
|
190
|
+
.as_deref()
|
|
191
|
+
.is_some_and(|s| !s.is_empty())
|
|
192
|
+
|| profile_launch
|
|
193
|
+
.env_overlay
|
|
194
|
+
.get("COPILOT_MODEL")
|
|
195
|
+
.is_some_and(|v| !v.is_empty());
|
|
196
|
+
if !has_model {
|
|
197
|
+
return Err(LifecycleError::RequirementUnmet(
|
|
198
|
+
"copilot BYOK profile requires a model (set COPILOT_MODEL, agent.model, or --model)"
|
|
199
|
+
.to_string(),
|
|
200
|
+
));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// §B1 + C-7-1 + C-6-2 + C-3-2 cr verdict v2 — Copilot launch-time argv 注入:
|
|
204
|
+
// -n <agent_id> 会话命名(main-help:104)→ resume-by-name + 人查 双键
|
|
205
|
+
// -C <workspace> 双保险 cwd(main-help:55-56),防 shell 包装意外
|
|
206
|
+
// --log-dir <path> per-worker 定向日志(help-logging)→ 故障期可读 + N18 隔离
|
|
207
|
+
// --log-level info 配套日志级别
|
|
208
|
+
// --disable-mcp-server <n>... C-3-2 残留 MCP server 按名禁(扫 mcp list)
|
|
209
|
+
if matches!(provider, Provider::Copilot) {
|
|
210
|
+
plan.argv.push("-n".to_string());
|
|
211
|
+
plan.argv.push(agent_id_raw.to_string());
|
|
212
|
+
plan.argv.push("-C".to_string());
|
|
213
|
+
plan.argv.push(workspace.to_string_lossy().to_string());
|
|
214
|
+
let log_dir = workspace
|
|
215
|
+
.join(".team")
|
|
216
|
+
.join("logs")
|
|
217
|
+
.join("copilot")
|
|
218
|
+
.join(agent_id_raw);
|
|
219
|
+
std::fs::create_dir_all(&log_dir)
|
|
220
|
+
.map_err(|e| LifecycleError::StatePersist(format!("{}: {e}", log_dir.display())))?;
|
|
221
|
+
plan.argv.push("--log-dir".to_string());
|
|
222
|
+
plan.argv.push(log_dir.to_string_lossy().to_string());
|
|
223
|
+
plan.argv.push("--log-level".to_string());
|
|
224
|
+
plan.argv.push("info".to_string());
|
|
225
|
+
// C-3-2/C-3-3 cr verdict v2 — spawn 前扫 `copilot mcp list` 找用户全局/
|
|
226
|
+
// workspace 的 MCP 残留,对每个非 team_orchestrator server 追加
|
|
227
|
+
// --disable-mcp-server <name>,并落 mcp-residual.txt + event。
|
|
228
|
+
apply_copilot_mcp_residual_disables(
|
|
229
|
+
&workspace,
|
|
230
|
+
agent_id_raw,
|
|
231
|
+
&mut plan.argv,
|
|
232
|
+
&log_dir,
|
|
233
|
+
)?;
|
|
234
|
+
}
|
|
235
|
+
fill_spawn_placeholders_full(&mut plan.argv, workspace, agent_id_raw, Some(&mcp_team_id));
|
|
236
|
+
let mut env =
|
|
237
|
+
inherited_env_with_team_overrides(workspace, agent_id_raw, Some(&mcp_team_id));
|
|
238
|
+
apply_profile_launch_env(&mut env, &profile_launch);
|
|
239
|
+
apply_mcp_auto_approval_env(&mut env, &safety);
|
|
240
|
+
// Python providers.py:145 + launch/core.py:253 — fresh launch runs the worker
|
|
241
|
+
// with cwd=workspace, same as the RS fork/add and restart paths.
|
|
242
|
+
let env_unset = crate::layout::worker_env::isolate_worker_spawn_env(
|
|
243
|
+
provider,
|
|
244
|
+
&mut env,
|
|
245
|
+
extend_worker_env_unset_for_effort(
|
|
246
|
+
profile_launch.env_unset.iter().cloned().collect(),
|
|
247
|
+
provider,
|
|
248
|
+
),
|
|
249
|
+
);
|
|
250
|
+
// BUG / C-1-2 / C-6-1 cr verdict — Copilot system_prompt 走 spawn env overlay +
|
|
251
|
+
// per-worker AGENTS.md(B2 灵魂件降级):写
|
|
252
|
+
// <workspace>/.team/runtime/copilot-instructions/<agent_id>/AGENTS.md
|
|
253
|
+
// 全文 == compile_worker_system_prompt 输出,并通过 spawn env
|
|
254
|
+
// `COPILOT_CUSTOM_INSTRUCTIONS_DIRS=<该目录>` 让 copilot CLI 加载。
|
|
255
|
+
// **禁** silent 写 ~/.copilot/AGENTS.md(C-1-2)+ **禁** -i 作首条消息(C-1-5)。
|
|
256
|
+
if matches!(provider, Provider::Copilot) {
|
|
257
|
+
apply_copilot_instructions_overlay(
|
|
258
|
+
workspace,
|
|
259
|
+
agent_id_raw,
|
|
260
|
+
system_prompt.as_str(),
|
|
261
|
+
&mut env,
|
|
262
|
+
)?;
|
|
263
|
+
// C-A-6 cr verdict v2 — Copilot worker env 全量继承下,用户 shell 的
|
|
264
|
+
// COPILOT_GITHUB_TOKEN / GH_TOKEN / GITHUB_TOKEN 会穿透 + 按 cmd-login 实证
|
|
265
|
+
// **优先于凭据库**(可能静默改变 auth 通道)。一期只观测不剥除(剥除是
|
|
266
|
+
// 行为变更,cr 裁);命中任一就发 warn event 让 user 可见。
|
|
267
|
+
let mut passthrough: Vec<String> = Vec::new();
|
|
268
|
+
for key in ["COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN"] {
|
|
269
|
+
if env.get(key).is_some_and(|v| !v.is_empty()) {
|
|
270
|
+
passthrough.push(key.to_string());
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if !passthrough.is_empty() {
|
|
274
|
+
let event_log = crate::event_log::EventLog::new(workspace);
|
|
275
|
+
let _ = event_log.write(
|
|
276
|
+
"provider.copilot.token_passthrough_warning",
|
|
277
|
+
serde_json::json!({
|
|
278
|
+
"agent_id": agent_id_raw,
|
|
279
|
+
"tokens": passthrough,
|
|
280
|
+
"reason": "user shell GITHUB_TOKEN family takes precedence over copilot credential store (cmd-login)",
|
|
281
|
+
}),
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
let spawn_epoch = u64::try_from(started.len()).unwrap_or(u64::MAX);
|
|
286
|
+
let spawned_at = spawn_timestamp_for_agent(u32::try_from(spawn_epoch).unwrap_or(u32::MAX));
|
|
287
|
+
// E6 层1 实证3 + 诊断留痕:落最终 worker argv(spawn 前的真实形态)。
|
|
288
|
+
// 任何"--session-id 预定 UUID 没生效"必须能从 events.jsonl 回答:argv 里到底有没有它。
|
|
289
|
+
// 抽出 --session-id 值单列,方便和盘上 ~/.claude/projects/<cwd> 实际落的 UUID 对账。
|
|
290
|
+
{
|
|
291
|
+
let session_id_in_argv = plan
|
|
292
|
+
.argv
|
|
293
|
+
.iter()
|
|
294
|
+
.position(|a| a == "--session-id")
|
|
295
|
+
.and_then(|i| plan.argv.get(i + 1))
|
|
296
|
+
.cloned();
|
|
297
|
+
let event_log = crate::event_log::EventLog::new(workspace);
|
|
298
|
+
let _ = event_log.write(
|
|
299
|
+
crate::event_log::PROVIDER_WORKER_SPAWN_ARGV,
|
|
300
|
+
crate::event_log::provider_worker_spawn_argv_fields(serde_json::json!({
|
|
301
|
+
"agent_id": agent_id_raw,
|
|
302
|
+
"provider": provider,
|
|
303
|
+
"argv": plan.argv,
|
|
304
|
+
"session_id_in_argv": session_id_in_argv,
|
|
305
|
+
"expected_session_id": plan.expected_session_id.as_ref().map(|s| s.as_str()),
|
|
306
|
+
"spawn_cwd": workspace.to_string_lossy(),
|
|
307
|
+
"spawned_at": spawned_at.as_str(),
|
|
308
|
+
"source": "launch",
|
|
309
|
+
"spawn_epoch": spawn_epoch,
|
|
310
|
+
})),
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
// 0.3.28 Step 4b: replaced the `adaptive_layout_plan` 3-pane tiling
|
|
314
|
+
// with Python-parity 1-window-per-agent placement. Window name =
|
|
315
|
+
// `agent_id`; first worker creates the session via spawn_first,
|
|
316
|
+
// subsequent workers each get a new window via spawn_into. No splits
|
|
317
|
+
// in the worker session — Step 8's `assert_overlay_call_site` would
|
|
318
|
+
// catch any drift if a split call snuck back in. The `placement`
|
|
319
|
+
// variable is set to None to signal "no adaptive layout" to all
|
|
320
|
+
// downstream consumers (display dict, layout_window persistence).
|
|
321
|
+
let placement: Option<LayoutPlacement> = None;
|
|
322
|
+
let window = WindowName::new(agent_id_raw);
|
|
323
|
+
let spawn = if started.is_empty() {
|
|
324
|
+
transport.spawn_first_with_env_unset(
|
|
325
|
+
session_name,
|
|
326
|
+
&window,
|
|
327
|
+
&plan.argv,
|
|
328
|
+
workspace,
|
|
329
|
+
&env,
|
|
330
|
+
&env_unset,
|
|
331
|
+
)
|
|
332
|
+
} else {
|
|
333
|
+
transport.spawn_into_with_env_unset(
|
|
334
|
+
session_name,
|
|
335
|
+
&window,
|
|
336
|
+
&plan.argv,
|
|
337
|
+
workspace,
|
|
338
|
+
&env,
|
|
339
|
+
&env_unset,
|
|
340
|
+
)
|
|
341
|
+
}
|
|
342
|
+
.map_err(|e| LifecycleError::Transport(e.to_string()))?;
|
|
343
|
+
let _ = adapter.handle_startup_prompts(
|
|
344
|
+
transport,
|
|
345
|
+
&Target::Pane(spawn.pane_id.clone()),
|
|
346
|
+
30,
|
|
347
|
+
0.5,
|
|
348
|
+
);
|
|
349
|
+
// Python launch/core.py:235-237 — runtime.fast toggles the provider's fast mode
|
|
350
|
+
// after spawn; provider specifics live behind the adapter (F032).
|
|
351
|
+
if runtime_fast {
|
|
352
|
+
let _ = adapter.enable_fast_mode(transport, &Target::Pane(spawn.pane_id.clone()));
|
|
353
|
+
}
|
|
354
|
+
if matches!(transport.liveness(&spawn.pane_id), Ok(PaneLiveness::Dead)) {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
if placement.is_some() {
|
|
358
|
+
configure_adaptive_pane_title(
|
|
359
|
+
workspace,
|
|
360
|
+
transport,
|
|
361
|
+
session_name,
|
|
362
|
+
&window,
|
|
363
|
+
&spawn.pane_id,
|
|
364
|
+
agent_id_raw,
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
let display = if placement.is_some() {
|
|
368
|
+
WorkerDisplay::Adaptive {
|
|
369
|
+
status: DisplayStatus::Opened,
|
|
370
|
+
window: Some(spawn.window.clone()),
|
|
371
|
+
workspace_window: None,
|
|
372
|
+
pane_id: Some(spawn.pane_id.clone()),
|
|
373
|
+
pane_title: Some(agent_id_raw.to_string()),
|
|
374
|
+
target: Some(spawn.pane_id.as_str().to_string()),
|
|
375
|
+
target_worker_session: Some(session_name.as_str().to_string()),
|
|
376
|
+
linked_session: None,
|
|
377
|
+
leader_session: Some(session_name.clone()),
|
|
378
|
+
display_session: None,
|
|
379
|
+
fallback: None,
|
|
380
|
+
}
|
|
381
|
+
} else {
|
|
382
|
+
WorkerDisplay::Blocked {
|
|
383
|
+
reason: AdaptiveBlockReason::NotImplementedThisPlatform,
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
started.push(StartedAgent {
|
|
387
|
+
agent_id,
|
|
388
|
+
start_mode: StartMode::Fresh,
|
|
389
|
+
target: spawn.pane_id.as_str().to_string(),
|
|
390
|
+
spawned_at,
|
|
391
|
+
session_id: None,
|
|
392
|
+
rollout_path: None,
|
|
393
|
+
pending_session_id: plan.expected_session_id.clone(),
|
|
394
|
+
claude_config_dir: profile_launch.claude_config_dir.clone(),
|
|
395
|
+
provider_projects_root: plan
|
|
396
|
+
.provider_projects_root
|
|
397
|
+
.clone()
|
|
398
|
+
.or_else(|| profile_launch.claude_projects_root.clone()),
|
|
399
|
+
managed_mcp_config: plan.managed_mcp_config || profile_launch.managed_mcp_config,
|
|
400
|
+
layout_window: placement
|
|
401
|
+
.as_ref()
|
|
402
|
+
.map(|placement| placement.layout_window.clone()),
|
|
403
|
+
layout_index: placement.as_ref().map(|placement| placement.layout_index),
|
|
404
|
+
pane_index: placement.as_ref().map(|placement| placement.pane_index),
|
|
405
|
+
display,
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
Ok(started)
|
|
409
|
+
}
|