name a live opencode row from the prompt until opencode names it

Same shape as the claude side, which has never shown a live row without a name.

OpenCode titles a session from the conversation and does it well, but asynchronously — so
for the whole time a turn is RUNNING, which is exactly what /chat/live shows, the session is
still called "New session - <ISO>". Its own title wins the moment it exists; until then the
row falls back to the prompt that started the session.

Kept per sessionKey, first turn only, so it stays the name of the conversation rather than
following whatever was asked most recently. Dropped with the session id it sits beside.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 17:00:13 +01:00
co-authored by Claude Opus 5
parent 90e0ca8ab4
commit ecc10ae6af
3 changed files with 32 additions and 6 deletions
+7 -6
View File
@@ -21,7 +21,7 @@ import {
renameOpenCodeSession,
isOpenCodeSessionId,
} from './opencode-sessions';
import { getOpenCodeSession } from './opencode/state';
import { getOpenCodePrompt, getOpenCodeSession } from './opencode/state';
import { listChatModels } from './list-models';
import { logger } from './logger';
import { readSttConfig } from '../server-settings/stt';
@@ -150,11 +150,12 @@ chatRouter.get('/live', async (ctx) => {
isGenerating: true,
pendingTasks: 0,
harness: 'opencode' as const,
// Null for the first seconds of a turn (the id is only known once the subprocess prints it), and
// null again while OpenCode still calls the session `New session - <ISO>` — `listOpenCodeSessions`
// normalises that placeholder to `(untitled)`, and a live row is better blank than named after a
// timestamp. The real title arrives on a later poll, because OpenCode writes it asynchronously.
title: meta?.title && meta.title !== '(untitled)' ? meta.title : null,
// OpenCode's own title wins as soon as it exists — it is derived from the conversation and is
// better than anything we would compose. Until then (and it titles asynchronously, so "until
// then" covers the whole time a turn is RUNNING, which is exactly what this panel shows) fall
// back to the prompt that started the session. Same shape as the Claude side, which has never
// shown a live row without a name.
title: meta?.title && meta.title !== '(untitled)' ? meta.title : (getOpenCodePrompt(sessionKey) ?? null),
cwd: meta?.cwd || null,
};
});