diff --git a/server/packages/sandbox-agent/src/cli.rs b/server/packages/sandbox-agent/src/cli.rs index 8ca965f..27fb545 100644 --- a/server/packages/sandbox-agent/src/cli.rs +++ b/server/packages/sandbox-agent/src/cli.rs @@ -620,14 +620,20 @@ fn run_opencode(cli: &CliConfig, args: &OpencodeArgs) -> Result<(), CliError> { crate::daemon::ensure_running(cli, &args.host, args.port, token.as_deref())?; write_stderr_line("gigacode startup: daemon is healthy")?; - write_stderr_line("gigacode startup: creating OpenCode session via /opencode/session")?; - let session_id = create_opencode_session( - &base_url, - token.as_deref(), - args.session_title.as_deref(), - yolo, - )?; - write_stdout_line(&format!("OpenCode session: {session_id}"))?; + let attach_session_id = if args.session_title.is_some() || yolo { + write_stderr_line("gigacode startup: creating OpenCode session via /opencode/session")?; + let session_id = create_opencode_session( + &base_url, + token.as_deref(), + args.session_title.as_deref(), + yolo, + )?; + write_stdout_line(&format!("OpenCode session: {session_id}"))?; + Some(session_id) + } else { + write_stderr_line("gigacode startup: attaching OpenCode without precreating a session")?; + None + }; let attach_url = format!("{base_url}/opencode"); write_stderr_line("gigacode startup: resolving OpenCode binary (installing if needed)")?; @@ -640,11 +646,12 @@ fn run_opencode(cli: &CliConfig, args: &OpencodeArgs) -> Result<(), CliError> { opencode_cmd .arg("attach") .arg(&attach_url) - .arg("--session") - .arg(&session_id) .stdin(Stdio::inherit()) .stdout(Stdio::inherit()) .stderr(Stdio::inherit()); + if let Some(session_id) = attach_session_id.as_deref() { + opencode_cmd.arg("--session").arg(session_id); + } if let Some(token) = token.as_deref() { opencode_cmd.arg("--password").arg(token); } diff --git a/server/packages/sandbox-agent/src/opencode_compat.rs b/server/packages/sandbox-agent/src/opencode_compat.rs index 1852a20..e6ee2aa 100644 --- a/server/packages/sandbox-agent/src/opencode_compat.rs +++ b/server/packages/sandbox-agent/src/opencode_compat.rs @@ -3796,10 +3796,20 @@ async fn oc_session_create( async fn oc_session_list(State(state): State>) -> impl IntoResponse { let sessions = state.inner.session_manager().list_sessions().await; let project_id = &state.opencode.default_project_id; - let values: Vec = sessions + let mut values: Vec = sessions .iter() .map(|s| session_info_to_opencode_value(s, project_id)) .collect(); + let mut seen_session_ids: HashSet = sessions + .iter() + .map(|session| session.session_id.clone()) + .collect(); + let compat_sessions = state.opencode.sessions.lock().await; + for (session_id, session) in compat_sessions.iter() { + if seen_session_ids.insert(session_id.clone()) { + values.push(session.to_value()); + } + } (StatusCode::OK, Json(json!(values))) } @@ -3829,6 +3839,10 @@ async fn oc_session_get( ) .into_response(); } + let sessions = state.opencode.sessions.lock().await; + if let Some(session) = sessions.get(&session_id) { + return (StatusCode::OK, Json(session.to_value())).into_response(); + } not_found("Session not found").into_response() } diff --git a/server/packages/sandbox-agent/tests/opencode-compat/session.test.ts b/server/packages/sandbox-agent/tests/opencode-compat/session.test.ts index bdb1052..94c8762 100644 --- a/server/packages/sandbox-agent/tests/opencode-compat/session.test.ts +++ b/server/packages/sandbox-agent/tests/opencode-compat/session.test.ts @@ -291,6 +291,41 @@ describe("OpenCode-compatible Session API", () => { expect(response.data?.title).toBe("Test"); }); + it("should keep session.get available during first prompt after /new-style creation", async () => { + const providers = await getProvidersViaHttp(); + const providerId = providers.connected.find( + (provider) => provider !== "mock" && typeof providers.default?.[provider] === "string" + ); + if (!providerId) { + return; + } + const modelId = providers.default?.[providerId]; + if (!modelId) { + return; + } + + const created = await client.session.create({ body: { title: "Race Repro" } }); + const sessionId = created.data?.id!; + expect(sessionId).toBeDefined(); + + const promptPromise = client.session.prompt({ + path: { id: sessionId }, + body: { + model: { providerID: providerId, modelID: modelId }, + parts: [{ type: "text", text: "hello after /new" }], + }, + }); + + await new Promise((resolve) => setTimeout(resolve, 25)); + + const getDuringPrompt = await client.session.get({ path: { id: sessionId } }); + expect(getDuringPrompt.error).toBeUndefined(); + expect(getDuringPrompt.data?.id).toBe(sessionId); + + // Best-effort settle; this assertion focuses on availability during the in-flight turn. + await promptPromise; + }); + it("should return error for non-existent session", async () => { const response = await client.session.get({ path: { id: "non-existent-session-id" },