#[path = "support/phase2/mod.rs"]
mod phase2;
use std::sync::Arc;
use std::time::Duration;
use agent_engine::daemon::{Daemon, DaemonOpts};
use agent_engine::session::socket_transport::SocketTransport;
use agent_engine::session::wire::*;
use agent_engine::session::*;
use agent_engine::{EngineHost, HostOpts};
use phase2::{spawn_stub, HomeGuard, Script, ANTHROPIC_SSE};
use serial_test::serial;
async fn next(t: &mut SocketTransport) -> Envelope {
tokio::time::timeout(Duration::from_secs(10), t.next_event())
.await
.expect("timely")
.expect("open")
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
#[serial]
async fn in_process_continue_of_live_daemon_session_refused() {
let guard = HomeGuard::new();
let (url, _hits, _bodies) = spawn_stub(Script::Sse(ANTHROPIC_SSE)).await;
std::env::set_var("SYNAPS_ANTHROPIC_BASE_URL", &url);
let host: Arc<EngineHost> =
EngineHost::boot_and_install(HostOpts { profile: None, no_extensions: true, mcp_discovery: agent_engine::McpDiscovery::Inline })
.await
.expect("host boot");
let d = Daemon::start(
host.clone(),
DaemonOpts {
runtime_dir: Some(guard.base_dir().join("run")),
..Default::default()
},
)
.await
.expect("daemon start");
let sock = d.paths.sock.clone();
let cwd = guard.home.path().to_path_buf();
let conn = SocketTransport::connect(&sock, Hello::new(ClientKind::Test))
.await
.unwrap();
let (mut t, snap) = SocketTransport::attach(
conn,
Attach::Create {
config: SessionConfig {
cwd: Some(cwd.clone()),
env: None,
model_override: Some("claude-sonnet-4-5".into()),
persist: true,
await_extensions: true,
..Default::default()
},
mode: AttachMode::Mirror,
},
)
.await
.expect("daemon attach");
let session_id = snap.meta.journal_id.clone();
t.send(SessionCommand::Submit {
text: "hello".into(),
attachments: vec![],
})
.await
.unwrap();
loop {
let e = next(&mut t).await;
if matches!(e.event, SessionEventWire::Idle) {
break;
}
}
let sessions_dir = agent_core::session_lock::sessions_dir();
let holder = agent_core::session_lock::LockHolder {
pid: std::process::id(),
kind: "tui".to_string(),
};
let err =
agent_core::session_lock::SessionLock::try_acquire(&sessions_dir, &session_id, holder);
let err = err.expect_err("lock must be held by the daemon actor");
let msg = err.to_string();
assert!(
msg.contains("is live in another process"),
"error must say 'is live in another process': {msg}"
);
assert!(
msg.contains("--attach"),
"error must suggest --attach: {msg}"
);
SocketTransport::shutdown(&sock, false).await.unwrap();
tokio::time::timeout(Duration::from_secs(15), d.wait())
.await
.expect("daemon wait");
tokio::time::sleep(Duration::from_millis(100)).await;
let holder2 = agent_core::session_lock::LockHolder {
pid: std::process::id(),
kind: "tui".to_string(),
};
let lock =
agent_core::session_lock::SessionLock::try_acquire(&sessions_dir, &session_id, holder2);
lock.expect("after daemon stop, lock must be acquirable");
}