fix: stop precreating opencode session and harden session lookup

This commit is contained in:
Nathan Flurry 2026-02-08 12:49:58 -08:00
parent fc7abd13f0
commit 48749ef3bf
3 changed files with 67 additions and 11 deletions

View file

@ -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);
}

View file

@ -3796,10 +3796,20 @@ async fn oc_session_create(
async fn oc_session_list(State(state): State<Arc<OpenCodeAppState>>) -> impl IntoResponse {
let sessions = state.inner.session_manager().list_sessions().await;
let project_id = &state.opencode.default_project_id;
let values: Vec<Value> = sessions
let mut values: Vec<Value> = sessions
.iter()
.map(|s| session_info_to_opencode_value(s, project_id))
.collect();
let mut seen_session_ids: HashSet<String> = 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()
}

View file

@ -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" },