@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.
Files changed (208) hide show
  1. package/Cargo.lock +8 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/Cargo.toml +6 -0
  4. package/crates/team-agent/src/cli/adapters.rs +6 -1
  5. package/crates/team-agent/src/cli/diagnose.rs +82 -0
  6. package/crates/team-agent/src/cli/emit.rs +3 -3
  7. package/crates/team-agent/src/cli/grok_slot.rs +299 -0
  8. package/crates/team-agent/src/cli/leader.rs +21 -7
  9. package/crates/team-agent/src/cli/leaders.rs +125 -1
  10. package/crates/team-agent/src/cli/mod.rs +20 -0
  11. package/crates/team-agent/src/cli/send/presentation.rs +7 -1
  12. package/crates/team-agent/src/cli/spec.rs +2 -0
  13. package/crates/team-agent/src/cli/status_port/compact.rs +28 -0
  14. package/crates/team-agent/src/cli/status_port/snapshot.rs +25 -1
  15. package/crates/team-agent/src/cli/tests/base.rs +26 -3
  16. package/crates/team-agent/src/cli/tests/compile.rs +1 -1
  17. package/crates/team-agent/src/cli/tests/missing_subcommands.rs +129 -19
  18. package/crates/team-agent/src/cli/tests/mod.rs +2 -2
  19. package/crates/team-agent/src/cli/tests/run_delegation.rs +1 -1
  20. package/crates/team-agent/src/cli/tests/shutdown_kill_plan.rs +387 -6
  21. package/crates/team-agent/src/cli/tests/status_send.rs +79 -10
  22. package/crates/team-agent/src/cli/tests/verb_validate.rs +1 -1
  23. package/crates/team-agent/src/communication_mode/mod.rs +6 -4
  24. package/crates/team-agent/src/compiler/tests.rs +23 -20
  25. package/crates/team-agent/src/compiler.rs +84 -5
  26. package/crates/team-agent/src/conpty/backend.rs +16 -0
  27. package/crates/team-agent/src/coordinator/backoff.rs +55 -0
  28. package/crates/team-agent/src/coordinator/conpty_shim.rs +82 -0
  29. package/crates/team-agent/src/coordinator/health.rs +173 -0
  30. package/crates/team-agent/src/coordinator/mod.rs +31 -0
  31. package/crates/team-agent/src/coordinator/orphan.rs +31 -0
  32. package/crates/team-agent/src/coordinator/runtime_detectors.rs +30 -0
  33. package/crates/team-agent/src/coordinator/runtime_observation.rs +25 -0
  34. package/crates/team-agent/src/coordinator/steps/abnormal.rs +59 -0
  35. package/crates/team-agent/src/coordinator/steps/delivery.rs +10 -0
  36. package/crates/team-agent/src/coordinator/steps/health_sync.rs +10 -0
  37. package/crates/team-agent/src/coordinator/steps/mod.rs +22 -0
  38. package/crates/team-agent/src/coordinator/steps/persist.rs +10 -0
  39. package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +10 -0
  40. package/crates/team-agent/src/coordinator/steps/session_gate.rs +10 -0
  41. package/crates/team-agent/src/coordinator/tick.rs +133 -22
  42. package/crates/team-agent/src/coordinator/types.rs +49 -0
  43. package/crates/team-agent/src/db/message_store.rs +39 -5
  44. package/crates/team-agent/src/layout/worker_env.rs +20 -3
  45. package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
  46. package/crates/team-agent/src/leader/provider_attribution.rs +53 -0
  47. package/crates/team-agent/src/leader/registry.rs +65 -0
  48. package/crates/team-agent/src/leader/start.rs +1080 -58
  49. package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1 -1
  50. package/crates/team-agent/src/lifecycle/display.rs +56 -0
  51. package/crates/team-agent/src/lifecycle/helpers.rs +57 -0
  52. package/crates/team-agent/src/lifecycle/launch/add_agent.rs +332 -14
  53. package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +69 -2
  54. package/crates/team-agent/src/lifecycle/launch/agent_state.rs +196 -2
  55. package/crates/team-agent/src/lifecycle/launch/clone_agent.rs +101 -11
  56. package/crates/team-agent/src/lifecycle/launch/cursor_create_chat.rs +229 -0
  57. package/crates/team-agent/src/lifecycle/launch/cursor_mcp.rs +332 -0
  58. package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +204 -457
  59. package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +24 -0
  60. package/crates/team-agent/src/lifecycle/launch/grok_per_seat.rs +260 -0
  61. package/crates/team-agent/src/lifecycle/launch/identity.rs +146 -0
  62. package/crates/team-agent/src/lifecycle/launch/layout.rs +60 -0
  63. package/crates/team-agent/src/lifecycle/launch/leader_context.rs +112 -0
  64. package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +530 -0
  65. package/crates/team-agent/src/lifecycle/launch/ownership.rs +40 -0
  66. package/crates/team-agent/src/lifecycle/launch/plan.rs +31 -0
  67. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +66 -0
  68. package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +78 -0
  69. package/crates/team-agent/src/lifecycle/launch/readiness.rs +85 -38
  70. package/crates/team-agent/src/lifecycle/launch/role_source.rs +44 -44
  71. package/crates/team-agent/src/lifecycle/launch/spawn.rs +101 -4
  72. package/crates/team-agent/src/lifecycle/launch/spec_state.rs +244 -48
  73. package/crates/team-agent/src/lifecycle/launch/state_projection.rs +109 -2
  74. package/crates/team-agent/src/lifecycle/launch/worker_env.rs +214 -1
  75. package/crates/team-agent/src/lifecycle/launch.rs +209 -43
  76. package/crates/team-agent/src/lifecycle/lock.rs +42 -1
  77. package/crates/team-agent/src/lifecycle/mod.rs +11 -0
  78. package/crates/team-agent/src/lifecycle/pane_input_lock.rs +161 -0
  79. package/crates/team-agent/src/lifecycle/profile_launch.rs +98 -0
  80. package/crates/team-agent/src/lifecycle/profile_smoke.rs +51 -1
  81. package/crates/team-agent/src/lifecycle/restart/agent.rs +213 -3
  82. package/crates/team-agent/src/lifecycle/restart/common.rs +373 -18
  83. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +27 -0
  84. package/crates/team-agent/src/lifecycle/restart/preflight.rs +25 -0
  85. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +165 -15
  86. package/crates/team-agent/src/lifecycle/restart/remove.rs +196 -5
  87. package/crates/team-agent/src/lifecycle/restart/selection.rs +72 -0
  88. package/crates/team-agent/src/lifecycle/restart/team_state.rs +22 -0
  89. package/crates/team-agent/src/lifecycle/restart.rs +29 -0
  90. package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1 -1
  91. package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +3 -4
  92. package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +1 -1
  93. package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +2 -2
  94. package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +4 -4
  95. package/crates/team-agent/src/lifecycle/tests/clone_agent_preserves_source_tools.rs +309 -0
  96. package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +159 -174
  97. package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +2 -2
  98. package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +15 -13
  99. package/crates/team-agent/src/lifecycle/tests/core.rs +4 -18
  100. package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +1 -1
  101. package/crates/team-agent/src/lifecycle/tests/cursor_mcp_overlay.rs +287 -0
  102. package/crates/team-agent/src/lifecycle/tests/cursor_require_explicit_model_red.rs +229 -0
  103. package/crates/team-agent/src/lifecycle/tests/cursor_restart_resume_red.rs +353 -0
  104. package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +1 -1
  105. package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +1 -1
  106. package/crates/team-agent/src/lifecycle/tests/g1_silent_faces.rs +784 -0
  107. package/crates/team-agent/src/lifecycle/tests/gate_fixtures.rs +562 -0
  108. package/crates/team-agent/src/lifecycle/tests/grok_effort_argv_red.rs +239 -0
  109. package/crates/team-agent/src/lifecycle/tests/grok_mcp_overlay_red.rs +498 -0
  110. package/crates/team-agent/src/lifecycle/tests/grok_require_explicit_model_red.rs +250 -0
  111. package/crates/team-agent/src/lifecycle/tests/grok_restart_resume_red.rs +293 -0
  112. package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +3 -0
  113. package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +3 -3
  114. package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +65 -40
  115. package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +20 -32
  116. package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +17 -9
  117. package/crates/team-agent/src/lifecycle/tests/mcp_tool_name_format_red.rs +69 -0
  118. package/crates/team-agent/src/lifecycle/tests/phase_b_contracts.rs +24 -23
  119. package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +98 -62
  120. package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +2 -2
  121. package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +1 -1
  122. package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +1 -1
  123. package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +3 -1
  124. package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -2
  125. package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +1 -1
  126. package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +1 -1
  127. package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +41 -8
  128. package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +3 -3
  129. package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1 -1
  130. package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +1 -1
  131. package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +1 -1
  132. package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1 -1
  133. package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1 -1
  134. package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +1 -1
  135. package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +60 -2
  136. package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +1 -1
  137. package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +2 -2
  138. package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +69 -26
  139. package/crates/team-agent/src/lifecycle/tests.rs +23 -15
  140. package/crates/team-agent/src/lifecycle/types.rs +61 -2
  141. package/crates/team-agent/src/lifecycle/worker_command_context.rs +200 -33
  142. package/crates/team-agent/src/mcp_server/tests/scoped.rs +100 -1
  143. package/crates/team-agent/src/mcp_server/tests.rs +196 -2
  144. package/crates/team-agent/src/messaging/delivery.rs +405 -40
  145. package/crates/team-agent/src/messaging/helpers.rs +2 -0
  146. package/crates/team-agent/src/messaging/leader_receiver.rs +53 -1
  147. package/crates/team-agent/src/messaging/results.rs +4 -0
  148. package/crates/team-agent/src/messaging/send.rs +34 -0
  149. package/crates/team-agent/src/messaging/tests/dup_inject.rs +406 -0
  150. package/crates/team-agent/src/messaging/tests/e23.rs +9 -0
  151. package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +66 -0
  152. package/crates/team-agent/src/messaging/tests/main_preserved.rs +1 -1
  153. package/crates/team-agent/src/messaging/tests/mod.rs +1 -0
  154. package/crates/team-agent/src/messaging/types.rs +4 -1
  155. package/crates/team-agent/src/model/enums.rs +14 -5
  156. package/crates/team-agent/src/model/permissions.rs +3 -0
  157. package/crates/team-agent/src/model/spec.rs +129 -6
  158. package/crates/team-agent/src/model/testdata/spec_invalid_a.yaml +3 -1
  159. package/crates/team-agent/src/model/testdata/team.spec.golden.yaml +3 -1
  160. package/crates/team-agent/src/model/testdata/team.spec.yaml +3 -1
  161. package/crates/team-agent/src/os_probe.rs +73 -2
  162. package/crates/team-agent/src/provider/adapter.rs +231 -4
  163. package/crates/team-agent/src/provider/adapters/claude.rs +5 -2
  164. package/crates/team-agent/src/provider/adapters/codex.rs +5 -2
  165. package/crates/team-agent/src/provider/adapters/copilot.rs +5 -2
  166. package/crates/team-agent/src/provider/adapters/cursor_agent.rs +86 -0
  167. package/crates/team-agent/src/provider/adapters/grok.rs +157 -0
  168. package/crates/team-agent/src/provider/adapters/mod.rs +2 -0
  169. package/crates/team-agent/src/provider/bypass_flags.rs +124 -0
  170. package/crates/team-agent/src/provider/classify.rs +4 -2
  171. package/crates/team-agent/src/provider/faults.rs +2 -1
  172. package/crates/team-agent/src/provider/mod.rs +13 -0
  173. package/crates/team-agent/src/provider/session/capture.rs +158 -20
  174. package/crates/team-agent/src/provider/session/context_fork/claude.rs +38 -1
  175. package/crates/team-agent/src/provider/session/context_fork/codex.rs +72 -1
  176. package/crates/team-agent/src/provider/session/context_fork/outcome.rs +57 -1
  177. package/crates/team-agent/src/provider/session/context_fork.rs +77 -3
  178. package/crates/team-agent/src/provider/session/mod.rs +18 -0
  179. package/crates/team-agent/src/provider/session/resume.rs +57 -0
  180. package/crates/team-agent/src/provider/session_scan/claude.rs +125 -1
  181. package/crates/team-agent/src/provider/session_scan/codex.rs +94 -1
  182. package/crates/team-agent/src/provider/session_scan/common.rs +204 -2
  183. package/crates/team-agent/src/provider/session_scan/copilot.rs +33 -1
  184. package/crates/team-agent/src/provider/session_scan/cursor.rs +538 -0
  185. package/crates/team-agent/src/provider/session_scan/grok.rs +288 -0
  186. package/crates/team-agent/src/provider/session_scan.rs +8 -0
  187. package/crates/team-agent/src/provider/submit_now.rs +94 -0
  188. package/crates/team-agent/src/provider/tests/adapter.rs +304 -0
  189. package/crates/team-agent/src/provider/types.rs +4 -2
  190. package/crates/team-agent/src/provider/wire.rs +23 -1
  191. package/crates/team-agent/src/state/persist.rs +301 -8
  192. package/crates/team-agent/src/state/repository.rs +5 -0
  193. package/crates/team-agent/src/tmux_backend/tests.rs +1737 -42
  194. package/crates/team-agent/src/tmux_backend.rs +1083 -75
  195. package/crates/team-agent/src/transport/test_support.rs +20 -0
  196. package/crates/team-agent/src/transport/tests/wire.rs +28 -2
  197. package/crates/team-agent/src/transport.rs +265 -1
  198. package/npm/install.mjs +129 -73
  199. package/package.json +4 -4
  200. package/skills/team-agent/SKILL.md +33 -238
  201. package/skills/team-agent/command-coverage.json +31 -0
  202. package/crates/team-agent/src/lifecycle/launch/approval.rs +0 -134
  203. package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +0 -59
  204. package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +0 -483
  205. package/crates/team-agent/src/lifecycle/launch/fork_pending.rs +0 -109
  206. package/crates/team-agent/src/lifecycle/launch/fork_state.rs +0 -447
  207. package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +0 -1187
  208. package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +0 -327
