stop showing opencode placeholder titles as if they were names

OpenCode titles a session from the conversation, but asynchronously — a finished turn of
ours ended up called "Single color in oc-red2.png", better than anything we would generate.
Until then the session is literally named "New session - 2026-08-10T15:44:17.178Z".

That window is exactly when a session is most visible: /chat/live shows turns that are
RUNNING, so the placeholder is what the panel catches, and a live row was being labelled
with a timestamp string.

Recognise it and treat it as untitled, so the good name arrives on its own. Passing --title
on the run was the other option and is worse: it fixes the transient case by permanently
replacing opencode own title with a truncated prompt, degrading it where it lasts longest.

A pattern match rather than startsWith, because a genuine title is allowed to begin with
those words. Two defects the tests caught while writing them: a whitespace-only title was
not treated as unnamed, and the mapping let undefined through where a string was required.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 16:56:07 +01:00
co-authored by Claude Opus 5
parent f0aa6dbf4b
commit 90e0ca8ab4
3 changed files with 50 additions and 4 deletions
+18 -1
View File
@@ -24,6 +24,23 @@ import { logger } from './logger';
* the `!cwd` escape never fired either and there was no configuration in which an OpenCode session
* appeared in /chat. Verified against the live server: 7 sessions present, 0 returned.
*/
/**
* OpenCode's own placeholder name for a session it has not titled yet.
*
* It titles a session from the conversation, but ASYNCHRONOUSLY and after there is something to read —
* a finished turn ends up called "Single color in oc-red2.png", which is better than anything we would
* generate. Until then the session is literally named `New session - 2026-08-10T15:44:17.178Z`.
*
* That window is exactly when a session is most visible: `/chat/live` shows turns that are RUNNING, so
* the placeholder is what the panel catches. Treating it as "not yet titled" lets the good name arrive
* on its own. Forcing `--title` on the run instead would fix the transient case by permanently
* replacing opencode's title with a truncated prompt — worse where it lasts longest.
*/
export const isPlaceholderOpenCodeTitle = (title: string | undefined): boolean => {
const trimmed = title?.trim();
return !trimmed || /^New session - \d{4}-\d{2}-\d{2}T[\d:.]+Z?$/.test(trimmed);
};
export async function listOpenCodeSessions(cwd?: string): Promise<ClaudeSessionSummary[]> {
try {
const { baseUrl } = await ensureServer();
@@ -35,7 +52,7 @@ export async function listOpenCodeSessions(cwd?: string): Promise<ClaudeSessionS
const updated = s.time?.updated ?? created;
return {
id: s.id,
title: s.title || '(untitled)',
title: isPlaceholderOpenCodeTitle(s.title) ? '(untitled)' : (s.title ?? '(untitled)'),
cwd: s.directory ?? '',
createdAt: new Date(created).toISOString(),
updatedAt: new Date(updated).toISOString(),