@team-agent/installer 0.5.69 → 0.5.71
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/mod.rs +42 -8
- package/crates/team-agent/src/compiler.rs +27 -37
- package/crates/team-agent/src/leader/start.rs +1 -1
- package/crates/team-agent/src/lifecycle/launch/pi_mcp.rs +130 -95
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +2 -12
- package/crates/team-agent/src/lifecycle/restart/common.rs +2 -12
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +23 -7
- package/crates/team-agent/src/lifecycle/restart.rs +2 -1
- package/crates/team-agent/src/lifecycle/tests/pi_catalog_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/pi_command_red.rs +36 -19
- package/crates/team-agent/src/lifecycle/tests/pi_compiler_red.rs +58 -22
- package/crates/team-agent/src/lifecycle/tests/pi_dual_entry_red.rs +27 -9
- package/crates/team-agent/src/lifecycle/tests/pi_executable_mcp_red.rs +212 -26
- package/crates/team-agent/src/lifecycle/tests/pi_session_delivery_red.rs +2 -2
- package/crates/team-agent/src/provider/adapters/pi.rs +22 -28
- package/package.json +4 -4
- package/skills/team-agent/SKILL.md +7 -1
|
@@ -3,8 +3,9 @@ use std::path::{Path, PathBuf};
|
|
|
3
3
|
use std::sync::atomic::{AtomicU32, Ordering};
|
|
4
4
|
|
|
5
5
|
use crate::lifecycle::launch::pi_mcp::{
|
|
6
|
-
parse_pi_list_models_table,
|
|
7
|
-
|
|
6
|
+
parse_pi_list_models_table, pi_seat_paths, resolve_pi_executable_chain,
|
|
7
|
+
validate_pi_adapter_identity, validate_pi_executable_chain, validate_pi_wrapper_source,
|
|
8
|
+
write_pi_wrapper, write_pi_wrapper_with_publish, PiAdapterIdentity, PiExecutableChain,
|
|
8
9
|
PiExecutableFileType, PiWrapperRequest,
|
|
9
10
|
};
|
|
10
11
|
use crate::provider::McpConfig;
|
|
@@ -18,15 +19,20 @@ const INDEX_DIGEST: &str = "16d260ac25b66346baab6ecef76680324336953bafc7be8cf95b
|
|
|
18
19
|
const CATALOG_DIGEST: &str = "726cedb6c3f6fe80a0d7b98918d8ed5063695e01f510a48f46c4bad5daab49fe";
|
|
19
20
|
static NEXT_ROOT: AtomicU32 = AtomicU32::new(0);
|
|
20
21
|
const PI_WRAPPER_CHILD: &str = "TEAM_AGENT_TEST_PI_WRAPPER_CHILD";
|
|
21
|
-
const
|
|
22
|
+
const PI_WRAPPER_AMBIENT_CHILD: &str = "TEAM_AGENT_TEST_PI_WRAPPER_AMBIENT_CHILD";
|
|
23
|
+
const PI_SYMLINK_CHILD: &str = "TEAM_AGENT_TEST_PI_SYMLINK_CHILD";
|
|
22
24
|
const PI_WRAPPER_PARENT_PID: &str = "TEAM_AGENT_TEST_PI_WRAPPER_PARENT_PID";
|
|
23
25
|
const PI_WRAPPER_TEST: &str = concat!(
|
|
24
26
|
"lifecycle::tests::pi_executable_mcp_red::",
|
|
25
27
|
"pi_wrapper_is_atomic_per_seat_and_embeds_exact_candidate"
|
|
26
28
|
);
|
|
27
|
-
const
|
|
29
|
+
const PI_WRAPPER_AMBIENT_TEST: &str = concat!(
|
|
28
30
|
"lifecycle::tests::pi_executable_mcp_red::",
|
|
29
|
-
"
|
|
31
|
+
"pi_wrapper_runtime_registration_does_not_copy_ambient_config"
|
|
32
|
+
);
|
|
33
|
+
const PI_SYMLINK_TEST: &str = concat!(
|
|
34
|
+
"lifecycle::tests::pi_executable_mcp_red::",
|
|
35
|
+
"pi_standard_npm_symlink_is_a_verified_launch_entry"
|
|
30
36
|
);
|
|
31
37
|
|
|
32
38
|
fn run_process_isolated(marker: &str, test_name: &str, body: impl FnOnce()) {
|
|
@@ -165,6 +171,60 @@ fn pi_executable_chain_freezes_wrapper_real_binary_catalog_and_plugin_identity()
|
|
|
165
171
|
std::fs::remove_dir_all(root).expect("remove executable chain fixture");
|
|
166
172
|
}
|
|
167
173
|
|
|
174
|
+
#[test]
|
|
175
|
+
fn pi_standard_npm_symlink_is_a_verified_launch_entry() {
|
|
176
|
+
run_process_isolated(PI_SYMLINK_CHILD, PI_SYMLINK_TEST, || {
|
|
177
|
+
assert_isolated_child();
|
|
178
|
+
let root = temp_root("npm-symlink");
|
|
179
|
+
let bin = root.join("bin");
|
|
180
|
+
let later_bin = root.join("later-bin");
|
|
181
|
+
let package_root = root.join("pi-mcp-adapter");
|
|
182
|
+
std::fs::create_dir_all(&bin).expect("create bin");
|
|
183
|
+
std::fs::create_dir_all(&later_bin).expect("create later bin");
|
|
184
|
+
std::fs::create_dir_all(&package_root).expect("create adapter root");
|
|
185
|
+
let real = root.join("cli.js");
|
|
186
|
+
let script = format!(
|
|
187
|
+
"#!/bin/sh\ncase \"$1\" in\n --version) printf '0.84.4\\n' ;;\n --list-models) printf 'provider model\\nteam-agent qwen3.8-27b\\n' ;;\n list) printf 'npm:pi-mcp-adapter\\n{}\\n' ;;\n *) exit 64 ;;\nesac\n",
|
|
188
|
+
package_root.display()
|
|
189
|
+
);
|
|
190
|
+
std::fs::write(&real, &script).expect("write protocol-capable Pi entry");
|
|
191
|
+
std::fs::set_permissions(&real, std::fs::Permissions::from_mode(0o755))
|
|
192
|
+
.expect("make Pi entry executable");
|
|
193
|
+
std::os::unix::fs::symlink(&real, bin.join("pi")).expect("create npm-style symlink");
|
|
194
|
+
std::fs::write(later_bin.join("pi"), script).expect("write later Pi wrapper");
|
|
195
|
+
std::fs::set_permissions(later_bin.join("pi"), std::fs::Permissions::from_mode(0o755))
|
|
196
|
+
.expect("make later Pi wrapper executable");
|
|
197
|
+
std::fs::write(
|
|
198
|
+
package_root.join("package.json"),
|
|
199
|
+
br#"{"name":"pi-mcp-adapter","version":"2.30.0","pi":{"extensions":["./index.ts"]}}"#,
|
|
200
|
+
)
|
|
201
|
+
.expect("write adapter package");
|
|
202
|
+
std::fs::write(
|
|
203
|
+
package_root.join("index.ts"),
|
|
204
|
+
b"export const createMcpAdapter = () => {};\n",
|
|
205
|
+
)
|
|
206
|
+
.expect("write adapter entry");
|
|
207
|
+
unsafe {
|
|
208
|
+
std::env::set_var(
|
|
209
|
+
"PATH",
|
|
210
|
+
std::env::join_paths([&bin, &later_bin]).expect("join fixture PATH"),
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
let (chain, models) = resolve_pi_executable_chain()
|
|
215
|
+
.expect("the first PATH Pi entry must define direct Pi behavior");
|
|
216
|
+
assert_eq!(chain.path_entry, bin.join("pi"));
|
|
217
|
+
assert_eq!(chain.path_entry_type, PiExecutableFileType::Symlink);
|
|
218
|
+
assert_eq!(chain.launch_executable, bin.join("pi"));
|
|
219
|
+
assert_eq!(
|
|
220
|
+
chain.real_binary,
|
|
221
|
+
std::fs::canonicalize(&real).expect("canonical real Pi entry")
|
|
222
|
+
);
|
|
223
|
+
assert_eq!(models, ["team-agent/qwen3.8-27b"]);
|
|
224
|
+
std::fs::remove_dir_all(root).expect("remove symlink fixture");
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
168
228
|
#[test]
|
|
169
229
|
fn pi_adapter_detector_requires_exact_2_30_0_entry_and_digest() {
|
|
170
230
|
let root = temp_root("adapter-identity");
|
|
@@ -234,18 +294,37 @@ fn pi_wrapper_is_atomic_body(hermetic: &HermeticTestEnv) {
|
|
|
234
294
|
);
|
|
235
295
|
|
|
236
296
|
let source = std::fs::read_to_string(&written).expect("read wrapper");
|
|
237
|
-
assert!(source.contains(adapter.index_ts.to_string_lossy().as_ref()));
|
|
238
297
|
assert!(source.contains(candidate.to_string_lossy().as_ref()));
|
|
239
298
|
assert!(source.contains("TEAM_AGENT_ID") && source.contains("worker-a"));
|
|
240
299
|
assert!(source.contains("TEAM_AGENT_OWNER_TEAM_ID") && source.contains("team-a"));
|
|
241
|
-
assert!(source.contains("
|
|
242
|
-
assert!(source.contains("
|
|
243
|
-
assert!(source.contains("toolPrefix") && source.contains("server"));
|
|
300
|
+
assert!(source.contains("pi-mcp-adapter:runtime-register:v1"));
|
|
301
|
+
assert!(source.contains("session_start") && source.contains("session_shutdown"));
|
|
244
302
|
assert!(source.contains("includeTools"));
|
|
245
303
|
assert!(source.contains("send_message") && source.contains("report_result"));
|
|
304
|
+
assert!(!source.contains("createMcpAdapter"));
|
|
305
|
+
assert!(!source.contains("directTools"));
|
|
306
|
+
assert!(!source.contains("toolPrefix"));
|
|
307
|
+
assert!(!source.contains("\"lifecycle\""));
|
|
246
308
|
assert!(!source.contains("command: \"team-agent\""));
|
|
247
309
|
assert!(!source.contains("--mcp-config"));
|
|
248
310
|
|
|
311
|
+
let other_destination = workspace.join(".team/runtime/pi/team-a/worker-b/team-mcp.ts");
|
|
312
|
+
write_pi_wrapper(PiWrapperRequest {
|
|
313
|
+
destination: &other_destination,
|
|
314
|
+
adapter: &adapter,
|
|
315
|
+
candidate_executable: &candidate,
|
|
316
|
+
mcp_config: &mcp_config(&candidate, &workspace, "worker-b"),
|
|
317
|
+
team_id: "team-a",
|
|
318
|
+
agent_id: "worker-b",
|
|
319
|
+
workspace: &workspace,
|
|
320
|
+
include_tools: &["send_message", "report_result"],
|
|
321
|
+
})
|
|
322
|
+
.expect("write second seat wrapper");
|
|
323
|
+
let other_source = std::fs::read_to_string(&other_destination).expect("read second wrapper");
|
|
324
|
+
assert!(source.contains("worker-a") && !source.contains("worker-b"));
|
|
325
|
+
assert!(other_source.contains("worker-b") && !other_source.contains("worker-a"));
|
|
326
|
+
assert_ne!(written, other_destination);
|
|
327
|
+
|
|
249
328
|
let entries = std::fs::read_dir(destination.parent().expect("wrapper parent"))
|
|
250
329
|
.expect("list wrapper parent")
|
|
251
330
|
.map(|entry| entry.expect("entry").file_name())
|
|
@@ -259,8 +338,126 @@ fn pi_wrapper_is_atomic_body(hermetic: &HermeticTestEnv) {
|
|
|
259
338
|
}
|
|
260
339
|
|
|
261
340
|
#[test]
|
|
262
|
-
fn
|
|
263
|
-
|
|
341
|
+
fn pi_leader_wrapper_materializes_from_empty_unicode_wsl_workspace() {
|
|
342
|
+
let root = temp_root("empty-unicode-leader");
|
|
343
|
+
let workspace = root.join("mnt/f/测试基础设施");
|
|
344
|
+
let candidate = root.join("candidate/team-agent");
|
|
345
|
+
std::fs::create_dir_all(&workspace).expect("create empty Unicode workspace");
|
|
346
|
+
std::fs::create_dir_all(candidate.parent().expect("candidate parent"))
|
|
347
|
+
.expect("create candidate parent");
|
|
348
|
+
std::fs::write(&candidate, b"candidate").expect("write candidate fixture");
|
|
349
|
+
assert!(
|
|
350
|
+
!workspace.join(".team").exists(),
|
|
351
|
+
"fixture must begin without a .team directory"
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
let paths = pi_seat_paths(&workspace, "current", "leader");
|
|
355
|
+
let adapter = adapter_identity(&root);
|
|
356
|
+
let written = write_pi_wrapper(PiWrapperRequest {
|
|
357
|
+
destination: &paths.wrapper,
|
|
358
|
+
adapter: &adapter,
|
|
359
|
+
candidate_executable: &candidate,
|
|
360
|
+
mcp_config: &mcp_config(&candidate, &workspace, "leader"),
|
|
361
|
+
team_id: "current",
|
|
362
|
+
agent_id: "leader",
|
|
363
|
+
workspace: &workspace,
|
|
364
|
+
include_tools: &["assign_task", "send_message"],
|
|
365
|
+
})
|
|
366
|
+
.expect("materialize Pi leader wrapper from an empty Unicode workspace");
|
|
367
|
+
|
|
368
|
+
assert_eq!(written, paths.wrapper);
|
|
369
|
+
assert!(written.is_file(), "published leader wrapper must exist");
|
|
370
|
+
assert!(
|
|
371
|
+
paths.runtime_root.is_dir(),
|
|
372
|
+
"leader runtime root must be created recursively"
|
|
373
|
+
);
|
|
374
|
+
let source = std::fs::read_to_string(&written).expect("read complete leader wrapper");
|
|
375
|
+
validate_pi_wrapper_source(&source, &adapter, &candidate)
|
|
376
|
+
.expect("published leader wrapper must be complete and valid");
|
|
377
|
+
assert!(source.contains("TEAM_AGENT_ID") && source.contains("leader"));
|
|
378
|
+
assert!(source.contains("TEAM_AGENT_OWNER_TEAM_ID") && source.contains("current"));
|
|
379
|
+
std::fs::remove_dir_all(root).expect("remove Unicode leader fixture");
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
#[test]
|
|
383
|
+
fn pi_wrapper_permissions_are_finalized_before_atomic_publish() {
|
|
384
|
+
let source = include_str!("../launch/pi_mcp.rs");
|
|
385
|
+
let write_start = source
|
|
386
|
+
.find("pub(crate) fn write_pi_wrapper")
|
|
387
|
+
.expect("write_pi_wrapper source");
|
|
388
|
+
let write_source = &source[write_start..];
|
|
389
|
+
let permissions = write_source
|
|
390
|
+
.find("file.set_permissions")
|
|
391
|
+
.expect("wrapper permissions must use the open temp-file handle");
|
|
392
|
+
let publish = write_source
|
|
393
|
+
.find("publish(&temp, request.destination)")
|
|
394
|
+
.expect("atomic wrapper publish");
|
|
395
|
+
|
|
396
|
+
assert!(
|
|
397
|
+
permissions < publish,
|
|
398
|
+
"DrvFS may not resolve the destination immediately after rename; finalize mode through the open temp-file handle before publishing"
|
|
399
|
+
);
|
|
400
|
+
assert!(
|
|
401
|
+
!write_source.contains("std::fs::set_permissions(request.destination"),
|
|
402
|
+
"atomic publish must not require a second destination-path lookup"
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
#[test]
|
|
407
|
+
fn pi_wrapper_publish_needs_no_final_path_metadata_lookup() {
|
|
408
|
+
let root = temp_root("publish-path-enoent");
|
|
409
|
+
let workspace = root.join("mnt/f/测试基础设施");
|
|
410
|
+
let candidate = root.join("candidate/team-agent");
|
|
411
|
+
std::fs::create_dir_all(&workspace).expect("create Unicode workspace");
|
|
412
|
+
std::fs::create_dir_all(candidate.parent().expect("candidate parent"))
|
|
413
|
+
.expect("create candidate parent");
|
|
414
|
+
std::fs::write(&candidate, b"candidate").expect("write candidate fixture");
|
|
415
|
+
|
|
416
|
+
let paths = pi_seat_paths(&workspace, "current", "leader");
|
|
417
|
+
let published_parent = paths.runtime_root.clone();
|
|
418
|
+
let hidden_parent = published_parent.with_file_name("leader-after-rename");
|
|
419
|
+
let adapter = adapter_identity(&root);
|
|
420
|
+
write_pi_wrapper_with_publish(
|
|
421
|
+
PiWrapperRequest {
|
|
422
|
+
destination: &paths.wrapper,
|
|
423
|
+
adapter: &adapter,
|
|
424
|
+
candidate_executable: &candidate,
|
|
425
|
+
mcp_config: &mcp_config(&candidate, &workspace, "leader"),
|
|
426
|
+
team_id: "current",
|
|
427
|
+
agent_id: "leader",
|
|
428
|
+
workspace: &workspace,
|
|
429
|
+
include_tools: &["assign_task", "send_message"],
|
|
430
|
+
},
|
|
431
|
+
|temp, destination| {
|
|
432
|
+
std::fs::rename(temp, destination)?;
|
|
433
|
+
std::fs::rename(&published_parent, &hidden_parent)?;
|
|
434
|
+
assert_eq!(
|
|
435
|
+
std::fs::metadata(destination)
|
|
436
|
+
.expect_err("simulate DrvFS final-path metadata ENOENT after rename")
|
|
437
|
+
.kind(),
|
|
438
|
+
std::io::ErrorKind::NotFound
|
|
439
|
+
);
|
|
440
|
+
Ok(())
|
|
441
|
+
},
|
|
442
|
+
)
|
|
443
|
+
.expect("successful publish must not perform final-path metadata operations");
|
|
444
|
+
|
|
445
|
+
let published = hidden_parent.join("team-mcp.ts");
|
|
446
|
+
assert!(published.is_file(), "rename published a complete wrapper");
|
|
447
|
+
assert_eq!(
|
|
448
|
+
std::fs::metadata(&published)
|
|
449
|
+
.expect("published wrapper metadata through converged path")
|
|
450
|
+
.permissions()
|
|
451
|
+
.mode()
|
|
452
|
+
& 0o777,
|
|
453
|
+
0o600
|
|
454
|
+
);
|
|
455
|
+
std::fs::remove_dir_all(root).expect("remove publish-path fixture");
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
#[test]
|
|
459
|
+
fn pi_wrapper_runtime_registration_does_not_copy_ambient_config() {
|
|
460
|
+
run_process_isolated(PI_WRAPPER_AMBIENT_CHILD, PI_WRAPPER_AMBIENT_TEST, || {
|
|
264
461
|
let hermetic = HermeticTestEnv::enter("pi-wrapper-negative-send");
|
|
265
462
|
assert_isolated_child();
|
|
266
463
|
pi_wrapper_import_failure_body(&hermetic);
|
|
@@ -293,24 +490,13 @@ fn pi_wrapper_import_failure_body(hermetic: &HermeticTestEnv) {
|
|
|
293
490
|
workspace: &workspace,
|
|
294
491
|
include_tools: &["send_message", "report_result"],
|
|
295
492
|
})
|
|
296
|
-
.expect("
|
|
493
|
+
.expect("runtime wrapper leaves ambient config to direct Pi");
|
|
297
494
|
let source = std::fs::read_to_string(&destination).expect("wrapper source");
|
|
298
495
|
assert!(!source.contains("ambient-evil"));
|
|
299
496
|
validate_pi_wrapper_source(&source, &adapter, &candidate)
|
|
300
|
-
.expect("
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
adapter.index_ts.to_string_lossy().as_ref(),
|
|
304
|
-
"/missing/pi-mcp-adapter/index.ts",
|
|
305
|
-
);
|
|
306
|
-
let error = validate_pi_wrapper_source(&broken, &adapter, &candidate)
|
|
307
|
-
.expect_err("broken import must refuse");
|
|
308
|
-
let text = error.to_string();
|
|
309
|
-
assert!(
|
|
310
|
-
text.contains("import") || text.contains("adapter"),
|
|
311
|
-
"got {text}"
|
|
312
|
-
);
|
|
313
|
-
assert!(!broken.contains("--mcp-config"));
|
|
497
|
+
.expect("runtime registration and exact candidate");
|
|
498
|
+
assert!(!source.contains("createMcpAdapter"));
|
|
499
|
+
assert!(!source.contains("--mcp-config"));
|
|
314
500
|
|
|
315
501
|
let missing_proxy_tools = source.replace("\"includeTools\"", "\"missingTools\"");
|
|
316
502
|
validate_pi_wrapper_source(&missing_proxy_tools, &adapter, &candidate)
|
|
@@ -56,8 +56,8 @@ fn resume_argv(path: &Path) -> Result<Vec<String>, ProviderError> {
|
|
|
56
56
|
build_pi_command_argv(PiCommandRequest {
|
|
57
57
|
executable: Path::new("/verified/pi"),
|
|
58
58
|
extension: Path::new("/workspace/.team/runtime/pi/team-a/worker-a/team-mcp.ts"),
|
|
59
|
-
model: "team-agent/qwen3.8-27b",
|
|
60
|
-
effort: ProviderEffort::Medium,
|
|
59
|
+
model: Some("team-agent/qwen3.8-27b"),
|
|
60
|
+
effort: Some(ProviderEffort::Medium),
|
|
61
61
|
system_prompt: "worker contract",
|
|
62
62
|
tool_categories: &["mcp_team"],
|
|
63
63
|
session_dir: Path::new("/workspace/.team/runtime/pi/team-a/worker-a/sessions"),
|
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
//! contract:
|
|
4
4
|
//! provides:
|
|
5
5
|
//! - name: build_pi_command_argv
|
|
6
|
-
//! what:
|
|
6
|
+
//! what: 仅从已验证输入构造保留 direct Pi 配置的 Team Agent argv
|
|
7
7
|
//! boundary:
|
|
8
8
|
//! - 不发现 executable、catalog、adapter 或 MCP 配置
|
|
9
9
|
//! - 除 resume exact backing 存在性外不读文件;不写文件、不启动进程
|
|
10
10
|
//! maturity: wired
|
|
11
11
|
//! ---
|
|
12
12
|
|
|
13
|
-
use std::collections::BTreeSet;
|
|
14
13
|
use std::path::Path;
|
|
15
14
|
|
|
16
15
|
use crate::model::enums::ProviderEffort;
|
|
@@ -47,8 +46,8 @@ pub(crate) enum PiSessionSelector<'a> {
|
|
|
47
46
|
pub(crate) struct PiCommandRequest<'a> {
|
|
48
47
|
pub executable: &'a Path,
|
|
49
48
|
pub extension: &'a Path,
|
|
50
|
-
pub model:
|
|
51
|
-
pub effort: ProviderEffort
|
|
49
|
+
pub model: Option<&'a str>,
|
|
50
|
+
pub effort: Option<ProviderEffort>,
|
|
52
51
|
pub system_prompt: &'a str,
|
|
53
52
|
pub tool_categories: &'a [&'a str],
|
|
54
53
|
pub session_dir: &'a Path,
|
|
@@ -57,7 +56,7 @@ pub(crate) struct PiCommandRequest<'a> {
|
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
/// ---
|
|
60
|
-
/// purpose: 从已验证 semantic request
|
|
59
|
+
/// purpose: 从已验证 semantic request 构造仅追加 Team Agent MCP/提示/session 的 Pi argv
|
|
61
60
|
/// returns: exact ordered fresh/resume argv
|
|
62
61
|
/// errors: 必需字段、mcp_team 或工具 category 非法时返回 ProviderError
|
|
63
62
|
/// ---
|
|
@@ -67,23 +66,21 @@ pub(crate) fn build_pi_command_argv(
|
|
|
67
66
|
if request.executable.as_os_str().is_empty()
|
|
68
67
|
|| request.extension.as_os_str().is_empty()
|
|
69
68
|
|| request.session_dir.as_os_str().is_empty()
|
|
70
|
-
|| request.model.trim().is_empty()
|
|
69
|
+
|| request.model.is_some_and(|model| model.trim().is_empty())
|
|
71
70
|
|| request.system_prompt.trim().is_empty()
|
|
72
71
|
|| request.agent_id.trim().is_empty()
|
|
73
72
|
{
|
|
74
73
|
return Err(ProviderError::Command(
|
|
75
|
-
"Pi command requires executable, extension,
|
|
74
|
+
"Pi command requires executable, extension, prompt, session directory, and agent id"
|
|
76
75
|
.to_string(),
|
|
77
76
|
));
|
|
78
77
|
}
|
|
79
78
|
|
|
80
|
-
let mut
|
|
79
|
+
let mut has_mcp = false;
|
|
81
80
|
for category in request.tool_categories {
|
|
82
81
|
match pi_tool_mapping(category) {
|
|
83
|
-
PiToolMapping::Mcp =>
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
PiToolMapping::Builtin(names) => tools.extend(names.iter().copied()),
|
|
82
|
+
PiToolMapping::Mcp => has_mcp = true,
|
|
83
|
+
PiToolMapping::Builtin(_) => {}
|
|
87
84
|
PiToolMapping::Unsupported => {
|
|
88
85
|
return Err(ProviderError::Command(format!(
|
|
89
86
|
"Pi does not support Team Agent tool category {category:?}"
|
|
@@ -91,7 +88,7 @@ pub(crate) fn build_pi_command_argv(
|
|
|
91
88
|
}
|
|
92
89
|
}
|
|
93
90
|
}
|
|
94
|
-
if !
|
|
91
|
+
if !has_mcp {
|
|
95
92
|
return Err(ProviderError::Command(
|
|
96
93
|
"Pi command requires mcp_team".to_string(),
|
|
97
94
|
));
|
|
@@ -99,26 +96,23 @@ pub(crate) fn build_pi_command_argv(
|
|
|
99
96
|
|
|
100
97
|
let mut argv = vec![
|
|
101
98
|
request.executable.to_string_lossy().into_owned(),
|
|
102
|
-
"--no-extensions".to_string(),
|
|
103
99
|
"-e".to_string(),
|
|
104
100
|
request.extension.to_string_lossy().into_owned(),
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
"--
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
"--
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
"--system-prompt".to_string(),
|
|
101
|
+
];
|
|
102
|
+
if let Some(model) = request.model {
|
|
103
|
+
argv.push("--model".to_string());
|
|
104
|
+
argv.push(model.to_string());
|
|
105
|
+
}
|
|
106
|
+
if let Some(effort) = request.effort {
|
|
107
|
+
argv.push("--thinking".to_string());
|
|
108
|
+
argv.push(effort.as_str().to_string());
|
|
109
|
+
}
|
|
110
|
+
argv.extend([
|
|
111
|
+
"--append-system-prompt".to_string(),
|
|
116
112
|
request.system_prompt.to_string(),
|
|
117
|
-
"--tools".to_string(),
|
|
118
|
-
tools.into_iter().collect::<Vec<_>>().join(","),
|
|
119
113
|
"--session-dir".to_string(),
|
|
120
114
|
request.session_dir.to_string_lossy().into_owned(),
|
|
121
|
-
];
|
|
115
|
+
]);
|
|
122
116
|
// The shared lifecycle materializer always revalidates resume root/path/header.
|
|
123
117
|
// Keep the pure argv builder usable before a fixture root is materialized, while
|
|
124
118
|
// refusing a missing exact file once the recorded path's parent exists.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-agent/installer",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.71",
|
|
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.71",
|
|
24
|
+
"@team-agent/cli-darwin-x64": "0.5.71",
|
|
25
|
+
"@team-agent/cli-linux-x64": "0.5.71"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node npm/bincheck.mjs",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: team-agent
|
|
3
3
|
description: Use only when the user explicitly asks to start, operate, inspect, shutdown, or restart a Team Agent team. Treat the team-agent CLI as a sealed appliance.
|
|
4
4
|
requires_team_agent: ">=0.5.0"
|
|
5
|
-
last_verified_against: "0.5.
|
|
5
|
+
last_verified_against: "0.5.71"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Team Agent
|
|
@@ -42,6 +42,12 @@ Grok / `cursor_agent` have no JSONL turn-state reader (classify → Unknown).
|
|
|
42
42
|
|
|
43
43
|
## Provider Prep
|
|
44
44
|
|
|
45
|
+
### Pi TeamMate roles
|
|
46
|
+
|
|
47
|
+
Use the standard role schema with `provider: pi`, a block-style `tools` list containing `mcp_team`, and the required boolean `dangerously_skip_permissions`. That role boolean is the only bypass setting; `TEAM.md` `dangerous_auto_approve` is compatibility input, not a second user-facing switch.
|
|
48
|
+
|
|
49
|
+
Pi TeamMates keep the same login, model, extensions/plugins, skills, context files, project trust, and tools as a direct `pi` run on that machine. Team Agent only appends the role contract, a per-seat Team MCP registration, and per-seat session storage; do not duplicate those direct Pi settings in the role. `model` and `effort` remain optional compatibility overrides, and explicit values are passed through.
|
|
50
|
+
|
|
45
51
|
### Cursor provider notes
|
|
46
52
|
|
|
47
53
|
Frontmatter: `provider: cursor_agent` (not `cursor`; launcher verb is `team-agent cursor`), `auth_mode: subscription`, `name:` required (omit → `missing front matter field name`). Also required: `role:`, `tools:`, `dangerously_skip_permissions:` (bool). Subscription needs no `profile`.
|