@team-agent/installer 0.5.42 → 0.5.44
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 +129 -54
- package/crates/team-agent/src/cli/diagnose.rs +1 -2
- package/crates/team-agent/src/cli/emit.rs +1 -7
- package/crates/team-agent/src/cli/helpers.rs +3 -1
- package/crates/team-agent/src/cli/leader.rs +2 -1
- package/crates/team-agent/src/cli/mod.rs +126 -16
- package/crates/team-agent/src/cli/named_address.rs +14 -5
- package/crates/team-agent/src/cli/profile.rs +19 -7
- package/crates/team-agent/src/cli/send.rs +9 -3
- package/crates/team-agent/src/cli/status.rs +8 -30
- package/crates/team-agent/src/cli/status_port.rs +1341 -1272
- package/crates/team-agent/src/cli/tests/base.rs +738 -660
- package/crates/team-agent/src/cli/tests/compile.rs +45 -18
- package/crates/team-agent/src/cli/tests/divergence.rs +462 -444
- package/crates/team-agent/src/cli/tests/lane_c.rs +365 -282
- package/crates/team-agent/src/cli/tests/leader_watch.rs +356 -329
- package/crates/team-agent/src/cli/tests/main_preserved.rs +672 -564
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +284 -224
- package/crates/team-agent/src/cli/tests/mod.rs +17 -7
- package/crates/team-agent/src/cli/tests/named_address.rs +8 -2
- package/crates/team-agent/src/cli/tests/peer_allow.rs +10 -2
- package/crates/team-agent/src/cli/tests/run_delegation.rs +314 -273
- package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +26 -15
- package/crates/team-agent/src/cli/tests/status_send.rs +707 -629
- package/crates/team-agent/src/cli/tests/verb_install_skill.rs +20 -4
- package/crates/team-agent/src/cli/tests/verb_profile.rs +46 -17
- package/crates/team-agent/src/cli/tests/verb_validate.rs +15 -3
- package/crates/team-agent/src/codex_app_server.rs +2 -5
- package/crates/team-agent/src/compiler/tests.rs +139 -33
- package/crates/team-agent/src/compiler.rs +55 -22
- package/crates/team-agent/src/conpty/backend.rs +23 -33
- package/crates/team-agent/src/coordinator/backoff.rs +2 -7
- package/crates/team-agent/src/coordinator/conpty_shim.rs +55 -67
- package/crates/team-agent/src/coordinator/health.rs +46 -31
- package/crates/team-agent/src/coordinator/mod.rs +3 -3
- package/crates/team-agent/src/coordinator/orphan.rs +22 -10
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +51 -56
- package/crates/team-agent/src/coordinator/tests/abnormal.rs +55 -19
- package/crates/team-agent/src/coordinator/tests/basics.rs +179 -41
- package/crates/team-agent/src/coordinator/tests/daemon.rs +53 -13
- package/crates/team-agent/src/coordinator/tests/health_sync.rs +78 -19
- package/crates/team-agent/src/coordinator/tests/main_preserved.rs +61 -11
- package/crates/team-agent/src/coordinator/tests/mod.rs +33 -39
- package/crates/team-agent/src/coordinator/tests/spine.rs +52 -12
- package/crates/team-agent/src/coordinator/tests/takeover.rs +73 -15
- package/crates/team-agent/src/coordinator/tests/tick_core.rs +50 -15
- package/crates/team-agent/src/coordinator/tests/watch.rs +74 -20
- package/crates/team-agent/src/db/message_store.rs +138 -30
- package/crates/team-agent/src/db/migration.rs +249 -61
- package/crates/team-agent/src/db/schema.rs +303 -82
- package/crates/team-agent/src/diagnose/comms.rs +9 -2
- package/crates/team-agent/src/diagnose/mod.rs +1 -3
- package/crates/team-agent/src/diagnose/orphans.rs +79 -61
- package/crates/team-agent/src/event_log.rs +70 -16
- package/crates/team-agent/src/layout/manager.rs +15 -4
- package/crates/team-agent/src/layout/mod.rs +4 -4
- package/crates/team-agent/src/layout/overlay.rs +10 -3
- package/crates/team-agent/src/layout/placement.rs +5 -1
- package/crates/team-agent/src/layout/recovery.rs +4 -2
- package/crates/team-agent/src/layout/runtime_sessions.rs +7 -7
- package/crates/team-agent/src/layout/sessions.rs +17 -9
- package/crates/team-agent/src/layout/tmux_endpoint.rs +1 -1
- package/crates/team-agent/src/layout/worker_env.rs +87 -19
- package/crates/team-agent/src/leader/helpers.rs +7 -1
- package/crates/team-agent/src/leader/lease.rs +199 -89
- package/crates/team-agent/src/leader/owner_bind.rs +55 -22
- package/crates/team-agent/src/leader/provider_attribution.rs +25 -6
- package/crates/team-agent/src/leader/rediscover/tests.rs +88 -24
- package/crates/team-agent/src/leader/rediscover.rs +74 -25
- package/crates/team-agent/src/leader/registry.rs +1 -1
- package/crates/team-agent/src/leader/start.rs +75 -54
- package/crates/team-agent/src/leader/takeover.rs +46 -11
- package/crates/team-agent/src/leader/tests/basics.rs +320 -167
- package/crates/team-agent/src/leader/tests/byte_findings.rs +361 -219
- package/crates/team-agent/src/leader/tests/identity.rs +428 -356
- package/crates/team-agent/src/leader/tests/idle.rs +285 -254
- package/crates/team-agent/src/leader/tests/lease_api.rs +338 -274
- package/crates/team-agent/src/leader/tests/lease_claim.rs +643 -593
- package/crates/team-agent/src/leader/tests/mod.rs +115 -99
- package/crates/team-agent/src/leader/tests/rediscover.rs +74 -22
- package/crates/team-agent/src/leader/tests/wake_start_owner.rs +237 -211
- package/crates/team-agent/src/lib.rs +4 -4
- package/crates/team-agent/src/lifecycle/display.rs +7 -3
- package/crates/team-agent/src/lifecycle/launch.rs +55 -15
- package/crates/team-agent/src/lifecycle/mod.rs +9 -1
- package/crates/team-agent/src/lifecycle/profile_launch.rs +77 -34
- package/crates/team-agent/src/lifecycle/profile_smoke.rs +3 -1
- package/crates/team-agent/src/lifecycle/restart/agent.rs +1 -6
- package/crates/team-agent/src/lifecycle/restart/common.rs +6 -2
- package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +1 -4
- package/crates/team-agent/src/lifecycle/restart/preflight.rs +6 -5
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +26 -22
- package/crates/team-agent/src/lifecycle/restart/remove.rs +45 -35
- package/crates/team-agent/src/lifecycle/restart/team_state.rs +63 -17
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +251 -84
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +198 -48
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +2 -1
- package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +152 -32
- package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +1 -5
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +3 -0
- package/crates/team-agent/src/lifecycle/tests.rs +2 -2
- package/crates/team-agent/src/main.rs +4 -4
- package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +47 -20
- package/crates/team-agent/src/mcp_server/mod.rs +11 -2
- package/crates/team-agent/src/mcp_server/types.rs +14 -3
- package/crates/team-agent/src/mcp_server/wire.rs +230 -68
- package/crates/team-agent/src/messaging/delivery.rs +20 -18
- package/crates/team-agent/src/messaging/helpers.rs +25 -4
- package/crates/team-agent/src/messaging/leader_receiver.rs +4 -5
- package/crates/team-agent/src/messaging/mod.rs +2 -3
- package/crates/team-agent/src/messaging/selftest.rs +46 -26
- package/crates/team-agent/src/messaging/tests/main_preserved.rs +47 -12
- package/crates/team-agent/src/messaging/tests/runtime.rs +57 -22
- package/crates/team-agent/src/messaging/tests/spine.rs +154 -40
- package/crates/team-agent/src/messaging/tests/wave2.rs +31 -32
- package/crates/team-agent/src/messaging/trust.rs +25 -2
- package/crates/team-agent/src/messaging/watchers.rs +37 -9
- package/crates/team-agent/src/model/enums.rs +65 -16
- package/crates/team-agent/src/model/ids.rs +12 -3
- package/crates/team-agent/src/model/paths.rs +28 -7
- package/crates/team-agent/src/model/permissions.rs +176 -33
- package/crates/team-agent/src/model/routing.rs +66 -20
- package/crates/team-agent/src/model/spec.rs +365 -69
- package/crates/team-agent/src/model/task_graph.rs +36 -9
- package/crates/team-agent/src/model/yaml/tests.rs +24 -6
- package/crates/team-agent/src/model/yaml.rs +7 -6
- package/crates/team-agent/src/packaging/install.rs +23 -9
- package/crates/team-agent/src/packaging/migrate.rs +5 -7
- package/crates/team-agent/src/packaging/mod.rs +9 -1
- package/crates/team-agent/src/packaging/repair.rs +13 -6
- package/crates/team-agent/src/packaging/tests.rs +63 -16
- package/crates/team-agent/src/packaging/types.rs +22 -7
- package/crates/team-agent/src/platform/argv.rs +4 -1
- package/crates/team-agent/src/platform/file_lock.rs +22 -8
- package/crates/team-agent/src/platform/process.rs +54 -24
- package/crates/team-agent/src/provider/adapters/claude.rs +1 -3
- package/crates/team-agent/src/provider/approvals/parsing.rs +134 -26
- package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +13 -3
- package/crates/team-agent/src/provider/classify.rs +127 -42
- package/crates/team-agent/src/provider/faults.rs +17 -5
- package/crates/team-agent/src/provider/helpers.rs +6 -5
- package/crates/team-agent/src/provider/startup_prompt.rs +75 -27
- package/crates/team-agent/src/state/persist.rs +28 -0
- package/crates/team-agent/src/state/repository.rs +8 -3
- package/crates/team-agent/src/tmux_backend/tests.rs +1637 -1398
- package/crates/team-agent/src/tmux_backend.rs +80 -44
- package/crates/team-agent/src/topology.rs +40 -20
- package/crates/team-agent/src/transport/test_support.rs +20 -23
- package/crates/team-agent/src/transport/tests/behavior.rs +292 -293
- package/crates/team-agent/src/transport/tests/mod.rs +178 -187
- package/crates/team-agent/src/transport/tests/wire.rs +561 -525
- package/crates/team-agent/src/transport.rs +12 -17
- package/crates/team-agent/src/transport_factory.rs +29 -14
- package/package.json +4 -4
|
@@ -13,7 +13,11 @@ fn skill_source() -> PathBuf {
|
|
|
13
13
|
CTR.fetch_add(1, Ordering::Relaxed)
|
|
14
14
|
));
|
|
15
15
|
std::fs::create_dir_all(dir.join("team-agent")).unwrap();
|
|
16
|
-
std::fs::write(
|
|
16
|
+
std::fs::write(
|
|
17
|
+
dir.join("team-agent").join("SKILL.md"),
|
|
18
|
+
b"---\nname: team-agent\n---\nbody\n",
|
|
19
|
+
)
|
|
20
|
+
.unwrap();
|
|
17
21
|
dir.join("team-agent")
|
|
18
22
|
}
|
|
19
23
|
|
|
@@ -34,7 +38,11 @@ fn dispatch_routes_install_skill_dry_run_all() {
|
|
|
34
38
|
],
|
|
35
39
|
&ws,
|
|
36
40
|
);
|
|
37
|
-
assert_eq!(
|
|
41
|
+
assert_eq!(
|
|
42
|
+
code,
|
|
43
|
+
ExitCode::Ok,
|
|
44
|
+
"`install-skill --target all --source <dir> --dry-run` must route + exit 0"
|
|
45
|
+
);
|
|
38
46
|
let _ = std::fs::remove_dir_all(&ws);
|
|
39
47
|
let _ = std::fs::remove_dir_all(source.parent().unwrap());
|
|
40
48
|
}
|
|
@@ -52,7 +60,11 @@ fn install_skill_missing_source_is_usage_error() {
|
|
|
52
60
|
],
|
|
53
61
|
&ws,
|
|
54
62
|
);
|
|
55
|
-
assert_eq!(
|
|
63
|
+
assert_eq!(
|
|
64
|
+
code,
|
|
65
|
+
ExitCode::Error,
|
|
66
|
+
"install-skill without --source must error (CliError::Usage -> emit_cli_error exit 1)"
|
|
67
|
+
);
|
|
56
68
|
let _ = std::fs::remove_dir_all(&ws);
|
|
57
69
|
}
|
|
58
70
|
|
|
@@ -70,7 +82,11 @@ fn install_skill_invalid_target_is_usage_error() {
|
|
|
70
82
|
],
|
|
71
83
|
&ws,
|
|
72
84
|
);
|
|
73
|
-
assert_eq!(
|
|
85
|
+
assert_eq!(
|
|
86
|
+
code,
|
|
87
|
+
ExitCode::Error,
|
|
88
|
+
"install-skill --target bogus must error (CliError::Usage -> emit_cli_error exit 1)"
|
|
89
|
+
);
|
|
74
90
|
let _ = std::fs::remove_dir_all(&ws);
|
|
75
91
|
let _ = std::fs::remove_dir_all(source.parent().unwrap());
|
|
76
92
|
}
|
|
@@ -11,8 +11,16 @@ fn profiles_dir(ws: &std::path::Path) -> std::path::PathBuf {
|
|
|
11
11
|
fn seed_official_api_profile(ws: &std::path::Path) {
|
|
12
12
|
let dir = profiles_dir(ws);
|
|
13
13
|
std::fs::create_dir_all(&dir).unwrap();
|
|
14
|
-
std::fs::write(
|
|
15
|
-
|
|
14
|
+
std::fs::write(
|
|
15
|
+
dir.join("AGENTS.md"),
|
|
16
|
+
"# Team Agent Profile Secret Boundary\n",
|
|
17
|
+
)
|
|
18
|
+
.unwrap();
|
|
19
|
+
std::fs::write(
|
|
20
|
+
dir.join("CLAUDE.md"),
|
|
21
|
+
"# Team Agent Profile Secret Boundary\n",
|
|
22
|
+
)
|
|
23
|
+
.unwrap();
|
|
16
24
|
std::fs::write(
|
|
17
25
|
dir.join("api_prof.env"),
|
|
18
26
|
"AUTH_MODE=official_api\nPROFILE_NAME=api_prof\nAPI_KEY=sk-test-secret\nMODEL=gpt-test\n",
|
|
@@ -62,7 +70,11 @@ fn profile_init_routes_and_creates_secret_boundary_files() {
|
|
|
62
70
|
]),
|
|
63
71
|
&ws,
|
|
64
72
|
);
|
|
65
|
-
assert_eq!(
|
|
73
|
+
assert_eq!(
|
|
74
|
+
code,
|
|
75
|
+
ExitCode::Ok,
|
|
76
|
+
"`profile init ... --json` must route and exit 0"
|
|
77
|
+
);
|
|
66
78
|
|
|
67
79
|
let dir = profiles_dir(&ws);
|
|
68
80
|
assert!(
|
|
@@ -97,7 +109,11 @@ fn profile_init_routes_and_creates_secret_boundary_files() {
|
|
|
97
109
|
]),
|
|
98
110
|
&ws,
|
|
99
111
|
);
|
|
100
|
-
assert_eq!(
|
|
112
|
+
assert_eq!(
|
|
113
|
+
second,
|
|
114
|
+
ExitCode::Ok,
|
|
115
|
+
"profile init is idempotent and still exits 0"
|
|
116
|
+
);
|
|
101
117
|
assert_eq!(
|
|
102
118
|
std::fs::read_to_string(dir.join("codex_sub.env")).unwrap(),
|
|
103
119
|
"AUTH_MODE=subscription\nPROFILE_NAME=codex_sub\n",
|
|
@@ -131,20 +147,21 @@ fn profile_doctor_routes_existing_ok_and_missing_error() {
|
|
|
131
147
|
]),
|
|
132
148
|
&ws,
|
|
133
149
|
);
|
|
134
|
-
assert_eq!(
|
|
150
|
+
assert_eq!(
|
|
151
|
+
existing,
|
|
152
|
+
ExitCode::Ok,
|
|
153
|
+
"profile doctor existing profile must exit 0"
|
|
154
|
+
);
|
|
135
155
|
|
|
136
156
|
let missing = run(
|
|
137
|
-
&profile_argv(&[
|
|
138
|
-
"profile",
|
|
139
|
-
"doctor",
|
|
140
|
-
"missing",
|
|
141
|
-
"--workspace",
|
|
142
|
-
".",
|
|
143
|
-
"--json",
|
|
144
|
-
]),
|
|
157
|
+
&profile_argv(&["profile", "doctor", "missing", "--workspace", ".", "--json"]),
|
|
145
158
|
&ws,
|
|
146
159
|
);
|
|
147
|
-
assert_eq!(
|
|
160
|
+
assert_eq!(
|
|
161
|
+
missing,
|
|
162
|
+
ExitCode::Error,
|
|
163
|
+
"profile doctor missing profile must exit 1"
|
|
164
|
+
);
|
|
148
165
|
let _ = std::fs::remove_dir_all(&ws);
|
|
149
166
|
}
|
|
150
167
|
|
|
@@ -165,18 +182,30 @@ fn profile_show_routes_and_preserves_redacted_secret_contract() {
|
|
|
165
182
|
&profile_argv(&["profile", "show", "api_prof", "--workspace", ".", "--json"]),
|
|
166
183
|
&ws,
|
|
167
184
|
);
|
|
168
|
-
assert_eq!(
|
|
185
|
+
assert_eq!(
|
|
186
|
+
code,
|
|
187
|
+
ExitCode::Ok,
|
|
188
|
+
"profile show existing profile must exit 0"
|
|
189
|
+
);
|
|
169
190
|
|
|
170
191
|
let missing = run(
|
|
171
192
|
&profile_argv(&["profile", "show", "missing", "--workspace", ".", "--json"]),
|
|
172
193
|
&ws,
|
|
173
194
|
);
|
|
174
|
-
assert_eq!(
|
|
195
|
+
assert_eq!(
|
|
196
|
+
missing,
|
|
197
|
+
ExitCode::Error,
|
|
198
|
+
"profile show missing profile must exit 1"
|
|
199
|
+
);
|
|
175
200
|
|
|
176
201
|
let code_human = run(
|
|
177
202
|
&profile_argv(&["profile", "show", "api_prof", "--workspace", "."]),
|
|
178
203
|
&ws,
|
|
179
204
|
);
|
|
180
|
-
assert_eq!(
|
|
205
|
+
assert_eq!(
|
|
206
|
+
code_human,
|
|
207
|
+
ExitCode::Ok,
|
|
208
|
+
"profile show human output path must route"
|
|
209
|
+
);
|
|
181
210
|
let _ = std::fs::remove_dir_all(&ws);
|
|
182
211
|
}
|
|
@@ -92,10 +92,18 @@ tasks:
|
|
|
92
92
|
status: "pending"
|
|
93
93
|
"#;
|
|
94
94
|
|
|
95
|
-
fn write_validate_spec(
|
|
95
|
+
fn write_validate_spec(
|
|
96
|
+
ws: &std::path::Path,
|
|
97
|
+
file_name: &str,
|
|
98
|
+
provider: &str,
|
|
99
|
+
) -> std::path::PathBuf {
|
|
96
100
|
let spec = VALIDATE_SPEC_TEMPLATE
|
|
97
101
|
.replace("__WS__", &ws.to_string_lossy())
|
|
98
|
-
.replacen(
|
|
102
|
+
.replacen(
|
|
103
|
+
"provider: \"fake\"",
|
|
104
|
+
&format!("provider: \"{provider}\""),
|
|
105
|
+
1,
|
|
106
|
+
);
|
|
99
107
|
let path = ws.join(file_name);
|
|
100
108
|
std::fs::write(&path, spec).unwrap();
|
|
101
109
|
path
|
|
@@ -179,6 +187,10 @@ fn dispatch_routes_validate_default_spec() {
|
|
|
179
187
|
let ws = tmp_workspace();
|
|
180
188
|
let _spec_path = write_validate_spec(&ws, "team.spec.yaml", "fake");
|
|
181
189
|
let code = run(&["validate".to_string(), "--json".to_string()], &ws);
|
|
182
|
-
assert_eq!(
|
|
190
|
+
assert_eq!(
|
|
191
|
+
code,
|
|
192
|
+
ExitCode::Ok,
|
|
193
|
+
"`validate --json` must route and exit 0 for default team.spec.yaml"
|
|
194
|
+
);
|
|
183
195
|
let _ = std::fs::remove_dir_all(&ws);
|
|
184
196
|
}
|
|
@@ -120,10 +120,7 @@ pub fn attach_probe(endpoint: &str, thread_id: &str) -> Result<AppServerBinding,
|
|
|
120
120
|
/// Batch 1 Windows N38 typed-unsupported stub. Callers see the same
|
|
121
121
|
/// `AppServerError` shape as Unix — no silent success, no panic.
|
|
122
122
|
#[cfg(not(unix))]
|
|
123
|
-
pub fn attach_probe(
|
|
124
|
-
_endpoint: &str,
|
|
125
|
-
_thread_id: &str,
|
|
126
|
-
) -> Result<AppServerBinding, AppServerError> {
|
|
123
|
+
pub fn attach_probe(_endpoint: &str, _thread_id: &str) -> Result<AppServerBinding, AppServerError> {
|
|
127
124
|
Err(AppServerError::SocketUnreachable(
|
|
128
125
|
"codex_app_server unix-socket client not supported on this platform \
|
|
129
126
|
(Windows); use codex CLI stdio or ConPTY worker delivery instead"
|
|
@@ -432,7 +429,7 @@ impl AppServerClient {
|
|
|
432
429
|
_ => Err(AppServerError::ProtocolMismatch(format!(
|
|
433
430
|
"{method} failed: {message}"
|
|
434
431
|
))),
|
|
435
|
-
}
|
|
432
|
+
};
|
|
436
433
|
}
|
|
437
434
|
return value
|
|
438
435
|
.get("result")
|
|
@@ -53,7 +53,13 @@ fn compact_json(v: &Value) -> String {
|
|
|
53
53
|
Value::Map(pairs) => {
|
|
54
54
|
let inner: Vec<String> = pairs
|
|
55
55
|
.iter()
|
|
56
|
-
.map(|(k, val)|
|
|
56
|
+
.map(|(k, val)| {
|
|
57
|
+
format!(
|
|
58
|
+
"{}:{}",
|
|
59
|
+
serde_json::to_string(k).unwrap(),
|
|
60
|
+
compact_json(val)
|
|
61
|
+
)
|
|
62
|
+
})
|
|
57
63
|
.collect();
|
|
58
64
|
format!("{{{}}}", inner.join(","))
|
|
59
65
|
}
|
|
@@ -148,7 +154,10 @@ fn front_matter_body_lstrip_strips_only_newlines() {
|
|
|
148
154
|
// body.lstrip("\n") removes leading NEWLINES but keeps the 2-space indent.
|
|
149
155
|
let p = write_tmp("lstrip.md", "---\nname: x\n---\n\n\n indented body\n");
|
|
150
156
|
let (meta, body) = read_front_matter(&p).unwrap();
|
|
151
|
-
assert_eq!(
|
|
157
|
+
assert_eq!(
|
|
158
|
+
meta,
|
|
159
|
+
Value::Map(vec![("name".to_string(), Value::Str("x".to_string()))])
|
|
160
|
+
);
|
|
152
161
|
assert_eq!(body, " indented body\n");
|
|
153
162
|
}
|
|
154
163
|
|
|
@@ -168,7 +177,8 @@ fn front_matter_non_object_errors() {
|
|
|
168
177
|
let p = write_tmp("list.md", "---\n- a\n- b\n---\nbody\n");
|
|
169
178
|
let err = read_front_matter(&p).unwrap_err();
|
|
170
179
|
assert!(
|
|
171
|
-
err.to_string()
|
|
180
|
+
err.to_string()
|
|
181
|
+
.contains("front matter must be a YAML object"),
|
|
172
182
|
"got: {err}"
|
|
173
183
|
);
|
|
174
184
|
}
|
|
@@ -203,9 +213,15 @@ fn compile_subscription_without_profile_is_thin_manifest() {
|
|
|
203
213
|
let team = build_team(TEAM_BASE, &[("implementer.md", ROLE_NOPROFILE)], &[]);
|
|
204
214
|
let spec = compile_team(&team).unwrap();
|
|
205
215
|
let agent = &spec.get("agents").and_then(Value::as_list).unwrap()[0];
|
|
206
|
-
assert_eq!(
|
|
216
|
+
assert_eq!(
|
|
217
|
+
agent.get("auth_mode").and_then(Value::as_str),
|
|
218
|
+
Some("subscription")
|
|
219
|
+
);
|
|
207
220
|
assert!(agent.get("profile").is_none(), "profile key must be absent");
|
|
208
|
-
assert!(
|
|
221
|
+
assert!(
|
|
222
|
+
agent.get("credential_ref").is_none(),
|
|
223
|
+
"credential_ref key must be absent"
|
|
224
|
+
);
|
|
209
225
|
}
|
|
210
226
|
|
|
211
227
|
// Runtime front-matter defaults: every knob from TEAM.md flows through without
|
|
@@ -247,7 +263,11 @@ const RUNTIME_DEFAULTS_JSON: &str = r#"{"version":1,"team":{"name":"doc-team","m
|
|
|
247
263
|
|
|
248
264
|
#[test]
|
|
249
265
|
fn compile_runtime_front_matter_defaults_match_python() {
|
|
250
|
-
let team = build_team(
|
|
266
|
+
let team = build_team(
|
|
267
|
+
RUNTIME_DEFAULTS_TEAM,
|
|
268
|
+
&[("implementer.md", RUNTIME_DEFAULTS_ROLE)],
|
|
269
|
+
&[],
|
|
270
|
+
);
|
|
251
271
|
let spec = compile_team(&team).unwrap();
|
|
252
272
|
assert_eq!(templated_compact_json(&spec), RUNTIME_DEFAULTS_JSON);
|
|
253
273
|
}
|
|
@@ -357,7 +377,11 @@ const TWO_AGENTS_JSON: &str = r#"{"version":1,"team":{"name":"doc-team","mode":"
|
|
|
357
377
|
#[test]
|
|
358
378
|
fn compile_two_agents_sorted_by_filename_with_routing_and_startup_order() {
|
|
359
379
|
// filenames intentionally out of name order to prove sorted(glob) is by filename.
|
|
360
|
-
let team = build_team(
|
|
380
|
+
let team = build_team(
|
|
381
|
+
TEAM_BASE,
|
|
382
|
+
&[("02-bravo.md", TWO_ROLE_B), ("01-alpha.md", TWO_ROLE_A)],
|
|
383
|
+
&[],
|
|
384
|
+
);
|
|
361
385
|
let spec = compile_team(&team).unwrap();
|
|
362
386
|
assert_eq!(templated_compact_json(&spec), TWO_AGENTS_JSON);
|
|
363
387
|
}
|
|
@@ -382,7 +406,8 @@ fn compile_missing_required_provider_field_errors() {
|
|
|
382
406
|
let team = build_team(TEAM_BASE, &[("implementer.md", ROLE_MISSING_PROVIDER)], &[]);
|
|
383
407
|
let err = compile_team(&team).unwrap_err();
|
|
384
408
|
assert!(
|
|
385
|
-
err.to_string()
|
|
409
|
+
err.to_string()
|
|
410
|
+
.contains("missing front matter field provider"),
|
|
386
411
|
"got: {err}"
|
|
387
412
|
);
|
|
388
413
|
}
|
|
@@ -403,9 +428,16 @@ Implement bounded tasks.
|
|
|
403
428
|
|
|
404
429
|
#[test]
|
|
405
430
|
fn compile_compatible_api_without_profile_errors() {
|
|
406
|
-
let team = build_team(
|
|
431
|
+
let team = build_team(
|
|
432
|
+
TEAM_BASE,
|
|
433
|
+
&[("implementer.md", ROLE_COMPATIBLE_NO_PROFILE)],
|
|
434
|
+
&[],
|
|
435
|
+
);
|
|
407
436
|
let err = compile_team(&team).unwrap_err();
|
|
408
|
-
assert!(
|
|
437
|
+
assert!(
|
|
438
|
+
err.to_string().contains("profile is required"),
|
|
439
|
+
"got: {err}"
|
|
440
|
+
);
|
|
409
441
|
}
|
|
410
442
|
|
|
411
443
|
// ════════════════════════ FIX-LOOP (wave-1) RED tests ════════════════════════
|
|
@@ -475,7 +507,10 @@ fn fix_a1_provider_models_precede_default_model() {
|
|
|
475
507
|
let tm = "---\nname: T\nprovider: codex\ndefault_model: team-y\nprovider_models:\n codex: pm-z\n---\nx\n";
|
|
476
508
|
let team = build_team(tm, &[("w.md", &role_nomodel("codex"))], &[]);
|
|
477
509
|
let spec = compile_team(&team).unwrap();
|
|
478
|
-
assert_eq!(
|
|
510
|
+
assert_eq!(
|
|
511
|
+
agent0(&spec).get("model").and_then(Value::as_str),
|
|
512
|
+
Some("pm-z")
|
|
513
|
+
);
|
|
479
514
|
}
|
|
480
515
|
|
|
481
516
|
#[test]
|
|
@@ -484,7 +519,10 @@ fn fix_a1_claude_aliases_to_claude_code_provider_models() {
|
|
|
484
519
|
let tm = "---\nname: T\nprovider: codex\nprovider_models:\n claude_code: cc-v\n---\nx\n";
|
|
485
520
|
let team = build_team(tm, &[("w.md", &role_nomodel("claude"))], &[]);
|
|
486
521
|
let spec = compile_team(&team).unwrap();
|
|
487
|
-
assert_eq!(
|
|
522
|
+
assert_eq!(
|
|
523
|
+
agent0(&spec).get("model").and_then(Value::as_str),
|
|
524
|
+
Some("cc-v")
|
|
525
|
+
);
|
|
488
526
|
}
|
|
489
527
|
|
|
490
528
|
#[test]
|
|
@@ -507,7 +545,11 @@ fn fix_a1_model_null_when_provider_absent_from_table() {
|
|
|
507
545
|
for prov in ["gemini_cli", "fake"] {
|
|
508
546
|
let team = build_team(TM_CODEX, &[("w.md", &role_nomodel(prov))], &[]);
|
|
509
547
|
let spec = compile_team(&team).unwrap();
|
|
510
|
-
assert_eq!(
|
|
548
|
+
assert_eq!(
|
|
549
|
+
agent0(&spec).get("model"),
|
|
550
|
+
Some(&Value::Null),
|
|
551
|
+
"provider {prov} → null"
|
|
552
|
+
);
|
|
511
553
|
}
|
|
512
554
|
}
|
|
513
555
|
|
|
@@ -526,7 +568,10 @@ fn fix_a2_objective_default_when_no_objective_and_no_body() {
|
|
|
526
568
|
let tm = "---\nname: T\nprovider: codex\n---\n";
|
|
527
569
|
let team = build_team(tm, &[("w.md", &role_nomodel("codex"))], &[]);
|
|
528
570
|
let spec = compile_team(&team).unwrap();
|
|
529
|
-
assert_eq!(
|
|
571
|
+
assert_eq!(
|
|
572
|
+
str_path(&spec, &["team", "objective"]),
|
|
573
|
+
"Team Agent document-driven team."
|
|
574
|
+
);
|
|
530
575
|
}
|
|
531
576
|
|
|
532
577
|
// ── A3 name ──
|
|
@@ -535,7 +580,12 @@ fn fix_a2_objective_default_when_no_objective_and_no_body() {
|
|
|
535
580
|
fn fix_a3_name_falls_back_to_parent_dir_name() {
|
|
536
581
|
// no `name` → team_dir.parent.name (NOT a hardcoded "team").
|
|
537
582
|
let tm = "---\nprovider: codex\n---\nx\n";
|
|
538
|
-
let team = build_layout(
|
|
583
|
+
let team = build_layout(
|
|
584
|
+
"my-parent-dir",
|
|
585
|
+
"leafteam",
|
|
586
|
+
Some(tm),
|
|
587
|
+
AgentsDir::WithRole(&role_nomodel("codex")),
|
|
588
|
+
);
|
|
539
589
|
let spec = compile_team(&team).unwrap();
|
|
540
590
|
assert_eq!(str_path(&spec, &["team", "name"]), "my-parent-dir");
|
|
541
591
|
}
|
|
@@ -558,7 +608,10 @@ fn fix_a5_session_name_slugifies_team_name() {
|
|
|
558
608
|
let tm = "---\nname: My Team!\nprovider: codex\n---\nx\n";
|
|
559
609
|
let team = build_team(tm, &[("w.md", &role_nomodel("codex"))], &[]);
|
|
560
610
|
let spec = compile_team(&team).unwrap();
|
|
561
|
-
assert_eq!(
|
|
611
|
+
assert_eq!(
|
|
612
|
+
str_path(&spec, &["runtime", "session_name"]),
|
|
613
|
+
"team-My-Team"
|
|
614
|
+
);
|
|
562
615
|
}
|
|
563
616
|
|
|
564
617
|
#[test]
|
|
@@ -576,7 +629,10 @@ fn fix_a6_tools_shell_maps_to_execute_bash() {
|
|
|
576
629
|
let role = "---\nname: w\nrole: R\nprovider: codex\nauth_mode: subscription\ntools:\n - shell\n - mcp_team\n---\nb\n";
|
|
577
630
|
let team = build_team(TM_CODEX, &[("w.md", role)], &[]);
|
|
578
631
|
let spec = compile_team(&team).expect("shell must normalize to execute_bash and compile");
|
|
579
|
-
assert_eq!(
|
|
632
|
+
assert_eq!(
|
|
633
|
+
agent0(&spec).get("tools"),
|
|
634
|
+
Some(&list_str(vec!["execute_bash", "mcp_team"]))
|
|
635
|
+
);
|
|
580
636
|
}
|
|
581
637
|
|
|
582
638
|
#[test]
|
|
@@ -584,7 +640,10 @@ fn fix_a6_missing_tools_errors() {
|
|
|
584
640
|
let role = "---\nname: w\nrole: R\nprovider: codex\nauth_mode: subscription\n---\nb\n";
|
|
585
641
|
let team = build_team(TM_CODEX, &[("w.md", role)], &[]);
|
|
586
642
|
let err = compile_team(&team).unwrap_err();
|
|
587
|
-
assert!(
|
|
643
|
+
assert!(
|
|
644
|
+
err.to_string().contains("missing front matter field tools"),
|
|
645
|
+
"got: {err}"
|
|
646
|
+
);
|
|
588
647
|
}
|
|
589
648
|
|
|
590
649
|
#[test]
|
|
@@ -592,7 +651,10 @@ fn fix_a6_tools_not_a_list_errors() {
|
|
|
592
651
|
let role = "---\nname: w\nrole: R\nprovider: codex\nauth_mode: subscription\ntools: justastring\n---\nb\n";
|
|
593
652
|
let team = build_team(TM_CODEX, &[("w.md", role)], &[]);
|
|
594
653
|
let err = compile_team(&team).unwrap_err();
|
|
595
|
-
assert!(
|
|
654
|
+
assert!(
|
|
655
|
+
err.to_string().contains("tools must be a list"),
|
|
656
|
+
"got: {err}"
|
|
657
|
+
);
|
|
596
658
|
}
|
|
597
659
|
|
|
598
660
|
// ── A7 system_prompt inline ──
|
|
@@ -602,7 +664,10 @@ fn fix_a7_empty_body_inline_falls_back_to_role() {
|
|
|
602
664
|
let role = "---\nname: w\nrole: Reviewer Role\nprovider: codex\nauth_mode: subscription\ntools:\n - mcp_team\n---\n";
|
|
603
665
|
let team = build_team(TM_CODEX, &[("w.md", role)], &[]);
|
|
604
666
|
let spec = compile_team(&team).unwrap();
|
|
605
|
-
assert_eq!(
|
|
667
|
+
assert_eq!(
|
|
668
|
+
str_path(agent0(&spec), &["system_prompt", "inline"]),
|
|
669
|
+
"Reviewer Role"
|
|
670
|
+
);
|
|
606
671
|
}
|
|
607
672
|
|
|
608
673
|
// ── A8 auth_mode ──
|
|
@@ -614,7 +679,8 @@ fn fix_a8_official_api_without_profile_errors() {
|
|
|
614
679
|
let team = build_team(TM_CODEX, &[("w.md", role)], &[]);
|
|
615
680
|
let err = compile_team(&team).unwrap_err();
|
|
616
681
|
assert!(
|
|
617
|
-
err.to_string()
|
|
682
|
+
err.to_string()
|
|
683
|
+
.contains("profile is required when auth_mode is 'official_api'"),
|
|
618
684
|
"got: {err}"
|
|
619
685
|
);
|
|
620
686
|
}
|
|
@@ -622,10 +688,15 @@ fn fix_a8_official_api_without_profile_errors() {
|
|
|
622
688
|
#[test]
|
|
623
689
|
fn fix_a8_compatible_api_error_names_the_auth_mode() {
|
|
624
690
|
// Full message form (the old test only checked the "profile is required" prefix).
|
|
625
|
-
let team = build_team(
|
|
691
|
+
let team = build_team(
|
|
692
|
+
TEAM_BASE,
|
|
693
|
+
&[("implementer.md", ROLE_COMPATIBLE_NO_PROFILE)],
|
|
694
|
+
&[],
|
|
695
|
+
);
|
|
626
696
|
let err = compile_team(&team).unwrap_err();
|
|
627
697
|
assert!(
|
|
628
|
-
err.to_string()
|
|
698
|
+
err.to_string()
|
|
699
|
+
.contains("profile is required when auth_mode is 'compatible_api'"),
|
|
629
700
|
"got: {err}"
|
|
630
701
|
);
|
|
631
702
|
}
|
|
@@ -638,8 +709,16 @@ fn fix_a9_bool_coercion_yes_and_one_are_true() {
|
|
|
638
709
|
let tm = format!("---\nname: T\nprovider: codex\ndangerous_auto_approve: {v}\nworker_to_worker: {v}\n---\nx\n");
|
|
639
710
|
let team = build_team(&tm, &[("w.md", &role_nomodel("codex"))], &[]);
|
|
640
711
|
let spec = compile_team(&team).unwrap();
|
|
641
|
-
assert_eq!(
|
|
642
|
-
|
|
712
|
+
assert_eq!(
|
|
713
|
+
get_path(&spec, &["runtime", "dangerous_auto_approve"]),
|
|
714
|
+
Some(&Value::Bool(true)),
|
|
715
|
+
"value {v}"
|
|
716
|
+
);
|
|
717
|
+
assert_eq!(
|
|
718
|
+
get_path(&spec, &["communication", "worker_to_worker"]),
|
|
719
|
+
Some(&Value::Bool(true)),
|
|
720
|
+
"value {v}"
|
|
721
|
+
);
|
|
643
722
|
}
|
|
644
723
|
}
|
|
645
724
|
|
|
@@ -650,7 +729,10 @@ fn fix_a9_bool_coercion_no_is_python_truthy() {
|
|
|
650
729
|
let tm = "---\nname: T\nprovider: codex\ndangerous_auto_approve: no\n---\nx\n";
|
|
651
730
|
let team = build_team(tm, &[("w.md", &role_nomodel("codex"))], &[]);
|
|
652
731
|
let spec = compile_team(&team).unwrap();
|
|
653
|
-
assert_eq!(
|
|
732
|
+
assert_eq!(
|
|
733
|
+
get_path(&spec, &["runtime", "dangerous_auto_approve"]),
|
|
734
|
+
Some(&Value::Bool(true))
|
|
735
|
+
);
|
|
654
736
|
}
|
|
655
737
|
|
|
656
738
|
#[test]
|
|
@@ -659,7 +741,10 @@ fn fix_a9_int_coercion_of_quoted_string() {
|
|
|
659
741
|
let tm = "---\nname: T\nprovider: codex\ntick_interval_sec: \"5\"\n---\nx\n";
|
|
660
742
|
let team = build_team(tm, &[("w.md", &role_nomodel("codex"))], &[]);
|
|
661
743
|
let spec = compile_team(&team).unwrap();
|
|
662
|
-
assert_eq!(
|
|
744
|
+
assert_eq!(
|
|
745
|
+
get_path(&spec, &["runtime", "tick_interval_sec"]),
|
|
746
|
+
Some(&Value::Int(5))
|
|
747
|
+
);
|
|
663
748
|
}
|
|
664
749
|
|
|
665
750
|
// ── A10 CRLF normalization ──
|
|
@@ -671,24 +756,42 @@ fn fix_a10_crlf_role_doc_front_matter_is_parsed() {
|
|
|
671
756
|
let crlf_role = "---\r\nname: crlfworker\r\nrole: R\r\nprovider: codex\r\nauth_mode: subscription\r\ntools:\r\n - mcp_team\r\n---\r\n\r\nbody\r\n";
|
|
672
757
|
let team = build_team(TM_CODEX, &[("w.md", crlf_role)], &[]);
|
|
673
758
|
let spec = compile_team(&team).expect("CRLF role doc must parse its front matter");
|
|
674
|
-
assert_eq!(
|
|
675
|
-
|
|
759
|
+
assert_eq!(
|
|
760
|
+
agent0(&spec).get("id").and_then(Value::as_str),
|
|
761
|
+
Some("crlfworker")
|
|
762
|
+
);
|
|
763
|
+
assert_eq!(
|
|
764
|
+
str_path(agent0(&spec), &["system_prompt", "inline"]),
|
|
765
|
+
"body"
|
|
766
|
+
);
|
|
676
767
|
}
|
|
677
768
|
|
|
678
769
|
// ── A11 error-message paths ──
|
|
679
770
|
|
|
680
771
|
#[test]
|
|
681
772
|
fn fix_a11_missing_team_md_message_includes_team_md_path() {
|
|
682
|
-
let team = build_layout(
|
|
773
|
+
let team = build_layout(
|
|
774
|
+
"p",
|
|
775
|
+
"teamdir",
|
|
776
|
+
None,
|
|
777
|
+
AgentsDir::WithRole(&role_nomodel("codex")),
|
|
778
|
+
);
|
|
683
779
|
let err = compile_team(&team).unwrap_err();
|
|
684
|
-
assert!(
|
|
780
|
+
assert!(
|
|
781
|
+
err.to_string().contains("/TEAM.md: missing TEAM.md"),
|
|
782
|
+
"got: {err}"
|
|
783
|
+
);
|
|
685
784
|
}
|
|
686
785
|
|
|
687
786
|
#[test]
|
|
688
787
|
fn fix_a11_missing_agents_dir_message_includes_agents_path() {
|
|
689
788
|
let team = build_layout("p", "teamdir", Some(TM_CODEX), AgentsDir::Missing);
|
|
690
789
|
let err = compile_team(&team).unwrap_err();
|
|
691
|
-
assert!(
|
|
790
|
+
assert!(
|
|
791
|
+
err.to_string()
|
|
792
|
+
.contains("/agents: missing agents directory"),
|
|
793
|
+
"got: {err}"
|
|
794
|
+
);
|
|
692
795
|
}
|
|
693
796
|
|
|
694
797
|
#[test]
|
|
@@ -697,5 +800,8 @@ fn fix_a11_agents_is_a_file_reports_no_role_docs() {
|
|
|
697
800
|
// gate, then the empty glob → "no role docs found" (path = agents dir).
|
|
698
801
|
let team = build_layout("p", "teamdir", Some(TM_CODEX), AgentsDir::File);
|
|
699
802
|
let err = compile_team(&team).unwrap_err();
|
|
700
|
-
assert!(
|
|
803
|
+
assert!(
|
|
804
|
+
err.to_string().contains("/agents: no role docs found"),
|
|
805
|
+
"got: {err}"
|
|
806
|
+
);
|
|
701
807
|
}
|