@team-agent/installer 0.5.49 → 0.5.51
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 +6 -4
- package/crates/team-agent/src/cli/emit.rs +91 -39
- package/crates/team-agent/src/cli/mod.rs +33 -15
- package/crates/team-agent/src/cli/named_address.rs +82 -53
- package/crates/team-agent/src/cli/send/coordinator.rs +163 -0
- package/crates/team-agent/src/cli/send/mailbox.rs +99 -0
- package/crates/team-agent/src/cli/send/persist.rs +154 -0
- package/crates/team-agent/src/cli/send/presentation.rs +333 -0
- package/crates/team-agent/src/cli/send/resolve.rs +361 -0
- package/crates/team-agent/src/cli/send.rs +103 -1308
- package/crates/team-agent/src/cli/spec.rs +2 -2
- package/crates/team-agent/src/cli/status_port/agents.rs +358 -0
- package/crates/team-agent/src/cli/status_port/approvals.rs +79 -0
- package/crates/team-agent/src/cli/status_port/compact.rs +207 -0
- package/crates/team-agent/src/cli/status_port/format.rs +145 -0
- package/crates/team-agent/src/cli/status_port/inbox.rs +36 -0
- package/crates/team-agent/src/cli/status_port/runtime.rs +195 -0
- package/crates/team-agent/src/cli/status_port/snapshot.rs +181 -0
- package/crates/team-agent/src/cli/status_port/store.rs +412 -0
- package/crates/team-agent/src/cli/status_port/tests.rs +54 -0
- package/crates/team-agent/src/cli/status_port.rs +47 -1548
- package/crates/team-agent/src/cli/tests/leader_watch.rs +1 -1
- package/crates/team-agent/src/cli/tests/named_address.rs +9 -7
- package/crates/team-agent/src/cli/tests/run_delegation.rs +2 -3
- package/crates/team-agent/src/cli/tests/status_send.rs +17 -33
- package/crates/team-agent/src/cli/types.rs +5 -8
- package/crates/team-agent/src/coordinator/conpty_shim.rs +34 -30
- package/crates/team-agent/src/coordinator/steps/abnormal.rs +135 -13
- package/crates/team-agent/src/coordinator/tick.rs +37 -0
- package/crates/team-agent/src/db/agent_health_capture.rs +18 -13
- package/crates/team-agent/src/db/message_store.rs +154 -44
- package/crates/team-agent/src/event_log.rs +73 -0
- package/crates/team-agent/src/leader/start.rs +28 -4
- package/crates/team-agent/src/lifecycle/launch/add_agent.rs +424 -0
- package/crates/team-agent/src/lifecycle/launch/add_agent_state.rs +297 -0
- package/crates/team-agent/src/lifecycle/launch/agent_state.rs +160 -0
- package/crates/team-agent/src/lifecycle/launch/approval.rs +134 -0
- package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +492 -0
- package/crates/team-agent/src/lifecycle/launch/fork_state.rs +297 -0
- package/crates/team-agent/src/lifecycle/launch/identity.rs +372 -0
- package/crates/team-agent/src/lifecycle/launch/layout.rs +313 -0
- package/crates/team-agent/src/lifecycle/launch/leader_context.rs +478 -0
- package/crates/team-agent/src/lifecycle/launch/mcp_config.rs +201 -0
- package/crates/team-agent/src/lifecycle/launch/ownership.rs +66 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start.rs +477 -0
- package/crates/team-agent/src/lifecycle/launch/quick_start_transport.rs +278 -0
- package/crates/team-agent/src/lifecycle/launch/readiness.rs +123 -0
- package/crates/team-agent/src/lifecycle/launch/spawn.rs +377 -0
- package/crates/team-agent/src/lifecycle/launch/spec_state.rs +434 -0
- package/crates/team-agent/src/lifecycle/launch/state_projection.rs +499 -0
- package/crates/team-agent/src/lifecycle/launch/worker_env.rs +438 -0
- package/crates/team-agent/src/lifecycle/launch.rs +119 -5351
- package/crates/team-agent/src/lifecycle/restart/agent.rs +44 -26
- package/crates/team-agent/src/lifecycle/restart/common.rs +53 -27
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +67 -26
- package/crates/team-agent/src/lifecycle/restart/remove.rs +435 -72
- package/crates/team-agent/src/lifecycle/restart.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/lane_ops.rs +575 -17
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +55 -2
- package/crates/team-agent/src/lifecycle/tests/lifecycle_lock.rs +24 -1
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +1 -1
- package/crates/team-agent/src/mcp_server/lifecycle_tools/agent_ops.rs +30 -7
- package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +8 -4
- package/crates/team-agent/src/mcp_server/mod.rs +2 -2
- package/crates/team-agent/src/mcp_server/tests/send.rs +22 -15
- package/crates/team-agent/src/mcp_server/tests/wire.rs +6 -0
- package/crates/team-agent/src/mcp_server/tools.rs +26 -15
- package/crates/team-agent/src/mcp_server/wire.rs +2 -18
- package/crates/team-agent/src/messaging/activity.rs +4 -2
- package/crates/team-agent/src/messaging/address.rs +86 -0
- package/crates/team-agent/src/messaging/delivery.rs +165 -39
- package/crates/team-agent/src/messaging/helpers.rs +17 -13
- package/crates/team-agent/src/messaging/leader_receiver.rs +60 -35
- package/crates/team-agent/src/messaging/mod.rs +11 -2
- package/crates/team-agent/src/messaging/persist.rs +309 -0
- package/crates/team-agent/src/messaging/results.rs +16 -24
- package/crates/team-agent/src/messaging/scheduler.rs +4 -2
- package/crates/team-agent/src/messaging/selftest.rs +19 -12
- package/crates/team-agent/src/messaging/send.rs +133 -58
- package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +305 -0
- package/crates/team-agent/src/messaging/tests/mod.rs +1 -0
- package/crates/team-agent/src/messaging/tests/runtime.rs +38 -17
- package/crates/team-agent/src/messaging/watchers.rs +13 -3
- package/crates/team-agent/src/redaction.rs +72 -2
- package/crates/team-agent/src/state/persist.rs +2 -1
- package/crates/team-agent/src/state/repository/tests.rs +47 -0
- package/crates/team-agent/src/state/repository.rs +59 -16
- package/package.json +4 -4
|
@@ -884,26 +884,38 @@ pub(super) fn stop_agent_at_paths(
|
|
|
884
884
|
transport: &dyn crate::transport::Transport,
|
|
885
885
|
) -> Result<StopAgentReport, LifecycleError> {
|
|
886
886
|
// golden operations.py:64-66: resolve_team_scoped_state -> owner gate, BEFORE the unknown-worker raise.
|
|
887
|
-
let
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
let
|
|
892
|
-
let
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
.
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
887
|
+
let seat = super::remove::resolve_seat(workspace, spec_workspace, agent_id, team, transport)?;
|
|
888
|
+
if seat.consistency == super::remove::SeatConsistency::Absent {
|
|
889
|
+
return Err(unknown_worker(agent_id));
|
|
890
|
+
}
|
|
891
|
+
let mut state = seat.state;
|
|
892
|
+
let spec = seat.spec;
|
|
893
|
+
let state_agent = state.get("agents").and_then(|v| v.get(agent_id.as_str()));
|
|
894
|
+
// Persisted-only seats must remain stoppable. Build the minimal provider
|
|
895
|
+
// projection consumed by mark_agent_stopped instead of requiring a spec
|
|
896
|
+
// row that is precisely what recovery is repairing.
|
|
897
|
+
let fallback_agent = YamlValue::Map(vec![(
|
|
898
|
+
"provider".to_string(),
|
|
899
|
+
YamlValue::Str(
|
|
900
|
+
state_agent
|
|
901
|
+
.and_then(|agent| agent.get("provider"))
|
|
902
|
+
.and_then(|value| value.as_str())
|
|
903
|
+
.unwrap_or("codex")
|
|
904
|
+
.to_string(),
|
|
905
|
+
),
|
|
906
|
+
)]);
|
|
907
|
+
let agent = find_spec_agent(&spec, agent_id).unwrap_or(&fallback_agent);
|
|
908
|
+
let session_name = seat.session;
|
|
909
|
+
let window = seat.window;
|
|
910
|
+
let pane_id = seat.physical.map(|pane| pane.pane_id).or_else(|| {
|
|
911
|
+
state
|
|
912
|
+
.get("agents")
|
|
913
|
+
.and_then(|agents| agents.get(agent_id.as_str()))
|
|
914
|
+
.and_then(|agent| agent.get("pane_id"))
|
|
915
|
+
.and_then(serde_json::Value::as_str)
|
|
916
|
+
.filter(|pane| !pane.is_empty())
|
|
917
|
+
.map(crate::transport::PaneId::new)
|
|
918
|
+
});
|
|
907
919
|
let target_str = pane_id
|
|
908
920
|
.as_ref()
|
|
909
921
|
.map(|pane| pane.as_str().to_string())
|
|
@@ -995,12 +1007,15 @@ pub(super) fn stop_agent_at_paths(
|
|
|
995
1007
|
}
|
|
996
1008
|
close_agent_display(&mut state, agent_id);
|
|
997
1009
|
mark_agent_stopped(&mut state, agent_id, agent, &window)?;
|
|
1010
|
+
let team_key = restart_projection_team_key(&state, team);
|
|
998
1011
|
// golden operations.py:95: save_team_scoped_state (team projection) — NOT a raw save, so a
|
|
999
1012
|
// multi-team workspace keeps the other teams' persisted runtime state instead of being clobbered.
|
|
1000
|
-
crate::state::
|
|
1001
|
-
|
|
1013
|
+
crate::state::repository::StateRepository::new(workspace).save(
|
|
1014
|
+
crate::state::repository::StateWriteIntent::StopAgent {
|
|
1015
|
+
team_key: &team_key,
|
|
1016
|
+
agent_id: agent_id.as_str(),
|
|
1017
|
+
},
|
|
1002
1018
|
&state,
|
|
1003
|
-
&[agent_id.as_str()],
|
|
1004
1019
|
)
|
|
1005
1020
|
.map_err(|e| LifecycleError::StatePersist(e.to_string()))?;
|
|
1006
1021
|
// golden operations.py:96-99: snapshot (side-effect), then state_file = write_team_state path.
|
|
@@ -1447,10 +1462,13 @@ fn reset_agent_at_paths(
|
|
|
1447
1462
|
sync_restart_team_projections(&mut state, &team_key);
|
|
1448
1463
|
// golden operations.py (reset): save_team_scoped_state on the team projection — same multi-team
|
|
1449
1464
|
// preservation as stop, not a raw save_runtime_state.
|
|
1450
|
-
crate::state::
|
|
1451
|
-
|
|
1465
|
+
crate::state::repository::StateRepository::new(workspace).save(
|
|
1466
|
+
crate::state::repository::StateWriteIntent::ResetAgent {
|
|
1467
|
+
team_key: &team_key,
|
|
1468
|
+
agent_id: agent_id.as_str(),
|
|
1469
|
+
discard_session,
|
|
1470
|
+
},
|
|
1452
1471
|
&state,
|
|
1453
|
-
&[agent_id.as_str()],
|
|
1454
1472
|
)
|
|
1455
1473
|
.map_err(|e| LifecycleError::StatePersist(e.to_string()))?;
|
|
1456
1474
|
// golden operations.py:125: write_team_state after the discard-save (the intermediate stopped snapshot).
|
|
@@ -373,8 +373,8 @@ pub(super) fn spawn_agent_window(
|
|
|
373
373
|
let tmux_endpoint = transport.tmux_endpoint();
|
|
374
374
|
let event_log = crate::event_log::EventLog::new(workspace);
|
|
375
375
|
let _ = event_log.write(
|
|
376
|
-
|
|
377
|
-
serde_json::json!({
|
|
376
|
+
crate::event_log::PROVIDER_WORKER_SPAWN_ARGV,
|
|
377
|
+
crate::event_log::provider_worker_spawn_argv_fields(serde_json::json!({
|
|
378
378
|
"agent_id": agent_id.as_str(),
|
|
379
379
|
"provider": provider,
|
|
380
380
|
"argv": plan.argv,
|
|
@@ -388,7 +388,7 @@ pub(super) fn spawn_agent_window(
|
|
|
388
388
|
"source": "restart",
|
|
389
389
|
"tmux_endpoint": tmux_endpoint,
|
|
390
390
|
"tmux_endpoint_source": tmux_endpoint_source.unwrap_or("transport"),
|
|
391
|
-
}),
|
|
391
|
+
})),
|
|
392
392
|
);
|
|
393
393
|
}
|
|
394
394
|
|
|
@@ -817,11 +817,13 @@ pub(super) fn save_restart_projected_state_with_capture_backfill_skip(
|
|
|
817
817
|
topology_authority_agent_ids: &[&str],
|
|
818
818
|
) -> Result<(), LifecycleError> {
|
|
819
819
|
sync_restart_team_projections(state, team_key);
|
|
820
|
-
crate::state::
|
|
821
|
-
|
|
820
|
+
crate::state::repository::StateRepository::new(workspace).save(
|
|
821
|
+
crate::state::repository::StateWriteIntent::RestartTeam {
|
|
822
|
+
team_key,
|
|
823
|
+
topology_authority_agent_ids,
|
|
824
|
+
skip_capture_backfill_agent_ids,
|
|
825
|
+
},
|
|
822
826
|
state,
|
|
823
|
-
skip_capture_backfill_agent_ids,
|
|
824
|
-
topology_authority_agent_ids,
|
|
825
827
|
)
|
|
826
828
|
.map_err(|e| LifecycleError::StatePersist(e.to_string()))
|
|
827
829
|
}
|
|
@@ -1316,21 +1318,16 @@ pub(crate) fn converge_missing_provider_sessions(
|
|
|
1316
1318
|
poll_interval,
|
|
1317
1319
|
restart_required_missing_session_agent_ids,
|
|
1318
1320
|
|progress| {
|
|
1319
|
-
let pending_agent_ids = progress.pending_agent_ids.clone();
|
|
1320
1321
|
write_session_convergence_progress_event(
|
|
1321
1322
|
workspace,
|
|
1322
1323
|
serde_json::json!({
|
|
1323
|
-
"ts": chrono::Utc::now().to_rfc3339(),
|
|
1324
|
-
"event": "provider.session.converging",
|
|
1325
1324
|
"iteration": progress.iteration,
|
|
1326
1325
|
"elapsed_ms": progress.elapsed_ms,
|
|
1327
1326
|
"deadline_ms": progress.deadline_ms,
|
|
1328
1327
|
"changed": progress.changed,
|
|
1329
1328
|
"assigned": progress.assigned,
|
|
1330
1329
|
"missing": progress.missing,
|
|
1331
|
-
"required_missing": progress.required_missing_agent_ids.clone(),
|
|
1332
1330
|
"required_missing_agent_ids": progress.required_missing_agent_ids,
|
|
1333
|
-
"pending": pending_agent_ids,
|
|
1334
1331
|
"pending_agent_ids": progress.pending_agent_ids,
|
|
1335
1332
|
"candidate_count_by_agent": progress.candidate_count_by_agent,
|
|
1336
1333
|
"remaining_ms": progress.remaining_ms,
|
|
@@ -1344,22 +1341,11 @@ pub(crate) fn converge_missing_provider_sessions(
|
|
|
1344
1341
|
|
|
1345
1342
|
fn write_session_convergence_progress_event(
|
|
1346
1343
|
workspace: &Path,
|
|
1347
|
-
|
|
1344
|
+
fields: serde_json::Value,
|
|
1348
1345
|
) -> Result<(), String> {
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
if let Some(parent) = path.parent() {
|
|
1353
|
-
std::fs::create_dir_all(parent).map_err(|e| e.to_string())?;
|
|
1354
|
-
}
|
|
1355
|
-
let line = serde_json::to_string(&event).map_err(|e| e.to_string())?;
|
|
1356
|
-
let mut file = std::fs::OpenOptions::new()
|
|
1357
|
-
.create(true)
|
|
1358
|
-
.append(true)
|
|
1359
|
-
.open(path)
|
|
1360
|
-
.map_err(|e| e.to_string())?;
|
|
1361
|
-
file.write_all(line.as_bytes())
|
|
1362
|
-
.and_then(|_| file.write_all(b"\n"))
|
|
1346
|
+
crate::event_log::EventLog::new(workspace)
|
|
1347
|
+
.write(crate::event_log::PROVIDER_SESSION_CONVERGING, fields)
|
|
1348
|
+
.map(|_| ())
|
|
1363
1349
|
.map_err(|e| e.to_string())
|
|
1364
1350
|
}
|
|
1365
1351
|
|
|
@@ -1837,6 +1823,46 @@ mod e36_transcript_backing_tests {
|
|
|
1837
1823
|
}
|
|
1838
1824
|
}
|
|
1839
1825
|
|
|
1826
|
+
#[test]
|
|
1827
|
+
fn convergence_event_uses_central_scrub_and_preserves_required_failure() {
|
|
1828
|
+
let scratch = ScratchDir::new("event-log-central");
|
|
1829
|
+
let marker = "synthetic-convergence-marker";
|
|
1830
|
+
write_session_convergence_progress_event(
|
|
1831
|
+
scratch.path(),
|
|
1832
|
+
serde_json::json!({
|
|
1833
|
+
"diagnostic": format!("mcp_servers.demo.env.OPENAI_API_KEY=\"{marker}\""),
|
|
1834
|
+
"required_missing_agent_ids": ["worker"],
|
|
1835
|
+
"pending_agent_ids": ["worker"],
|
|
1836
|
+
}),
|
|
1837
|
+
)
|
|
1838
|
+
.expect("central EventLog write");
|
|
1839
|
+
let bytes = std::fs::read_to_string(
|
|
1840
|
+
scratch
|
|
1841
|
+
.path()
|
|
1842
|
+
.join(".team")
|
|
1843
|
+
.join("logs")
|
|
1844
|
+
.join("events.jsonl"),
|
|
1845
|
+
)
|
|
1846
|
+
.expect("events");
|
|
1847
|
+
assert!(!bytes.contains(marker));
|
|
1848
|
+
assert!(bytes.contains("[REDACTED]"));
|
|
1849
|
+
|
|
1850
|
+
let blocked = ScratchDir::new("event-log-required");
|
|
1851
|
+
std::fs::create_dir_all(
|
|
1852
|
+
blocked
|
|
1853
|
+
.path()
|
|
1854
|
+
.join(".team")
|
|
1855
|
+
.join("logs")
|
|
1856
|
+
.join("events.jsonl"),
|
|
1857
|
+
)
|
|
1858
|
+
.expect("occupy event path with directory");
|
|
1859
|
+
assert!(write_session_convergence_progress_event(
|
|
1860
|
+
blocked.path(),
|
|
1861
|
+
serde_json::json!({"pending_agent_ids": ["worker"]}),
|
|
1862
|
+
)
|
|
1863
|
+
.is_err());
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1840
1866
|
// E36 fix-B RED→GREEN: a real Claude worker that sent a message has its session
|
|
1841
1867
|
// transcript landed at <projects_root>/<slug>/<session_id>.jsonl, but neither
|
|
1842
1868
|
// rollout_path was persisted to state nor a session.captured event was logged.
|
|
@@ -1841,11 +1841,13 @@ fn save_restart_state_with_lifecycle_topology_authority_and_capture_backfill_ski
|
|
|
1841
1841
|
.map(String::as_str)
|
|
1842
1842
|
.collect::<Vec<_>>();
|
|
1843
1843
|
sync_restart_team_projections(state, team_key);
|
|
1844
|
-
crate::state::
|
|
1845
|
-
|
|
1844
|
+
crate::state::repository::StateRepository::new(workspace).save(
|
|
1845
|
+
crate::state::repository::StateWriteIntent::RestartTeam {
|
|
1846
|
+
team_key,
|
|
1847
|
+
topology_authority_agent_ids: &topology_agent_ids,
|
|
1848
|
+
skip_capture_backfill_agent_ids: &skip_capture_backfill_agent_ids,
|
|
1849
|
+
},
|
|
1846
1850
|
state,
|
|
1847
|
-
&skip_capture_backfill_agent_ids,
|
|
1848
|
-
&topology_agent_ids,
|
|
1849
1851
|
)
|
|
1850
1852
|
.map_err(|e| LifecycleError::StatePersist(e.to_string()))
|
|
1851
1853
|
}
|
|
@@ -1858,7 +1860,11 @@ fn save_restart_session_repairs(
|
|
|
1858
1860
|
) -> Result<(), LifecycleError> {
|
|
1859
1861
|
let repairs = collect_session_repair_fields(state, agent_ids);
|
|
1860
1862
|
sync_restart_team_projections(state, team_key);
|
|
1861
|
-
|
|
1863
|
+
let repository = crate::state::repository::StateRepository::new(workspace);
|
|
1864
|
+
match repository.save(
|
|
1865
|
+
crate::state::repository::StateWriteIntent::RestartSessionRepair { team_key },
|
|
1866
|
+
state,
|
|
1867
|
+
) {
|
|
1862
1868
|
Ok(()) => Ok(()),
|
|
1863
1869
|
Err(crate::state::StateError::SaveConflict(_)) => {
|
|
1864
1870
|
let mut latest =
|
|
@@ -1868,7 +1874,11 @@ fn save_restart_session_repairs(
|
|
|
1868
1874
|
apply_session_repair_fields(&mut latest, agent_id, repair);
|
|
1869
1875
|
}
|
|
1870
1876
|
sync_restart_team_projections(&mut latest, team_key);
|
|
1871
|
-
|
|
1877
|
+
repository
|
|
1878
|
+
.save(
|
|
1879
|
+
crate::state::repository::StateWriteIntent::RestartSessionRepair { team_key },
|
|
1880
|
+
&latest,
|
|
1881
|
+
)
|
|
1872
1882
|
.map_err(|e| LifecycleError::StatePersist(e.to_string()))?;
|
|
1873
1883
|
*state = latest;
|
|
1874
1884
|
Ok(())
|
|
@@ -2266,8 +2276,8 @@ fn write_fake_harness_spawn_argv_event(
|
|
|
2266
2276
|
tmux_endpoint_source: Option<&str>,
|
|
2267
2277
|
) {
|
|
2268
2278
|
let _ = crate::event_log::EventLog::new(workspace).write(
|
|
2269
|
-
|
|
2270
|
-
serde_json::json!({
|
|
2279
|
+
crate::event_log::PROVIDER_WORKER_SPAWN_ARGV,
|
|
2280
|
+
crate::event_log::provider_worker_spawn_argv_fields(serde_json::json!({
|
|
2271
2281
|
"agent_id": decision.agent_id.as_str(),
|
|
2272
2282
|
"provider": agent_provider(agent),
|
|
2273
2283
|
"argv": [],
|
|
@@ -2277,7 +2287,7 @@ fn write_fake_harness_spawn_argv_event(
|
|
|
2277
2287
|
"source": "restart",
|
|
2278
2288
|
"tmux_endpoint": transport.tmux_endpoint(),
|
|
2279
2289
|
"tmux_endpoint_source": tmux_endpoint_source.unwrap_or("transport"),
|
|
2280
|
-
}),
|
|
2290
|
+
})),
|
|
2281
2291
|
);
|
|
2282
2292
|
}
|
|
2283
2293
|
|
|
@@ -2924,15 +2934,7 @@ fn write_restart_resume_decision_event(
|
|
|
2924
2934
|
forced_fresh_convergence: Option<&crate::session_capture::SessionConvergence>,
|
|
2925
2935
|
unresumable: Option<&crate::lifecycle::types::UnresumableWorker>,
|
|
2926
2936
|
) -> Result<(), LifecycleError> {
|
|
2927
|
-
use std::io::Write as _;
|
|
2928
|
-
|
|
2929
|
-
let path = workspace.join(".team").join("logs").join("events.jsonl");
|
|
2930
|
-
if let Some(parent) = path.parent() {
|
|
2931
|
-
std::fs::create_dir_all(parent).map_err(|e| LifecycleError::StatePersist(e.to_string()))?;
|
|
2932
|
-
}
|
|
2933
2937
|
let mut event = serde_json::json!({
|
|
2934
|
-
"ts": chrono::Utc::now().to_rfc3339(),
|
|
2935
|
-
"event": crate::lifecycle::types::event_names::RESTART_RESUME_DECISION,
|
|
2936
2938
|
"worker_id": worker_id,
|
|
2937
2939
|
"has_first_send_at": first_send_at.is_some(),
|
|
2938
2940
|
"has_session_id": session_id.is_some(),
|
|
@@ -3039,15 +3041,12 @@ fn write_restart_resume_decision_event(
|
|
|
3039
3041
|
}
|
|
3040
3042
|
}
|
|
3041
3043
|
}
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
.
|
|
3048
|
-
.map_err(|e| LifecycleError::StatePersist(e.to_string()))?;
|
|
3049
|
-
file.write_all(line.as_bytes())
|
|
3050
|
-
.and_then(|_| file.write_all(b"\n"))
|
|
3044
|
+
crate::event_log::EventLog::new(workspace)
|
|
3045
|
+
.write(
|
|
3046
|
+
crate::lifecycle::types::event_names::RESTART_RESUME_DECISION,
|
|
3047
|
+
event,
|
|
3048
|
+
)
|
|
3049
|
+
.map(|_| ())
|
|
3051
3050
|
.map_err(|e| LifecycleError::StatePersist(e.to_string()))
|
|
3052
3051
|
}
|
|
3053
3052
|
|
|
@@ -3419,6 +3418,48 @@ mod tests {
|
|
|
3419
3418
|
TransportError,
|
|
3420
3419
|
};
|
|
3421
3420
|
|
|
3421
|
+
#[test]
|
|
3422
|
+
fn resume_decision_event_uses_central_scrub_and_preserves_required_failure() {
|
|
3423
|
+
let marker = "synthetic-resume-decision-marker";
|
|
3424
|
+
let workspace =
|
|
3425
|
+
std::env::temp_dir().join(format!("ta-resume-decision-event-{}", std::process::id()));
|
|
3426
|
+
let _ = std::fs::remove_dir_all(&workspace);
|
|
3427
|
+
write_restart_resume_decision_event(
|
|
3428
|
+
&workspace,
|
|
3429
|
+
"worker",
|
|
3430
|
+
Some(format!("mcp_servers.demo.env.OPENAI_API_KEY=\"{marker}\"")),
|
|
3431
|
+
None,
|
|
3432
|
+
false,
|
|
3433
|
+
"fresh_start",
|
|
3434
|
+
false,
|
|
3435
|
+
None,
|
|
3436
|
+
None,
|
|
3437
|
+
)
|
|
3438
|
+
.expect("central EventLog write");
|
|
3439
|
+
let bytes =
|
|
3440
|
+
std::fs::read_to_string(workspace.join(".team").join("logs").join("events.jsonl"))
|
|
3441
|
+
.expect("events");
|
|
3442
|
+
assert!(!bytes.contains(marker));
|
|
3443
|
+
assert!(bytes.contains("[REDACTED]"));
|
|
3444
|
+
let _ = std::fs::remove_dir_all(&workspace);
|
|
3445
|
+
|
|
3446
|
+
std::fs::create_dir_all(workspace.join(".team").join("logs").join("events.jsonl"))
|
|
3447
|
+
.expect("occupy event path with directory");
|
|
3448
|
+
let result = write_restart_resume_decision_event(
|
|
3449
|
+
&workspace,
|
|
3450
|
+
"worker",
|
|
3451
|
+
None,
|
|
3452
|
+
None,
|
|
3453
|
+
false,
|
|
3454
|
+
"fresh_start",
|
|
3455
|
+
false,
|
|
3456
|
+
None,
|
|
3457
|
+
None,
|
|
3458
|
+
);
|
|
3459
|
+
assert!(matches!(result, Err(LifecycleError::StatePersist(_))));
|
|
3460
|
+
let _ = std::fs::remove_dir_all(workspace);
|
|
3461
|
+
}
|
|
3462
|
+
|
|
3422
3463
|
struct RespawnEpochTransport;
|
|
3423
3464
|
|
|
3424
3465
|
impl Transport for RespawnEpochTransport {
|