@@ -1,483 +0,0 @@
1
- //!
2
- use super::*;
3
-
4
- pub(super) struct ForkPostSpawnInput<'a> {
5
- pub workspace: &'a Path,
6
- pub transport: &'a dyn Transport,
7
- pub session_name: &'a SessionName,
8
- pub window: &'a WindowName,
9
- pub mcp_config_path: &'a Path,
10
- pub agent_id: &'a AgentId,
11
- pub profile_launch: &'a crate::provider::ProviderProfileLaunch,
12
- pub team_key: &'a str,
13
- pub spawn: &'a crate::transport::SpawnResult,
14
- }
15
-
16
- pub(super) fn ensure_fork_spawn_live(input: ForkPostSpawnInput<'_>) -> Result<(), LifecycleError> {
17
- let rollback = || {
18
- rollback_fork_after_spawn(
19
- input.workspace,
20
- input.transport,
21
- input.session_name,
22
- input.window,
23
- input.mcp_config_path,
24
- input.agent_id,
25
- input.profile_launch,
26
- input.team_key,
27
- );
28
- };
29
- if !matches!(
30
- input.transport.liveness(&input.spawn.pane_id),
31
- Ok(PaneLiveness::Live)
32
- ) {
33
- rollback();
34
- return Err(LifecycleError::RequirementUnmet(format!(
35
- "fork process is not live after spawn: agent={} pane={}",
36
- input.agent_id,
37
- input.spawn.pane_id.as_str()
38
- )));
39
- }
40
- Ok(())
41
- }
42
-
43
- pub(super) fn prepare_claude_fork_backing(
44
- provider: Provider,
45
- plan: &crate::provider::CommandPlan,
46
- source_backing: &Path,
47
- source_session_id: &crate::provider::SessionId,
48
- source_agent_id: &AgentId,
49
- target_agent_id: &AgentId,
50
- ) -> Result<Option<crate::provider::adapters::claude_fork::ClaudeForkMaterialization>, LifecycleError>
51
- {
52
- if !matches!(provider, Provider::Claude | Provider::ClaudeCode) {
53
- return Ok(None);
54
- }
55
- let target_session_id = plan.expected_session_id.as_ref().ok_or_else(|| {
56
- LifecycleError::Provider("claude fork plan has no snapshot session id".to_string())
57
- })?;
58
- crate::provider::adapters::claude_fork::materialize_claude_fork(
59
- source_backing,
60
- source_session_id,
61
- target_session_id,
62
- source_agent_id.as_str(),
63
- target_agent_id.as_str(),
64
- )
65
- .map(Some)
66
- .map_err(|error| {
67
- LifecycleError::Provider(format!(
68
- "context_fork_unavailable: claude snapshot copy failed before spawn: {error}"
69
- ))
70
- })
71
- }
72
-
73
- pub(super) struct ForkCoordinatorInput<'a> {
74
- pub workspace: &'a Path,
75
- pub team_key: &'a str,
76
- pub agent_id: &'a AgentId,
77
- pub transport: &'a dyn Transport,
78
- pub session_name: &'a SessionName,
79
- pub window: &'a WindowName,
80
- pub mcp_config_path: &'a Path,
81
- pub profile_launch: &'a crate::provider::ProviderProfileLaunch,
82
- }
83
-
84
- pub(super) fn start_fork_coordinator(
85
- input: ForkCoordinatorInput<'_>,
86
- ) -> Result<bool, LifecycleError> {
87
- let rollback = || {
88
- rollback_fork_after_spawn(
89
- input.workspace,
90
- input.transport,
91
- input.session_name,
92
- input.window,
93
- input.mcp_config_path,
94
- input.agent_id,
95
- input.profile_launch,
96
- input.team_key,
97
- );
98
- };
99
- if let Err(error) = maybe_fail_fork_after_spawn("start_coordinator") {
100
- rollback();
101
- return Err(error);
102
- }
103
- crate::coordinator::start_coordinator(&crate::coordinator::WorkspacePath::new(
104
- input.workspace.to_path_buf(),
105
- ))
106
- .map(|report| report.ok)
107
- .map_err(|error| {
108
- rollback();
109
- LifecycleError::StatePersist(error.to_string())
110
- })
111
- }
112
-
113
- pub(super) struct ForkFinalizeInput<'a> {
114
- pub workspace: &'a Path,
115
- pub team_key: &'a str,
116
- pub source_agent_id: &'a AgentId,
117
- pub agent_id: &'a AgentId,
118
- pub spec_agent: &'a Value,
119
- pub safety: &'a DangerousApproval,
120
- pub plan: &'a crate::provider::CommandPlan,
121
- pub profile_launch: &'a crate::provider::ProviderProfileLaunch,
122
- pub spawn: &'a crate::transport::SpawnResult,
123
- pub profile_dir: &'a Path,
124
- pub dynamic_role_file: &'a Path,
125
- pub context_proof: &'a crate::provider::session::ContextForkProof,
126
- pub spawned_at: &'a str,
127
- pub spawn_epoch: u64,
128
- }
129
-
130
- #[derive(Debug, Clone, PartialEq, Eq)]
131
- pub(crate) struct ContextForkFinalized {
132
- source_agent_id: String,
133
- agent_id: String,
134
- session_id: String,
135
- rollout_path: String,
136
- captured_via: String,
137
- attribution_confidence: String,
138
- }
139
-
140
- impl ContextForkFinalized {
141
- fn new(
142
- source_agent_id: &str,
143
- agent_id: &str,
144
- session_id: &str,
145
- rollout_path: &Path,
146
- captured_via: &str,
147
- attribution_confidence: &str,
148
- ) -> Self {
149
- Self {
150
- source_agent_id: source_agent_id.to_string(),
151
- agent_id: agent_id.to_string(),
152
- session_id: session_id.to_string(),
153
- rollout_path: rollout_path.to_string_lossy().to_string(),
154
- captured_via: captured_via.to_string(),
155
- attribution_confidence: attribution_confidence.to_string(),
156
- }
157
- }
158
-
159
- fn from_captured(
160
- source_agent_id: &str,
161
- agent_id: &str,
162
- captured: &crate::provider::CapturedSession,
163
- ) -> Option<Self> {
164
- let captured_via = serde_json::to_value(captured.captured_via).ok()?;
165
- let attribution_confidence = serde_json::to_value(captured.attribution_confidence).ok()?;
166
- Some(Self::new(
167
- source_agent_id,
168
- agent_id,
169
- captured.session_id.as_ref()?.as_str(),
170
- captured.rollout_path.as_ref()?.as_path(),
171
- captured_via.as_str()?,
172
- attribution_confidence.as_str()?,
173
- ))
174
- }
175
-
176
- pub(crate) fn write_audit(
177
- &self,
178
- event_log: &crate::event_log::EventLog,
179
- ) -> Result<(), crate::event_log::EventLogError> {
180
- event_log
181
- .write(
182
- crate::lifecycle::types::event_names::CONTEXT_FORK,
183
- serde_json::json!({
184
- "source_agent_id": self.source_agent_id,
185
- "agent_id": self.agent_id,
186
- "session_id": self.session_id,
187
- "rollout_path": self.rollout_path,
188
- "captured_via": self.captured_via,
189
- "attribution_confidence": self.attribution_confidence,
190
- }),
191
- )
192
- .map(|_| ())
193
- }
194
- }
195
-
196
- pub(super) fn finalize_fork_state(
197
- input: ForkFinalizeInput<'_>,
198
- ) -> Result<ContextForkFinalized, LifecycleError> {
199
- let _lock = acquire_agent_lifecycle_lock(LifecycleLockRequest {
200
- workspace: input.workspace,
201
- operation: "fork-agent-finalize",
202
- team: Some(input.team_key),
203
- agent_id: Some(input.agent_id),
204
- })?;
205
- let mut next_state = crate::state::selector::resolve_active_team(
206
- input.workspace,
207
- Some(input.team_key),
208
- crate::state::selector::SelectorMode::RequireSpec,
209
- )
210
- .map_err(|error| LifecycleError::TeamSelect(error.to_string()))?
211
- .state;
212
- upsert_forked_agent_state(
213
- &mut next_state,
214
- input.source_agent_id,
215
- input.agent_id,
216
- input.spec_agent,
217
- input.safety,
218
- input.plan,
219
- input.profile_launch,
220
- input.spawn,
221
- input.workspace,
222
- Some(input.profile_dir),
223
- input.dynamic_role_file,
224
- input.context_proof,
225
- input.spawned_at,
226
- input.spawn_epoch,
227
- )?;
228
- if let Some(agent) = next_state
229
- .get_mut("agents")
230
- .and_then(serde_json::Value::as_object_mut)
231
- .and_then(|agents| agents.get_mut(input.agent_id.as_str()))
232
- .and_then(serde_json::Value::as_object_mut)
233
- {
234
- persist_effective_approval_policy(agent, input.safety);
235
- }
236
- maybe_fail_fork_after_spawn("save_runtime_state")?;
237
- crate::state::repository::StateRepository::new(input.workspace)
238
- .save(
239
- crate::state::repository::StateWriteIntent::ForkAgent {
240
- team_key: input.team_key,
241
- agent_id: input.agent_id.as_str(),
242
- },
243
- &next_state,
244
- )
245
- .map_err(|error| LifecycleError::StatePersist(error.to_string()))?;
246
- Ok(ContextForkFinalized::new(
247
- input.source_agent_id.as_str(),
248
- input.agent_id.as_str(),
249
- input.context_proof.new_session_id.as_str(),
250
- &input.context_proof.backing_path,
251
- &input.context_proof.captured_via,
252
- &input.context_proof.attribution_confidence,
253
- ))
254
- }
255
-
256
- pub(super) struct ForkPendingFinalizeInput<'a> {
257
- pub workspace: &'a Path,
258
- pub team_key: &'a str,
259
- pub source_agent_id: &'a AgentId,
260
- pub agent_id: &'a AgentId,
261
- pub spec_agent: &'a Value,
262
- pub safety: &'a DangerousApproval,
263
- pub plan: &'a crate::provider::CommandPlan,
264
- pub profile_launch: &'a crate::provider::ProviderProfileLaunch,
265
- pub spawn: &'a crate::transport::SpawnResult,
266
- pub profile_dir: &'a Path,
267
- pub dynamic_role_file: &'a Path,
268
- pub pending: &'a crate::provider::session::PendingContextFork,
269
- pub spawn_epoch: u64,
270
- }
271
-
272
- pub(super) fn finalize_pending_fork_state(
273
- input: ForkPendingFinalizeInput<'_>,
274
- ) -> Result<(), LifecycleError> {
275
- let _lock = acquire_agent_lifecycle_lock(LifecycleLockRequest {
276
- workspace: input.workspace,
277
- operation: "fork-agent-pending",
278
- team: Some(input.team_key),
279
- agent_id: Some(input.agent_id),
280
- })?;
281
- let mut next_state = crate::state::selector::resolve_active_team(
282
- input.workspace,
283
- Some(input.team_key),
284
- crate::state::selector::SelectorMode::RequireSpec,
285
- )
286
- .map_err(|error| LifecycleError::TeamSelect(error.to_string()))?
287
- .state;
288
- upsert_pending_forked_agent_state(
289
- &mut next_state,
290
- input.source_agent_id,
291
- input.agent_id,
292
- input.spec_agent,
293
- input.safety,
294
- input.plan,
295
- input.profile_launch,
296
- input.spawn,
297
- Some(input.profile_dir),
298
- input.dynamic_role_file,
299
- input.pending,
300
- input.spawn_epoch,
301
- )?;
302
- maybe_fail_fork_after_spawn("save_runtime_state")?;
303
- crate::state::repository::StateRepository::new(input.workspace)
304
- .save(
305
- crate::state::repository::StateWriteIntent::ForkAgent {
306
- team_key: input.team_key,
307
- agent_id: input.agent_id.as_str(),
308
- },
309
- &next_state,
310
- )
311
- .map_err(|error| LifecycleError::StatePersist(error.to_string()))
312
- }
313
-
314
- pub(crate) fn finalize_pending_fork_capture(
315
- agent: &mut serde_json::Map<String, serde_json::Value>,
316
- captured: &crate::provider::CapturedSession,
317
- ) -> Option<ContextForkFinalized> {
318
- let Some(session_id) = captured.session_id.as_ref() else {
319
- return None;
320
- };
321
- let Some(rollout_path) = captured.rollout_path.as_ref() else {
322
- return None;
323
- };
324
- if agent
325
- .get("fork_source_session_id")
326
- .and_then(serde_json::Value::as_str)
327
- == Some(session_id.as_str())
328
- {
329
- return None;
330
- }
331
- let finalized = ContextForkFinalized::from_captured(
332
- agent
333
- .get("forked_from")
334
- .and_then(serde_json::Value::as_str)
335
- .unwrap_or_default(),
336
- agent
337
- .get("agent_id")
338
- .and_then(serde_json::Value::as_str)
339
- .or_else(|| {
340
- agent
341
- .get("pending_target_agent")
342
- .and_then(serde_json::Value::as_str)
343
- })
344
- .unwrap_or_default(),
345
- captured,
346
- )?;
347
- agent.insert(
348
- "session_id".to_string(),
349
- serde_json::json!(session_id.as_str()),
350
- );
351
- agent.insert(
352
- "rollout_path".to_string(),
353
- serde_json::json!(rollout_path.as_path().to_string_lossy()),
354
- );
355
- agent.insert(
356
- "captured_at".to_string(),
357
- serde_json::json!(chrono::Utc::now().to_rfc3339()),
358
- );
359
- agent.insert(
360
- "captured_via".to_string(),
361
- serde_json::to_value(captured.captured_via).unwrap_or(serde_json::Value::Null),
362
- );
363
- agent.insert(
364
- "attribution_confidence".to_string(),
365
- serde_json::to_value(captured.attribution_confidence).unwrap_or(serde_json::Value::Null),
366
- );
367
- agent.remove("_pending_session_id");
368
- agent.remove("attribution_ambiguous");
369
- agent.remove("pending_target_agent");
370
- agent.remove("pending_grace_secs");
371
- agent.insert("capture_state".to_string(), serde_json::json!("captured"));
372
- Some(finalized)
373
- }
374
-
375
- #[cfg(test)]
376
- mod tests {
377
- use super::*;
378
-
379
- #[test]
380
- fn context_fork_finalized_writes_catalog_event_and_canonical_fields() {
381
- let workspace = std::env::temp_dir().join(format!(
382
- "team-agent-context-fork-audit-{}",
383
- std::process::id()
384
- ));
385
- let _ = std::fs::remove_dir_all(&workspace);
386
- std::fs::create_dir_all(&workspace).expect("create audit workspace");
387
- let event_log = crate::event_log::EventLog::new(&workspace);
388
- ContextForkFinalized::new(
389
- "source",
390
- "target",
391
- "target-session",
392
- Path::new("/tmp/target.jsonl"),
393
- "context_fork_verified",
394
- "high",
395
- )
396
- .write_audit(&event_log)
397
- .expect("write context fork audit");
398
-
399
- let events = event_log.tail(0).expect("read context fork audit");
400
- let event = events.last().expect("context fork audit event");
401
- assert_eq!(
402
- event.get("event").and_then(serde_json::Value::as_str),
403
- Some(crate::lifecycle::types::event_names::CONTEXT_FORK)
404
- );
405
- assert_eq!(
406
- event
407
- .get("source_agent_id")
408
- .and_then(serde_json::Value::as_str),
409
- Some("source")
410
- );
411
- assert_eq!(
412
- event.get("agent_id").and_then(serde_json::Value::as_str),
413
- Some("target")
414
- );
415
- assert!(event.get("prompt").is_none());
416
- let _ = std::fs::remove_dir_all(workspace);
417
- }
418
- }
419
-
420
- pub(super) fn verify_fork_registration(
421
- workspace: &Path,
422
- team_key: &str,
423
- agent_id: &AgentId,
424
- spawn: &crate::transport::SpawnResult,
425
- window: &WindowName,
426
- ) -> Result<(), LifecycleError> {
427
- let saved = crate::state::projection::select_runtime_state(workspace, Some(team_key))
428
- .map_err(|error| LifecycleError::StatePersist(error.to_string()))?;
429
- let agent = saved
430
- .get("agents")
431
- .and_then(|agents| agents.get(agent_id.as_str()))
432
- .ok_or_else(|| LifecycleError::StatePersist("canonical team row is missing".to_string()))?;
433
- if agent.get("pane_id").and_then(serde_json::Value::as_str) != Some(spawn.pane_id.as_str()) {
434
- return Err(LifecycleError::StatePersist(
435
- "canonical team pane_id does not match spawned pane".to_string(),
436
- ));
437
- }
438
- if agent.get("window").and_then(serde_json::Value::as_str) != Some(window.as_str()) {
439
- return Err(LifecycleError::StatePersist(
440
- "canonical team window does not match spawned window".to_string(),
441
- ));
442
- }
443
- if let Some(pid) = spawn.child_pid {
444
- if agent.get("pane_pid").and_then(serde_json::Value::as_u64) != Some(u64::from(pid)) {
445
- return Err(LifecycleError::StatePersist(
446
- "canonical team pane_pid does not match spawned process".to_string(),
447
- ));
448
- }
449
- }
450
- Ok(())
451
- }
452
-
453
- pub(super) fn rollback_fork_after_spawn(
454
- workspace: &Path,
455
- transport: &dyn Transport,
456
- session_name: &SessionName,
457
- window: &WindowName,
458
- mcp_config_path: &Path,
459
- agent_id: &AgentId,
460
- profile_launch: &crate::provider::ProviderProfileLaunch,
461
- team_key: &str,
462
- ) {
463
- let _ = transport.kill_window(&Target::SessionWindow {
464
- session: session_name.clone(),
465
- window: window.clone(),
466
- });
467
- if let Ok(_lock) = acquire_agent_lifecycle_lock(LifecycleLockRequest {
468
- workspace,
469
- operation: "fork-agent-rollback",
470
- team: Some(team_key),
471
- agent_id: Some(agent_id),
472
- }) {
473
- let _ = crate::lifecycle::restart::remove::remove_agent_with_transport_locked(
474
- workspace,
475
- agent_id,
476
- true,
477
- true,
478
- Some(team_key),
479
- transport,
480
- );
481
- }
482
- cleanup_fork_mcp_artifacts(workspace, agent_id, mcp_config_path, profile_launch);
483
- }
@@ -1,109 +0,0 @@
1
- use super::*;
2
-
3
- #[allow(clippy::too_many_arguments)]
4
- pub(crate) fn upsert_pending_forked_agent_state(
5
- state: &mut serde_json::Value,
6
- source_agent_id: &AgentId,
7
- as_agent_id: &AgentId,
8
- spec_agent: &Value,
9
- safety: &DangerousApproval,
10
- plan: &crate::provider::CommandPlan,
11
- profile_launch: &crate::provider::ProviderProfileLaunch,
12
- spawn: &crate::transport::SpawnResult,
13
- profile_dir: Option<&Path>,
14
- dynamic_role_file: &Path,
15
- pending: &crate::provider::session::PendingContextFork,
16
- spawn_epoch: u64,
17
- ) -> Result<(), LifecycleError> {
18
- let root = state.as_object_mut().ok_or_else(|| {
19
- LifecycleError::StatePersist("runtime state root is not an object".to_string())
20
- })?;
21
- let agents = root
22
- .entry("agents".to_string())
23
- .or_insert_with(|| serde_json::json!({}))
24
- .as_object_mut()
25
- .ok_or_else(|| {
26
- LifecycleError::StatePersist("runtime state agents is not an object".to_string())
27
- })?;
28
- let mut entry = serde_json::Map::new();
29
- entry.insert("status".to_string(), serde_json::json!("running"));
30
- entry.insert(
31
- "capture_state".to_string(),
32
- serde_json::json!("pending_context_fork"),
33
- );
34
- entry.insert(
35
- "agent_id".to_string(),
36
- serde_json::json!(as_agent_id.as_str()),
37
- );
38
- entry.insert(
39
- "window".to_string(),
40
- serde_json::json!(as_agent_id.as_str()),
41
- );
42
- entry.insert(
43
- "forked_from".to_string(),
44
- serde_json::json!(source_agent_id.as_str()),
45
- );
46
- entry.insert(
47
- "fork_source_session_id".to_string(),
48
- serde_json::json!(pending.source_session_id.as_str()),
49
- );
50
- entry.insert(
51
- "pending_target_agent".to_string(),
52
- serde_json::json!(pending.target_agent),
53
- );
54
- entry.insert(
55
- "dynamic_role_file".to_string(),
56
- serde_json::json!(dynamic_role_file.to_string_lossy().to_string()),
57
- );
58
- entry.insert(
59
- "role_source_ownership".to_string(),
60
- serde_json::json!("managed"),
61
- );
62
- entry.insert(
63
- "spawn_cwd".to_string(),
64
- serde_json::json!(pending
65
- .scanner_context
66
- .spawn_cwd
67
- .to_string_lossy()
68
- .to_string()),
69
- );
70
- entry.insert(
71
- "pane_id".to_string(),
72
- serde_json::json!(spawn.pane_id.as_str()),
73
- );
74
- entry.insert(
75
- "spawned_at".to_string(),
76
- serde_json::json!(pending.spawned_at),
77
- );
78
- entry.insert("spawn_epoch".to_string(), serde_json::json!(spawn_epoch));
79
- if let Some(pid) = spawn.child_pid {
80
- entry.insert("pane_pid".to_string(), serde_json::json!(pid));
81
- }
82
- for key in [
83
- "provider",
84
- "auth_mode",
85
- "model",
86
- "profile",
87
- "role",
88
- "effort",
89
- ] {
90
- if let Some(value) = spec_agent.get(key) {
91
- entry.insert(key.to_string(), yaml_value_to_json(value));
92
- }
93
- }
94
- if spec_agent.get("profile").is_some() {
95
- if let Some(profile_dir) = profile_dir {
96
- entry.insert(
97
- "_profile_dir".to_string(),
98
- serde_json::json!(profile_dir.to_string_lossy().to_string()),
99
- );
100
- }
101
- }
102
- persist_command_plan_state(&mut entry, plan, profile_launch);
103
- persist_effective_approval_policy(&mut entry, safety);
104
- agents.insert(
105
- as_agent_id.as_str().to_string(),
106
- serde_json::Value::Object(entry),
107
- );
108
- Ok(())
109
- }