@team-agent/installer 0.5.111 → 0.5.113
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 +93 -779
- package/crates/team-agent/src/cli/diagnose.rs +2 -424
- package/crates/team-agent/src/cli/emit.rs +158 -373
- package/crates/team-agent/src/cli/leaders.rs +32 -32
- package/crates/team-agent/src/cli/mod.rs +8 -15
- package/crates/team-agent/src/cli/named_address.rs +17 -16
- package/crates/team-agent/src/cli/route.rs +4 -4
- package/crates/team-agent/src/cli/send/presentation.rs +19 -18
- package/crates/team-agent/src/cli/send/resolve.rs +11 -11
- package/crates/team-agent/src/cli/send.rs +4 -4
- package/crates/team-agent/src/cli/spec.rs +29 -32
- package/crates/team-agent/src/cli/status.rs +7 -7
- package/crates/team-agent/src/cli/tests/base.rs +6 -6
- package/crates/team-agent/src/cli/tests/divergence.rs +3 -2
- package/crates/team-agent/src/cli/tests/lane_c.rs +1 -1
- package/crates/team-agent/src/cli/tests/leader_watch.rs +2 -86
- package/crates/team-agent/src/cli/tests/main_preserved.rs +15 -140
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +1 -258
- package/crates/team-agent/src/cli/tests/mod.rs +0 -2
- package/crates/team-agent/src/cli/tests/named_address.rs +3 -3
- package/crates/team-agent/src/cli/tests/status_send.rs +7 -7
- package/crates/team-agent/src/cli/triage.rs +53 -2
- package/crates/team-agent/src/cli/types.rs +12 -116
- package/crates/team-agent/src/compiler/tests.rs +12 -0
- package/crates/team-agent/src/compiler.rs +0 -9
- package/crates/team-agent/src/coordinator/health.rs +3 -283
- package/crates/team-agent/src/coordinator/mod.rs +0 -5
- package/crates/team-agent/src/coordinator/tests/basics.rs +0 -9
- package/crates/team-agent/src/coordinator/tests/mod.rs +0 -1
- package/crates/team-agent/src/coordinator/tests/tick_core.rs +0 -17
- package/crates/team-agent/src/coordinator/types.rs +1 -21
- package/crates/team-agent/src/db/message_store.rs +0 -42
- package/crates/team-agent/src/db/migration.rs +49 -9
- package/crates/team-agent/src/db/schema.rs +27 -20
- package/crates/team-agent/src/leader/owner_bind.rs +0 -24
- package/crates/team-agent/src/leader/start.rs +5 -0
- package/crates/team-agent/src/leader/tests/byte_findings.rs +0 -20
- package/crates/team-agent/src/leader/tests/identity.rs +21 -32
- package/crates/team-agent/src/leader/tests/lease_claim.rs +0 -55
- package/crates/team-agent/src/lifecycle/launch/identity.rs +172 -13
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +325 -14
- package/crates/team-agent/src/lifecycle/launch.rs +2 -2
- package/crates/team-agent/src/lifecycle/profile_launch.rs +58 -46
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +97 -52
- package/crates/team-agent/src/lifecycle/restart.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -51
- package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +3 -2
- package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +23 -23
- package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +29 -55
- package/crates/team-agent/src/messaging/mod.rs +0 -2
- package/crates/team-agent/src/messaging/results.rs +0 -58
- package/crates/team-agent/src/messaging/tests/runtime.rs +0 -46
- package/crates/team-agent/src/provider/adapter.rs +8 -59
- package/crates/team-agent/src/provider/adapters/claude.rs +6 -14
- package/crates/team-agent/src/provider/adapters/codex.rs +8 -18
- package/crates/team-agent/src/provider/adapters/copilot.rs +5 -22
- package/crates/team-agent/src/provider/adapters/cursor_agent.rs +8 -45
- package/crates/team-agent/src/provider/adapters/grok.rs +7 -31
- package/crates/team-agent/src/provider/adapters/pi.rs +7 -18
- package/crates/team-agent/src/provider/command_helpers.rs +45 -0
- package/crates/team-agent/src/provider/mod.rs +1 -0
- package/crates/team-agent/src/provider/model_catalog.rs +21 -28
- package/crates/team-agent/src/provider/tests/adapter.rs +70 -0
- package/crates/team-agent/src/provider/tests/command_helpers.rs +55 -0
- package/crates/team-agent/src/state/repository.rs +1 -5
- package/package.json +4 -4
- package/templates/team_state.md +1 -1
- package/crates/team-agent/src/cli/tests/peer_allow.rs +0 -164
- package/crates/team-agent/src/cli/tests/verb_validate.rs +0 -195
- package/crates/team-agent/src/coordinator/tests/watch.rs +0 -223
- package/crates/team-agent/src/messaging/peers.rs +0 -34
|
@@ -1090,52 +1090,6 @@ fn collect_rejects_message_scoped_result_without_matching_recipient() {
|
|
|
1090
1090
|
);
|
|
1091
1091
|
}
|
|
1092
1092
|
|
|
1093
|
-
#[test]
|
|
1094
|
-
fn allow_peer_talk_records_bidirectional_allowlist_and_event() {
|
|
1095
|
-
let ws = tmp_ws("allowpeer");
|
|
1096
|
-
let out = allow_peer_talk(&ws, "alice", "bob").unwrap();
|
|
1097
|
-
assert_eq!(out.get("ok").and_then(|v| v.as_bool()), Some(true));
|
|
1098
|
-
assert_eq!(out.get("a").and_then(|v| v.as_str()), Some("alice"));
|
|
1099
|
-
assert_eq!(out.get("b").and_then(|v| v.as_str()), Some("bob"));
|
|
1100
|
-
assert_eq!(
|
|
1101
|
-
out.get("status").and_then(|v| v.as_str()),
|
|
1102
|
-
Some("compat_noop")
|
|
1103
|
-
);
|
|
1104
|
-
assert_eq!(
|
|
1105
|
-
out.get("reason").and_then(|v| v.as_str()),
|
|
1106
|
-
Some("team_scoped_peer_messages_enabled")
|
|
1107
|
-
);
|
|
1108
|
-
|
|
1109
|
-
let store = store_for(&ws);
|
|
1110
|
-
let conn = seed_conn(&store);
|
|
1111
|
-
let rows = conn
|
|
1112
|
-
.prepare("select a, b from peer_allowlist order by a, b")
|
|
1113
|
-
.unwrap()
|
|
1114
|
-
.query_map([], |row| {
|
|
1115
|
-
Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?))
|
|
1116
|
-
})
|
|
1117
|
-
.unwrap()
|
|
1118
|
-
.collect::<Result<Vec<_>, _>>()
|
|
1119
|
-
.unwrap();
|
|
1120
|
-
assert_eq!(
|
|
1121
|
-
rows,
|
|
1122
|
-
vec![
|
|
1123
|
-
("alice".to_string(), "bob".to_string()),
|
|
1124
|
-
("bob".to_string(), "alice".to_string()),
|
|
1125
|
-
]
|
|
1126
|
-
);
|
|
1127
|
-
|
|
1128
|
-
let events = EventLog::new(&ws).tail(10).unwrap();
|
|
1129
|
-
let event = events
|
|
1130
|
-
.iter()
|
|
1131
|
-
.find(|event| {
|
|
1132
|
-
event.get("event").and_then(|v| v.as_str()) == Some("communication.peer_allowed")
|
|
1133
|
-
})
|
|
1134
|
-
.expect("communication.peer_allowed event");
|
|
1135
|
-
assert_eq!(event.get("a").and_then(|v| v.as_str()), Some("alice"));
|
|
1136
|
-
assert_eq!(event.get("b").and_then(|v| v.as_str()), Some("bob"));
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
1093
|
// ════════════════════════════════════════════════════════════════════════
|
|
1140
1094
|
// GROUP R — run_comms_selftest: §84 / MUST-NOT-13 zero-provider-SDK gate.
|
|
1141
1095
|
// diagnose/comms.py:21-47. The whole point: assert zero provider client calls.
|
|
@@ -300,6 +300,10 @@ pub fn get_adapter(p: Provider) -> Box<dyn ProviderAdapter> {
|
|
|
300
300
|
Box::new(BasicProviderAdapter { provider: p })
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
+
#[cfg(test)]
|
|
304
|
+
#[path = "tests/command_helpers.rs"]
|
|
305
|
+
mod command_helpers_tests;
|
|
306
|
+
|
|
303
307
|
#[derive(Debug, Clone, Copy)]
|
|
304
308
|
pub(crate) struct BasicProviderAdapter {
|
|
305
309
|
provider: Provider,
|
|
@@ -480,22 +484,13 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
480
484
|
Ok(argv)
|
|
481
485
|
}
|
|
482
486
|
Provider::Grok => Ok(grok_launch_command(
|
|
483
|
-
self,
|
|
484
|
-
auth_mode,
|
|
485
|
-
mcp_config,
|
|
486
487
|
system_prompt,
|
|
487
488
|
model,
|
|
488
489
|
dangerously_skip_permissions,
|
|
489
490
|
)?),
|
|
490
491
|
Provider::CursorAgent => Ok(cursor_agent_base_command(
|
|
491
|
-
self,
|
|
492
|
-
auth_mode,
|
|
493
|
-
mcp_config,
|
|
494
|
-
system_prompt,
|
|
495
492
|
model,
|
|
496
493
|
dangerously_skip_permissions,
|
|
497
|
-
false,
|
|
498
|
-
None,
|
|
499
494
|
)?),
|
|
500
495
|
Provider::Pi => Err(ProviderError::Command(
|
|
501
496
|
"Pi commands require the shared lifecycle materializer".to_string(),
|
|
@@ -596,14 +591,9 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
596
591
|
// never written and restart could not resume.
|
|
597
592
|
let expected = next_session_token();
|
|
598
593
|
let mut argv = grok_base_command(
|
|
599
|
-
self,
|
|
600
|
-
ctx.auth_mode,
|
|
601
|
-
ctx.mcp_config,
|
|
602
594
|
ctx.system_prompt,
|
|
603
595
|
ctx.model,
|
|
604
596
|
ctx.dangerously_skip_permissions,
|
|
605
|
-
ctx.profile_launch
|
|
606
|
-
.is_some_and(|profile| profile.managed_mcp_config),
|
|
607
597
|
ctx.effort,
|
|
608
598
|
)?;
|
|
609
599
|
argv.push("--session-id".to_string());
|
|
@@ -637,15 +627,8 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
637
627
|
// U-01 create-chat 无登录超时;禁止发明 --session-id。
|
|
638
628
|
// pending 只能是 CLI chatId,本拍不造假 uuid。
|
|
639
629
|
let argv = cursor_agent_base_command(
|
|
640
|
-
self,
|
|
641
|
-
ctx.auth_mode,
|
|
642
|
-
ctx.mcp_config,
|
|
643
|
-
ctx.system_prompt,
|
|
644
630
|
ctx.model,
|
|
645
631
|
ctx.dangerously_skip_permissions,
|
|
646
|
-
ctx.profile_launch
|
|
647
|
-
.is_some_and(|profile| profile.managed_mcp_config),
|
|
648
|
-
ctx.effort,
|
|
649
632
|
)?;
|
|
650
633
|
Ok(CommandPlan {
|
|
651
634
|
argv,
|
|
@@ -816,7 +799,7 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
816
799
|
// copilot --resume 接受 id|name)。
|
|
817
800
|
Provider::Copilot => {
|
|
818
801
|
let mut argv =
|
|
819
|
-
|
|
802
|
+
copilot_base_command(auth_mode, mcp_config, system_prompt, model, dangerously_skip_permissions);
|
|
820
803
|
argv.push("--resume".to_string());
|
|
821
804
|
argv.push(session_id.as_str().to_string());
|
|
822
805
|
Ok(argv)
|
|
@@ -824,13 +807,9 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
824
807
|
// Grok resume: base + `--resume <id>` (grok -r/--resume 接受 id|title)。
|
|
825
808
|
Provider::Grok => {
|
|
826
809
|
let mut argv = grok_base_command(
|
|
827
|
-
self,
|
|
828
|
-
auth_mode,
|
|
829
|
-
mcp_config,
|
|
830
810
|
system_prompt,
|
|
831
811
|
model,
|
|
832
812
|
dangerously_skip_permissions,
|
|
833
|
-
false,
|
|
834
813
|
None,
|
|
835
814
|
)?;
|
|
836
815
|
argv.push("--resume".to_string());
|
|
@@ -839,16 +818,7 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
839
818
|
}
|
|
840
819
|
// Cursor resume: base + `--resume <chatId>`。
|
|
841
820
|
Provider::CursorAgent => {
|
|
842
|
-
let mut argv = cursor_agent_base_command(
|
|
843
|
-
self,
|
|
844
|
-
auth_mode,
|
|
845
|
-
mcp_config,
|
|
846
|
-
system_prompt,
|
|
847
|
-
model,
|
|
848
|
-
dangerously_skip_permissions,
|
|
849
|
-
false,
|
|
850
|
-
None,
|
|
851
|
-
)?;
|
|
821
|
+
let mut argv = cursor_agent_base_command(model, dangerously_skip_permissions)?;
|
|
852
822
|
// Put --resume immediately after `agent` so the CLI treats it as
|
|
853
823
|
// the session mode rather than a trailing flag after --workspace.
|
|
854
824
|
argv.insert(1, "--resume".to_string());
|
|
@@ -941,14 +911,9 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
941
911
|
));
|
|
942
912
|
};
|
|
943
913
|
let mut argv = grok_base_command(
|
|
944
|
-
self,
|
|
945
|
-
ctx.auth_mode,
|
|
946
|
-
ctx.mcp_config,
|
|
947
914
|
ctx.system_prompt,
|
|
948
915
|
ctx.model,
|
|
949
916
|
ctx.dangerously_skip_permissions,
|
|
950
|
-
ctx.profile_launch
|
|
951
|
-
.is_some_and(|profile| profile.managed_mcp_config),
|
|
952
917
|
ctx.effort,
|
|
953
918
|
)?;
|
|
954
919
|
argv.push("--resume".to_string());
|
|
@@ -968,15 +933,8 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
968
933
|
));
|
|
969
934
|
}
|
|
970
935
|
let mut argv = cursor_agent_base_command(
|
|
971
|
-
self,
|
|
972
|
-
ctx.auth_mode,
|
|
973
|
-
ctx.mcp_config,
|
|
974
|
-
ctx.system_prompt,
|
|
975
936
|
ctx.model,
|
|
976
937
|
ctx.dangerously_skip_permissions,
|
|
977
|
-
ctx.profile_launch
|
|
978
|
-
.is_some_and(|profile| profile.managed_mcp_config),
|
|
979
|
-
ctx.effort,
|
|
980
938
|
)?;
|
|
981
939
|
argv.push("--resume".to_string());
|
|
982
940
|
argv.push(raw.to_string());
|
|
@@ -1067,13 +1025,9 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
1067
1025
|
// (grok help: --fork-session 需配合 --resume/--continue)。
|
|
1068
1026
|
Provider::Grok => {
|
|
1069
1027
|
let mut argv = grok_base_command(
|
|
1070
|
-
self,
|
|
1071
|
-
auth_mode,
|
|
1072
|
-
mcp_config,
|
|
1073
1028
|
system_prompt,
|
|
1074
1029
|
model,
|
|
1075
1030
|
dangerously_skip_permissions,
|
|
1076
|
-
false,
|
|
1077
1031
|
None,
|
|
1078
1032
|
)?;
|
|
1079
1033
|
argv.push("--session-id".to_string());
|
|
@@ -1191,7 +1145,7 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
1191
1145
|
.ok_or_else(|| {
|
|
1192
1146
|
ProviderError::Command("copilot fork root was not materialized".to_string())
|
|
1193
1147
|
})?;
|
|
1194
|
-
let mut argv =
|
|
1148
|
+
let mut argv = copilot_base_command(
|
|
1195
1149
|
ctx.auth_mode,
|
|
1196
1150
|
ctx.mcp_config,
|
|
1197
1151
|
ctx.system_prompt,
|
|
@@ -1214,14 +1168,9 @@ impl ProviderAdapter for BasicProviderAdapter {
|
|
|
1214
1168
|
));
|
|
1215
1169
|
};
|
|
1216
1170
|
let mut argv = grok_base_command(
|
|
1217
|
-
self,
|
|
1218
|
-
ctx.auth_mode,
|
|
1219
|
-
ctx.mcp_config,
|
|
1220
1171
|
ctx.system_prompt,
|
|
1221
1172
|
ctx.model,
|
|
1222
1173
|
ctx.dangerously_skip_permissions,
|
|
1223
|
-
ctx.profile_launch
|
|
1224
|
-
.is_some_and(|profile| profile.managed_mcp_config),
|
|
1225
1174
|
ctx.effort,
|
|
1226
1175
|
)?;
|
|
1227
1176
|
argv.push("--session-id".to_string());
|
|
@@ -1405,7 +1354,7 @@ pub(crate) fn json_inline(value: &serde_json::Value) -> String {
|
|
|
1405
1354
|
// the extracted base-command functions, not directly by this file.
|
|
1406
1355
|
use super::adapters::claude::{claude_base_command, claude_launch_command};
|
|
1407
1356
|
use super::adapters::codex::codex_base_command;
|
|
1408
|
-
use super::adapters::copilot::
|
|
1357
|
+
use super::adapters::copilot::copilot_base_command;
|
|
1409
1358
|
use super::adapters::cursor_agent::cursor_agent_base_command;
|
|
1410
1359
|
use super::adapters::fake::fake_worker_command;
|
|
1411
1360
|
use super::adapters::grok::{grok_base_command, grok_launch_command};
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
use crate::model::enums::{AuthMode, Provider};
|
|
12
12
|
use crate::provider::adapter::{next_session_token, prompt_needs_native_mcp, BasicProviderAdapter};
|
|
13
13
|
use crate::provider::{McpConfig, ProviderAdapter, ProviderError};
|
|
14
|
+
use crate::provider::command_helpers::{append_opt_pair, append_pair};
|
|
14
15
|
|
|
15
16
|
pub(crate) fn claude_launch_command(
|
|
16
17
|
adapter: &BasicProviderAdapter,
|
|
@@ -30,8 +31,7 @@ pub(crate) fn claude_launch_command(
|
|
|
30
31
|
false,
|
|
31
32
|
None,
|
|
32
33
|
)?;
|
|
33
|
-
argv
|
|
34
|
-
argv.push(next_session_token());
|
|
34
|
+
append_pair(&mut argv, "--session-id", next_session_token());
|
|
35
35
|
Ok(argv)
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -54,21 +54,14 @@ pub(crate) fn claude_base_command(
|
|
|
54
54
|
.expect("claude provider must define a bypass flag");
|
|
55
55
|
argv.push(flag.to_string());
|
|
56
56
|
} else {
|
|
57
|
-
argv
|
|
58
|
-
argv.push("default".to_string());
|
|
59
|
-
}
|
|
60
|
-
if let Some(model) = model {
|
|
61
|
-
argv.push("--model".to_string());
|
|
62
|
-
argv.push(model.to_string());
|
|
57
|
+
append_pair(&mut argv, "--permission-mode", "default");
|
|
63
58
|
}
|
|
59
|
+
append_opt_pair(&mut argv, "--model", model);
|
|
64
60
|
if let Some(effort) = effort {
|
|
65
61
|
argv.push("--effort".to_string());
|
|
66
62
|
argv.push(effort.as_str().to_string());
|
|
67
63
|
}
|
|
68
|
-
|
|
69
|
-
argv.push("--append-system-prompt".to_string());
|
|
70
|
-
argv.push(prompt.to_string());
|
|
71
|
-
}
|
|
64
|
+
append_opt_pair(&mut argv, "--append-system-prompt", system_prompt);
|
|
72
65
|
if !managed_mcp_config
|
|
73
66
|
&& (mcp_config.is_some()
|
|
74
67
|
|| auth_mode == AuthMode::CompatibleApi
|
|
@@ -79,8 +72,7 @@ pub(crate) fn claude_base_command(
|
|
|
79
72
|
} else {
|
|
80
73
|
serde_json::json!({"mcpServers": adapter.mcp_config(auth_mode)?.raw})
|
|
81
74
|
};
|
|
82
|
-
argv
|
|
83
|
-
argv.push(raw.to_string());
|
|
75
|
+
append_pair(&mut argv, "--mcp-config", raw.to_string());
|
|
84
76
|
}
|
|
85
77
|
Ok(argv)
|
|
86
78
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
use crate::model::enums::AuthMode;
|
|
10
10
|
use crate::provider::adapter::json_inline;
|
|
11
|
+
use crate::provider::command_helpers::{append_opt_pair, append_pair};
|
|
11
12
|
use crate::provider::{McpConfig, ProviderCommandOverrides};
|
|
12
13
|
|
|
13
14
|
pub(crate) fn codex_base_command(
|
|
@@ -36,24 +37,17 @@ pub(crate) fn codex_base_command(
|
|
|
36
37
|
"--disable".to_string(),
|
|
37
38
|
"apps".to_string(),
|
|
38
39
|
]);
|
|
39
|
-
|
|
40
|
-
argv.push("--profile".to_string());
|
|
41
|
-
argv.push(profile.to_string());
|
|
42
|
-
}
|
|
40
|
+
append_opt_pair(&mut argv, "--profile", overrides.and_then(|o| o.codex_profile.as_deref()));
|
|
43
41
|
if dangerously_skip_permissions {
|
|
44
42
|
// 0.5.66 bypass 单源:flag 由 provider_bypass_flag 表供给。
|
|
45
43
|
let flag = crate::provider::bypass_flags::provider_bypass_flag(crate::model::enums::Provider::Codex)
|
|
46
44
|
.expect("codex provider must define a bypass flag");
|
|
47
45
|
argv.push(flag.to_string());
|
|
48
46
|
}
|
|
49
|
-
|
|
50
|
-
argv.push("--model".to_string());
|
|
51
|
-
argv.push(model.to_string());
|
|
52
|
-
}
|
|
47
|
+
append_opt_pair(&mut argv, "--model", model);
|
|
53
48
|
if let Some(overrides) = overrides {
|
|
54
49
|
for config in &overrides.codex_config {
|
|
55
|
-
argv
|
|
56
|
-
argv.push(config.clone());
|
|
50
|
+
append_pair(&mut argv, "-c", config.clone());
|
|
57
51
|
}
|
|
58
52
|
}
|
|
59
53
|
if let Some(effort) = effort {
|
|
@@ -66,8 +60,7 @@ pub(crate) fn codex_base_command(
|
|
|
66
60
|
.replace('\\', "\\\\")
|
|
67
61
|
.replace('"', "\\\"")
|
|
68
62
|
.replace('\n', "\\n");
|
|
69
|
-
argv
|
|
70
|
-
argv.push(format!("developer_instructions=\"{escaped}\""));
|
|
63
|
+
append_pair(&mut argv, "-c", format!("developer_instructions=\"{escaped}\""));
|
|
71
64
|
}
|
|
72
65
|
if let Some(config) = mcp_config {
|
|
73
66
|
append_codex_mcp_overrides(&mut argv, &config.raw);
|
|
@@ -91,8 +84,7 @@ pub(crate) fn append_codex_mcp_overrides(argv: &mut Vec<String>, raw: &serde_jso
|
|
|
91
84
|
if key == "env" {
|
|
92
85
|
if let Some(env) = value.as_object() {
|
|
93
86
|
for (env_key, env_value) in env {
|
|
94
|
-
argv
|
|
95
|
-
argv.push(format!(
|
|
87
|
+
append_pair(argv, "-c", format!(
|
|
96
88
|
"mcp_servers.{name}.env.{env_key}={}",
|
|
97
89
|
json_inline(env_value)
|
|
98
90
|
));
|
|
@@ -100,12 +92,10 @@ pub(crate) fn append_codex_mcp_overrides(argv: &mut Vec<String>, raw: &serde_jso
|
|
|
100
92
|
}
|
|
101
93
|
continue;
|
|
102
94
|
}
|
|
103
|
-
argv
|
|
104
|
-
argv.push(format!("mcp_servers.{name}.{key}={}", json_inline(value)));
|
|
95
|
+
append_pair(argv, "-c", format!("mcp_servers.{name}.{key}={}", json_inline(value)));
|
|
105
96
|
}
|
|
106
97
|
// Every MCP server gets a 600s tool timeout so long-running
|
|
107
98
|
// team_orchestrator calls (report_result etc.) survive the codex default.
|
|
108
|
-
argv
|
|
109
|
-
argv.push(format!("mcp_servers.{name}.tool_timeout_sec=600.0"));
|
|
99
|
+
append_pair(argv, "-c", format!("mcp_servers.{name}.tool_timeout_sec=600.0"));
|
|
110
100
|
}
|
|
111
101
|
}
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
//!
|
|
4
4
|
//! Extracted from `provider/adapter.rs` (0.4.x decoupling step 2). Pure
|
|
5
5
|
//! extraction — byte-identical to the original inline forms. Scope kept
|
|
6
|
-
//! small: base command +
|
|
6
|
+
//! small: base command + MCP type→transport
|
|
7
7
|
//! translation. Auth hint (`copilot_auth_hint`) stays in `adapter.rs`;
|
|
8
8
|
//! session-store scanning lives under `provider/session_scan/copilot.rs`.
|
|
9
9
|
|
|
10
10
|
use crate::model::enums::AuthMode;
|
|
11
11
|
use crate::provider::McpConfig;
|
|
12
|
+
use crate::provider::command_helpers::{append_opt_pair, append_pair};
|
|
12
13
|
|
|
13
14
|
pub(crate) fn copilot_base_command(
|
|
14
15
|
auth_mode: AuthMode,
|
|
@@ -35,18 +36,13 @@ pub(crate) fn copilot_base_command(
|
|
|
35
36
|
argv.push(flag.to_string());
|
|
36
37
|
}
|
|
37
38
|
// mcp_team ∈ canonical → approval-free (whole-server pattern).
|
|
38
|
-
argv
|
|
39
|
-
argv
|
|
40
|
-
if let Some(model) = model {
|
|
41
|
-
argv.push("--model".to_string());
|
|
42
|
-
argv.push(model.to_string());
|
|
43
|
-
}
|
|
39
|
+
append_pair(&mut argv, "--allow-tool", "team_orchestrator");
|
|
40
|
+
append_opt_pair(&mut argv, "--model", model);
|
|
44
41
|
if let Some(config) = mcp_config {
|
|
45
42
|
// Copilot mcp config schema field is `transport` (stdio|http|sse),
|
|
46
43
|
// not the canonical `type`. McpConfig.raw is canonical; only copilot
|
|
47
44
|
// translates type→transport for --additional-mcp-config.
|
|
48
|
-
argv
|
|
49
|
-
argv.push(copilot_translate_mcp_config(&config.raw).to_string());
|
|
45
|
+
append_pair(&mut argv, "--additional-mcp-config", copilot_translate_mcp_config(&config.raw).to_string());
|
|
50
46
|
}
|
|
51
47
|
argv
|
|
52
48
|
}
|
|
@@ -77,16 +73,3 @@ pub(crate) fn copilot_translate_mcp_config(raw: &serde_json::Value) -> serde_jso
|
|
|
77
73
|
}
|
|
78
74
|
serde_json::Value::Object(translated)
|
|
79
75
|
}
|
|
80
|
-
|
|
81
|
-
/// Resume path = base + `--resume <sid>` (drop --session-id); kept separate
|
|
82
|
-
/// to avoid the plan accidentally emitting --session-id and --resume in the
|
|
83
|
-
/// same frame.
|
|
84
|
-
pub(crate) fn copilot_base_command_resume(
|
|
85
|
-
auth_mode: AuthMode,
|
|
86
|
-
mcp_config: Option<&McpConfig>,
|
|
87
|
-
system_prompt: Option<&str>,
|
|
88
|
-
model: Option<&str>,
|
|
89
|
-
dangerously_skip_permissions: bool,
|
|
90
|
-
) -> Vec<String> {
|
|
91
|
-
copilot_base_command(auth_mode, mcp_config, system_prompt, model, dangerously_skip_permissions)
|
|
92
|
-
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//! contract:
|
|
4
4
|
//! provides:
|
|
5
5
|
//! - name: cursor_agent_base_command
|
|
6
|
-
//! what: 只组已实测文档化 flag;
|
|
6
|
+
//! what: 只组已实测文档化 flag;MCP 由 launch overlay 写盘
|
|
7
7
|
//! boundary:
|
|
8
8
|
//! - 不调未文档化 flag(--system-prompt / --allowed-tools)
|
|
9
9
|
//! - 不写 grok 那种 cwd 独占闸
|
|
@@ -22,60 +22,23 @@
|
|
|
22
22
|
//! 随时可能消失。可以在注释里记录,代码里不许调。
|
|
23
23
|
//! `--allowed-tools` 正反行为未验证,不写「支持工具白名单」。
|
|
24
24
|
|
|
25
|
-
use crate::
|
|
26
|
-
use crate::provider::
|
|
27
|
-
use crate::provider::{McpConfig, ProviderError};
|
|
28
|
-
|
|
29
|
-
pub(crate) fn cursor_agent_launch_command(
|
|
30
|
-
adapter: &BasicProviderAdapter,
|
|
31
|
-
auth_mode: AuthMode,
|
|
32
|
-
mcp_config: Option<&McpConfig>,
|
|
33
|
-
system_prompt: Option<&str>,
|
|
34
|
-
model: Option<&str>,
|
|
35
|
-
dangerously_skip_permissions: bool,
|
|
36
|
-
) -> Result<Vec<String>, ProviderError> {
|
|
37
|
-
cursor_agent_base_command(
|
|
38
|
-
adapter,
|
|
39
|
-
auth_mode,
|
|
40
|
-
mcp_config,
|
|
41
|
-
system_prompt,
|
|
42
|
-
model,
|
|
43
|
-
dangerously_skip_permissions,
|
|
44
|
-
false,
|
|
45
|
-
None,
|
|
46
|
-
)
|
|
47
|
-
}
|
|
25
|
+
use crate::provider::ProviderError;
|
|
26
|
+
use crate::provider::command_helpers::{append_opt_pair, append_pair};
|
|
48
27
|
|
|
49
28
|
pub(crate) fn cursor_agent_base_command(
|
|
50
|
-
adapter: &BasicProviderAdapter,
|
|
51
|
-
auth_mode: AuthMode,
|
|
52
|
-
mcp_config: Option<&McpConfig>,
|
|
53
|
-
system_prompt: Option<&str>,
|
|
54
29
|
model: Option<&str>,
|
|
55
30
|
dangerously_skip_permissions: bool,
|
|
56
|
-
managed_mcp_config: bool,
|
|
57
|
-
effort: Option<crate::model::enums::ProviderEffort>,
|
|
58
31
|
) -> Result<Vec<String>, ProviderError> {
|
|
59
32
|
let mut argv = vec!["agent".to_string()];
|
|
60
33
|
if dangerously_skip_permissions {
|
|
61
34
|
// Explicit bypass also opts into Cursor's trusted, unsandboxed launch.
|
|
62
35
|
argv.push("--trust".to_string());
|
|
63
|
-
argv
|
|
64
|
-
argv.push("disabled".to_string());
|
|
36
|
+
append_pair(&mut argv, "--sandbox", "disabled");
|
|
65
37
|
argv.push("--force".to_string());
|
|
66
38
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
// Cursor `--effort` flag 不存在;框架在调用方丢掉 effort。绝不发明 flag。
|
|
72
|
-
let _ = effort;
|
|
73
|
-
// system_prompt 不入 argv(help 无 --rules / --append-system-prompt)。
|
|
74
|
-
// launch 写 `<workspace>/.cursor/rules/team-agent-role-<agent_id>.mdc`。
|
|
75
|
-
// mcp_config 也不入 argv(无 --mcp-config)。launch 写 `.cursor/mcp.json`
|
|
76
|
-
// 并 `agent mcp enable team_orchestrator`。这里收下以免静默丢弃。
|
|
77
|
-
let _ = (adapter, auth_mode, mcp_config, managed_mcp_config, system_prompt);
|
|
78
|
-
argv.push("--workspace".to_string());
|
|
79
|
-
argv.push("{workspace}".to_string());
|
|
39
|
+
append_opt_pair(&mut argv, "--model", model);
|
|
40
|
+
// Cursor has no `--effort`, system-prompt, or MCP config flag.
|
|
41
|
+
// Launch materializes role rules and the `.cursor/mcp.json` overlay.
|
|
42
|
+
append_pair(&mut argv, "--workspace", "{workspace}");
|
|
80
43
|
Ok(argv)
|
|
81
44
|
}
|
|
@@ -30,62 +30,38 @@
|
|
|
30
30
|
//! → `native_mcp_config: false`; MCP reaches the worker via launch-path
|
|
31
31
|
//! `<cwd>/.grok/config.toml` (`apply_grok_mcp_overlay`).
|
|
32
32
|
|
|
33
|
-
use crate::
|
|
34
|
-
use crate::provider::
|
|
35
|
-
use crate::provider::{
|
|
33
|
+
use crate::provider::adapter::next_session_token;
|
|
34
|
+
use crate::provider::ProviderError;
|
|
35
|
+
use crate::provider::command_helpers::{append_opt_pair, append_pair};
|
|
36
36
|
|
|
37
37
|
pub(crate) fn grok_launch_command(
|
|
38
|
-
adapter: &BasicProviderAdapter,
|
|
39
|
-
auth_mode: AuthMode,
|
|
40
|
-
mcp_config: Option<&McpConfig>,
|
|
41
38
|
system_prompt: Option<&str>,
|
|
42
39
|
model: Option<&str>,
|
|
43
40
|
dangerously_skip_permissions: bool,
|
|
44
41
|
) -> Result<Vec<String>, ProviderError> {
|
|
45
|
-
let mut argv = grok_base_command(
|
|
46
|
-
|
|
47
|
-
auth_mode,
|
|
48
|
-
mcp_config,
|
|
49
|
-
system_prompt,
|
|
50
|
-
model,
|
|
51
|
-
dangerously_skip_permissions,
|
|
52
|
-
false,
|
|
53
|
-
None,
|
|
54
|
-
)?;
|
|
55
|
-
argv.push("--session-id".to_string());
|
|
56
|
-
argv.push(next_session_token());
|
|
42
|
+
let mut argv = grok_base_command(system_prompt, model, dangerously_skip_permissions, None)?;
|
|
43
|
+
append_pair(&mut argv, "--session-id", next_session_token());
|
|
57
44
|
Ok(argv)
|
|
58
45
|
}
|
|
59
46
|
|
|
60
47
|
pub(crate) fn grok_base_command(
|
|
61
|
-
adapter: &BasicProviderAdapter,
|
|
62
|
-
auth_mode: AuthMode,
|
|
63
|
-
mcp_config: Option<&McpConfig>,
|
|
64
48
|
system_prompt: Option<&str>,
|
|
65
49
|
model: Option<&str>,
|
|
66
50
|
dangerously_skip_permissions: bool,
|
|
67
|
-
managed_mcp_config: bool,
|
|
68
51
|
effort: Option<crate::model::enums::ProviderEffort>,
|
|
69
52
|
) -> Result<Vec<String>, ProviderError> {
|
|
70
53
|
let mut argv = vec!["grok".to_string()];
|
|
71
54
|
if dangerously_skip_permissions {
|
|
72
55
|
argv.push("--always-approve".to_string());
|
|
73
56
|
}
|
|
74
|
-
|
|
75
|
-
argv.push("--model".to_string());
|
|
76
|
-
argv.push(model.to_string());
|
|
77
|
-
}
|
|
57
|
+
append_opt_pair(&mut argv, "--model", model.map(str::trim).filter(|value| !value.is_empty()));
|
|
78
58
|
if let Some(effort) = effort {
|
|
79
59
|
// Grok accepts `--effort` as alias for `--reasoning-effort`.
|
|
80
60
|
argv.push("--effort".to_string());
|
|
81
61
|
argv.push(effort.as_str().to_string());
|
|
82
62
|
}
|
|
83
|
-
|
|
84
|
-
argv.push("--rules".to_string());
|
|
85
|
-
argv.push(prompt.to_string());
|
|
86
|
-
}
|
|
63
|
+
append_opt_pair(&mut argv, "--rules", system_prompt);
|
|
87
64
|
// Grok CLI has no `--mcp-config` flag — the claude inline-MCP block is
|
|
88
65
|
// intentionally absent. Launch writes `<cwd>/.grok/config.toml`.
|
|
89
|
-
let _ = (adapter, auth_mode, mcp_config, managed_mcp_config);
|
|
90
66
|
Ok(argv)
|
|
91
67
|
}
|
|
@@ -14,6 +14,7 @@ use std::path::Path;
|
|
|
14
14
|
|
|
15
15
|
use crate::model::enums::ProviderEffort;
|
|
16
16
|
use crate::provider::ProviderError;
|
|
17
|
+
use crate::provider::command_helpers::{append_opt_pair, append_pair};
|
|
17
18
|
|
|
18
19
|
#[derive(Debug, Clone, Copy)]
|
|
19
20
|
pub(crate) enum PiSessionSelector<'a> {
|
|
@@ -60,38 +61,27 @@ pub(crate) fn build_pi_command_argv(
|
|
|
60
61
|
"-e".to_string(),
|
|
61
62
|
request.extension.to_string_lossy().into_owned(),
|
|
62
63
|
];
|
|
63
|
-
|
|
64
|
-
argv.push("--model".to_string());
|
|
65
|
-
argv.push(model.to_string());
|
|
66
|
-
}
|
|
64
|
+
append_opt_pair(&mut argv, "--model", request.model);
|
|
67
65
|
if let Some(effort) = request.effort {
|
|
68
66
|
argv.push("--thinking".to_string());
|
|
69
67
|
argv.push(effort.as_str().to_string());
|
|
70
68
|
}
|
|
71
|
-
argv.
|
|
72
|
-
"--append-system-prompt".to_string(),
|
|
73
|
-
request.system_prompt.to_string(),
|
|
74
|
-
]);
|
|
69
|
+
append_pair(&mut argv, "--append-system-prompt", request.system_prompt);
|
|
75
70
|
if let Some(session_dir) = request.session_dir {
|
|
76
|
-
argv.
|
|
77
|
-
"--session-dir".to_string(),
|
|
78
|
-
session_dir.to_string_lossy().into_owned(),
|
|
79
|
-
]);
|
|
71
|
+
append_pair(&mut argv, "--session-dir", session_dir.to_string_lossy().into_owned());
|
|
80
72
|
}
|
|
81
73
|
// The shared lifecycle materializer always revalidates resume root/path/header.
|
|
82
74
|
// Keep the pure argv builder usable before a fixture root is materialized, while
|
|
83
75
|
// refusing a missing exact file once the recorded path's parent exists.
|
|
84
76
|
match request.session {
|
|
85
77
|
PiSessionSelector::Fresh { session_id } if !session_id.trim().is_empty() => {
|
|
86
|
-
argv
|
|
87
|
-
argv.push(session_id.to_string());
|
|
78
|
+
append_pair(&mut argv, "--session-id", session_id);
|
|
88
79
|
}
|
|
89
80
|
PiSessionSelector::Resume { path }
|
|
90
81
|
if !path.as_os_str().is_empty()
|
|
91
82
|
&& (!path.parent().is_some_and(Path::is_dir) || path.is_file()) =>
|
|
92
83
|
{
|
|
93
|
-
argv
|
|
94
|
-
argv.push(path.to_string_lossy().into_owned());
|
|
84
|
+
append_pair(&mut argv, "--session", path.to_string_lossy().into_owned());
|
|
95
85
|
}
|
|
96
86
|
PiSessionSelector::Resume { path } if !path.as_os_str().is_empty() => {
|
|
97
87
|
return Err(ProviderError::ResumeUnavailable(format!(
|
|
@@ -105,7 +95,6 @@ pub(crate) fn build_pi_command_argv(
|
|
|
105
95
|
));
|
|
106
96
|
}
|
|
107
97
|
}
|
|
108
|
-
argv
|
|
109
|
-
argv.push(request.agent_id.to_string());
|
|
98
|
+
append_pair(&mut argv, "--name", request.agent_id);
|
|
110
99
|
Ok(argv)
|
|
111
100
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//! Pure append operations. Callers own ordering, filtering, escaping and env-unset policy.
|
|
2
|
+
use std::collections::BTreeMap;
|
|
3
|
+
|
|
4
|
+
pub(crate) fn append_pair(argv: &mut Vec<String>, flag: &str, value: impl Into<String>) {
|
|
5
|
+
argv.push(flag.to_string());
|
|
6
|
+
argv.push(value.into());
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
pub(crate) fn append_opt_pair(argv: &mut Vec<String>, flag: &str, value: Option<&str>) {
|
|
10
|
+
if let Some(value) = value {
|
|
11
|
+
append_pair(argv, flag, value);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
pub(crate) fn append_env_pair(env: &mut BTreeMap<String, String>, key: &str, value: Option<&str>) {
|
|
16
|
+
if let Some(value) = value {
|
|
17
|
+
env.insert(key.to_string(), value.to_string());
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
#[cfg(test)]
|
|
22
|
+
mod tests {
|
|
23
|
+
use super::*;
|
|
24
|
+
|
|
25
|
+
#[test]
|
|
26
|
+
fn append_preserves_order_and_raw_values() {
|
|
27
|
+
let mut argv = vec!["cli".to_string()];
|
|
28
|
+
append_opt_pair(&mut argv, "--absent", None);
|
|
29
|
+
append_opt_pair(&mut argv, "--empty", Some(""));
|
|
30
|
+
append_pair(&mut argv, "--raw", " \"'\\\n ".to_string());
|
|
31
|
+
append_pair(&mut argv, "--repeat", "first");
|
|
32
|
+
append_pair(&mut argv, "--repeat", "last");
|
|
33
|
+
assert_eq!(argv, ["cli", "--empty", "", "--raw", " \"'\\\n ", "--repeat", "first", "--repeat", "last"]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#[test]
|
|
37
|
+
fn env_append_preserves_absence_empty_values_and_last_write() {
|
|
38
|
+
let mut env = BTreeMap::from([("Z".to_string(), "old".to_string())]);
|
|
39
|
+
append_env_pair(&mut env, "Z", None);
|
|
40
|
+
assert_eq!(env["Z"], "old");
|
|
41
|
+
append_env_pair(&mut env, "A", Some(""));
|
|
42
|
+
append_env_pair(&mut env, "Z", Some(" \"'\\\n "));
|
|
43
|
+
assert_eq!(env.into_iter().collect::<Vec<_>>(), vec![("A".to_string(), "".to_string()), ("Z".to_string(), " \"'\\\n ".to_string())]);
|
|
44
|
+
}
|
|
45
|
+
}
|