name the live opencode rows
They were permanently unnamed, and the two halves needed to name them already existed on officer: the sidecar reports its own sessionKey because that is all it has, while the ses_ id arrives separately over opencode:session and is recorded in opencode/state.ts. Nothing joined them. /chat/live joins them now, so no protocol or sidecar change — widening LiveOpenCodeSession would have meant sending the sidecar a fact it told officer in the first place. One list call names every row rather than one transcript load each, and it is skipped when nothing is running or no id has been reported, so an idle Live panel never touches the serve. Verified against a real turn, which also showed the design working as intended: the first poll has no id yet and shows nothing, the next shows title and cwd. That window is real and short, and showing nothing beats showing a key the user has never seen. Worth knowing: opencode titles its own sessions "New session - <ISO timestamp>", so the row is located but not meaningfully named. That is genuinely its title, not a bug here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ import {
|
||||
renameOpenCodeSession,
|
||||
isOpenCodeSessionId,
|
||||
} from './opencode-sessions';
|
||||
import { getOpenCodeSession } from './opencode/state';
|
||||
import { listChatModels } from './list-models';
|
||||
import { logger } from './logger';
|
||||
import { readSttConfig } from '../server-settings/stt';
|
||||
@@ -121,24 +122,41 @@ chatRouter.get('/live', async (ctx) => {
|
||||
});
|
||||
|
||||
// OpenCode rows carry less, and the shape says so rather than faking parity. `isGenerating` is always
|
||||
// true because a subprocess exists only while it generates; `pendingTasks` is 0 because `opencode run`
|
||||
// has no background-task concept.
|
||||
// true because a subprocess exists only while it generates, and `pendingTasks` is 0 because
|
||||
// `opencode run` has no background-task concept — a number there would imply one.
|
||||
//
|
||||
// Title and cwd are ALWAYS null here, not merely null-until-known: naming a session means reading
|
||||
// OpenCode's store, which is keyed on the `ses_…` id, and `LiveOpenCodeSession` does not carry one —
|
||||
// the runner reports it separately over `opencode:session` and nothing correlates the two. So a
|
||||
// running OpenCode turn shows up unnamed, every time. Giving it a title means widening that type
|
||||
// first; guessing from `sessionKey` would be inventing one.
|
||||
const openCodeSessions = liveOpenCode.map((session) => ({
|
||||
// Naming them needs a second hop. The sidecar reports only its own `sessionKey`, because that is all it
|
||||
// has; OpenCode's store is keyed on `ses_…`, which the runner reports separately over `opencode:session`
|
||||
// and officer records in `opencode/state.ts`. So the correlation lives HERE, and it is the reason these
|
||||
// rows used to be permanently unnamed — the two halves existed and nothing joined them.
|
||||
const withIds = liveOpenCode.map((session) => ({
|
||||
sessionKey: session.sessionKey,
|
||||
claudeSessionId: null,
|
||||
isGenerating: true,
|
||||
pendingTasks: 0,
|
||||
harness: 'opencode' as const,
|
||||
title: null,
|
||||
cwd: null,
|
||||
openCodeId: getOpenCodeSession(session.sessionKey) ?? null,
|
||||
}));
|
||||
|
||||
// One list call names every row, rather than one transcript load each — `loadOpenCodeSession` rebuilds
|
||||
// a whole conversation to read its title. Skipped entirely when nothing is running or nothing has
|
||||
// reported an id yet, so an idle Live panel never touches the serve. Failure degrades to unnamed:
|
||||
// `listOpenCodeSessions` already swallows and returns [], which matches how the rest of this route
|
||||
// fails.
|
||||
const named = withIds.some((r) => r.openCodeId) ? await listOpenCodeSessions() : [];
|
||||
const byId = new Map(named.map((s) => [s.id, s]));
|
||||
|
||||
const openCodeSessions = withIds.map(({ sessionKey, openCodeId }) => {
|
||||
const meta = openCodeId ? byId.get(openCodeId) : undefined;
|
||||
return {
|
||||
sessionKey,
|
||||
claudeSessionId: openCodeId,
|
||||
isGenerating: true,
|
||||
pendingTasks: 0,
|
||||
harness: 'opencode' as const,
|
||||
// Still null for the first seconds of a turn: the id is only known once the subprocess prints it.
|
||||
// That window is real and short, and showing nothing beats showing a key the user has never seen.
|
||||
title: meta?.title ?? null,
|
||||
cwd: meta?.cwd || null,
|
||||
};
|
||||
});
|
||||
|
||||
return ctx.json({ sessions: [...sessions, ...openCodeSessions] });
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user