@team-agent/installer 0.5.60 → 0.5.62
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 +49 -3
- package/crates/team-agent/src/cli/attach_app_server_leader.rs +1 -0
- package/crates/team-agent/src/cli/diagnose.rs +184 -0
- package/crates/team-agent/src/cli/emit.rs +104 -21
- package/crates/team-agent/src/cli/helpers.rs +1 -0
- package/crates/team-agent/src/cli/leader.rs +14 -2
- package/crates/team-agent/src/cli/leaders.rs +2 -0
- package/crates/team-agent/src/cli/mod.rs +296 -8
- package/crates/team-agent/src/cli/named_address.rs +26 -2
- package/crates/team-agent/src/cli/profile.rs +2 -1
- package/crates/team-agent/src/cli/send/coordinator.rs +1 -0
- package/crates/team-agent/src/cli/send/mailbox.rs +3 -0
- package/crates/team-agent/src/cli/send/persist.rs +1 -0
- package/crates/team-agent/src/cli/send/presentation.rs +3 -0
- package/crates/team-agent/src/cli/send/resolve.rs +206 -1
- package/crates/team-agent/src/cli/send.rs +2 -1
- package/crates/team-agent/src/cli/spec.rs +5 -1
- package/crates/team-agent/src/cli/status.rs +2 -1
- package/crates/team-agent/src/cli/status_port/compact.rs +1 -0
- package/crates/team-agent/src/cli/status_port/format.rs +1 -0
- package/crates/team-agent/src/cli/status_port/inbox.rs +1 -0
- package/crates/team-agent/src/cli/status_port/store.rs +7 -3
- package/crates/team-agent/src/cli/tests/named_address.rs +65 -0
- package/crates/team-agent/src/cli/tests/verb_profile.rs +38 -0
- package/crates/team-agent/src/cli/types.rs +36 -2
- package/crates/team-agent/src/communication_mode/mod.rs +54 -0
- package/crates/team-agent/src/compiler/tests.rs +5 -5
- package/crates/team-agent/src/compiler.rs +54 -1
- package/crates/team-agent/src/conpty/backend.rs +1 -0
- package/crates/team-agent/src/conpty/mod.rs +1 -0
- package/crates/team-agent/src/coordinator/backoff.rs +5 -8
- package/crates/team-agent/src/coordinator/conpty_shim.rs +3 -2
- package/crates/team-agent/src/coordinator/health.rs +1 -0
- package/crates/team-agent/src/coordinator/mod.rs +2 -2
- package/crates/team-agent/src/coordinator/orphan.rs +4 -116
- package/crates/team-agent/src/coordinator/runtime_detectors.rs +1 -0
- package/crates/team-agent/src/coordinator/runtime_observation.rs +1 -0
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +6 -5
- package/crates/team-agent/src/coordinator/steps/delivery.rs +1 -0
- package/crates/team-agent/src/coordinator/steps/health_sync.rs +1 -0
- package/crates/team-agent/src/coordinator/steps/mod.rs +2 -0
- package/crates/team-agent/src/coordinator/steps/persist.rs +1 -0
- package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +1 -0
- package/crates/team-agent/src/coordinator/steps/session_gate.rs +1 -0
- package/crates/team-agent/src/coordinator/tests/abnormal.rs +0 -174
- package/crates/team-agent/src/coordinator/tests/api_error_recovery.rs +0 -9
- package/crates/team-agent/src/coordinator/tests/basics.rs +0 -184
- package/crates/team-agent/src/coordinator/tests/mod.rs +2 -17
- package/crates/team-agent/src/coordinator/tests/tick_core.rs +0 -42
- package/crates/team-agent/src/coordinator/tick.rs +24 -28
- package/crates/team-agent/src/coordinator/types.rs +3 -176
- package/crates/team-agent/src/db/agent_health_capture.rs +1 -0
- package/crates/team-agent/src/db/message_store.rs +47 -2
- package/crates/team-agent/src/db/migration.rs +5 -4
- package/crates/team-agent/src/db/mod.rs +1 -0
- package/crates/team-agent/src/db/schema.rs +1 -0
- package/crates/team-agent/src/diagnose/comms.rs +2 -1
- package/crates/team-agent/src/diagnose/mod.rs +1 -0
- package/crates/team-agent/src/diagnose/orphans.rs +1 -0
- package/crates/team-agent/src/fake_worker.rs +21 -1
- package/crates/team-agent/src/layout/manager.rs +3 -7
- package/crates/team-agent/src/layout/mod.rs +2 -2
- package/crates/team-agent/src/layout/overlay.rs +6 -1
- package/crates/team-agent/src/layout/placement.rs +1 -0
- package/crates/team-agent/src/layout/recovery.rs +3 -0
- package/crates/team-agent/src/layout/runtime_sessions.rs +8 -3
- package/crates/team-agent/src/layout/sessions.rs +8 -1
- package/crates/team-agent/src/layout/tmux_endpoint.rs +3 -0
- package/crates/team-agent/src/layout/worker_env.rs +3 -0
- package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
- package/crates/team-agent/src/leader/helpers.rs +1 -0
- package/crates/team-agent/src/leader/incident.rs +1 -0
- package/crates/team-agent/src/leader/lease.rs +27 -22
- package/crates/team-agent/src/leader/mod.rs +2 -3
- package/crates/team-agent/src/leader/owner_bind.rs +13 -3
- package/crates/team-agent/src/leader/provider_attribution.rs +1 -0
- package/crates/team-agent/src/leader/rediscover.rs +4 -17
- package/crates/team-agent/src/leader/registry.rs +3 -15
- package/crates/team-agent/src/leader/start.rs +1072 -186
- package/crates/team-agent/src/leader/takeover.rs +10 -232
- package/crates/team-agent/src/leader/tests/claim_leader_mailbox_flush_contract.rs +544 -0
- package/crates/team-agent/src/leader/tests/historical_inventory_upgrade_gate_red.rs +624 -0
- package/crates/team-agent/src/leader/tests/identity.rs +71 -59
- package/crates/team-agent/src/leader/tests/identity_session_names.rs +42 -0
- package/crates/team-agent/src/leader/tests/idle.rs +0 -193
- package/crates/team-agent/src/leader/tests/lease_api.rs +0 -45
- package/crates/team-agent/src/leader/tests/mod.rs +4 -41
- package/crates/team-agent/src/leader/tests/rehomed_env.rs +83 -0
- package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1273 -0
- package/crates/team-agent/src/leader/tests/terminal_and_replay_vs_rerender_invariants_red.rs +417 -0
- package/crates/team-agent/src/leader/tests/wake_start_owner.rs +22 -103
- package/crates/team-agent/src/leader/types.rs +1 -28
- package/crates/team-agent/src/lib.rs +7 -0
- package/crates/team-agent/src/lifecycle/display.rs +7 -1
- package/crates/team-agent/src/lifecycle/helpers.rs +3 -1
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +2 -2
- package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +1 -0
- package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +1 -1
- package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +1 -0
- package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +1 -0
- package/crates/team-agent/src/lifecycle/launch/plan.rs +2 -2
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +4 -26
- package/crates/team-agent/src/lifecycle/launch/readiness.rs +0 -27
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +2 -28
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +4 -32
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +1 -0
- package/crates/team-agent/src/lifecycle/launch.rs +11 -12
- package/crates/team-agent/src/lifecycle/mod.rs +2 -1
- package/crates/team-agent/src/lifecycle/restart/agent.rs +4 -4
- package/crates/team-agent/src/lifecycle/restart/common.rs +1 -1
- package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +2 -2
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +13 -5
- package/crates/team-agent/src/lifecycle/restart/remove.rs +1 -1
- package/crates/team-agent/src/lifecycle/restart/selection.rs +3 -3
- package/crates/team-agent/src/lifecycle/restart.rs +13 -11
- package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1007 -0
- package/crates/team-agent/src/lifecycle/tests/b0_legacy_snapshot_nonauthority_contract.rs +265 -0
- package/crates/team-agent/src/lifecycle/tests/b0_reader_hideone_audit_contract.rs +459 -0
- package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +849 -0
- package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +323 -0
- package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +423 -0
- package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +737 -0
- package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +1187 -0
- package/crates/team-agent/src/lifecycle/tests/codex_weak_window_attribution_red.rs +683 -0
- package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +394 -0
- package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +1856 -0
- package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +917 -0
- package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +481 -0
- package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +301 -0
- package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +516 -0
- package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +986 -0
- package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +651 -0
- package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +2 -0
- package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +332 -0
- package/crates/team-agent/src/lifecycle/tests/realmachine_clusters_1_6_red.rs +846 -0
- package/crates/team-agent/src/lifecycle/tests/realmachine_residual_g1_g4_red.rs +648 -0
- package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +910 -0
- package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +630 -0
- package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +1083 -0
- package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +1677 -0
- package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +410 -0
- package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +428 -0
- package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +1032 -0
- package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +869 -0
- package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1249 -0
- package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +342 -0
- package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +542 -0
- package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1103 -0
- package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1276 -0
- package/crates/team-agent/src/lifecycle/tests/team_key_retirement_roster_red.rs +145 -0
- package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +603 -0
- package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +837 -0
- package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +928 -0
- package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +951 -0
- package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +327 -0
- package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +760 -0
- package/crates/team-agent/src/lifecycle/tests.rs +59 -0
- package/crates/team-agent/src/lifecycle/types.rs +0 -9
- package/crates/team-agent/src/lifecycle/worker_command_context.rs +29 -11
- package/crates/team-agent/src/mcp_server/helpers.rs +1 -0
- package/crates/team-agent/src/mcp_server/lifecycle_tools/mod.rs +1 -0
- package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +1 -0
- package/crates/team-agent/src/mcp_server/mod.rs +1 -0
- package/crates/team-agent/src/mcp_server/normalize.rs +5 -3
- package/crates/team-agent/src/mcp_server/tests/send.rs +26 -0
- package/crates/team-agent/src/mcp_server/tests/wire.rs +1 -1
- package/crates/team-agent/src/mcp_server/tools.rs +101 -66
- package/crates/team-agent/src/mcp_server/types.rs +6 -18
- package/crates/team-agent/src/mcp_server/wire.rs +32 -26
- package/crates/team-agent/src/messaging/activity.rs +1 -0
- package/crates/team-agent/src/messaging/address.rs +1 -0
- package/crates/team-agent/src/messaging/delivery.rs +109 -41
- package/crates/team-agent/src/messaging/helpers.rs +2 -1
- package/crates/team-agent/src/messaging/leader_channel.rs +34 -10
- package/crates/team-agent/src/messaging/leader_receiver.rs +42 -24
- package/crates/team-agent/src/messaging/mod.rs +10 -14
- package/crates/team-agent/src/messaging/peers.rs +2 -0
- package/crates/team-agent/src/messaging/persist.rs +3 -1
- package/crates/team-agent/src/messaging/presentation.rs +111 -1
- package/crates/team-agent/src/messaging/results.rs +166 -17
- package/crates/team-agent/src/messaging/scheduler.rs +1 -0
- package/crates/team-agent/src/messaging/selftest.rs +1 -1
- package/crates/team-agent/src/messaging/send.rs +135 -89
- package/crates/team-agent/src/messaging/tests/leader_channel.rs +6 -6
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +187 -0
- package/crates/team-agent/src/messaging/tests/mod.rs +5 -0
- package/crates/team-agent/src/messaging/tests/runtime.rs +13 -6
- package/crates/team-agent/src/messaging/types.rs +7 -2
- package/crates/team-agent/src/messaging/watchers.rs +24 -13
- package/crates/team-agent/src/model/enums.rs +1 -0
- package/crates/team-agent/src/model/errors.rs +1 -0
- package/crates/team-agent/src/model/ids.rs +1 -0
- package/crates/team-agent/src/model/mod.rs +2 -0
- package/crates/team-agent/src/model/name_similarity.rs +1 -0
- package/crates/team-agent/src/model/pane_authority_refusal.rs +359 -0
- package/crates/team-agent/src/model/paths.rs +1 -0
- package/crates/team-agent/src/model/permissions.rs +3 -2
- package/crates/team-agent/src/model/routing.rs +1 -0
- package/crates/team-agent/src/model/spec.rs +17 -0
- package/crates/team-agent/src/model/task_graph.rs +1 -0
- package/crates/team-agent/src/model/yaml/tests.rs +1 -0
- package/crates/team-agent/src/model/yaml.rs +1 -0
- package/crates/team-agent/src/packaging/install.rs +9 -147
- package/crates/team-agent/src/packaging/migrate.rs +2 -0
- package/crates/team-agent/src/packaging/mod.rs +1 -0
- package/crates/team-agent/src/packaging/repair.rs +2 -0
- package/crates/team-agent/src/packaging/tests.rs +29 -218
- package/crates/team-agent/src/packaging/types.rs +8 -15
- package/crates/team-agent/src/platform/argv.rs +4 -0
- package/crates/team-agent/src/platform/errors.rs +10 -1
- package/crates/team-agent/src/platform/file_lock.rs +4 -157
- package/crates/team-agent/src/platform/mod.rs +0 -61
- package/crates/team-agent/src/platform/process.rs +1 -0
- package/crates/team-agent/src/provider/adapter.rs +1 -0
- package/crates/team-agent/src/provider/adapters/claude.rs +1 -0
- package/crates/team-agent/src/provider/adapters/claude_fork.rs +1 -0
- package/crates/team-agent/src/provider/adapters/codex.rs +1 -0
- package/crates/team-agent/src/provider/adapters/copilot.rs +1 -0
- package/crates/team-agent/src/provider/adapters/copilot_fork.rs +1 -0
- package/crates/team-agent/src/provider/adapters/fake.rs +1 -0
- package/crates/team-agent/src/provider/adapters/mod.rs +1 -0
- package/crates/team-agent/src/provider/approvals/mod.rs +1 -0
- package/crates/team-agent/src/provider/approvals/parsing.rs +2 -5
- package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +6 -5
- package/crates/team-agent/src/provider/classify.rs +1 -0
- package/crates/team-agent/src/provider/faults.rs +1 -26
- package/crates/team-agent/src/provider/helpers.rs +1 -0
- package/crates/team-agent/src/provider/mod.rs +1 -1
- package/crates/team-agent/src/provider/session/capture.rs +41 -22
- package/crates/team-agent/src/provider/session/context_fork/claude.rs +1 -0
- package/crates/team-agent/src/provider/session/context_fork/codex.rs +1 -0
- package/crates/team-agent/src/provider/session/context_fork/outcome.rs +1 -0
- package/crates/team-agent/src/provider/session/context_fork.rs +1 -0
- package/crates/team-agent/src/provider/session/mod.rs +2 -4
- package/crates/team-agent/src/provider/session/resume.rs +2 -209
- package/crates/team-agent/src/provider/session_scan/claude.rs +93 -3
- package/crates/team-agent/src/provider/session_scan/codex.rs +1 -0
- package/crates/team-agent/src/provider/session_scan/common.rs +24 -7
- package/crates/team-agent/src/provider/session_scan/copilot.rs +1 -0
- package/crates/team-agent/src/provider/session_scan.rs +6 -42
- package/crates/team-agent/src/provider/startup_prompt.rs +1 -0
- package/crates/team-agent/src/provider/types.rs +1 -0
- package/crates/team-agent/src/provider/wire.rs +1 -0
- package/crates/team-agent/src/state/identity.rs +16 -5
- package/crates/team-agent/src/state/identity_keys.rs +1 -0
- package/crates/team-agent/src/state/mod.rs +2 -0
- package/crates/team-agent/src/state/owner_gate.rs +5 -0
- package/crates/team-agent/src/state/ownership.rs +10 -4
- package/crates/team-agent/src/state/paths.rs +12 -0
- package/crates/team-agent/src/state/persist.rs +9 -3
- package/crates/team-agent/src/state/projection.rs +15 -2
- package/crates/team-agent/src/state/repository/intent.rs +1 -0
- package/crates/team-agent/src/state/repository/tests.rs +1 -0
- package/crates/team-agent/src/state/repository.rs +7 -0
- package/crates/team-agent/src/state/selector.rs +1 -0
- package/crates/team-agent/src/tmux_backend/tests.rs +39 -0
- package/crates/team-agent/src/tmux_backend.rs +63 -0
- package/crates/team-agent/src/topology.rs +3 -0
- package/crates/team-agent/src/transport.rs +15 -0
- package/package.json +4 -4
- package/skills/team-agent/command-coverage.json +379 -0
- package/crates/team-agent/src/leader/inject.rs +0 -33
- package/crates/team-agent/src/provider/command.rs +0 -80
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//!
|
|
1
2
|
use super::mailbox::maybe_enqueue_offline_leader_mailbox;
|
|
2
3
|
use super::persist::persist_resolved_target;
|
|
3
4
|
use crate::cli::{CliError, SendArgs};
|
|
@@ -207,7 +208,7 @@ pub(super) fn send_to_logical_to(
|
|
|
207
208
|
&args.workspace,
|
|
208
209
|
name,
|
|
209
210
|
content,
|
|
210
|
-
args.sender.
|
|
211
|
+
args.sender.display_name(),
|
|
211
212
|
args.task.as_deref(),
|
|
212
213
|
&error,
|
|
213
214
|
)? {
|
|
@@ -359,3 +360,207 @@ pub(super) fn send_to_resolved_name(
|
|
|
359
360
|
}
|
|
360
361
|
Ok(value)
|
|
361
362
|
}
|
|
363
|
+
|
|
364
|
+
#[cfg(test)]
|
|
365
|
+
mod a13_red_tests {
|
|
366
|
+
#![allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
|
|
367
|
+
|
|
368
|
+
use super::send_to_resolved_name;
|
|
369
|
+
use crate::cli::named_address::{NamedTargetKind, ResolvedNamedAddress};
|
|
370
|
+
use crate::cli::SendArgs;
|
|
371
|
+
use crate::message_store::MessageStore;
|
|
372
|
+
use crate::messaging::TrustedSender;
|
|
373
|
+
use crate::model::ids::AgentId;
|
|
374
|
+
use serde_json::{json, Value};
|
|
375
|
+
use std::path::{Path, PathBuf};
|
|
376
|
+
use std::sync::atomic::{AtomicU64, Ordering};
|
|
377
|
+
|
|
378
|
+
fn fresh_workspace(tag: &str) -> PathBuf {
|
|
379
|
+
static NEXT: AtomicU64 = AtomicU64::new(0);
|
|
380
|
+
let workspace = std::env::temp_dir().join(format!(
|
|
381
|
+
"team-agent-a13-{tag}-{}-{}",
|
|
382
|
+
std::process::id(),
|
|
383
|
+
NEXT.fetch_add(1, Ordering::Relaxed)
|
|
384
|
+
));
|
|
385
|
+
let _ = std::fs::remove_dir_all(&workspace);
|
|
386
|
+
std::fs::create_dir_all(workspace.join(".team/runtime")).expect("create fixture");
|
|
387
|
+
workspace
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
fn seed_team(workspace: &Path, team: &str) {
|
|
391
|
+
let session = format!("team-{team}");
|
|
392
|
+
let state = json!({
|
|
393
|
+
"active_team_key": team,
|
|
394
|
+
"status": "alive",
|
|
395
|
+
"session_name": session,
|
|
396
|
+
"agents": {},
|
|
397
|
+
"teams": {
|
|
398
|
+
team: {
|
|
399
|
+
"status": "alive",
|
|
400
|
+
"session_name": session,
|
|
401
|
+
"agents": {},
|
|
402
|
+
"leader_receiver": {"status": "unbound"}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
crate::state::persist::save_runtime_state(workspace, &state).expect("save fixture state");
|
|
407
|
+
MessageStore::open(workspace).expect("create fixture store");
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
fn send_args(workspace: &Path) -> SendArgs {
|
|
411
|
+
SendArgs {
|
|
412
|
+
target: None,
|
|
413
|
+
message: vec!["payload".to_string()],
|
|
414
|
+
targets: None,
|
|
415
|
+
workspace: workspace.to_path_buf(),
|
|
416
|
+
team: None,
|
|
417
|
+
task: None,
|
|
418
|
+
sender: TrustedSender::from_runtime_identity_with_source(
|
|
419
|
+
AgentId::new("worker-1"),
|
|
420
|
+
workspace.display(),
|
|
421
|
+
"sender-team",
|
|
422
|
+
),
|
|
423
|
+
no_ack: false,
|
|
424
|
+
no_wait: true,
|
|
425
|
+
watch_result: false,
|
|
426
|
+
timeout: 0.0,
|
|
427
|
+
confirm_human: false,
|
|
428
|
+
json: true,
|
|
429
|
+
message_id: None,
|
|
430
|
+
presentation: crate::messaging::presentation::PresentationRequest::default(),
|
|
431
|
+
pane: None,
|
|
432
|
+
to_name: Some("other-team/leader".to_string()),
|
|
433
|
+
to_leader: None,
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
fn leader_address(sender_workspace: &Path, target_workspace: &Path) -> ResolvedNamedAddress {
|
|
438
|
+
ResolvedNamedAddress {
|
|
439
|
+
raw_name: format!("{}::other-team/leader", target_workspace.display()),
|
|
440
|
+
target_kind: NamedTargetKind::Leader,
|
|
441
|
+
sender_workspace: sender_workspace.to_path_buf(),
|
|
442
|
+
target_workspace: target_workspace.to_path_buf(),
|
|
443
|
+
team_key: Some("other-team".to_string()),
|
|
444
|
+
agent_id: None,
|
|
445
|
+
pane_id: String::new(),
|
|
446
|
+
session_name: Some("team-other-team".to_string()),
|
|
447
|
+
window_name: None,
|
|
448
|
+
tmux_endpoint: None,
|
|
449
|
+
transport_kind: Some("direct_tmux".to_string()),
|
|
450
|
+
app_server: None,
|
|
451
|
+
state_pane_id: None,
|
|
452
|
+
state_pane_stale: false,
|
|
453
|
+
agent_status: None,
|
|
454
|
+
warning: None,
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
fn message_count(workspace: &Path) -> i64 {
|
|
459
|
+
let store = MessageStore::open(workspace).expect("open fixture store");
|
|
460
|
+
crate::db::schema::open_db(store.db_path())
|
|
461
|
+
.expect("open fixture database")
|
|
462
|
+
.query_row("select count(*) from messages", [], |row| row.get(0))
|
|
463
|
+
.expect("count fixture messages")
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
#[test]
|
|
467
|
+
fn a13_worker_cross_workspace_leader_send_preserves_target_scope() {
|
|
468
|
+
let sender_workspace = fresh_workspace("sender");
|
|
469
|
+
let target_workspace = fresh_workspace("target");
|
|
470
|
+
seed_team(&target_workspace, "other-team");
|
|
471
|
+
let mut args = send_args(&sender_workspace);
|
|
472
|
+
let to_name = format!("{}::other-team/leader", target_workspace.display());
|
|
473
|
+
args.to_name = Some(to_name.clone());
|
|
474
|
+
let value = super::send_to_logical_to(&args, &to_name, "payload")
|
|
475
|
+
.expect("cross-workspace leader send must preserve a structured outcome");
|
|
476
|
+
assert_eq!(
|
|
477
|
+
value["to_name"],
|
|
478
|
+
json!(to_name),
|
|
479
|
+
"cross-workspace receipt must retain the original target: {value}"
|
|
480
|
+
);
|
|
481
|
+
assert_eq!(
|
|
482
|
+
value["sender"],
|
|
483
|
+
json!(format!(
|
|
484
|
+
"{}::sender-team/worker-1",
|
|
485
|
+
sender_workspace.display()
|
|
486
|
+
)),
|
|
487
|
+
"cross-workspace receipt must retain the full sender: {value}"
|
|
488
|
+
);
|
|
489
|
+
assert_eq!(
|
|
490
|
+
message_count(&sender_workspace),
|
|
491
|
+
0,
|
|
492
|
+
"cross-workspace send must not fold into the sender workspace"
|
|
493
|
+
);
|
|
494
|
+
assert_eq!(
|
|
495
|
+
message_count(&target_workspace),
|
|
496
|
+
1,
|
|
497
|
+
"cross-workspace send must persist in the target workspace"
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
let _ = std::fs::remove_dir_all(sender_workspace);
|
|
501
|
+
let _ = std::fs::remove_dir_all(target_workspace);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
#[test]
|
|
505
|
+
fn a13_worker_cross_workspace_identity_is_preserved_in_row_and_render() {
|
|
506
|
+
let sender_workspace = fresh_workspace("identity-sender");
|
|
507
|
+
let target_workspace = fresh_workspace("identity-target");
|
|
508
|
+
seed_team(&target_workspace, "other-team");
|
|
509
|
+
let mut args = send_args(&sender_workspace);
|
|
510
|
+
args.to_name = Some(format!("{}::other-team/leader", target_workspace.display()));
|
|
511
|
+
let resolved = leader_address(&sender_workspace, &target_workspace);
|
|
512
|
+
|
|
513
|
+
send_to_resolved_name(&args, &resolved, "identity payload")
|
|
514
|
+
.expect("cross-workspace identity send must persist");
|
|
515
|
+
let store = MessageStore::open(&target_workspace).expect("open target store");
|
|
516
|
+
let connection = crate::db::schema::open_db(store.db_path()).expect("open target db");
|
|
517
|
+
let sender: String = connection
|
|
518
|
+
.query_row("select sender from messages limit 1", [], |row| row.get(0))
|
|
519
|
+
.expect("read sender");
|
|
520
|
+
let expected = format!("{}::sender-team/worker-1", sender_workspace.display());
|
|
521
|
+
assert_eq!(
|
|
522
|
+
sender, expected,
|
|
523
|
+
"durable row must retain the full sender identity"
|
|
524
|
+
);
|
|
525
|
+
let rendered = crate::messaging::delivery::render_message(
|
|
526
|
+
&sender,
|
|
527
|
+
None,
|
|
528
|
+
"identity payload",
|
|
529
|
+
"msg-a13",
|
|
530
|
+
);
|
|
531
|
+
assert!(
|
|
532
|
+
rendered.contains(&expected),
|
|
533
|
+
"rendered pane must show full sender: {rendered}"
|
|
534
|
+
);
|
|
535
|
+
assert!(
|
|
536
|
+
!rendered.contains("from leader"),
|
|
537
|
+
"worker must not render as leader: {rendered}"
|
|
538
|
+
);
|
|
539
|
+
|
|
540
|
+
let _ = std::fs::remove_dir_all(sender_workspace);
|
|
541
|
+
let _ = std::fs::remove_dir_all(target_workspace);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
#[test]
|
|
545
|
+
fn a13_worker_in_workspace_leader_send_remains_allowed() {
|
|
546
|
+
let workspace = fresh_workspace("in-workspace");
|
|
547
|
+
seed_team(&workspace, "other-team");
|
|
548
|
+
let args = send_args(&workspace);
|
|
549
|
+
let resolved = leader_address(&workspace, &workspace);
|
|
550
|
+
|
|
551
|
+
let value = send_to_resolved_name(&args, &resolved, "payload")
|
|
552
|
+
.expect("in-workspace leader send should preserve the existing path");
|
|
553
|
+
assert_ne!(
|
|
554
|
+
value["reason"],
|
|
555
|
+
Value::String("target_out_of_scope".to_string()),
|
|
556
|
+
"in-workspace send must not be classified as cross-workspace: {value}"
|
|
557
|
+
);
|
|
558
|
+
assert_eq!(
|
|
559
|
+
message_count(&workspace),
|
|
560
|
+
1,
|
|
561
|
+
"allowed send must persist once"
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
let _ = std::fs::remove_dir_all(workspace);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//!
|
|
1
2
|
//! cli · send — `cmd_send` + target 解析(`_send_target`)+ `SendArgs`→`SendOptions` 翻译
|
|
2
3
|
//! (`send_options_from_args`,旗标取反语义)。
|
|
3
4
|
|
|
@@ -180,7 +181,7 @@ pub fn send_to_canonical_leader_target(
|
|
|
180
181
|
}
|
|
181
182
|
|
|
182
183
|
/// `_send_target`(`commands.py:181-184`):`--to` comma-split fanout / `target` 单值 / None。
|
|
183
|
-
pub fn send_target(targets: Option<&str>, target: Option<&str>) -> MessageTarget {
|
|
184
|
+
pub(super) fn send_target(targets: Option<&str>, target: Option<&str>) -> MessageTarget {
|
|
184
185
|
if let Some(targets) = targets.filter(|s| !s.is_empty()) {
|
|
185
186
|
let recipients: Vec<String> = targets
|
|
186
187
|
.split(',')
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//!
|
|
1
2
|
//! C1 command registry: one catalog for help visibility, known-command gates,
|
|
2
3
|
//! suggestion candidates, and command-tier governance.
|
|
3
4
|
|
|
@@ -76,6 +77,7 @@ pub(crate) enum DispatchKind {
|
|
|
76
77
|
InstallSkill,
|
|
77
78
|
Profile,
|
|
78
79
|
Collect,
|
|
80
|
+
Results,
|
|
79
81
|
Diagnose,
|
|
80
82
|
Preflight,
|
|
81
83
|
WaitReady,
|
|
@@ -120,6 +122,7 @@ pub(crate) const ALL_DISPATCH_KINDS: &[DispatchKind] = &[
|
|
|
120
122
|
DispatchKind::InstallSkill,
|
|
121
123
|
DispatchKind::Profile,
|
|
122
124
|
DispatchKind::Collect,
|
|
125
|
+
DispatchKind::Results,
|
|
123
126
|
DispatchKind::Diagnose,
|
|
124
127
|
DispatchKind::Preflight,
|
|
125
128
|
DispatchKind::WaitReady,
|
|
@@ -155,9 +158,10 @@ pub(crate) struct CommandSpec {
|
|
|
155
158
|
#[rustfmt::skip]
|
|
156
159
|
pub(crate) const COMMAND_SPECS: &[CommandSpec] = &[
|
|
157
160
|
CommandSpec { name: "quick-start", tier: CommandTier::Core, category: CommandCategory::Start, kind: CommandKind::Dispatch(DispatchKind::QuickStart), summary: "start or attach a team from TEAM.md", usage: "usage: team-agent quick-start [TEAMDIR] [--workspace WORKSPACE] [--name NAME] [--team-id TEAM|--team TEAM] [--yes] [--no-display] [--backend tmux|conpty] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, alias_of: None, sunset: None, action: None, governance: None },
|
|
158
|
-
CommandSpec { name: "send", tier: CommandTier::Core, category: CommandCategory::Daily, kind: CommandKind::Dispatch(DispatchKind::Send), summary: "persist a message for an in-team short name or fully-qualified logical recipient", usage: "usage: team-agent send TO MESSAGE... [--workspace WORKSPACE] [--team TEAM] [--
|
|
161
|
+
CommandSpec { name: "send", tier: CommandTier::Core, category: CommandCategory::Daily, kind: CommandKind::Dispatch(DispatchKind::Send), summary: "persist a message for an in-team short name or fully-qualified logical recipient", usage: "usage: team-agent send TO MESSAGE... [--workspace WORKSPACE] [--team TEAM] [--mailbox] [--json]\nTO forms are co-equal: an in-team short name (for example `team-agent send reviewer \"Review\"`) or `<workspace>::<team>/<agent>`.", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, alias_of: None, sunset: Some("next compatibility release"), action: Some("use positional logical TO and the returned message id"), governance: None },
|
|
159
162
|
CommandSpec { name: "status", tier: CommandTier::Core, category: CommandCategory::Daily, kind: CommandKind::Dispatch(DispatchKind::Status), summary: "show current team status", usage: "usage: team-agent status [AGENT] [--workspace WORKSPACE] [--team TEAM] [--summary|--json] [--detail]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
|
|
160
163
|
CommandSpec { name: "collect", tier: CommandTier::Core, category: CommandCategory::Daily, kind: CommandKind::Dispatch(DispatchKind::Collect), summary: "collect reported results", usage: "usage: team-agent collect [--workspace WORKSPACE] [--team TEAM] [--result-file FILE] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
|
|
164
|
+
CommandSpec { name: "results", tier: CommandTier::Core, category: CommandCategory::Daily, kind: CommandKind::Dispatch(DispatchKind::Results), summary: "read reported results for a case", usage: "usage: team-agent results --case CASE_ID [--workspace WORKSPACE] [--team TEAM] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
|
|
161
165
|
CommandSpec { name: "restart", tier: CommandTier::Core, category: CommandCategory::TeamLifecycle, kind: CommandKind::Dispatch(DispatchKind::Restart), summary: "restart the selected team", usage: "usage: team-agent restart [WORKSPACE] [--team TEAM] [--allow-fresh] [--session-converge-deadline SECONDS] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, alias_of: None, sunset: None, action: None, governance: None },
|
|
162
166
|
CommandSpec { name: "shutdown", tier: CommandTier::Core, category: CommandCategory::TeamLifecycle, kind: CommandKind::Dispatch(DispatchKind::Shutdown), summary: "stop the selected team", usage: "usage: team-agent shutdown [--workspace WORKSPACE] [--team TEAM] [--keep-logs] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::No, alias_of: None, sunset: None, action: None, governance: None },
|
|
163
167
|
CommandSpec { name: "add-agent", tier: CommandTier::Core, category: CommandCategory::WorkerLifecycle, kind: CommandKind::Dispatch(DispatchKind::AddAgent), summary: "add or force-recreate a worker", usage: "usage: team-agent add-agent AGENT --role-file FILE [--force] [--workspace WORKSPACE] [--team TEAM] [--no-display] [--json]", default_help: true, command_help: true, suggestion_index: true, token_usage: TokenUsage::Conditional, alias_of: None, sunset: None, action: None, governance: None },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//!
|
|
1
2
|
//! cli · status — 五行 triage summary 渲染 + agent 分类计数(commands.py
|
|
2
3
|
//! `_format_status_summary` / `_agent_summary_counts` / `_interaction_counts` /
|
|
3
4
|
//! `_latest_result_line` + `cmd_status` 的 compact 不变量)。
|
|
@@ -100,7 +101,7 @@ fn stale_agent_bucket(agent: &Value) -> Option<SummaryBucket> {
|
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
/// `_interaction_counts`(`commands.py:292-306`):遍历 agents 的 `interacted` 字段。
|
|
103
|
-
pub fn interaction_counts(agents: &Value) -> InteractionCounts {
|
|
104
|
+
pub(super) fn interaction_counts(agents: &Value) -> InteractionCounts {
|
|
104
105
|
let mut counts = InteractionCounts::default();
|
|
105
106
|
let Some(agent_map) = agents.as_object() else {
|
|
106
107
|
return counts;
|
|
@@ -393,12 +393,16 @@ pub(super) fn count_undelivered_backlog(
|
|
|
393
393
|
conn: &rusqlite::Connection,
|
|
394
394
|
owner_team_id: Option<&str>,
|
|
395
395
|
) -> Result<i64, CliError> {
|
|
396
|
-
//
|
|
396
|
+
// Delivery debt is broader than requeue eligibility: a physically injected
|
|
397
|
+
// row awaiting a future receipt remains undelivered even though it must not
|
|
398
|
+
// be claimed or injected again.
|
|
397
399
|
let sql = match owner_team_id {
|
|
398
400
|
Some(_) => "select count(*) from messages
|
|
399
|
-
where owner_team_id = ?1 and status in
|
|
401
|
+
where owner_team_id = ?1 and status in
|
|
402
|
+
('accepted','pending','queued','queued_until_trust','injected_awaiting_receipt')",
|
|
400
403
|
None => "select count(*) from messages
|
|
401
|
-
where status in
|
|
404
|
+
where status in
|
|
405
|
+
('accepted','pending','queued','queued_until_trust','injected_awaiting_receipt')",
|
|
402
406
|
};
|
|
403
407
|
let count: i64 = match owner_team_id {
|
|
404
408
|
Some(team) => conn
|
|
@@ -135,6 +135,17 @@ fn assert_kind(
|
|
|
135
135
|
assert!(n38.contains("Log: "), "{n38}");
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
fn assert_broad_leader_not_attached(err: NamedAddressError) {
|
|
139
|
+
assert_eq!(err.kind, NamedAddressErrorKind::LeaderNotAttached);
|
|
140
|
+
assert_eq!(err.channel_reason, None);
|
|
141
|
+
let value = err.to_json();
|
|
142
|
+
assert_eq!(value["reason"], json!("leader_not_attached"));
|
|
143
|
+
assert!(
|
|
144
|
+
value.get("channel_reason").is_none(),
|
|
145
|
+
"broad refusal must not expose a typed channel reason: {value}"
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
138
149
|
fn named_send_args(
|
|
139
150
|
ws: &Path,
|
|
140
151
|
to_name: Option<&str>,
|
|
@@ -372,6 +383,60 @@ fn resolve_leader_name_not_live() {
|
|
|
372
383
|
let _ = std::fs::remove_dir_all(&ws);
|
|
373
384
|
}
|
|
374
385
|
|
|
386
|
+
#[test]
|
|
387
|
+
fn resolve_leader_receiver_not_attached_stays_broad() {
|
|
388
|
+
let ws = named_ws("leader-receiver-not-attached");
|
|
389
|
+
let mut team = leader_team("alpha-session", "%leader", "/tmp/leader.sock");
|
|
390
|
+
team["leader_receiver"]["status"] = json!("rebind_required");
|
|
391
|
+
seed_state(
|
|
392
|
+
&ws,
|
|
393
|
+
state_with_teams(json!({
|
|
394
|
+
"alpha": team
|
|
395
|
+
})),
|
|
396
|
+
);
|
|
397
|
+
let transport = OfflineTransport::new()
|
|
398
|
+
.with_tmux_endpoint("/tmp/leader.sock")
|
|
399
|
+
.with_targets(vec![pane("alpha-session-leader", "codex", "%leader")]);
|
|
400
|
+
|
|
401
|
+
let err = resolve_name_with_transport(&ws, "alpha/leader", &transport).unwrap_err();
|
|
402
|
+
assert_broad_leader_not_attached(err);
|
|
403
|
+
let _ = std::fs::remove_dir_all(&ws);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
#[test]
|
|
407
|
+
fn resolve_leader_pane_not_live_stays_broad() {
|
|
408
|
+
let ws = named_ws("leader-pane-not-live");
|
|
409
|
+
seed_state(
|
|
410
|
+
&ws,
|
|
411
|
+
state_with_teams(json!({
|
|
412
|
+
"alpha": leader_team("alpha-session", "%missing", "/tmp/leader.sock")
|
|
413
|
+
})),
|
|
414
|
+
);
|
|
415
|
+
let transport = OfflineTransport::new().with_tmux_endpoint("/tmp/leader.sock");
|
|
416
|
+
|
|
417
|
+
let err = resolve_name_with_transport(&ws, "alpha/leader", &transport).unwrap_err();
|
|
418
|
+
assert_broad_leader_not_attached(err);
|
|
419
|
+
let _ = std::fs::remove_dir_all(&ws);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
#[test]
|
|
423
|
+
fn resolve_leader_probe_failure_stays_broad() {
|
|
424
|
+
let ws = named_ws("leader-probe-failed");
|
|
425
|
+
seed_state(
|
|
426
|
+
&ws,
|
|
427
|
+
state_with_teams(json!({
|
|
428
|
+
"alpha": leader_team("alpha-session", "%leader", "/tmp/leader.sock")
|
|
429
|
+
})),
|
|
430
|
+
);
|
|
431
|
+
let transport = OfflineTransport::new()
|
|
432
|
+
.with_tmux_endpoint("/tmp/leader.sock")
|
|
433
|
+
.with_list_targets_error("injected leader pane probe failure");
|
|
434
|
+
|
|
435
|
+
let err = resolve_name_with_transport(&ws, "alpha/leader", &transport).unwrap_err();
|
|
436
|
+
assert_broad_leader_not_attached(err);
|
|
437
|
+
let _ = std::fs::remove_dir_all(&ws);
|
|
438
|
+
}
|
|
439
|
+
|
|
375
440
|
#[test]
|
|
376
441
|
fn resolve_leader_name_after_rebind() {
|
|
377
442
|
let ws = named_ws("leader-rebind");
|
|
@@ -122,6 +122,44 @@ fn profile_init_routes_and_creates_secret_boundary_files() {
|
|
|
122
122
|
let _ = std::fs::remove_dir_all(&ws);
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
#[test]
|
|
126
|
+
fn compatible_api_profile_init_template_contains_common_five_fields() {
|
|
127
|
+
let ws = tmp_workspace();
|
|
128
|
+
let code = run(
|
|
129
|
+
&profile_argv(&[
|
|
130
|
+
"profile",
|
|
131
|
+
"init",
|
|
132
|
+
"compat",
|
|
133
|
+
"--workspace",
|
|
134
|
+
".",
|
|
135
|
+
"--auth-mode",
|
|
136
|
+
"compatible_api",
|
|
137
|
+
"--json",
|
|
138
|
+
]),
|
|
139
|
+
&ws,
|
|
140
|
+
);
|
|
141
|
+
assert_eq!(
|
|
142
|
+
code,
|
|
143
|
+
ExitCode::Ok,
|
|
144
|
+
"compatible_api profile init must route and exit 0"
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
let expected =
|
|
148
|
+
"AUTH_MODE=compatible_api\nPROFILE_NAME=compat\nBASE_URL=\nAPI_KEY=\nMODEL=\n";
|
|
149
|
+
let dir = profiles_dir(&ws);
|
|
150
|
+
assert_eq!(
|
|
151
|
+
std::fs::read_to_string(dir.join("compat.env")).unwrap(),
|
|
152
|
+
expected,
|
|
153
|
+
"compatible_api profile must expose the documented five-field template"
|
|
154
|
+
);
|
|
155
|
+
assert_eq!(
|
|
156
|
+
std::fs::read_to_string(dir.join("compat.example.env")).unwrap(),
|
|
157
|
+
expected,
|
|
158
|
+
"compatible_api example must expose the documented five-field template"
|
|
159
|
+
);
|
|
160
|
+
let _ = std::fs::remove_dir_all(&ws);
|
|
161
|
+
}
|
|
162
|
+
|
|
125
163
|
// Golden source:
|
|
126
164
|
// - profiles/core.py:95-119 doctor_profile returns ok=true for existing profiles and
|
|
127
165
|
// ok=false for missing profiles; parser.py:506-508 maps result.ok false to exit 1.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//!
|
|
1
2
|
//! cli · types — 错误信封 / 退出码 / 命令结果载体 / leader passthrough 参数 /
|
|
2
3
|
//! 每子命令的 clap-style arg 结构 / 五行 summary 计数桶。
|
|
3
4
|
|
|
@@ -13,7 +14,7 @@ use crate::transport::PaneId;
|
|
|
13
14
|
/// `{ok:false, error, action, log, reason?, session_name?, next_actions?}`(字节级保留)。
|
|
14
15
|
/// 人读时打 `error:`/`action:`/`log:` 三行到 stderr(`helpers.py:132-134`)。
|
|
15
16
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
16
|
-
pub struct CliErrorPayload {
|
|
17
|
+
pub(crate) struct CliErrorPayload {
|
|
17
18
|
/// 恒 `false`(错误信封)。
|
|
18
19
|
pub ok: bool,
|
|
19
20
|
pub error: String,
|
|
@@ -70,7 +71,7 @@ impl From<crate::messaging::MessagingError> for CliError {
|
|
|
70
71
|
impl CliError {
|
|
71
72
|
/// `_cli_error_payload`(`helpers.py:137-155`):落盘日志后构造稳定信封。tmux session
|
|
72
73
|
/// 冲突时按 command 富化 reason/session_name/next_actions(`helpers.py:158-187`)。
|
|
73
|
-
pub fn to_payload(&self, log_path: &Path, command: &str) -> CliErrorPayload {
|
|
74
|
+
pub(crate) fn to_payload(&self, log_path: &Path, command: &str) -> CliErrorPayload {
|
|
74
75
|
let error = self.to_string();
|
|
75
76
|
let mut payload = CliErrorPayload {
|
|
76
77
|
ok: false,
|
|
@@ -107,6 +108,10 @@ impl CliError {
|
|
|
107
108
|
payload.action = String::from(
|
|
108
109
|
"move the Team Agent launcher flag before `--`; only provider flags belong after `--`",
|
|
109
110
|
);
|
|
111
|
+
} else if error.contains("managed launcher refuses a different ambient tmux server") {
|
|
112
|
+
payload.action = String::from(
|
|
113
|
+
"leave the current tmux client and retry, or pass `--allow-nested-attach` before `--` to opt in to a nested managed attach",
|
|
114
|
+
);
|
|
110
115
|
}
|
|
111
116
|
payload
|
|
112
117
|
}
|
|
@@ -160,6 +165,8 @@ pub struct CmdResult {
|
|
|
160
165
|
pub exit: ExitCode,
|
|
161
166
|
/// `--json` 旗标(决定 `emit` 与 inbox 摘要落 stderr vs stdout,`parser.py:505-506`)。
|
|
162
167
|
pub as_json: bool,
|
|
168
|
+
/// Preserve nested object insertion order for byte-faithful stored envelopes.
|
|
169
|
+
pub preserve_json_order: bool,
|
|
163
170
|
}
|
|
164
171
|
|
|
165
172
|
impl CmdResult {
|
|
@@ -174,6 +181,20 @@ impl CmdResult {
|
|
|
174
181
|
output: CmdOutput::Json(value),
|
|
175
182
|
exit,
|
|
176
183
|
as_json,
|
|
184
|
+
preserve_json_order: false,
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
pub fn from_ordered_json(value: Value) -> Self {
|
|
188
|
+
let exit = if value.get("ok").and_then(Value::as_bool) == Some(false) {
|
|
189
|
+
ExitCode::Error
|
|
190
|
+
} else {
|
|
191
|
+
ExitCode::Ok
|
|
192
|
+
};
|
|
193
|
+
Self {
|
|
194
|
+
output: CmdOutput::Json(value),
|
|
195
|
+
exit,
|
|
196
|
+
as_json: true,
|
|
197
|
+
preserve_json_order: true,
|
|
177
198
|
}
|
|
178
199
|
}
|
|
179
200
|
pub fn human(text: impl Into<String>) -> Self {
|
|
@@ -181,6 +202,7 @@ impl CmdResult {
|
|
|
181
202
|
output: CmdOutput::Human(text.into()),
|
|
182
203
|
exit: ExitCode::Ok,
|
|
183
204
|
as_json: false,
|
|
205
|
+
preserve_json_order: false,
|
|
184
206
|
}
|
|
185
207
|
}
|
|
186
208
|
pub fn none() -> Self {
|
|
@@ -188,6 +210,7 @@ impl CmdResult {
|
|
|
188
210
|
output: CmdOutput::None,
|
|
189
211
|
exit: ExitCode::Ok,
|
|
190
212
|
as_json: false,
|
|
213
|
+
preserve_json_order: false,
|
|
191
214
|
}
|
|
192
215
|
}
|
|
193
216
|
}
|
|
@@ -210,6 +233,8 @@ pub struct LeaderLauncherArgs {
|
|
|
210
233
|
pub attach_session: Option<String>,
|
|
211
234
|
/// 0.3.16 topology opt-out: keep the old external/current-pane leader launcher path.
|
|
212
235
|
pub external_leader: bool,
|
|
236
|
+
/// N41 explicit opt-in for attaching a managed leader from another tmux server.
|
|
237
|
+
pub allow_nested_attach: bool,
|
|
213
238
|
}
|
|
214
239
|
|
|
215
240
|
// =============================================================================
|
|
@@ -643,6 +668,15 @@ pub struct CollectArgs {
|
|
|
643
668
|
pub team: Option<String>,
|
|
644
669
|
}
|
|
645
670
|
|
|
671
|
+
/// Read-only lookup of all stored result envelopes for one case.
|
|
672
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
673
|
+
pub struct ResultsArgs {
|
|
674
|
+
pub case_id: String,
|
|
675
|
+
pub workspace: PathBuf,
|
|
676
|
+
pub team: Option<String>,
|
|
677
|
+
pub json: bool,
|
|
678
|
+
}
|
|
679
|
+
|
|
646
680
|
/// `diagnose`(`parser.py:298`) runtime health report, distinct from `doctor`.
|
|
647
681
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
648
682
|
pub struct DiagnoseArgs {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//!
|
|
2
|
+
//! Official communication modes shared by configuration and projection consumers.
|
|
3
|
+
|
|
4
|
+
/// The complete product-supported communication-mode catalog.
|
|
5
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
6
|
+
pub enum CommunicationMode {
|
|
7
|
+
LeaderCentric,
|
|
8
|
+
Orchestrated,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const LEADER_CENTRIC_RUNTIME_CONTRACT: &str = r#"# Team Agent communication contract: leader_centric
|
|
12
|
+
|
|
13
|
+
- Progress, blockers, questions: team_orchestrator.send_message(to='leader', content='...')
|
|
14
|
+
|
|
15
|
+
When you receive a message from the leader or a teammate, you MUST respond
|
|
16
|
+
through MCP tools. Writing a reply in your terminal does nothing — the sender
|
|
17
|
+
will never see it."#;
|
|
18
|
+
|
|
19
|
+
const ORCHESTRATED_RUNTIME_CONTRACT: &str = r#"# Team Agent communication contract: orchestrated
|
|
20
|
+
|
|
21
|
+
- Send progress only through the declared channel for the assigned task.
|
|
22
|
+
- Respond to task-related messages through Team Agent MCP tools.
|
|
23
|
+
- A pure ACK, unrelated status, or non-task message does not require a response."#;
|
|
24
|
+
|
|
25
|
+
impl CommunicationMode {
|
|
26
|
+
pub const ALL: &[Self] = &[Self::LeaderCentric, Self::Orchestrated];
|
|
27
|
+
|
|
28
|
+
pub const fn as_str(self) -> &'static str {
|
|
29
|
+
match self {
|
|
30
|
+
Self::LeaderCentric => "leader_centric",
|
|
31
|
+
Self::Orchestrated => "orchestrated",
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
pub fn parse(value: &str) -> Option<Self> {
|
|
36
|
+
Self::ALL
|
|
37
|
+
.iter()
|
|
38
|
+
.copied()
|
|
39
|
+
.find(|mode| mode.as_str() == value)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
pub const fn runtime_contract(self) -> &'static str {
|
|
43
|
+
match self {
|
|
44
|
+
Self::LeaderCentric => LEADER_CENTRIC_RUNTIME_CONTRACT,
|
|
45
|
+
Self::Orchestrated => ORCHESTRATED_RUNTIME_CONTRACT,
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
impl Default for CommunicationMode {
|
|
51
|
+
fn default() -> Self {
|
|
52
|
+
Self::LeaderCentric
|
|
53
|
+
}
|
|
54
|
+
}
|