@team-agent/installer 0.5.65 → 0.5.68
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 +8 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/Cargo.toml +6 -0
- package/crates/team-agent/src/cli/adapters.rs +6 -1
- package/crates/team-agent/src/cli/diagnose.rs +82 -0
- package/crates/team-agent/src/cli/emit.rs +3 -3
- package/crates/team-agent/src/cli/grok_slot.rs +299 -0
- package/crates/team-agent/src/cli/leader.rs +21 -7
- package/crates/team-agent/src/cli/leaders.rs +125 -1
- package/crates/team-agent/src/cli/mod.rs +20 -0
- package/crates/team-agent/src/cli/send/presentation.rs +7 -1
- package/crates/team-agent/src/cli/spec.rs +2 -0
- package/crates/team-agent/src/cli/status_port/compact.rs +28 -0
- package/crates/team-agent/src/cli/status_port/snapshot.rs +25 -1
- package/crates/team-agent/src/cli/tests/base.rs +26 -3
- package/crates/team-agent/src/cli/tests/compile.rs +1 -1
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +129 -19
- package/crates/team-agent/src/cli/tests/mod.rs +2 -2
- package/crates/team-agent/src/cli/tests/run_delegation.rs +1 -1
- package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +387 -6
- package/crates/team-agent/src/cli/tests/status_send.rs +79 -10
- package/crates/team-agent/src/cli/tests/verb_validate.rs +1 -1
- package/crates/team-agent/src/communication_mode/mod.rs +6 -4
- package/crates/team-agent/src/compiler/tests.rs +23 -20
- package/crates/team-agent/src/compiler.rs +84 -5
- package/crates/team-agent/src/conpty/backend.rs +16 -0
- package/crates/team-agent/src/coordinator/backoff.rs +55 -0
- package/crates/team-agent/src/coordinator/conpty_shim.rs +82 -0
- package/crates/team-agent/src/coordinator/health.rs +173 -0
- package/crates/team-agent/src/coordinator/mod.rs +31 -0
- package/crates/team-agent/src/coordinator/orphan.rs +31 -0
- package/crates/team-agent/src/coordinator/runtime_detectors.rs +30 -0
- package/crates/team-agent/src/coordinator/runtime_observation.rs +25 -0
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +59 -0
- package/crates/team-agent/src/coordinator/steps/delivery.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/health_sync.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/mod.rs +22 -0
- package/crates/team-agent/src/coordinator/steps/persist.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +10 -0
- package/crates/team-agent/src/coordinator/steps/session_gate.rs +10 -0
- package/crates/team-agent/src/coordinator/tick.rs +133 -22
- package/crates/team-agent/src/coordinator/types.rs +49 -0
- package/crates/team-agent/src/db/message_store.rs +39 -5
- package/crates/team-agent/src/layout/worker_env.rs +20 -3
- package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
- package/crates/team-agent/src/leader/provider_attribution.rs +53 -0
- package/crates/team-agent/src/leader/registry.rs +65 -0
- package/crates/team-agent/src/leader/start.rs +1080 -58
- package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/display.rs +56 -0
- package/crates/team-agent/src/lifecycle/helpers.rs +57 -0
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +332 -14
- package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +69 -2
- package/crates/team-agent/src/lifecycle/launch/agent_state.rs +196 -2
- package/crates/team-agent/src/lifecycle/launch/clone_agent.rs +101 -11
- package/crates/team-agent/src/lifecycle/launch/cursor_create_chat.rs +229 -0
- package/crates/team-agent/src/lifecycle/launch/cursor_mcp.rs +332 -0
- package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +204 -457
- package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +24 -0
- package/crates/team-agent/src/lifecycle/launch/grok_per_seat.rs +260 -0
- package/crates/team-agent/src/lifecycle/launch/identity.rs +146 -0
- package/crates/team-agent/src/lifecycle/launch/layout.rs +60 -0
- package/crates/team-agent/src/lifecycle/launch/leader_context.rs +112 -0
- package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +530 -0
- package/crates/team-agent/src/lifecycle/launch/ownership.rs +40 -0
- package/crates/team-agent/src/lifecycle/launch/plan.rs +31 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +66 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +78 -0
- package/crates/team-agent/src/lifecycle/launch/readiness.rs +85 -38
- package/crates/team-agent/src/lifecycle/launch/role_source.rs +44 -44
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +101 -4
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +244 -48
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +109 -2
- package/crates/team-agent/src/lifecycle/launch/worker_env.rs +214 -1
- package/crates/team-agent/src/lifecycle/launch.rs +209 -43
- package/crates/team-agent/src/lifecycle/lock.rs +42 -1
- package/crates/team-agent/src/lifecycle/mod.rs +11 -0
- package/crates/team-agent/src/lifecycle/pane_input_lock.rs +161 -0
- package/crates/team-agent/src/lifecycle/profile_launch.rs +98 -0
- package/crates/team-agent/src/lifecycle/profile_smoke.rs +51 -1
- package/crates/team-agent/src/lifecycle/restart/agent.rs +213 -3
- package/crates/team-agent/src/lifecycle/restart/common.rs +373 -18
- package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +27 -0
- package/crates/team-agent/src/lifecycle/restart/preflight.rs +25 -0
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +165 -15
- package/crates/team-agent/src/lifecycle/restart/remove.rs +196 -5
- package/crates/team-agent/src/lifecycle/restart/selection.rs +72 -0
- package/crates/team-agent/src/lifecycle/restart/team_state.rs +22 -0
- package/crates/team-agent/src/lifecycle/restart.rs +29 -0
- package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +3 -4
- package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +4 -4
- package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +309 -0
- package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +159 -174
- package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +15 -13
- package/crates/team-agent/src/lifecycle/tests/core.rs +4 -18
- package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/cursor_mcp_overlay.rs +287 -0
- package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +229 -0
- package/crates/team-agent/src/lifecycle/tests/cursor_restart_resume_red.rs +353 -0
- package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/g1_silent_faces.rs +784 -0
- package/crates/team-agent/src/lifecycle/tests/gate_fixtures.rs +562 -0
- package/crates/team-agent/src/lifecycle/tests/grok_effort_argv_red.rs +239 -0
- package/crates/team-agent/src/lifecycle/tests/grok_mcp_overlay_red.rs +498 -0
- package/crates/team-agent/src/lifecycle/tests/grok_require_explicit_model_red.rs +250 -0
- package/crates/team-agent/src/lifecycle/tests/grok_restart_resume_red.rs +293 -0
- package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +3 -0
- package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +3 -3
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +65 -40
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +20 -32
- package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +17 -9
- package/crates/team-agent/src/lifecycle/tests/mcp_tool_name_format_red.rs +69 -0
- package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +24 -23
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +98 -62
- package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +3 -1
- package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +41 -8
- package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +3 -3
- package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +60 -2
- package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +2 -2
- package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +69 -26
- package/crates/team-agent/src/lifecycle/tests.rs +23 -15
- package/crates/team-agent/src/lifecycle/types.rs +61 -2
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +200 -33
- package/crates/team-agent/src/mcp_server/tests/scoped.rs +100 -1
- package/crates/team-agent/src/mcp_server/tests.rs +196 -2
- package/crates/team-agent/src/messaging/delivery.rs +405 -40
- package/crates/team-agent/src/messaging/helpers.rs +2 -0
- package/crates/team-agent/src/messaging/leader_receiver.rs +53 -1
- package/crates/team-agent/src/messaging/results.rs +4 -0
- package/crates/team-agent/src/messaging/send.rs +34 -0
- package/crates/team-agent/src/messaging/tests/dup_inject.rs +406 -0
- package/crates/team-agent/src/messaging/tests/e23.rs +9 -0
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +66 -0
- package/crates/team-agent/src/messaging/tests/main_preserved.rs +1 -1
- package/crates/team-agent/src/messaging/tests/mod.rs +1 -0
- package/crates/team-agent/src/messaging/types.rs +4 -1
- package/crates/team-agent/src/model/enums.rs +14 -5
- package/crates/team-agent/src/model/permissions.rs +3 -0
- package/crates/team-agent/src/model/spec.rs +129 -6
- package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +3 -1
- package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +3 -1
- package/crates/team-agent/src/model/testdata/team.spec.yaml +3 -1
- package/crates/team-agent/src/os_probe.rs +73 -2
- package/crates/team-agent/src/provider/adapter.rs +231 -4
- package/crates/team-agent/src/provider/adapters/claude.rs +5 -2
- package/crates/team-agent/src/provider/adapters/codex.rs +5 -2
- package/crates/team-agent/src/provider/adapters/copilot.rs +5 -2
- package/crates/team-agent/src/provider/adapters/cursor_agent.rs +86 -0
- package/crates/team-agent/src/provider/adapters/grok.rs +157 -0
- package/crates/team-agent/src/provider/adapters/mod.rs +2 -0
- package/crates/team-agent/src/provider/bypass_flags.rs +124 -0
- package/crates/team-agent/src/provider/classify.rs +4 -2
- package/crates/team-agent/src/provider/faults.rs +2 -1
- package/crates/team-agent/src/provider/mod.rs +13 -0
- package/crates/team-agent/src/provider/session/capture.rs +158 -20
- package/crates/team-agent/src/provider/session/context_fork/claude.rs +38 -1
- package/crates/team-agent/src/provider/session/context_fork/codex.rs +72 -1
- package/crates/team-agent/src/provider/session/context_fork/outcome.rs +57 -1
- package/crates/team-agent/src/provider/session/context_fork.rs +77 -3
- package/crates/team-agent/src/provider/session/mod.rs +18 -0
- package/crates/team-agent/src/provider/session/resume.rs +57 -0
- package/crates/team-agent/src/provider/session_scan/claude.rs +125 -1
- package/crates/team-agent/src/provider/session_scan/codex.rs +94 -1
- package/crates/team-agent/src/provider/session_scan/common.rs +204 -2
- package/crates/team-agent/src/provider/session_scan/copilot.rs +33 -1
- package/crates/team-agent/src/provider/session_scan/cursor.rs +538 -0
- package/crates/team-agent/src/provider/session_scan/grok.rs +288 -0
- package/crates/team-agent/src/provider/session_scan.rs +8 -0
- package/crates/team-agent/src/provider/submit_now.rs +94 -0
- package/crates/team-agent/src/provider/tests/adapter.rs +304 -0
- package/crates/team-agent/src/provider/types.rs +4 -2
- package/crates/team-agent/src/provider/wire.rs +23 -1
- package/crates/team-agent/src/state/persist.rs +301 -8
- package/crates/team-agent/src/state/repository.rs +5 -0
- package/crates/team-agent/src/tmux_backend/tests.rs +1737 -42
- package/crates/team-agent/src/tmux_backend.rs +1083 -75
- package/crates/team-agent/src/transport/test_support.rs +20 -0
- package/crates/team-agent/src/transport/tests/wire.rs +28 -2
- package/crates/team-agent/src/transport.rs +265 -1
- package/npm/install.mjs +129 -73
- package/package.json +4 -4
- package/skills/team-agent/SKILL.md +33 -238
- package/skills/team-agent/command-coverage.json +31 -0
- package/crates/team-agent/src/lifecycle/launch/approval.rs +0 -134
- package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +0 -59
- package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +0 -483
- package/crates/team-agent/src/lifecycle/launch/fork_pending.rs +0 -109
- package/crates/team-agent/src/lifecycle/launch/fork_state.rs +0 -447
- package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +0 -1187
- package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +0 -327
|
@@ -1,7 +1,27 @@
|
|
|
1
|
+
//! ---
|
|
2
|
+
//! purpose: 从 spec 里的一个 agent 定义,编译出 worker 启动所需的系统提示词与工具串
|
|
3
|
+
//! contract:
|
|
4
|
+
//! provides:
|
|
5
|
+
//! - name: WorkerCommandAgent
|
|
6
|
+
//! what: 从 YAML 或 JSON 读出的单 agent 命令上下文
|
|
7
|
+
//! - name: compile_worker_system_prompt
|
|
8
|
+
//! what: 按身份、runtime 契约、通信模式、角色正文、输出契约、权限说明拼系统提示词
|
|
9
|
+
//! - name: resolved_tool_strings_for_command
|
|
10
|
+
//! what: 解析出该 agent 的工具串,声明了 bypass 时追加 dangerous_auto_approve 哨兵
|
|
11
|
+
//! depends:
|
|
12
|
+
//! - crate::model::permissions
|
|
13
|
+
//! - crate::communication_mode
|
|
14
|
+
//! - crate::provider::bypass_flags
|
|
15
|
+
//! boundary:
|
|
16
|
+
//! - 只产出字符串,不 spawn 进程、不写盘
|
|
17
|
+
//! - bypass 只认 agent 自身声明,不从 team/runtime/leader argv 继承
|
|
18
|
+
//! - provider 没有 bypass argv 定义时报错,不静默降级
|
|
19
|
+
//! maturity: wired
|
|
20
|
+
//! ---
|
|
1
21
|
use std::path::Path;
|
|
2
22
|
|
|
3
23
|
use crate::communication_mode::CommunicationMode;
|
|
4
|
-
use crate::lifecycle::types::
|
|
24
|
+
use crate::lifecycle::types::LifecycleError;
|
|
5
25
|
use crate::model::enums::{Enforcement, Provider};
|
|
6
26
|
use crate::model::ids::AgentId;
|
|
7
27
|
use crate::model::permissions::{resolve_permissions, AgentPermissionInput};
|
|
@@ -13,9 +33,9 @@ output. All communication must go through Team Agent MCP tools.
|
|
|
13
33
|
|
|
14
34
|
## Communication (mandatory)
|
|
15
35
|
|
|
16
|
-
- Coordinate with teammate:
|
|
17
|
-
- Broadcast to all teammates:
|
|
18
|
-
- Task complete:
|
|
36
|
+
- Coordinate with teammate: {send_message}(to='<agent_id>', content='...')
|
|
37
|
+
- Broadcast to all teammates: {send_message}(to='*', content='...')
|
|
38
|
+
- Task complete: {report_result}(summary='...') — call exactly once
|
|
19
39
|
|
|
20
40
|
## Rules
|
|
21
41
|
|
|
@@ -29,7 +49,7 @@ output. All communication must go through Team Agent MCP tools.
|
|
|
29
49
|
// semantics (leader-attach dependence + fallback status) that the
|
|
30
50
|
// generic runtime section deliberately leaves out.
|
|
31
51
|
const RESULT_ENVELOPE_OUTPUT_CONTRACT: &str =
|
|
32
|
-
"Final completion must call
|
|
52
|
+
"Final completion must call {report_result} exactly once with a short summary \
|
|
33
53
|
and optional status/changes/tests; the MCP runtime injects the result into the attached leader pane. \
|
|
34
54
|
If no leader is attached, the tool returns a fallback/failed result instead of completion.";
|
|
35
55
|
|
|
@@ -42,9 +62,21 @@ pub(crate) struct WorkerCommandAgent {
|
|
|
42
62
|
system_prompt_file: Option<String>,
|
|
43
63
|
output_contract_format: Option<String>,
|
|
44
64
|
communication_mode: CommunicationMode,
|
|
65
|
+
/// 0.5.66 bypass 单源:agent-level `dangerously_skip_permissions`(必填 bool)。
|
|
66
|
+
dangerously_skip_permissions: bool,
|
|
45
67
|
}
|
|
46
68
|
|
|
47
69
|
impl WorkerCommandAgent {
|
|
70
|
+
/// ---
|
|
71
|
+
/// purpose: 从 spec 的 YAML agent 节点读出命令上下文
|
|
72
|
+
/// params:
|
|
73
|
+
/// agent: 单个 agent 的 YAML 节点
|
|
74
|
+
/// fallback_id: agent 节点没写 id 时的兜底 id
|
|
75
|
+
/// provider: 已解析的 provider
|
|
76
|
+
/// returns: 填好的 WorkerCommandAgent
|
|
77
|
+
/// errors: communication_mode 取值非法时返回 LifecycleError
|
|
78
|
+
/// contract_id: lifecycle.worker_command_agent.from_source
|
|
79
|
+
/// ---
|
|
48
80
|
pub(crate) fn from_yaml(
|
|
49
81
|
agent: &crate::model::yaml::Value,
|
|
50
82
|
fallback_id: Option<&str>,
|
|
@@ -92,9 +124,23 @@ impl WorkerCommandAgent {
|
|
|
92
124
|
.get("communication_mode")
|
|
93
125
|
.and_then(crate::model::yaml::Value::as_str),
|
|
94
126
|
)?,
|
|
127
|
+
dangerously_skip_permissions: matches!(
|
|
128
|
+
agent.get("dangerously_skip_permissions"),
|
|
129
|
+
Some(crate::model::yaml::Value::Bool(true))
|
|
130
|
+
),
|
|
95
131
|
})
|
|
96
132
|
}
|
|
97
133
|
|
|
134
|
+
/// ---
|
|
135
|
+
/// purpose: 从 runtime state 的 JSON agent 节点读出命令上下文
|
|
136
|
+
/// params:
|
|
137
|
+
/// agent: 单个 agent 的 JSON 节点
|
|
138
|
+
/// fallback_id: agent 节点没写 id 时的兜底 id
|
|
139
|
+
/// provider: 已解析的 provider
|
|
140
|
+
/// returns: 填好的 WorkerCommandAgent
|
|
141
|
+
/// errors: communication_mode 取值非法时返回 LifecycleError
|
|
142
|
+
/// contract_id: lifecycle.worker_command_agent.from_source
|
|
143
|
+
/// ---
|
|
98
144
|
pub(crate) fn from_json(
|
|
99
145
|
agent: &serde_json::Value,
|
|
100
146
|
fallback_id: Option<&str>,
|
|
@@ -142,10 +188,19 @@ impl WorkerCommandAgent {
|
|
|
142
188
|
.get("communication_mode")
|
|
143
189
|
.and_then(serde_json::Value::as_str),
|
|
144
190
|
)?,
|
|
191
|
+
dangerously_skip_permissions: agent
|
|
192
|
+
.get("dangerously_skip_permissions")
|
|
193
|
+
.and_then(serde_json::Value::as_bool)
|
|
194
|
+
.unwrap_or(false),
|
|
145
195
|
})
|
|
146
196
|
}
|
|
147
197
|
}
|
|
148
198
|
|
|
199
|
+
/// ---
|
|
200
|
+
/// purpose: 拼出 worker 的系统提示词,身份段必须排在最前
|
|
201
|
+
/// returns: 各段以空行分隔的提示词,空段被丢弃
|
|
202
|
+
/// errors: 角色正文读取失败或权限解析失败时返回 LifecycleError
|
|
203
|
+
/// ---
|
|
149
204
|
pub(crate) fn compile_worker_system_prompt(
|
|
150
205
|
agent: &WorkerCommandAgent,
|
|
151
206
|
) -> Result<String, LifecycleError> {
|
|
@@ -153,13 +208,15 @@ pub(crate) fn compile_worker_system_prompt(
|
|
|
153
208
|
// identity line anchors the very first section (live Python worker argv confirms).
|
|
154
209
|
// C-1 cr verdict / B2 灵魂件 — identity 必须 FIRST(MUST-4 行为层守:空白上下文问
|
|
155
210
|
// "你是谁"必须先答 Team Agent worker 身份)。runtime contract 跟后。
|
|
211
|
+
let send_message = mcp_tool_name(agent.provider, "team_orchestrator", "send_message");
|
|
212
|
+
let report_result = mcp_tool_name(agent.provider, "team_orchestrator", "report_result");
|
|
156
213
|
let mut chunks = vec![
|
|
157
214
|
identity_section(agent),
|
|
158
|
-
runtime_contract_section(),
|
|
159
|
-
agent.communication_mode.runtime_contract()
|
|
215
|
+
runtime_contract_section(&send_message, &report_result),
|
|
216
|
+
agent.communication_mode.runtime_contract(&send_message),
|
|
160
217
|
role_body(agent)?,
|
|
161
218
|
];
|
|
162
|
-
if let Some(contract) = output_contract(agent) {
|
|
219
|
+
if let Some(contract) = output_contract(agent, &report_result) {
|
|
163
220
|
chunks.push(contract);
|
|
164
221
|
}
|
|
165
222
|
if let Some(notes) = permission_notes(agent)? {
|
|
@@ -172,22 +229,72 @@ pub(crate) fn compile_worker_system_prompt(
|
|
|
172
229
|
.join("\n\n"))
|
|
173
230
|
}
|
|
174
231
|
|
|
232
|
+
/// ---
|
|
233
|
+
/// purpose: 解析该 agent 最终生效的工具串
|
|
234
|
+
/// params:
|
|
235
|
+
/// provider: 用于查 bypass argv 定义的 provider
|
|
236
|
+
/// returns: 排序后的工具串;agent 声明 bypass 时末尾追加 dangerous_auto_approve
|
|
237
|
+
/// errors: 权限解析失败,或声明了 bypass 但该 provider 没有 bypass argv 定义时返回 RequirementUnmet
|
|
238
|
+
/// ---
|
|
175
239
|
pub(crate) fn resolved_tool_strings_for_command(
|
|
176
240
|
agent: &WorkerCommandAgent,
|
|
177
241
|
provider: Provider,
|
|
178
|
-
safety: &DangerousApproval,
|
|
179
242
|
) -> Result<Vec<String>, LifecycleError> {
|
|
180
243
|
let mut tools: Vec<String> = resolve_agent_permissions(agent, provider)?
|
|
181
244
|
.sorted_tool_strings()
|
|
182
245
|
.into_iter()
|
|
183
246
|
.map(str::to_string)
|
|
184
247
|
.collect();
|
|
185
|
-
|
|
186
|
-
|
|
248
|
+
// 0.5.66 bypass 单源:读 agent 自己的 `dangerously_skip_permissions` bool,
|
|
249
|
+
// 不再收 team/runtime/leader argv 派生的 `DangerousApproval`。true → 加
|
|
250
|
+
// `dangerous_auto_approve` 哨兵(adapter 消费点不变);provider 无 bypass
|
|
251
|
+
// argv 定义 → fail-loud,不静默 fallback。
|
|
252
|
+
if agent.dangerously_skip_permissions {
|
|
253
|
+
let flag = crate::provider::bypass_flags::provider_bypass_flag(provider).ok_or_else(
|
|
254
|
+
|| {
|
|
255
|
+
LifecycleError::RequirementUnmet(format!(
|
|
256
|
+
"provider {} has no bypass argv flag defined; dangerously_skip_permissions=true cannot be honored",
|
|
257
|
+
provider_display_name(provider)
|
|
258
|
+
))
|
|
259
|
+
},
|
|
260
|
+
)?;
|
|
261
|
+
debug_assert!(flag.starts_with("--"));
|
|
262
|
+
if !tools.iter().any(|tool| tool == "dangerous_auto_approve") {
|
|
263
|
+
tools.push("dangerous_auto_approve".to_string());
|
|
264
|
+
}
|
|
187
265
|
}
|
|
188
266
|
Ok(tools)
|
|
189
267
|
}
|
|
190
268
|
|
|
269
|
+
/// Provider-facing MCP tool name. Only verified call forms are filled in.
|
|
270
|
+
/// Unverified providers keep the historical dotted spelling.
|
|
271
|
+
fn mcp_tool_name(provider: Provider, server: &str, tool: &str) -> String {
|
|
272
|
+
match provider {
|
|
273
|
+
Provider::Claude | Provider::ClaudeCode => format!("mcp__{server}__{tool}"),
|
|
274
|
+
Provider::Grok => format!("{server}__{tool}"),
|
|
275
|
+
// CursorAgent / Codex / Copilot / GeminiCli / Fake: 未验证,沿用现状点号。
|
|
276
|
+
// CursorAgent 不可与 grok 同臂:仓库里没有活转录,`{server}__{tool}` 是推断。
|
|
277
|
+
Provider::CursorAgent
|
|
278
|
+
| Provider::Codex
|
|
279
|
+
| Provider::Copilot
|
|
280
|
+
| Provider::GeminiCli
|
|
281
|
+
| Provider::Fake => format!("{server}.{tool}"),
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
fn provider_display_name(provider: Provider) -> &'static str {
|
|
286
|
+
match provider {
|
|
287
|
+
Provider::Claude => "claude",
|
|
288
|
+
Provider::ClaudeCode => "claude_code",
|
|
289
|
+
Provider::Codex => "codex",
|
|
290
|
+
Provider::Copilot => "copilot",
|
|
291
|
+
Provider::GeminiCli => "gemini_cli",
|
|
292
|
+
Provider::Grok => "grok",
|
|
293
|
+
Provider::CursorAgent => "cursor_agent",
|
|
294
|
+
Provider::Fake => "fake",
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
191
298
|
fn resolve_agent_permissions(
|
|
192
299
|
agent: &WorkerCommandAgent,
|
|
193
300
|
provider: Provider,
|
|
@@ -201,8 +308,10 @@ fn resolve_agent_permissions(
|
|
|
201
308
|
.map_err(|e| LifecycleError::Compile(e.to_string()))
|
|
202
309
|
}
|
|
203
310
|
|
|
204
|
-
fn runtime_contract_section() -> String {
|
|
205
|
-
RUNTIME_CONTRACT_SECTION
|
|
311
|
+
fn runtime_contract_section(send_message: &str, report_result: &str) -> String {
|
|
312
|
+
RUNTIME_CONTRACT_SECTION
|
|
313
|
+
.replace("{send_message}", send_message)
|
|
314
|
+
.replace("{report_result}", report_result)
|
|
206
315
|
}
|
|
207
316
|
|
|
208
317
|
fn communication_mode(value: Option<&str>) -> Result<CommunicationMode, LifecycleError> {
|
|
@@ -236,9 +345,9 @@ fn role_body(agent: &WorkerCommandAgent) -> Result<String, LifecycleError> {
|
|
|
236
345
|
Ok(chunks.join("\n\n"))
|
|
237
346
|
}
|
|
238
347
|
|
|
239
|
-
fn output_contract(agent: &WorkerCommandAgent) -> Option<String> {
|
|
348
|
+
fn output_contract(agent: &WorkerCommandAgent, report_result: &str) -> Option<String> {
|
|
240
349
|
(agent.output_contract_format.as_deref() == Some("result_envelope_v1"))
|
|
241
|
-
.then(|| RESULT_ENVELOPE_OUTPUT_CONTRACT.
|
|
350
|
+
.then(|| RESULT_ENVELOPE_OUTPUT_CONTRACT.replace("{report_result}", report_result))
|
|
242
351
|
}
|
|
243
352
|
|
|
244
353
|
fn permission_notes(agent: &WorkerCommandAgent) -> Result<Option<String>, LifecycleError> {
|
|
@@ -282,20 +391,6 @@ fn provider_default_prompt_only_tools(provider: Provider) -> &'static [&'static
|
|
|
282
391
|
#[cfg(test)]
|
|
283
392
|
mod tests {
|
|
284
393
|
use super::*;
|
|
285
|
-
use crate::lifecycle::types::DangerousApprovalSource;
|
|
286
|
-
|
|
287
|
-
fn disabled_safety() -> DangerousApproval {
|
|
288
|
-
DangerousApproval {
|
|
289
|
-
enabled: false,
|
|
290
|
-
source: DangerousApprovalSource::Disabled,
|
|
291
|
-
inherited: false,
|
|
292
|
-
provider: None,
|
|
293
|
-
flag: None,
|
|
294
|
-
worker_capability_above_leader: false,
|
|
295
|
-
ancestry_binary_name: None,
|
|
296
|
-
unexpected_binary: false,
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
394
|
|
|
300
395
|
#[test]
|
|
301
396
|
fn empty_tools_use_role_defaults_and_aliases_resolve_before_command() {
|
|
@@ -308,10 +403,10 @@ mod tests {
|
|
|
308
403
|
system_prompt_file: None,
|
|
309
404
|
output_contract_format: None,
|
|
310
405
|
communication_mode: CommunicationMode::default(),
|
|
406
|
+
dangerously_skip_permissions: false,
|
|
311
407
|
};
|
|
312
408
|
let tools =
|
|
313
|
-
resolved_tool_strings_for_command(&agent, Provider::ClaudeCode
|
|
314
|
-
.unwrap();
|
|
409
|
+
resolved_tool_strings_for_command(&agent, Provider::ClaudeCode).unwrap();
|
|
315
410
|
assert_eq!(
|
|
316
411
|
tools,
|
|
317
412
|
[
|
|
@@ -330,11 +425,82 @@ mod tests {
|
|
|
330
425
|
..agent
|
|
331
426
|
};
|
|
332
427
|
let tools =
|
|
333
|
-
resolved_tool_strings_for_command(&agent, Provider::ClaudeCode
|
|
334
|
-
.unwrap();
|
|
428
|
+
resolved_tool_strings_for_command(&agent, Provider::ClaudeCode).unwrap();
|
|
335
429
|
assert_eq!(tools, ["fs_list", "fs_read", "fs_write", "mcp_team"]);
|
|
336
430
|
}
|
|
337
431
|
|
|
432
|
+
// 0.5.66 bypass 单源 §4.1:true → tools 追加 `dangerous_auto_approve` 哨兵。
|
|
433
|
+
#[test]
|
|
434
|
+
fn test_true_field_produces_bypass_flag_argv() {
|
|
435
|
+
let agent = WorkerCommandAgent {
|
|
436
|
+
id: Some("dev".to_string()),
|
|
437
|
+
provider: Provider::ClaudeCode,
|
|
438
|
+
role: Some("developer".to_string()),
|
|
439
|
+
declared_tools: Some(vec!["mcp_team".to_string()]),
|
|
440
|
+
system_prompt_inline: None,
|
|
441
|
+
system_prompt_file: None,
|
|
442
|
+
output_contract_format: None,
|
|
443
|
+
communication_mode: CommunicationMode::default(),
|
|
444
|
+
dangerously_skip_permissions: true,
|
|
445
|
+
};
|
|
446
|
+
let tools = resolved_tool_strings_for_command(&agent, Provider::ClaudeCode).unwrap();
|
|
447
|
+
assert!(
|
|
448
|
+
tools.iter().any(|tool| tool == "dangerous_auto_approve"),
|
|
449
|
+
"true field must append the dangerous_auto_approve sentinel; tools={tools:?}"
|
|
450
|
+
);
|
|
451
|
+
assert!(
|
|
452
|
+
tools.iter().any(|tool| tool == "mcp_team"),
|
|
453
|
+
"normal tools must remain; tools={tools:?}"
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// 0.5.66 bypass 单源 §4.1:false → 不追加哨兵。
|
|
458
|
+
#[test]
|
|
459
|
+
fn test_false_field_produces_no_bypass_flag_argv() {
|
|
460
|
+
let agent = WorkerCommandAgent {
|
|
461
|
+
id: Some("dev".to_string()),
|
|
462
|
+
provider: Provider::ClaudeCode,
|
|
463
|
+
role: Some("developer".to_string()),
|
|
464
|
+
declared_tools: Some(vec!["mcp_team".to_string()]),
|
|
465
|
+
system_prompt_inline: None,
|
|
466
|
+
system_prompt_file: None,
|
|
467
|
+
output_contract_format: None,
|
|
468
|
+
communication_mode: CommunicationMode::default(),
|
|
469
|
+
dangerously_skip_permissions: false,
|
|
470
|
+
};
|
|
471
|
+
let tools = resolved_tool_strings_for_command(&agent, Provider::ClaudeCode).unwrap();
|
|
472
|
+
assert!(
|
|
473
|
+
!tools.iter().any(|tool| tool == "dangerous_auto_approve"),
|
|
474
|
+
"false field must not append the sentinel; tools={tools:?}"
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// 0.5.66 bypass 单源 §4.1:provider 未定义 bypass 参数 + true → fail-loud。
|
|
479
|
+
#[test]
|
|
480
|
+
fn test_provider_without_bypass_flag_definition_errors() {
|
|
481
|
+
let agent = WorkerCommandAgent {
|
|
482
|
+
id: Some("dev".to_string()),
|
|
483
|
+
provider: Provider::GeminiCli,
|
|
484
|
+
role: Some("developer".to_string()),
|
|
485
|
+
declared_tools: Some(vec!["mcp_team".to_string()]),
|
|
486
|
+
system_prompt_inline: None,
|
|
487
|
+
system_prompt_file: None,
|
|
488
|
+
output_contract_format: None,
|
|
489
|
+
communication_mode: CommunicationMode::default(),
|
|
490
|
+
dangerously_skip_permissions: true,
|
|
491
|
+
};
|
|
492
|
+
let err = resolved_tool_strings_for_command(&agent, Provider::GeminiCli).unwrap_err();
|
|
493
|
+
let msg = err.to_string();
|
|
494
|
+
assert!(
|
|
495
|
+
msg.contains("no bypass argv flag defined"),
|
|
496
|
+
"provider without bypass flag + true must fail-loud; got {msg}"
|
|
497
|
+
);
|
|
498
|
+
assert!(
|
|
499
|
+
msg.contains("gemini_cli"),
|
|
500
|
+
"error must name the provider; got {msg}"
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
|
|
338
504
|
#[test]
|
|
339
505
|
fn system_prompt_uses_identity_then_runtime_contract_python_order() {
|
|
340
506
|
// #264 D6: Python truth source (prompt.py:39, live ps confirmed) builds
|
|
@@ -353,6 +519,7 @@ mod tests {
|
|
|
353
519
|
system_prompt_file: None,
|
|
354
520
|
output_contract_format: Some("result_envelope_v1".to_string()),
|
|
355
521
|
communication_mode: CommunicationMode::default(),
|
|
522
|
+
dangerously_skip_permissions: false,
|
|
356
523
|
};
|
|
357
524
|
let prompt = compile_worker_system_prompt(&agent).unwrap();
|
|
358
525
|
assert!(
|
|
@@ -1,3 +1,73 @@
|
|
|
1
|
+
/// ---
|
|
2
|
+
/// purpose: scoped MCP no-compaction update_state filesystem contract
|
|
3
|
+
/// contract:
|
|
4
|
+
/// provides:
|
|
5
|
+
/// - name: update_state_path_contracts
|
|
6
|
+
/// what: proves the raw no-compaction response preserves its state-file path in an owned fixture
|
|
7
|
+
/// depends:
|
|
8
|
+
/// - name: mcp_state_fixture
|
|
9
|
+
/// what: fixture-owned root and provenance from tests.rs
|
|
10
|
+
/// boundary:
|
|
11
|
+
/// - retain the raw {ok,state_file} result shape
|
|
12
|
+
/// - the owned selector checks workspace, state-file, and runtime paths remain fixture-owned
|
|
13
|
+
/// maturity: wired
|
|
14
|
+
/// ---
|
|
15
|
+
|
|
16
|
+
fn verify_cleanup_surviving_receipt(
|
|
17
|
+
receipt_path: &std::path::Path,
|
|
18
|
+
raw_workspace: &std::path::Path,
|
|
19
|
+
canonical_workspace: &std::path::Path,
|
|
20
|
+
state_file: &std::path::Path,
|
|
21
|
+
runtime_state: &std::path::Path,
|
|
22
|
+
) {
|
|
23
|
+
let bytes = std::fs::read(receipt_path)
|
|
24
|
+
.expect("provenance receipt must survive fixture Drop");
|
|
25
|
+
let receipt: Value = serde_json::from_slice(&bytes).expect("provenance receipt must be JSON");
|
|
26
|
+
assert_eq!(receipt["schema"], json!("mcp-state-provenance-v1"));
|
|
27
|
+
let payload = &receipt["payload"];
|
|
28
|
+
let payload_bytes = serde_json::to_vec(payload).unwrap();
|
|
29
|
+
assert_eq!(
|
|
30
|
+
receipt["payload_sha256"],
|
|
31
|
+
json!(format!("{:x}", sha2::Sha256::digest(&payload_bytes)))
|
|
32
|
+
);
|
|
33
|
+
assert_eq!(
|
|
34
|
+
payload["raw_workspace"],
|
|
35
|
+
json!(raw_workspace.display().to_string())
|
|
36
|
+
);
|
|
37
|
+
assert_eq!(
|
|
38
|
+
payload["canonical_workspace"],
|
|
39
|
+
json!(canonical_workspace.display().to_string())
|
|
40
|
+
);
|
|
41
|
+
assert_eq!(
|
|
42
|
+
payload["resolved_state_file"],
|
|
43
|
+
json!(state_file.display().to_string())
|
|
44
|
+
);
|
|
45
|
+
assert_eq!(
|
|
46
|
+
payload["runtime_state_path"],
|
|
47
|
+
json!(runtime_state.display().to_string())
|
|
48
|
+
);
|
|
49
|
+
let facts = payload["facts"].as_array().unwrap();
|
|
50
|
+
for name in [
|
|
51
|
+
"root",
|
|
52
|
+
"raw_workspace",
|
|
53
|
+
"canonical_workspace",
|
|
54
|
+
"team_dir",
|
|
55
|
+
"runtime_dir",
|
|
56
|
+
"state_parent",
|
|
57
|
+
"runtime_state",
|
|
58
|
+
"state_file",
|
|
59
|
+
] {
|
|
60
|
+
let fact = facts
|
|
61
|
+
.iter()
|
|
62
|
+
.find(|fact| fact["name"] == json!(name))
|
|
63
|
+
.unwrap_or_else(|| panic!("provenance fact missing: {name}"));
|
|
64
|
+
assert!(fact["path"].as_str().is_some());
|
|
65
|
+
assert!(fact["uid"].as_u64().is_some());
|
|
66
|
+
assert!(fact["mode"].as_u64().is_some());
|
|
67
|
+
assert!(fact["device"].as_u64().is_some());
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
1
71
|
#[test]
|
|
2
72
|
fn dispatch_send_message_worker_accepted_returned_verbatim() {
|
|
3
73
|
// A-7: accepted requires a REAL stored message_id (no fabricated ids), so the
|
|
@@ -52,8 +122,10 @@
|
|
|
52
122
|
// state_file (not a golden whitelist key), so the key vanishes.
|
|
53
123
|
#[test]
|
|
54
124
|
fn update_state_state_file_survives_no_compaction() {
|
|
125
|
+
let fixture = McpStateFixture::new("writable");
|
|
126
|
+
let expected = fixture.state_file("team_state.md");
|
|
55
127
|
let tools = TeamOrchestratorTools::with_identity(
|
|
56
|
-
&
|
|
128
|
+
&fixture.workspace,
|
|
57
129
|
Some(AgentId::new("leader")),
|
|
58
130
|
None,
|
|
59
131
|
);
|
|
@@ -62,6 +134,33 @@
|
|
|
62
134
|
assert!(v.get("state_file").and_then(Value::as_str).is_some(),
|
|
63
135
|
"state_file must survive (update_state is not _compact_tool_result'd)");
|
|
64
136
|
assert_eq!(keys(&v), vec!["ok", "state_file"]);
|
|
137
|
+
let returned_state_file = PathBuf::from(v["state_file"].as_str().unwrap());
|
|
138
|
+
assert_eq!(returned_state_file, expected);
|
|
139
|
+
let runtime_state = crate::state::persist::runtime_state_path(&fixture.workspace);
|
|
140
|
+
assert!(fixture.under_root(&returned_state_file));
|
|
141
|
+
assert!(fixture.under_root(&runtime_state));
|
|
142
|
+
let canonical_workspace =
|
|
143
|
+
crate::model::paths::canonical_run_workspace(&fixture.workspace).unwrap();
|
|
144
|
+
let receipt = fixture.record_provenance(&fixture.workspace, &returned_state_file);
|
|
145
|
+
let root = fixture.root.clone();
|
|
146
|
+
drop(fixture);
|
|
147
|
+
assert!(!root.exists(), "fixture root must be removed before receipt verification");
|
|
148
|
+
verify_cleanup_surviving_receipt(
|
|
149
|
+
&receipt,
|
|
150
|
+
&root.join("workspace"),
|
|
151
|
+
&canonical_workspace,
|
|
152
|
+
&returned_state_file,
|
|
153
|
+
&runtime_state,
|
|
154
|
+
);
|
|
155
|
+
let receipt_bytes = std::fs::read(&receipt).unwrap();
|
|
156
|
+
println!(
|
|
157
|
+
"F10_PROVENANCE_RECEIPT path={} sha256={:x} payload={}",
|
|
158
|
+
receipt.display(),
|
|
159
|
+
sha2::Sha256::digest(&receipt_bytes),
|
|
160
|
+
String::from_utf8_lossy(&receipt_bytes)
|
|
161
|
+
);
|
|
162
|
+
std::fs::remove_file(&receipt).unwrap();
|
|
163
|
+
assert!(!receipt.exists(), "cleanup must remove only the exact receipt path");
|
|
65
164
|
}
|
|
66
165
|
|
|
67
166
|
// ── #36 report_result setdefault: populated envelope keys WIN over args ─────
|