From 049600df721bd0b59d66d807394bb2c7d06db397 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 2 Aug 2026 12:57:51 +0000 Subject: [PATCH 1/6] fix: discover repository-local .agents skills in Settings Skills listing ignored the active project when the OpenCode client directory was unset, so project `.agents/skills` files were created but never shown. Prefer the active project path (matching Commands/Agents) and soft-fall back to it on skill API routes when directory is omitted. Co-authored-by: Serhii Dziupin --- .../ui/src/stores/useSkillsCatalogStore.ts | 36 ++-- packages/ui/src/stores/useSkillsStore.test.ts | 103 ++++++++++++ packages/ui/src/stores/useSkillsStore.ts | 107 +++++++----- .../web/server/lib/opencode/DOCUMENTATION.md | 4 + .../web/server/lib/opencode/skill-routes.js | 42 ++++- .../server/lib/opencode/skill-routes.test.js | 157 ++++++++++++++++++ .../web/server/lib/opencode/skills.test.js | 39 ++++- 7 files changed, 420 insertions(+), 68 deletions(-) create mode 100644 packages/ui/src/stores/useSkillsStore.test.ts create mode 100644 packages/web/server/lib/opencode/skill-routes.test.js diff --git a/packages/ui/src/stores/useSkillsCatalogStore.ts b/packages/ui/src/stores/useSkillsCatalogStore.ts index 139348fb..6585d152 100644 --- a/packages/ui/src/stores/useSkillsCatalogStore.ts +++ b/packages/ui/src/stores/useSkillsCatalogStore.ts @@ -15,6 +15,7 @@ import type { import { invalidateSkillsLoadCache, refreshSkillsAfterOpenCodeRestart, useSkillsStore } from '@/stores/useSkillsStore'; import { opencodeClient } from '@/lib/opencode/client'; +import { useProjectsStore } from '@/stores/useProjectsStore'; import { startConfigUpdate, finishConfigUpdate, updateConfigUpdateMessage } from '@/lib/configUpdate'; import { runtimeFetch } from '@/lib/runtime-fetch'; @@ -45,20 +46,21 @@ const getSkillsCatalogCacheKey = (directory: string | null): string => { return directory?.trim() || DEFAULT_SKILLS_CATALOG_CACHE_KEY; }; -const getCurrentDirectory = (): string | null => { - const opencodeDirectory = opencodeClient.getDirectory(); - if (typeof opencodeDirectory === 'string' && opencodeDirectory.trim().length > 0) { - return opencodeDirectory; - } - +const getRequestDirectory = (): string | null => { try { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const store = (window as any).__zustand_directory_store__; - if (store) { - return store.getState().currentDirectory; + const projectsStore = useProjectsStore.getState(); + const activeProject = projectsStore.getActiveProject?.(); + + if (activeProject?.path?.trim()) { + return activeProject.path.trim(); } - } catch { - // ignore + + const clientDir = opencodeClient.getDirectory(); + if (clientDir?.trim()) { + return clientDir.trim(); + } + } catch (err) { + console.warn('[SkillsCatalogStore] Error resolving config directory:', err); } return null; @@ -118,7 +120,7 @@ export const useSkillsCatalogStore = create()( setSelectedSource: (id) => set({ selectedSourceId: id }), loadCatalog: async (options) => { - const currentDirectory = getCurrentDirectory(); + const currentDirectory = getRequestDirectory(); const cacheKey = getSkillsCatalogCacheKey(currentDirectory); const now = Date.now(); const loadedAt = skillsCatalogLastLoadedAt.get(cacheKey) ?? 0; @@ -222,7 +224,7 @@ export const useSkillsCatalogStore = create()( set({ isLoadingSource: true, lastCatalogError: null }); try { - const currentDirectory = getCurrentDirectory(); + const currentDirectory = getRequestDirectory(); const refresh = options?.refresh ? '&refresh=true' : ''; const queryParams = currentDirectory ? `?directory=${encodeURIComponent(currentDirectory)}&sourceId=${encodeURIComponent(sourceId)}${refresh}` @@ -293,7 +295,7 @@ export const useSkillsCatalogStore = create()( set({ isLoadingMore: true }); try { - const currentDirectory = getCurrentDirectory(); + const currentDirectory = getRequestDirectory(); const parts = [`sourceId=${encodeURIComponent(selectedSourceId)}`]; if (currentDirectory) { parts.push(`directory=${encodeURIComponent(currentDirectory)}`); @@ -355,7 +357,7 @@ export const useSkillsCatalogStore = create()( scanRepo: async (request) => { set({ isScanning: true, lastScanError: null, scanResults: null }); try { - const currentDirectory = getCurrentDirectory(); + const currentDirectory = getRequestDirectory(); const queryParams = currentDirectory ? `?directory=${encodeURIComponent(currentDirectory)}` : ''; const response = await runtimeFetch(`/api/config/skills/scan${queryParams}`, { @@ -391,7 +393,7 @@ export const useSkillsCatalogStore = create()( const directoryOverride = typeof options?.directory === 'string' && options.directory.trim().length > 0 ? options.directory.trim() : null; - const currentDirectory = directoryOverride ?? getCurrentDirectory(); + const currentDirectory = directoryOverride ?? getRequestDirectory(); const queryParams = currentDirectory ? `?directory=${encodeURIComponent(currentDirectory)}` : ''; const response = await runtimeFetch(`/api/config/skills/install${queryParams}`, { diff --git a/packages/ui/src/stores/useSkillsStore.test.ts b/packages/ui/src/stores/useSkillsStore.test.ts new file mode 100644 index 00000000..010e5b55 --- /dev/null +++ b/packages/ui/src/stores/useSkillsStore.test.ts @@ -0,0 +1,103 @@ +import { beforeEach, describe, expect, mock, test } from 'bun:test'; + +const activeProjectPath = '/workspace/project-with-agents-skills'; + +let runtimeFetchCalls: Array<{ url: string; headers?: HeadersInit }> = []; +let runtimeFetchImpl: (url: string, init?: RequestInit) => Promise = async () => ( + new Response(JSON.stringify({ skills: [] }), { + headers: { 'Content-Type': 'application/json' }, + }) +); +let getDirectoryImpl: () => string | undefined = () => undefined; + +const runtimeFetchMock = async (url: string, init?: RequestInit) => { + runtimeFetchCalls.push({ url: String(url), headers: init?.headers }); + return runtimeFetchImpl(url, init); +}; + +mock.module('@/lib/opencode/client', () => ({ + opencodeClient: { + getDirectory: () => getDirectoryImpl(), + checkHealth: async () => true, + }, +})); + +mock.module('@/stores/useProjectsStore', () => ({ + useProjectsStore: { + getState: () => ({ + getActiveProject: () => ({ path: activeProjectPath }), + }), + }, +})); + +mock.module('@/lib/runtime-fetch', () => ({ + runtimeFetch: runtimeFetchMock, +})); + +mock.module('@/lib/background-network', () => ({ + runBackgroundNetworkTask: async (task: () => Promise) => task(), +})); + +mock.module('@/lib/configUpdate', () => ({ + startConfigUpdate: mock(() => undefined), + finishConfigUpdate: mock(() => undefined), + updateConfigUpdateMessage: mock(() => undefined), +})); + +mock.module('@/lib/configSync', () => ({ + emitConfigChange: mock(() => undefined), + scopeMatches: mock(() => false), + subscribeToConfigChanges: mock(() => () => undefined), +})); + +mock.module('./utils/safeStorage', () => ({ + createDeferredSafeJSONStorage: () => ({ + getItem: async () => null, + setItem: async () => undefined, + removeItem: async () => undefined, + }), +})); + +const { invalidateSkillsLoadCache, useSkillsStore } = await import('./useSkillsStore'); + +describe('useSkillsStore directory resolution', () => { + beforeEach(() => { + runtimeFetchCalls = []; + getDirectoryImpl = () => undefined; + runtimeFetchImpl = async () => new Response(JSON.stringify({ + skills: [{ + name: 'repo-local-skill', + path: `${activeProjectPath}/.agents/skills/repo-local-skill/SKILL.md`, + scope: 'project', + source: 'agents', + sources: { md: { description: 'Repository local' } }, + }], + }), { + headers: { 'Content-Type': 'application/json' }, + }); + + invalidateSkillsLoadCache(activeProjectPath); + useSkillsStore.setState({ + selectedSkillName: null, + skills: [], + isLoading: false, + skillDraft: null, + }); + }); + + test('loadSkills scopes discovery to the active project even when client directory is unset', async () => { + const loaded = await useSkillsStore.getState().loadSkills(); + + expect(loaded).toBe(true); + expect(runtimeFetchCalls.length).toBe(1); + expect(runtimeFetchCalls[0]?.url).toContain(`directory=${encodeURIComponent(activeProjectPath)}`); + expect(useSkillsStore.getState().skills).toEqual([{ + name: 'repo-local-skill', + path: `${activeProjectPath}/.agents/skills/repo-local-skill/SKILL.md`, + scope: 'project', + source: 'agents', + description: 'Repository local', + group: undefined, + }]); + }); +}); diff --git a/packages/ui/src/stores/useSkillsStore.ts b/packages/ui/src/stores/useSkillsStore.ts index 2ffe69dd..eab17af2 100644 --- a/packages/ui/src/stores/useSkillsStore.ts +++ b/packages/ui/src/stores/useSkillsStore.ts @@ -10,23 +10,29 @@ import { import { createDeferredSafeJSONStorage } from "./utils/safeStorage"; import { runtimeFetch } from "@/lib/runtime-fetch"; import { runBackgroundNetworkTask } from "@/lib/background-network"; +import { useProjectsStore } from "@/stores/useProjectsStore"; import { opencodeClient } from '@/lib/opencode/client'; -const getCurrentDirectory = (): string | null => { - const opencodeDirectory = opencodeClient.getDirectory(); - if (typeof opencodeDirectory === 'string' && opencodeDirectory.trim().length > 0) { - return opencodeDirectory; - } - +// Prefer the active project path so Settings/Skills discovery matches the +// project selector (and Commands/Agents). Falling back only to the session +// directory misses repository-local `.agents/skills` when the client directory +// is unset or points elsewhere while an active project exists. +const getRequestDirectory = (): string | null => { try { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const store = (window as any).__zustand_directory_store__; - if (store) { - return store.getState().currentDirectory; + const projectsStore = useProjectsStore.getState(); + const activeProject = projectsStore.getActiveProject?.(); + + if (activeProject?.path?.trim()) { + return activeProject.path.trim(); } - } catch { - // ignore + + const clientDir = opencodeClient.getDirectory(); + if (clientDir?.trim()) { + return clientDir.trim(); + } + } catch (err) { + console.warn('[SkillsStore] Error resolving config directory:', err); } return null; @@ -169,7 +175,7 @@ const getSkillsCacheKey = (directory: string | null): string => { return directory?.trim() || DEFAULT_SKILLS_CACHE_KEY; }; -export const invalidateSkillsLoadCache = (directory: string | null = getCurrentDirectory()) => { +export const invalidateSkillsLoadCache = (directory: string | null = getRequestDirectory()) => { skillsLastLoadedAt.delete(getSkillsCacheKey(directory)); }; @@ -198,8 +204,8 @@ export const useSkillsStore = create()( }, loadSkills: async () => { - const currentDirectory = getCurrentDirectory(); - const cacheKey = getSkillsCacheKey(currentDirectory); + const directory = getRequestDirectory(); + const cacheKey = getSkillsCacheKey(directory); const now = Date.now(); const loadedAt = skillsLastLoadedAt.get(cacheKey) ?? 0; const hasCachedSkills = get().skills.length > 0; @@ -220,9 +226,12 @@ export const useSkillsStore = create()( for (let attempt = 0; attempt < 3; attempt++) { try { - const queryParams = currentDirectory ? `?directory=${encodeURIComponent(currentDirectory)}` : ''; + const queryParams = directory ? `?directory=${encodeURIComponent(directory)}` : ''; - const response = await runBackgroundNetworkTask(() => runtimeFetch(`/api/config/skills${queryParams}`, { priority: 'low' })); + const response = await runBackgroundNetworkTask(() => runtimeFetch(`/api/config/skills${queryParams}`, { + priority: 'low', + headers: directory ? { 'x-opencode-directory': directory } : undefined, + })); if (!response.ok) { throw new Error(`Failed to list skills: ${response.status}`); } @@ -263,10 +272,12 @@ export const useSkillsStore = create()( getSkillDetail: async (name: string) => { try { - const currentDirectory = getCurrentDirectory(); - const queryParams = currentDirectory ? `?directory=${encodeURIComponent(currentDirectory)}` : ''; + const directory = getRequestDirectory(); + const queryParams = directory ? `?directory=${encodeURIComponent(directory)}` : ''; - const response = await runtimeFetch(`/api/config/skills/${encodeURIComponent(name)}${queryParams}`); + const response = await runtimeFetch(`/api/config/skills/${encodeURIComponent(name)}${queryParams}`, { + headers: directory ? { 'x-opencode-directory': directory } : undefined, + }); if (!response.ok) { return null; } @@ -291,12 +302,15 @@ export const useSkillsStore = create()( if (config.source) skillConfig.source = config.source; if (config.supportingFiles) skillConfig.supportingFiles = config.supportingFiles; - const currentDirectory = getCurrentDirectory(); - const queryParams = currentDirectory ? `?directory=${encodeURIComponent(currentDirectory)}` : ''; + const directory = getRequestDirectory(); + const queryParams = directory ? `?directory=${encodeURIComponent(directory)}` : ''; const response = await runtimeFetch(`/api/config/skills/${encodeURIComponent(config.name)}${queryParams}`, { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { + 'Content-Type': 'application/json', + ...(directory ? { 'x-opencode-directory': directory } : {}), + }, body: JSON.stringify(skillConfig) }); @@ -307,7 +321,7 @@ export const useSkillsStore = create()( } const needsReload = payload?.requiresReload ?? false; - invalidateSkillsLoadCache(currentDirectory); + invalidateSkillsLoadCache(directory); if (needsReload) { requiresReload = true; await refreshSkillsAfterOpenCodeRestart({ @@ -342,12 +356,15 @@ export const useSkillsStore = create()( if (config.supportingFiles !== undefined) skillConfig.supportingFiles = config.supportingFiles; if (config.targetPath !== undefined) skillConfig.targetPath = config.targetPath; - const currentDirectory = getCurrentDirectory(); - const queryParams = currentDirectory ? `?directory=${encodeURIComponent(currentDirectory)}` : ''; + const directory = getRequestDirectory(); + const queryParams = directory ? `?directory=${encodeURIComponent(directory)}` : ''; const response = await runtimeFetch(`/api/config/skills/${encodeURIComponent(name)}${queryParams}`, { method: 'PATCH', - headers: { 'Content-Type': 'application/json' }, + headers: { + 'Content-Type': 'application/json', + ...(directory ? { 'x-opencode-directory': directory } : {}), + }, body: JSON.stringify(skillConfig) }); @@ -358,7 +375,7 @@ export const useSkillsStore = create()( } const needsReload = payload?.requiresReload ?? false; - invalidateSkillsLoadCache(currentDirectory); + invalidateSkillsLoadCache(directory); if (needsReload) { requiresReload = true; await refreshSkillsAfterOpenCodeRestart({ @@ -386,11 +403,12 @@ export const useSkillsStore = create()( startConfigUpdate("Deleting skill..."); let requiresReload = false; try { - const currentDirectory = getCurrentDirectory(); - const queryParams = currentDirectory ? `?directory=${encodeURIComponent(currentDirectory)}` : ''; + const directory = getRequestDirectory(); + const queryParams = directory ? `?directory=${encodeURIComponent(directory)}` : ''; const response = await runtimeFetch(`/api/config/skills/${encodeURIComponent(name)}${queryParams}`, { - method: 'DELETE' + method: 'DELETE', + headers: directory ? { 'x-opencode-directory': directory } : undefined, }); const payload = await response.json().catch(() => null); @@ -400,7 +418,7 @@ export const useSkillsStore = create()( } const needsReload = payload?.requiresReload ?? false; - invalidateSkillsLoadCache(currentDirectory); + invalidateSkillsLoadCache(directory); if (needsReload) { requiresReload = true; await refreshSkillsAfterOpenCodeRestart({ @@ -436,11 +454,12 @@ export const useSkillsStore = create()( readSupportingFile: async (skillName: string, filePath: string) => { try { - const currentDirectory = getCurrentDirectory(); - const queryParams = currentDirectory ? `&directory=${encodeURIComponent(currentDirectory)}` : ''; + const directory = getRequestDirectory(); + const queryParams = directory ? `&directory=${encodeURIComponent(directory)}` : ''; const response = await runtimeFetch( - `/api/config/skills/${encodeURIComponent(skillName)}/files/${encodeURIComponent(filePath)}?${queryParams.slice(1)}` + `/api/config/skills/${encodeURIComponent(skillName)}/files/${encodeURIComponent(filePath)}?${queryParams.slice(1)}`, + { headers: directory ? { 'x-opencode-directory': directory } : undefined }, ); if (!response.ok) { return null; @@ -455,14 +474,17 @@ export const useSkillsStore = create()( writeSupportingFile: async (skillName: string, filePath: string, content: string) => { try { - const currentDirectory = getCurrentDirectory(); - const queryParams = currentDirectory ? `?directory=${encodeURIComponent(currentDirectory)}` : ''; + const directory = getRequestDirectory(); + const queryParams = directory ? `?directory=${encodeURIComponent(directory)}` : ''; const response = await runtimeFetch( `/api/config/skills/${encodeURIComponent(skillName)}/files/${encodeURIComponent(filePath)}${queryParams}`, { method: 'PUT', - headers: { 'Content-Type': 'application/json' }, + headers: { + 'Content-Type': 'application/json', + ...(directory ? { 'x-opencode-directory': directory } : {}), + }, body: JSON.stringify({ content }) } ); @@ -475,12 +497,15 @@ export const useSkillsStore = create()( deleteSupportingFile: async (skillName: string, filePath: string) => { try { - const currentDirectory = getCurrentDirectory(); - const queryParams = currentDirectory ? `?directory=${encodeURIComponent(currentDirectory)}` : ''; + const directory = getRequestDirectory(); + const queryParams = directory ? `?directory=${encodeURIComponent(directory)}` : ''; const response = await runtimeFetch( `/api/config/skills/${encodeURIComponent(skillName)}/files/${encodeURIComponent(filePath)}${queryParams}`, - { method: 'DELETE' } + { + method: 'DELETE', + headers: directory ? { 'x-opencode-directory': directory } : undefined, + } ); return response.ok; diff --git a/packages/web/server/lib/opencode/DOCUMENTATION.md b/packages/web/server/lib/opencode/DOCUMENTATION.md index c57ecb8b..becb92d8 100644 --- a/packages/web/server/lib/opencode/DOCUMENTATION.md +++ b/packages/web/server/lib/opencode/DOCUMENTATION.md @@ -353,6 +353,10 @@ an authoritative loopback callback URL even when OpenChamber binds port `0`. - Skills config CRUD and metadata under `/api/config/skills*` - Skills catalog listing/source pagination, scan, and install routes - Supporting skill file read/write/delete routes + - Directory resolution prefers an explicit request directory, then soft-falls + back to the active project / `lastDirectory` so repository-local + `.agents/skills` and `.opencode/skills` remain discoverable when the client + omits `directory`. Requests without any project still list user-scoped skills. ## Public exports (proxy.js) - `registerOpenCodeProxy(app, dependencies)`: registers OpenCode proxy routes and middleware. diff --git a/packages/web/server/lib/opencode/skill-routes.js b/packages/web/server/lib/opencode/skill-routes.js index b66680a5..ad8dae59 100644 --- a/packages/web/server/lib/opencode/skill-routes.js +++ b/packages/web/server/lib/opencode/skill-routes.js @@ -200,9 +200,33 @@ export const registerSkillRoutes = (app, dependencies) => { return null; }; + // Prefer an explicit request directory, then soft-fallback to the active + // project / lastDirectory so repository-local skills stay visible when the + // client omits `directory` (create already used resolveProjectDirectory). + const resolveSkillsDirectory = async (req) => { + const optional = await resolveOptionalProjectDirectory(req); + if (optional.error) { + return optional; + } + if (optional.directory) { + return optional; + } + + try { + const fallback = await resolveProjectDirectory(req); + if (fallback.directory) { + return { directory: fallback.directory, error: null }; + } + } catch { + // ignore — listing user-scoped skills without a project is valid + } + + return { directory: null, error: null }; + }; + app.get('/api/config/skills', async (req, res) => { try { - const { directory, error } = await resolveOptionalProjectDirectory(req); + const { directory, error } = await resolveSkillsDirectory(req); if (error) { return res.status(400).json({ error }); } @@ -257,7 +281,7 @@ export const registerSkillRoutes = (app, dependencies) => { app.get('/api/config/skills/catalog/source', async (req, res) => { try { - const { directory, error } = await resolveOptionalProjectDirectory(req); + const { directory, error } = await resolveSkillsDirectory(req); if (error) { return res.status(400).json({ ok: false, error: { kind: 'invalidSource', message: error } }); } @@ -518,7 +542,7 @@ export const registerSkillRoutes = (app, dependencies) => { app.get('/api/config/skills/:name', async (req, res) => { try { const skillName = req.params.name; - const { directory, error } = await resolveOptionalProjectDirectory(req); + const { directory, error } = await resolveSkillsDirectory(req); if (error) { return res.status(400).json({ error }); } @@ -546,7 +570,7 @@ export const registerSkillRoutes = (app, dependencies) => { if (isUnsafeSkillRelativePath(filePath)) { return res.status(400).json({ error: 'Invalid file path' }); } - const { directory, error } = await resolveOptionalProjectDirectory(req); + const { directory, error } = await resolveSkillsDirectory(req); if (error) { return res.status(400).json({ error }); } @@ -579,7 +603,7 @@ export const registerSkillRoutes = (app, dependencies) => { const { scope, source: skillSource, ...config } = req.body; const { directory, error } = scope === SKILL_SCOPE.PROJECT ? await resolveProjectDirectory(req) - : await resolveOptionalProjectDirectory(req); + : await resolveSkillsDirectory(req); if (error || (scope === SKILL_SCOPE.PROJECT && !directory)) { return res.status(400).json({ error: error || 'Project skill creation requires a directory' }); } @@ -606,7 +630,7 @@ export const registerSkillRoutes = (app, dependencies) => { try { const skillName = req.params.name; const updates = req.body; - const { directory, error } = await resolveOptionalProjectDirectory(req); + const { directory, error } = await resolveSkillsDirectory(req); if (error) { return res.status(400).json({ error }); } @@ -637,7 +661,7 @@ export const registerSkillRoutes = (app, dependencies) => { return res.status(400).json({ error: 'Invalid file path' }); } const { content } = req.body; - const { directory, error } = await resolveOptionalProjectDirectory(req); + const { directory, error } = await resolveSkillsDirectory(req); if (error) { return res.status(400).json({ error }); } @@ -671,7 +695,7 @@ export const registerSkillRoutes = (app, dependencies) => { if (isUnsafeSkillRelativePath(filePath)) { return res.status(400).json({ error: 'Invalid file path' }); } - const { directory, error } = await resolveOptionalProjectDirectory(req); + const { directory, error } = await resolveSkillsDirectory(req); if (error) { return res.status(400).json({ error }); } @@ -701,7 +725,7 @@ export const registerSkillRoutes = (app, dependencies) => { app.delete('/api/config/skills/:name', async (req, res) => { try { const skillName = req.params.name; - const { directory, error } = await resolveOptionalProjectDirectory(req); + const { directory, error } = await resolveSkillsDirectory(req); if (error) { return res.status(400).json({ error }); } diff --git a/packages/web/server/lib/opencode/skill-routes.test.js b/packages/web/server/lib/opencode/skill-routes.test.js new file mode 100644 index 00000000..c83f7f84 --- /dev/null +++ b/packages/web/server/lib/opencode/skill-routes.test.js @@ -0,0 +1,157 @@ +import { afterEach, describe, expect, it } from 'vitest'; +import express from 'express'; +import fs from 'fs'; +import os from 'os'; +import path from 'path'; +import { registerSkillRoutes } from './skill-routes.js'; +import { + createSkill, + deleteSkill, + discoverSkills, + getSkillSources, + mergeDiscoveredSkills, + updateSkill, +} from './skills.js'; +import { + SKILL_DIR, + SKILL_SCOPE, + deleteSkillSupportingFile, + readSkillSupportingFile, + writeSkillSupportingFile, +} from './shared.js'; + +const createTempProject = () => { + const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'oc-skill-routes-')); + fs.mkdirSync(path.join(projectRoot, '.git')); + return projectRoot; +}; + +const startSkillsApp = ({ projectRoot }) => { + const app = express(); + app.use(express.json()); + + registerSkillRoutes(app, { + fs, + path, + os, + resolveProjectDirectory: async () => ({ directory: projectRoot, error: null }), + resolveOptionalProjectDirectory: async (req) => { + const queryDirectory = Array.isArray(req.query?.directory) + ? req.query.directory[0] + : req.query?.directory; + if (!queryDirectory) { + return { directory: null, error: null }; + } + return { directory: String(queryDirectory), error: null }; + }, + readSettingsFromDisk: async () => ({}), + sanitizeSkillCatalogs: (value) => value, + isUnsafeSkillRelativePath: () => false, + refreshOpenCodeAfterConfigChange: async () => {}, + clientReloadDelayMs: 0, + buildOpenCodeUrl: () => 'http://127.0.0.1:9/', + getOpenCodeAuthHeaders: () => ({}), + getOpenCodePort: () => 0, + getSkillSources, + discoverSkills, + mergeDiscoveredSkills, + createSkill, + updateSkill, + deleteSkill, + readSkillSupportingFile, + writeSkillSupportingFile, + deleteSkillSupportingFile, + SKILL_SCOPE, + SKILL_DIR, + getCuratedSkillsSources: () => [], + getCacheKey: () => 'k', + getCachedScan: () => null, + setCachedScan: () => {}, + parseSkillRepoSource: () => ({ ok: false }), + scanSkillsRepository: async () => ({ ok: false }), + installSkillsFromRepository: async () => ({ ok: false }), + scanClawdHubPage: async () => ({ ok: false }), + installSkillsFromClawdHub: async () => ({ ok: false }), + isClawdHubSource: () => false, + getProfiles: () => [], + getProfile: () => null, + }); + + const server = app.listen(0); + const { port } = server.address(); + return { + baseUrl: `http://127.0.0.1:${port}`, + close: () => new Promise((resolve, reject) => { + server.close((error) => (error ? reject(error) : resolve())); + }), + }; +}; + +describe('skill-routes directory soft fallback', () => { + /** @type {string | null} */ + let projectRoot = null; + /** @type {{ close: () => Promise } | null} */ + let appHandle = null; + + afterEach(async () => { + if (appHandle) { + await appHandle.close(); + appHandle = null; + } + if (projectRoot) { + fs.rmSync(projectRoot, { recursive: true, force: true }); + projectRoot = null; + } + }); + + it('lists repository-local .agents skills after create even when list omits directory', async () => { + projectRoot = createTempProject(); + appHandle = startSkillsApp({ projectRoot }); + + const createResponse = await fetch(`${appHandle.baseUrl}/api/config/skills/repo-local-skill`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + description: 'Created without list directory', + instructions: 'Do the thing.', + scope: 'project', + source: 'agents', + }), + }); + expect(createResponse.status).toBe(200); + expect(fs.existsSync(path.join(projectRoot, '.agents', 'skills', 'repo-local-skill', 'SKILL.md'))).toBe(true); + + const listResponse = await fetch(`${appHandle.baseUrl}/api/config/skills`); + expect(listResponse.status).toBe(200); + const payload = await listResponse.json(); + expect(payload.skills.map((skill) => skill.name)).toContain('repo-local-skill'); + const skill = payload.skills.find((entry) => entry.name === 'repo-local-skill'); + expect(skill.scope).toBe('project'); + expect(skill.source).toBe('agents'); + }); + + it('lists manually created repository-local .agents skills via active-project fallback', async () => { + projectRoot = createTempProject(); + const skillDir = path.join(projectRoot, '.agents', 'skills', 'manual-repo-skill'); + fs.mkdirSync(skillDir, { recursive: true }); + fs.writeFileSync( + path.join(skillDir, 'SKILL.md'), + [ + '---', + 'name: manual-repo-skill', + 'description: Manual repository skill', + '---', + '', + 'Instructions', + '', + ].join('\n'), + 'utf8', + ); + + appHandle = startSkillsApp({ projectRoot }); + const listResponse = await fetch(`${appHandle.baseUrl}/api/config/skills`); + expect(listResponse.status).toBe(200); + const payload = await listResponse.json(); + expect(payload.skills.map((skill) => skill.name)).toContain('manual-repo-skill'); + }); +}); diff --git a/packages/web/server/lib/opencode/skills.test.js b/packages/web/server/lib/opencode/skills.test.js index 95f9c722..ffa3a6f2 100644 --- a/packages/web/server/lib/opencode/skills.test.js +++ b/packages/web/server/lib/opencode/skills.test.js @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import fsPromises from 'fs/promises'; import os from 'os'; import path from 'path'; -import { getSkillSources, mergeDiscoveredSkills } from './skills.js'; +import { discoverSkills, getSkillSources, mergeDiscoveredSkills } from './skills.js'; describe('skills', () => { it('merges locally discovered skills missing from OpenCode live discovery', () => { @@ -24,6 +24,43 @@ describe('skills', () => { ]); }); + it('discovers repository-local .agents skills for the project directory', async () => { + const tempRoot = await fsPromises.mkdtemp(path.join(os.tmpdir(), 'oc-project-agents-')); + const skillDir = path.join(tempRoot, '.agents', 'skills', 'repo-local-skill'); + const skillPath = path.join(skillDir, 'SKILL.md'); + + try { + await fsPromises.mkdir(skillDir, { recursive: true }); + await fsPromises.mkdir(path.join(tempRoot, '.git')); + await fsPromises.writeFile( + skillPath, + [ + '---', + 'name: repo-local-skill', + 'description: Repository-local agents skill', + '---', + '', + 'Use this skill in this repository.', + '', + ].join('\n'), + 'utf8', + ); + + const discovered = discoverSkills(tempRoot); + const match = discovered.find((skill) => skill.name === 'repo-local-skill'); + + expect(match).toEqual({ + name: 'repo-local-skill', + path: skillPath, + scope: 'project', + source: 'agents', + description: 'Repository-local agents skill', + }); + } finally { + await fsPromises.rm(tempRoot, { recursive: true, force: true }); + } + }); + it('resolves built-in OpenCode skill content without parsing virtual locations as files', () => { const sources = getSkillSources( 'customize-opencode', From aa1875b6f08d5d071b9b31da0ba5e5cfc0db5b44 Mon Sep 17 00:00:00 2001 From: Serhii Dziupin Date: Sun, 2 Aug 2026 16:03:32 +0300 Subject: [PATCH 2/6] perf: start terminal sessions before renderer mounts --- .opencode/screenshots/terminal-final.png | Bin 0 -> 80461 bytes .../screenshots/terminal-parallel-start.png | Bin 0 -> 78896 bytes .opencode/screenshots/terminal-reset.png | Bin 0 -> 91555 bytes .opencode/screenshots/terminal-startup.png | Bin 0 -> 11961 bytes bun.lock | 8 +-- .../components/terminal/TerminalViewport.tsx | 52 +++++++++++++++++- .../ui/src/components/views/TerminalView.tsx | 41 +++++++++----- .../__tests__/terminalViewportRemount.test.ts | 29 +++++++++- .../web/server/lib/terminal/DOCUMENTATION.md | 1 + 9 files changed, 108 insertions(+), 23 deletions(-) create mode 100644 .opencode/screenshots/terminal-final.png create mode 100644 .opencode/screenshots/terminal-parallel-start.png create mode 100644 .opencode/screenshots/terminal-reset.png create mode 100644 .opencode/screenshots/terminal-startup.png diff --git a/.opencode/screenshots/terminal-final.png b/.opencode/screenshots/terminal-final.png new file mode 100644 index 0000000000000000000000000000000000000000..ab8d0cbb59285a69277666a93a226bbd147441df GIT binary patch literal 80461 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 literal 0 HcmV?d00001 diff --git a/.opencode/screenshots/terminal-parallel-start.png b/.opencode/screenshots/terminal-parallel-start.png new file mode 100644 index 0000000000000000000000000000000000000000..84c137bafef92285e2ba4307c4228f9421d8fdbf GIT binary patch literal 78896 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

^l{SU36zHLvdHa<~LHM}R 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 literal 0 HcmV?d00001 diff --git a/.opencode/screenshots/terminal-reset.png b/.opencode/screenshots/terminal-reset.png new file mode 100644 index 0000000000000000000000000000000000000000..447a9da0975256b6713392577df0f151c94c947c GIT binary patch literal 91555 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�Y0k?K*v{L1BG)A`N2d2in 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 j^h3Y^`hh|=&2Lm)bAS9_l{H+- literal 0 HcmV?d00001 diff --git a/bun.lock b/bun.lock index 070da26b..e9ab4100 100644 --- a/bun.lock +++ b/bun.lock @@ -97,7 +97,7 @@ }, "packages/electron": { "name": "@openchamber/electron", - "version": "1.17.1", + "version": "1.17.2", "dependencies": { "@openchamber/web": "workspace:*", "better-sqlite3": "^12.10.0", @@ -134,7 +134,7 @@ }, "packages/ui": { "name": "@openchamber/ui", - "version": "1.17.1", + "version": "1.17.2", "dependencies": { "@aparajita/capacitor-secure-storage": "^8.0.0", "@base-ui/react": "^1.4.0", @@ -239,7 +239,7 @@ }, "packages/vscode": { "name": "openchamber", - "version": "1.17.1", + "version": "1.17.2", "dependencies": { "@openchamber/ui": "workspace:*", "@opencode-ai/sdk": "1.18.11", @@ -262,7 +262,7 @@ }, "packages/web": { "name": "@openchamber/web", - "version": "1.17.1", + "version": "1.17.2", "bin": { "openchamber": "./bin/cli.js", }, diff --git a/packages/ui/src/components/terminal/TerminalViewport.tsx b/packages/ui/src/components/terminal/TerminalViewport.tsx index 90b834d3..cd86497f 100644 --- a/packages/ui/src/components/terminal/TerminalViewport.tsx +++ b/packages/ui/src/components/terminal/TerminalViewport.tsx @@ -18,6 +18,42 @@ import type { TerminalChunk } from '@/stores/useTerminalStore'; let ghosttyPromise: Promise | null = null; const loadGhostty = (): Promise => ghosttyPromise ??= Ghostty.load(); +type TerminalSize = { cols: number; rows: number }; + +const getProvisionalTerminalSize = ( + container: HTMLDivElement, + fontFamily: string, + fontSize: number, +): TerminalSize | null => { + if (typeof window === 'undefined' || typeof document === 'undefined') return null; + + const context = document.createElement('canvas').getContext('2d'); + if (!context || container.clientWidth < 24 || container.clientHeight < 24) return null; + + context.font = `${fontSize}px ${fontFamily}`; + const metrics = context.measureText('M'); + const cellWidth = Math.ceil(metrics.width); + const cellHeight = Math.ceil( + (metrics.actualBoundingBoxAscent || fontSize * 0.8) + + (metrics.actualBoundingBoxDescent || fontSize * 0.2), + ) + 2; + if (cellWidth < 1 || cellHeight < 1) return null; + + const style = window.getComputedStyle(container); + const horizontalPadding = + (Number.parseInt(style.paddingLeft, 10) || 0) + + (Number.parseInt(style.paddingRight, 10) || 0); + const verticalPadding = + (Number.parseInt(style.paddingTop, 10) || 0) + + (Number.parseInt(style.paddingBottom, 10) || 0); + + // Match Ghostty FitAddon's 15px scrollbar reservation and minimum dimensions. + return { + cols: Math.max(2, Math.floor((container.clientWidth - horizontalPadding - 15) / cellWidth)), + rows: Math.max(1, Math.floor((container.clientHeight - verticalPadding) / cellHeight)), + }; +}; + export type TerminalController = { focus: () => void; fit: () => void; @@ -47,7 +83,8 @@ const TerminalViewport = React.forwardRef(({ const fitRef = React.useRef(null); const inputRef = React.useRef(onInput); const resizeRef = React.useRef(onResize); - const lastSizeRef = React.useRef<{ cols: number; rows: number } | null>(null); + const lastSizeRef = React.useRef(null); + const provisionalSizeRef = React.useRef(null); const lastChunkRef = React.useRef(null); const writeQueueRef = React.useRef(''); const outputRewriteCarryRef = React.useRef(''); @@ -65,6 +102,14 @@ const TerminalViewport = React.forwardRef(({ visibleRef.current = isVisible; safeResetRef.current = getGhosttySafeResetSequence(theme.background); + React.useLayoutEffect(() => { + const container = containerRef.current; + if (!container) return; + const size = getProvisionalTerminalSize(container, fontFamily, fontSize); + provisionalSizeRef.current = size; + if (size) resizeRef.current(size.cols, size.rows); + }, [fontFamily, fontSize]); + const fit = React.useCallback(() => { const container = containerRef.current; const terminal = terminalRef.current; @@ -168,7 +213,10 @@ const TerminalViewport = React.forwardRef(({ loadGhostty().then((ghostty) => { if (disposed) return; - terminal = new GhosttyTerminal(getGhosttyTerminalOptions(fontFamily, fontSize, theme, ghostty, false)); + terminal = new GhosttyTerminal({ + ...getGhosttyTerminalOptions(fontFamily, fontSize, theme, ghostty, false), + ...(provisionalSizeRef.current ?? {}), + }); const fitAddon = new FitAddon(); terminal.loadAddon(fitAddon); terminal.open(container); diff --git a/packages/ui/src/components/views/TerminalView.tsx b/packages/ui/src/components/views/TerminalView.tsx index a373f880..0c8e6465 100644 --- a/packages/ui/src/components/views/TerminalView.tsx +++ b/packages/ui/src/components/views/TerminalView.tsx @@ -26,6 +26,8 @@ type TerminalViewProps = { visible?: boolean; }; +const FALLBACK_TERMINAL_SIZE = { cols: 80, rows: 24 } as const; + export const TerminalView: React.FC = ({ visible }) => { const { t } = useI18n(); const { terminal, runtime } = useRuntimeAPIs(); @@ -109,7 +111,6 @@ export const TerminalView: React.FC = ({ visible }) => { const [isReconnectPending, setIsReconnectPending] = React.useState(false); const [activeModifier, setActiveModifier] = React.useState(null); const [isRestarting, setIsRestarting] = React.useState(false); - const [hasViewportSize, setHasViewportSize] = React.useState(false); const streamCleanupRef = React.useRef<(() => void) | null>(null); const activeTerminalIdRef = React.useRef(null); @@ -118,7 +119,7 @@ export const TerminalView: React.FC = ({ visible }) => { const directoryRef = React.useRef(effectiveDirectory); const terminalControllerRef = React.useRef(null); const lastViewportSizeRef = React.useRef<{ cols: number; rows: number } | null>(null); - const isTerminalVisibleRef = React.useRef(false); + const pendingTerminalCreatesRef = React.useRef(new Set()); const previewScanTailRef = React.useRef(''); const pendingPreviewProbeUrlsRef = React.useRef>(new Set()); const previewProbeGenerationRef = React.useRef(0); @@ -157,10 +158,6 @@ export const TerminalView: React.FC = ({ visible }) => { } }, [isTerminalVisible]); - React.useEffect(() => { - isTerminalVisibleRef.current = isTerminalVisible; - }, [isTerminalVisible]); - React.useEffect(() => { terminalIdRef.current = terminalSessionId; }, [terminalSessionId]); @@ -441,11 +438,17 @@ export const TerminalView: React.FC = ({ visible }) => { return; } - const size = lastViewportSizeRef.current; - if (!size && isTerminalVisibleRef.current) { + const createKey = `${directory}\u0000${tabId}`; + if (pendingTerminalCreatesRef.current.has(createKey)) { return; } + // Launch the shell while Ghostty is still loading and fitting. + // The backend accepts 80x24, then receives the measured size as + // soon as the viewport is ready. + const initialSize = lastViewportSizeRef.current ?? FALLBACK_TERMINAL_SIZE; + pendingTerminalCreatesRef.current.add(createKey); + setConnectionError(null); setIsFatalError(false); setIsReconnectPending(false); @@ -454,8 +457,8 @@ export const TerminalView: React.FC = ({ visible }) => { const session = await terminal.createSession({ cwd: directory, sessionId: tabId, - cols: size?.cols, - rows: size?.rows, + cols: initialSize.cols, + rows: initialSize.rows, shell: terminalShell, loginShell: terminalLoginShell, ...terminalAppearanceRef.current, @@ -476,6 +479,14 @@ export const TerminalView: React.FC = ({ visible }) => { setTabSessionId(directory, tabId, session.sessionId); if (!stillActive) return; + + const viewportSize = lastViewportSizeRef.current; + if ( + viewportSize && + (viewportSize.cols !== initialSize.cols || viewportSize.rows !== initialSize.rows) + ) { + void terminal.resize({ sessionId: session.sessionId, ...viewportSize }).catch(() => {}); + } terminalId = session.sessionId; } catch (error) { if (!cancelled) { @@ -489,6 +500,8 @@ export const TerminalView: React.FC = ({ visible }) => { setConnecting(directory, tabId, false); } return; + } finally { + pendingTerminalCreatesRef.current.delete(createKey); } } @@ -513,7 +526,6 @@ export const TerminalView: React.FC = ({ visible }) => { terminalLifecycle, activeTabId, hasOpenedTerminalViewport, - hasViewportSize, enableTabs, terminalHydrated, ensureDirectory, @@ -568,7 +580,7 @@ export const TerminalView: React.FC = ({ visible }) => { resetTerminalPreviewScan(); try { - const size = lastViewportSizeRef.current ?? { cols: 80, rows: 24 }; + const size = lastViewportSizeRef.current ?? FALLBACK_TERMINAL_SIZE; const restarted = await terminal.restartSession(originalSessionId, { cwd: effectiveDirectory, shell: terminalShell, loginShell: terminalLoginShell, ...size, ...terminalAppearanceRef.current }); const owningTab = useTerminalStore.getState().getDirectoryState(effectiveDirectory)?.tabs.find((tab) => tab.id === tabId); if (owningTab?.terminalSessionId !== originalSessionId) return; @@ -696,11 +708,10 @@ export const TerminalView: React.FC = ({ visible }) => { const previous = lastViewportSizeRef.current; if (!previous) { lastViewportSizeRef.current = { cols, rows }; - if (!terminalIdRef.current) setHasViewportSize(true); } else if (previous.cols !== cols || previous.rows !== rows) { lastViewportSizeRef.current = { cols, rows }; } - if (!isTerminalVisibleRef.current) { + if (!isTerminalVisible) { return; } const terminalId = terminalIdRef.current; @@ -709,7 +720,7 @@ export const TerminalView: React.FC = ({ visible }) => { }); }, - [terminal] + [isTerminalVisible, terminal] ); const handleModifierToggle = React.useCallback( diff --git a/packages/ui/src/components/views/__tests__/terminalViewportRemount.test.ts b/packages/ui/src/components/views/__tests__/terminalViewportRemount.test.ts index f33bd2cd..6b03ec22 100644 --- a/packages/ui/src/components/views/__tests__/terminalViewportRemount.test.ts +++ b/packages/ui/src/components/views/__tests__/terminalViewportRemount.test.ts @@ -2,8 +2,8 @@ * Regression guard for slow terminal opening on Linux. * * `TerminalViewport` is keyed by `terminalViewportKey`. That key used to include - * the PTY session id, which is null until `createSession` resolves. Because the - * viewport must mount first to report its size before a session can be created, + * the PTY session id, which is null until `createSession` resolves. Historically, + * the viewport had to mount first to report its size before session creation, so * every terminal open built a Ghostty terminal (WASM VT + 2D canvas renderer + * font atlas), threw it away when the session id arrived, and built a second one. * The same churn repeated on reconnect and on every incidental session-id change, @@ -12,6 +12,8 @@ * * Viewport identity must therefore be directory + tab only. Session changes are * handled by the chunk replay path, which resets the existing terminal in place. + * New sessions start concurrently with a container-derived size (or 80x24) and + * resize after their viewport fits. */ import { describe, expect, test } from 'bun:test'; import { readFileSync } from 'node:fs'; @@ -61,4 +63,27 @@ describe('terminal viewport remount guard', () => { expect(terminalViewSource).toContain('getBuffer('); expect(terminalViewSource).not.toContain('activeTab?.bufferChunks'); }); + + test('starts the PTY before Ghostty reports its first viewport size', () => { + expect(terminalViewSource).toContain('const FALLBACK_TERMINAL_SIZE = { cols: 80, rows: 24 } as const;'); + expect(terminalViewSource).toContain('const initialSize = lastViewportSizeRef.current ?? FALLBACK_TERMINAL_SIZE;'); + expect(terminalViewSource).not.toContain('if (!size && isTerminalVisibleRef.current)'); + expect(terminalViewSource).toContain('cols: initialSize.cols'); + expect(terminalViewSource).toContain('rows: initialSize.rows'); + expect(terminalViewSource).toContain('void terminal.resize({ sessionId: session.sessionId, ...viewportSize })'); + expect(terminalViewSource).toContain('if (!isTerminalVisible) {'); + expect(terminalViewSource).not.toContain('isTerminalVisibleRef'); + }); + + test('deduplicates create attempts while the viewport layout settles', () => { + expect(terminalViewSource).toContain('pendingTerminalCreatesRef.current.has(createKey)'); + expect(terminalViewSource).toContain('pendingTerminalCreatesRef.current.delete(createKey)'); + }); + + test('derives the initial PTY size before Ghostty mounts', () => { + expect(terminalViewportSource).toContain('const getProvisionalTerminalSize'); + expect(terminalViewportSource).toContain('React.useLayoutEffect(() => {'); + expect(terminalViewportSource).toContain('resizeRef.current(size.cols, size.rows)'); + expect(terminalViewportSource).toContain('...(provisionalSizeRef.current ?? {})'); + }); }); diff --git a/packages/web/server/lib/terminal/DOCUMENTATION.md b/packages/web/server/lib/terminal/DOCUMENTATION.md index ffc27411..fb943ab0 100644 --- a/packages/web/server/lib/terminal/DOCUMENTATION.md +++ b/packages/web/server/lib/terminal/DOCUMENTATION.md @@ -23,6 +23,7 @@ HTTP remains the authenticated command plane for create, resize, appearance upda - IDs are client-provided or generated with `randomUUID()`. - Concurrent creates for one ID are single-flight only when working directory and shell preference match. Existing IDs cannot be reused for another working directory. - Dimensions are bounded to 1-1000 columns and 1-500 rows; input is capped at 64 KiB. +- A client may create before its renderer has mounted. It derives an initial size from the container and font metrics (falling back to 80x24 when unavailable), then sends a resize once Ghostty reports its final dimensions. This allows shell startup and renderer initialization to overlap. - PTY children explicitly clear `NODE_CHANNEL_FD`; daemon IPC descriptors are host-private and invalid after PTY descriptor cleanup. - `GET /api/terminal/shells` reports shell IDs available on the active server using the same augmented PATH provided to spawned PTYs, plus whether each executable has a supported login-mode argument. `auto` preserves environment/platform fallback order; an explicit unavailable shell fails creation instead of silently running a different shell. Login mode is opt-in and uses only built-in arguments for known shells. Preference changes affect new sessions and explicit restarts, not running PTYs. - PTY data and exit callbacks enter one FIFO queue. Stale callbacks from replaced processes are ignored. From 0de1be65eba1040c2224753d89dc4ba1e727e682 Mon Sep 17 00:00:00 2001 From: catan271 Date: Fri, 31 Jul 2026 18:05:07 +0700 Subject: [PATCH 3/6] fix(chat): normalize bash output by stripping ANSI sequences and applying terminal control codes --- .../chat/message/parts/ToolPart.test.ts | 72 ++++++++-- .../chat/message/parts/ToolPart.tsx | 2 +- .../chat/message/parts/toolOutput.ts | 125 +++++++++++++++++- 3 files changed, 187 insertions(+), 12 deletions(-) diff --git a/packages/ui/src/components/chat/message/parts/ToolPart.test.ts b/packages/ui/src/components/chat/message/parts/ToolPart.test.ts index d31d076e..2042f014 100644 --- a/packages/ui/src/components/chat/message/parts/ToolPart.test.ts +++ b/packages/ui/src/components/chat/message/parts/ToolPart.test.ts @@ -1,5 +1,6 @@ import { describe, expect, test } from 'bun:test'; +import { renderTerminalOutput } from './toolOutput'; import { readTaskTagSessionIdFromOutput } from './taskSessionIdParser'; import { tryParseJsonOutput } from '../toolRenderers'; import { getStreamingThrottleText } from '../../hooks/useStreamingTextThrottle'; @@ -7,18 +8,73 @@ import { getStreamingOutputAppend, getToolOutput } from './toolOutput'; import { getToolDescriptionFallback } from './toolRenderUtils'; describe('getToolOutput', () => { - test('prefers authoritative state output', () => { - expect(getToolOutput('bash', 'final output', 'streamed output')).toBe('final output'); - expect(getToolOutput('bash', '', 'streamed output')).toBe(''); + test('prefers state.output for completed tools', () => { + expect(getToolOutput('bash', 'final output', 'partial output', 'completed')).toBe('final output'); }); - test('falls back to streamed metadata output for bash', () => { - expect(getToolOutput('bash', undefined, 'streamed output')).toBe('streamed output'); - expect(getToolOutput('bash', undefined, '')).toBe(undefined); + test('normalizes completed bash state output while preserving final-output precedence', () => { + expect(getToolOutput('bash', '\u001B[32mFinal output\u001B[0m', 'partial output', 'completed')).toBe('Final output'); }); - test('does not expose metadata output for other tools', () => { - expect(getToolOutput('read', undefined, 'metadata output')).toBe(undefined); + test('falls back to metadata.output for bash tools without state output', () => { + expect(getToolOutput('bash', undefined, 'partial output', 'completed')).toBe('partial output'); + }); + + test('normalizes bash metadata output for completed state', () => { + expect(getToolOutput('bash', undefined, 'Progress 10%\r\u001B[2KProgress 90%', 'completed')).toBe('Progress 90%'); + }); + + test('does not normalize bash output while running', () => { + expect(getToolOutput('bash', '\u001B[32mRunning\u001B[0m', undefined, 'running')).toBe('\u001B[32mRunning\u001B[0m'); + expect(getToolOutput('bash', undefined, 'Progress\r\u001B[2K', 'running')).toBe('Progress\r\u001B[2K'); + }); + + test('ignores metadata.output for non-bash tools', () => { + expect(getToolOutput('read', undefined, 'partial output', 'completed')).toBe(undefined); + expect(getToolOutput('read', 'final output', 'partial output', 'completed')).toBe('final output'); + }); + + test('returns undefined when bash has no output', () => { + expect(getToolOutput('bash', undefined, undefined, 'completed')).toBe(undefined); + }); + + test('ignores empty metadata.output for bash', () => { + expect(getToolOutput('bash', undefined, '', 'completed')).toBe(undefined); + }); +}); + +describe('renderTerminalOutput', () => { + test('renders carriage-return progress updates as their latest value', () => { + expect(renderTerminalOutput('Downloading 10%\r\u001B[2KDownloading 90%')).toBe('Downloading 90%'); + }); + + test('removes ANSI styles while preserving the output text', () => { + expect(renderTerminalOutput('\u001B[32mComplete\u001B[0m\n')).toBe('Complete\n'); + }); + + test('applies cursor-up progress updates to the prior line', () => { + expect(renderTerminalOutput('First\nWorking\u001B[1A\r\u001B[2KDone\n')).toBe('Done\nWorking'); + }); + + test('CSI K erases from cursor to end of line', () => { + expect(renderTerminalOutput('Hello World\u001B[5G\u001B[K')).toBe('Hell'); + }); + + test('CSI 1 K erases from beginning of line through cursor, preserving suffix', () => { + expect(renderTerminalOutput('Hello World\u001B[6G\u001B[1K')).toBe(' World'); + }); + + test('CSI 2 K erases entire line', () => { + expect(renderTerminalOutput('Hello World\u001B[2K')).toBe(''); + }); + + test('handles large single-line output without quadratic slowdown', () => { + const largeLine = 'A'.repeat(50000) + '\u001B[0m'; + const start = performance.now(); + const result = renderTerminalOutput(largeLine); + const elapsed = performance.now() - start; + expect(result).toBe('A'.repeat(50000)); + expect(elapsed).toBeLessThan(1000); }); }); diff --git a/packages/ui/src/components/chat/message/parts/ToolPart.tsx b/packages/ui/src/components/chat/message/parts/ToolPart.tsx index b703c356..612ab076 100644 --- a/packages/ui/src/components/chat/message/parts/ToolPart.tsx +++ b/packages/ui/src/components/chat/message/parts/ToolPart.tsx @@ -1596,7 +1596,7 @@ const ToolExpandedContent: React.FC = React.memo(({ const stateWithData = state as ToolStateWithMetadata; const metadata = stateWithData.metadata; const input = stateWithData.input; - const rawOutput = getToolOutput(part.tool, stateWithData.output, metadata?.output); + const rawOutput = getToolOutput(part.tool, stateWithData.output, metadata?.output, state.status); const hasStringOutput = typeof rawOutput === 'string' && rawOutput.length > 0; const rawOutputString = typeof rawOutput === 'string' ? rawOutput : ''; const isStreamingBash = part.tool === 'bash' && state.status === 'running'; diff --git a/packages/ui/src/components/chat/message/parts/toolOutput.ts b/packages/ui/src/components/chat/message/parts/toolOutput.ts index 88d1d0ab..5eb8dc91 100644 --- a/packages/ui/src/components/chat/message/parts/toolOutput.ts +++ b/packages/ui/src/components/chat/message/parts/toolOutput.ts @@ -1,14 +1,133 @@ +const ensureLine = (lines: string[][], row: number): void => { + while (lines.length <= row) { + lines.push([]); + } +}; + +const writeTerminalCharacter = (lines: string[][], row: number, column: number, character: string): void => { + ensureLine(lines, row); + const line = lines[row]; + while (line.length < column) { + line.push(' '); + } + line[column] = character; +}; + +export const renderTerminalOutput = (output: string): string => { + if (!output.includes('\u001B') && !output.includes('\r') && !output.includes('\b')) { + return output; + } + + const lines: string[][] = [[]]; + let row = 0; + let column = 0; + + for (let index = 0; index < output.length; index += 1) { + const character = output[index]; + + if (character === '\n') { + row += 1; + column = 0; + ensureLine(lines, row); + continue; + } + if (character === '\r') { + column = 0; + continue; + } + if (character === '\b') { + column = Math.max(0, column - 1); + continue; + } + if (character !== '\u001B') { + writeTerminalCharacter(lines, row, column, character); + column += 1; + continue; + } + + const nextCharacter = output[index + 1]; + if (nextCharacter === '[') { + const sequenceStart = index + 2; + let sequenceEnd = sequenceStart; + while (sequenceEnd < output.length && !/[\x40-\x7E]/.test(output[sequenceEnd])) { + sequenceEnd += 1; + } + if (sequenceEnd === output.length) { + break; + } + + const command = output[sequenceEnd]; + const parameters = output.slice(sequenceStart, sequenceEnd).split(';').map((value) => Number.parseInt(value, 10) || 0); + const count = parameters[0] || 1; + if (command === 'A') { + row = Math.max(0, row - count); + } else if (command === 'B') { + row += count; + ensureLine(lines, row); + } else if (command === 'C') { + column += count; + } else if (command === 'D') { + column = Math.max(0, column - count); + } else if (command === 'G') { + column = Math.max(0, count - 1); + } else if (command === 'H' || command === 'f') { + row = Math.max(0, (parameters[0] || 1) - 1); + column = Math.max(0, (parameters[1] || 1) - 1); + ensureLine(lines, row); + } else if (command === 'K') { + ensureLine(lines, row); + const line = lines[row]; + const mode = parameters[0]; + if (mode === 1) { + for (let i = 0; i <= column && i < line.length; i += 1) { + line[i] = ' '; + } + } else if (mode === 2) { + lines[row] = []; + } else { + line.length = Math.min(line.length, column); + } + } + index = sequenceEnd; + continue; + } + + if (nextCharacter === ']') { + const terminator = output.indexOf('\u0007', index + 2); + const stringTerminator = output.indexOf('\u001B\\', index + 2); + const end = terminator === -1 + ? stringTerminator + : stringTerminator === -1 + ? terminator + : Math.min(terminator, stringTerminator); + if (end === -1) { + break; + } + index = output[end] === '\u0007' ? end : end + 1; + continue; + } + + index += 1; + } + + return lines.map((line) => line.join('')).join('\n'); +}; + export const getToolOutput = ( tool: string, stateOutput: unknown, metadataOutput: unknown, + status?: string, ): string | undefined => { + const isBash = tool === 'bash'; + const shouldNormalize = isBash && status !== 'running'; + if (typeof stateOutput === 'string') { - return stateOutput; + return shouldNormalize ? renderTerminalOutput(stateOutput) : stateOutput; } - if (tool === 'bash' && typeof metadataOutput === 'string' && metadataOutput.length > 0) { - return metadataOutput; + if (isBash && typeof metadataOutput === 'string' && metadataOutput.length > 0) { + return shouldNormalize ? renderTerminalOutput(metadataOutput) : metadataOutput; } return undefined; From 576791d0248899bb831e0345c4aae6d3c172189e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 3 Aug 2026 08:23:37 +0000 Subject: [PATCH 4/6] fix: invalidate skills cache with loadSkills directory key performConfigRefresh passed client-directory-first path into invalidateSkillsLoadCache, missing the active-project cache key used by loadSkills after the repository-local skills discovery fix. Co-authored-by: Serhii Dziupin --- packages/ui/src/stores/useAgentsStore.ts | 4 +++- packages/ui/src/stores/useSkillsStore.test.ts | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/stores/useAgentsStore.ts b/packages/ui/src/stores/useAgentsStore.ts index 1412bc0a..4e7beef2 100644 --- a/packages/ui/src/stores/useAgentsStore.ts +++ b/packages/ui/src/stores/useAgentsStore.ts @@ -700,7 +700,9 @@ async function performConfigRefresh(options: { uiRefreshTasks.push(commandsStore.loadCommands().then(() => undefined)); } if (refreshSkills) { - invalidateSkillsLoadCache(currentDirectory); + // Match loadSkills cache key (active-project-first). Passing client/directory-store + // path here misses the key when those diverge after getRequestDirectory(). + invalidateSkillsLoadCache(); uiRefreshTasks.push(skillsStore.loadSkills().then(() => undefined)); uiRefreshTasks.push(skillsCatalogStore.loadCatalog({ refresh: true }).then(() => undefined)); } diff --git a/packages/ui/src/stores/useSkillsStore.test.ts b/packages/ui/src/stores/useSkillsStore.test.ts index 010e5b55..f75fb711 100644 --- a/packages/ui/src/stores/useSkillsStore.test.ts +++ b/packages/ui/src/stores/useSkillsStore.test.ts @@ -100,4 +100,20 @@ describe('useSkillsStore directory resolution', () => { group: undefined, }]); }); + + test('invalidateSkillsLoadCache() with no argument clears the active-project cache key used by loadSkills', async () => { + expect(await useSkillsStore.getState().loadSkills()).toBe(true); + expect(runtimeFetchCalls.length).toBe(1); + + // Wrong key: client-directory-first null maps to __default__, not the active project. + invalidateSkillsLoadCache(null); + expect(await useSkillsStore.getState().loadSkills()).toBe(true); + expect(runtimeFetchCalls.length).toBe(1); + + // Default resolution must match loadSkills (active project first). + invalidateSkillsLoadCache(); + expect(await useSkillsStore.getState().loadSkills()).toBe(true); + expect(runtimeFetchCalls.length).toBe(2); + expect(runtimeFetchCalls[1]?.url).toContain(`directory=${encodeURIComponent(activeProjectPath)}`); + }); }); From 88937ade7253af99b2fb93420ec8dc10a8ca03c2 Mon Sep 17 00:00:00 2001 From: Serhii Dziupin Date: Mon, 3 Aug 2026 12:29:32 +0300 Subject: [PATCH 5/6] fix(terminal): start PTY before viewport mounts without dropping output or startup replies Terminal creation no longer waits for the Ghostty viewport to report its size: it starts the PTY immediately with a container/font-derived provisional size (falling back to 80x24), then resizes once the real viewport dimensions are known, with a dedupe guard while sizing settles. Starting the shell earlier means it can emit device/theme queries before a browser terminal is attached to answer them, so the server now answers primary device attribute queries itself (Fish blocks ~10s on this at startup) and bun-pty buffers output emitted before a data subscriber attaches. Also fixes a few WebSocket transport reconnect races surfaced by session creation now overlapping renderer setup. --- bun-patches/bun-pty@0.4.8.patch | 77 +++++++++ bun.lock | 1 + package.json | 3 +- .../ui/src/components/views/TerminalView.tsx | 47 +++--- .../__tests__/terminalViewportRemount.test.ts | 50 +++--- packages/ui/src/lib/terminalApi.test.ts | 151 ++++++++++++++++++ packages/ui/src/lib/terminalApi.ts | 129 +++++++++------ .../web/server/lib/terminal/DOCUMENTATION.md | 3 +- packages/web/server/lib/terminal/runtime.js | 4 +- .../web/server/lib/terminal/runtime.test.js | 4 +- .../web/server/lib/terminal/theme-response.js | 28 +++- .../lib/terminal/theme-response.test.js | 23 +++ 12 files changed, 421 insertions(+), 99 deletions(-) create mode 100644 bun-patches/bun-pty@0.4.8.patch diff --git a/bun-patches/bun-pty@0.4.8.patch b/bun-patches/bun-pty@0.4.8.patch new file mode 100644 index 00000000..f65b3bba --- /dev/null +++ b/bun-patches/bun-pty@0.4.8.patch @@ -0,0 +1,77 @@ +diff --git a/src/terminal.ts b/src/terminal.ts +index ec248d46a939f8a09cd669e853cefb126922c80a..c0473bc625edda7be2ade987e8aa3bd99160ce67 100644 +--- a/src/terminal.ts ++++ b/src/terminal.ts +@@ -11,6 +11,7 @@ export const DEFAULT_COLS = 80; + export const DEFAULT_ROWS = 24; + export const DEFAULT_FILE = "sh"; + export const DEFAULT_NAME = "xterm"; ++const INITIAL_OUTPUT_BUFFER_LIMIT = 512 * 1024; + + /** + * Quote a string for shell-words compatible splitting on the Rust side. +@@ -136,6 +137,8 @@ export class Terminal implements IPty { + + private _readLoop = false; + private _closing = false; ++ private _hasDataSubscriber = false; ++ private _initialOutput = ""; + + // TextDecoder with streaming mode to properly handle UTF-8 across chunk boundaries + // Without this, multi-byte characters (like box-drawing ─) that span chunks become � +@@ -191,12 +194,29 @@ export class Terminal implements IPty { + } + + get onData() { +- return this._onData.event; ++ return (listener: (data: string) => void) => { ++ const disposable = this._onData.event(listener); ++ if (!this._hasDataSubscriber) { ++ this._hasDataSubscriber = true; ++ const initialOutput = this._initialOutput; ++ this._initialOutput = ""; ++ if (initialOutput) listener(initialOutput); ++ } ++ return disposable; ++ }; + } + get onExit() { + return this._onExit.event; + } + ++ private _emitData(data: string) { ++ if (this._hasDataSubscriber) { ++ this._onData.fire(data); ++ } else { ++ this._initialOutput = `${this._initialOutput}${data}`.slice(-INITIAL_OUTPUT_BUFFER_LIMIT); ++ } ++ } ++ + /* ------------- IO methods ------------- */ + + write(data: string) { +@@ -235,13 +255,13 @@ export class Terminal implements IPty { + // This prevents corruption when multi-byte chars span chunk boundaries + const decoded = this._decoder.decode(buf.subarray(0, n), { stream: true }); + if (decoded) { +- this._onData.fire(decoded); ++ this._emitData(decoded); + } + } else if (n === -2) { + // CHILD_EXITED - flush any remaining bytes in the decoder + const remaining = this._decoder.decode(); + if (remaining) { +- this._onData.fire(remaining); ++ this._emitData(remaining); + } + const exitCode = lib.symbols.bun_pty_get_exit_code(this.handle); + this._onExit.fire({ exitCode }); +@@ -250,7 +270,7 @@ export class Terminal implements IPty { + // error - flush decoder before breaking + const remaining = this._decoder.decode(); + if (remaining) { +- this._onData.fire(remaining); ++ this._emitData(remaining); + } + break; + } else { diff --git a/bun.lock b/bun.lock index e9ab4100..53e44eae 100644 --- a/bun.lock +++ b/bun.lock @@ -354,6 +354,7 @@ ], "patchedDependencies": { "@tanstack/virtual-core@3.17.3": "bun-patches/@tanstack+virtual-core+3.17.3.patch", + "bun-pty@0.4.8": "bun-patches/bun-pty@0.4.8.patch", }, "overrides": { "@codemirror/language": "6.12.2", diff --git a/package.json b/package.json index 31f9c141..d8e44f4f 100644 --- a/package.json +++ b/package.json @@ -178,6 +178,7 @@ "vite": "^7.1.2" }, "patchedDependencies": { - "@tanstack/virtual-core@3.17.3": "bun-patches/@tanstack+virtual-core+3.17.3.patch" + "@tanstack/virtual-core@3.17.3": "bun-patches/@tanstack+virtual-core+3.17.3.patch", + "bun-pty@0.4.8": "bun-patches/bun-pty@0.4.8.patch" } } diff --git a/packages/ui/src/components/views/TerminalView.tsx b/packages/ui/src/components/views/TerminalView.tsx index 0c8e6465..c97f608e 100644 --- a/packages/ui/src/components/views/TerminalView.tsx +++ b/packages/ui/src/components/views/TerminalView.tsx @@ -421,7 +421,7 @@ export const TerminalView: React.FC = ({ visible }) => { } const tab = state.tabs.find((t) => t.id === tabId) ?? state.tabs[0]; - let terminalId = tab?.terminalSessionId ?? null; + const terminalId = tab?.terminalSessionId ?? null; const terminalLifecycle = tab?.lifecycle ?? 'idle'; const isActionTab = Boolean(tab?.label?.startsWith('Action:')); const buffer = useTerminalStore.getState().getBuffer(directory, tabId); @@ -487,18 +487,27 @@ export const TerminalView: React.FC = ({ visible }) => { ) { void terminal.resize({ sessionId: session.sessionId, ...viewportSize }).catch(() => {}); } - terminalId = session.sessionId; + // Storing the session ID reruns this effect. Let that next + // effect own stream startup: starting here would be torn + // down immediately by this effect's cleanup. + return; } catch (error) { - if (!cancelled) { - setConnectionError( - error instanceof Error - ? error.message - : t('terminalView.error.startSessionFailed') - ); - setIsFatalError(true); - setIsReconnectPending(false); - setConnecting(directory, tabId, false); - } + const owningTab = useTerminalStore.getState().getDirectoryState(directory)?.tabs.find((entry) => entry.id === tabId); + if (!owningTab || owningTab.terminalSessionId) return; + + setConnecting(directory, tabId, false); + // Strict Mode replaces the first effect while its create + // request is pending. `cancelled` therefore does not mean + // this tab stopped owning the request; use current store + // ownership so a rejected create cannot leave it spinning. + if (directoryRef.current !== directory || activeTabIdRef.current !== tabId) return; + setConnectionError( + error instanceof Error + ? error.message + : t('terminalView.error.startSessionFailed') + ); + setIsFatalError(true); + setIsReconnectPending(false); return; } finally { pendingTerminalCreatesRef.current.delete(createKey); @@ -706,9 +715,7 @@ export const TerminalView: React.FC = ({ visible }) => { const handleViewportResize = React.useCallback( (cols: number, rows: number) => { const previous = lastViewportSizeRef.current; - if (!previous) { - lastViewportSizeRef.current = { cols, rows }; - } else if (previous.cols !== cols || previous.rows !== rows) { + if (!previous || previous.cols !== cols || previous.rows !== rows) { lastViewportSizeRef.current = { cols, rows }; } if (!isTerminalVisible) { @@ -716,9 +723,7 @@ export const TerminalView: React.FC = ({ visible }) => { } const terminalId = terminalIdRef.current; if (!terminalId) return; - void terminal.resize({ sessionId: terminalId, cols, rows }).catch(() => { - - }); + void terminal.resize({ sessionId: terminalId, cols, rows }).catch(() => {}); }, [isTerminalVisible, terminal] ); @@ -812,11 +817,7 @@ export const TerminalView: React.FC = ({ visible }) => { // here tore down and rebuilt the Ghostty terminal (WASM VT + canvas + font // atlas) a second time the moment `createSession` resolved, doubling the cost // of every terminal open. Session changes are handled by the chunk replay path. - const terminalViewportKey = React.useMemo(() => { - const directoryPart = effectiveDirectory ?? 'no-dir'; - const tabPart = activeTabId ?? 'no-tab'; - return `${directoryPart}::${tabPart}`; - }, [effectiveDirectory, activeTabId]); + const terminalViewportKey = `${effectiveDirectory ?? 'no-dir'}::${activeTabId ?? 'no-tab'}`; React.useEffect(() => { if (!isTerminalVisible || useTouchTerminalInput) { diff --git a/packages/ui/src/components/views/__tests__/terminalViewportRemount.test.ts b/packages/ui/src/components/views/__tests__/terminalViewportRemount.test.ts index 6b03ec22..d03379c1 100644 --- a/packages/ui/src/components/views/__tests__/terminalViewportRemount.test.ts +++ b/packages/ui/src/components/views/__tests__/terminalViewportRemount.test.ts @@ -27,27 +27,15 @@ const terminalViewportSource = readFileSync( 'utf-8', ); -const viewportKeyBlock = (() => { - const start = terminalViewSource.indexOf('const terminalViewportKey = React.useMemo('); - expect(start).toBeGreaterThan(-1); - const end = terminalViewSource.indexOf('}, [', start); - expect(end).toBeGreaterThan(start); - return terminalViewSource.slice(start, terminalViewSource.indexOf(');', end)); -})(); +const viewportKeyDeclaration = terminalViewSource + .split('\n') + .find((line) => line.includes('const terminalViewportKey =')) ?? ''; describe('terminal viewport remount guard', () => { test('viewport identity excludes the PTY session id', () => { - expect(viewportKeyBlock).toContain('effectiveDirectory'); - expect(viewportKeyBlock).toContain('activeTabId'); - expect(viewportKeyBlock).not.toContain('terminalSessionId'); - }); - - test('viewport key memo does not depend on the PTY session id', () => { - const dependencyStart = terminalViewSource.indexOf('}, [', terminalViewSource.indexOf('const terminalViewportKey')); - const dependencies = terminalViewSource.slice(dependencyStart, terminalViewSource.indexOf(']', dependencyStart)); - expect(dependencies).toContain('effectiveDirectory'); - expect(dependencies).toContain('activeTabId'); - expect(dependencies).not.toContain('terminalSessionId'); + expect(viewportKeyDeclaration).toContain('effectiveDirectory'); + expect(viewportKeyDeclaration).toContain('activeTabId'); + expect(viewportKeyDeclaration).not.toContain('terminalSessionId'); }); test('replay discontinuities reset the terminal in place instead of remounting it', () => { @@ -80,6 +68,32 @@ describe('terminal viewport remount guard', () => { expect(terminalViewSource).toContain('pendingTerminalCreatesRef.current.delete(createKey)'); }); + test('lets the session-ID effect own stream startup after creating a tab', () => { + const createStart = terminalViewSource.indexOf('if (!terminalId) {'); + const createEnd = terminalViewSource.indexOf('if (!terminalId || cancelled) return;', createStart); + expect(createStart).toBeGreaterThan(-1); + expect(createEnd).toBeGreaterThan(createStart); + const createBlock = terminalViewSource.slice(createStart, createEnd); + + expect(createBlock).toContain('setTabSessionId(directory, tabId, session.sessionId);'); + expect(createBlock).toContain('Let that next'); + expect(createBlock).not.toContain('startStream('); + }); + + test('clears a current tab from connecting when a strict-mode create rejects', () => { + const createStart = terminalViewSource.indexOf('if (!terminalId) {'); + const catchStart = terminalViewSource.indexOf('} catch (error) {', createStart); + const catchEnd = terminalViewSource.indexOf('} finally {', catchStart); + expect(catchStart).toBeGreaterThan(createStart); + expect(catchEnd).toBeGreaterThan(catchStart); + const catchBlock = terminalViewSource.slice(catchStart, catchEnd); + + expect(catchBlock).toContain('owningTab.terminalSessionId'); + expect(catchBlock).toContain('activeTabIdRef.current !== tabId'); + expect(catchBlock).toContain('setConnecting(directory, tabId, false);'); + expect(catchBlock).not.toContain('if (!cancelled)'); + }); + test('derives the initial PTY size before Ghostty mounts', () => { expect(terminalViewportSource).toContain('const getProvisionalTerminalSize'); expect(terminalViewportSource).toContain('React.useLayoutEffect(() => {'); diff --git a/packages/ui/src/lib/terminalApi.test.ts b/packages/ui/src/lib/terminalApi.test.ts index b02aa0ac..7573984a 100644 --- a/packages/ui/src/lib/terminalApi.test.ts +++ b/packages/ui/src/lib/terminalApi.test.ts @@ -93,6 +93,157 @@ describe('terminal transport', () => { transport.dispose(); }); + test('invalidates URL auth when the current socket closes before opening', async () => { + const socket = new FakeSocket(); + let cleared = 0; + const transport = new TerminalTransport({ + refreshAuth: async () => '', + openSocket: () => socket, + clearUrlAuthToken: () => { cleared += 1; }, + }); + + const unsubscribe = transport.subscribe('term-1', { onEvent: () => {} }); + await tick(); + socket.close(); + await tick(); + + expect(cleared).toBe(1); + unsubscribe(); + transport.dispose(); + }); + + test('invalidates URL auth before retrying a pre-open socket error', async () => { + const socket = new FakeSocket(); + let cleared = 0; + const transport = new TerminalTransport({ + refreshAuth: async () => '', + openSocket: () => socket, + clearUrlAuthToken: () => { cleared += 1; }, + }); + + const unsubscribe = transport.subscribe('term-1', { onEvent: () => {} }); + await tick(); + socket.onerror?.(); + + expect(cleared).toBe(1); + unsubscribe(); + transport.dispose(); + }); + + test('does not let a cancelled opening reconnect a replacement subscription', async () => { + const sockets = [new FakeSocket(), new FakeSocket()]; + let socketIndex = 0; + const replacementEvents: string[] = []; + const transport = new TerminalTransport({ + refreshAuth: async () => '', + openSocket: () => sockets[socketIndex++]!, + }); + + const unsubscribeFirst = transport.subscribe('term-1', { onEvent: () => {} }); + await tick(); + unsubscribeFirst(); + + const unsubscribeReplacement = transport.subscribe('term-1', { + onEvent: (event) => replacementEvents.push(event.type), + }); + await tick(); + sockets[1]?.open(); + await tick(); + + expect(replacementEvents).not.toContain('reconnecting'); + unsubscribeReplacement(); + transport.dispose(); + }); + + test('starts a fresh reconnect sequence after every terminal has detached', async () => { + const firstEvents: number[] = []; + const replacementEvents: number[] = []; + const transport = new TerminalTransport({ + refreshAuth: async () => '', + openSocket: () => { throw new Error('offline'); }, + }); + + const unsubscribeFirst = transport.subscribe('term-1', { + onEvent: (event) => { + if (event.type === 'reconnecting' && typeof event.attempt === 'number') firstEvents.push(event.attempt); + }, + }); + await tick(); + await tick(); + expect(firstEvents).toEqual([1]); + + unsubscribeFirst(); + const unsubscribeReplacement = transport.subscribe('term-2', { + onEvent: (event) => { + if (event.type === 'reconnecting' && typeof event.attempt === 'number') replacementEvents.push(event.attempt); + }, + }); + await tick(); + await tick(); + + expect(replacementEvents).toEqual([1]); + unsubscribeReplacement(); + transport.dispose(); + }); + + test('waits a minute before reconnecting while hidden', async () => { + const originalSetTimeout = globalThis.setTimeout; + const originalDocument = Object.getOwnPropertyDescriptor(globalThis, 'document'); + const delays: number[] = []; + let transport: TerminalTransport | null = null; + + Object.defineProperty(globalThis, 'document', { + configurable: true, + value: { + visibilityState: 'hidden', + addEventListener: () => {}, + removeEventListener: () => {}, + }, + }); + globalThis.setTimeout = ((handler: TimerHandler, timeout?: number, ...args: unknown[]) => { + delays.push(Number(timeout ?? 0)); + if (timeout === 0) return originalSetTimeout(handler, 0, ...args); + return 0 as unknown as ReturnType; + }) as typeof setTimeout; + + try { + transport = new TerminalTransport({ + refreshAuth: async () => '', + openSocket: () => { throw new Error('offline'); }, + }); + transport.subscribe('term-1', { onEvent: () => {} }); + await tick(); + await tick(); + + expect(delays).toContain(60_000); + } finally { + transport?.dispose(); + globalThis.setTimeout = originalSetTimeout; + if (originalDocument) Object.defineProperty(globalThis, 'document', originalDocument); + else delete (globalThis as { document?: unknown }).document; + } + }); + + test('attaches a remaining same-terminal subscriber after the first one leaves', async () => { + const socket = new FakeSocket(); + const transport = new TerminalTransport({ refreshAuth: async () => '', openSocket: () => socket }); + + const unsubscribeOther = transport.subscribe('term-other', { onEvent: () => {} }); + await tick(); + socket.open(); + await tick(); + + const unsubscribeFirst = transport.subscribe('term-1', { onEvent: () => {} }); + const unsubscribeRemaining = transport.subscribe('term-1', { onEvent: () => {} }); + unsubscribeFirst(); + await tick(); + + expect(socket.sent.filter((message) => message.t === 'attach' && message.s === 'term-1')).toHaveLength(1); + unsubscribeRemaining(); + unsubscribeOther(); + transport.dispose(); + }); + test('releases replay projections when the last subscriber detaches', async () => { const socket = new FakeSocket(); const transport = new TerminalTransport({ refreshAuth: async () => '', openSocket: () => socket }); diff --git a/packages/ui/src/lib/terminalApi.ts b/packages/ui/src/lib/terminalApi.ts index 3e555bf7..4bfce9d5 100644 --- a/packages/ui/src/lib/terminalApi.ts +++ b/packages/ui/src/lib/terminalApi.ts @@ -3,7 +3,7 @@ import { openRuntimeWebSocket } from './relay/runtime-socket'; import type { RelayTunnelWebSocket } from './relay/tunnel-client'; import { runtimeFetch } from './runtime-fetch'; import { getRuntimeUrlResolver } from './runtime-url'; -import { refreshRuntimeUrlAuthToken } from './runtime-auth'; +import { clearRuntimeUrlAuthToken, refreshRuntimeUrlAuthToken } from './runtime-auth'; import { isTerminalShell } from './terminalShell'; type Message = Record & { t: string; s?: string; q?: number }; @@ -66,12 +66,12 @@ const trimProjection = (value: string): string => { type TerminalTransportDependencies = { refreshAuth: () => Promise; openSocket: () => RelayTunnelWebSocket; + clearUrlAuthToken?: () => void; }; export class TerminalTransport { private socket: RelayTunnelWebSocket | null = null; private opening: Promise | null = null; - private openingGeneration: number | null = null; private subscribers = new Map>(); private projections = new Map(); private reconnectTimer: ReturnType | null = null; @@ -85,6 +85,7 @@ export class TerminalTransport { constructor(private readonly dependencies: TerminalTransportDependencies = { refreshAuth: refreshRuntimeUrlAuthToken, openSocket: () => openRuntimeWebSocket(getRuntimeUrlResolver().websocket('/api/terminal/ws')), + clearUrlAuthToken: clearRuntimeUrlAuthToken, }) {} subscribe(sessionId: string, handlers: TerminalHandlers): () => void { @@ -100,7 +101,13 @@ export class TerminalTransport { handlers.onEvent({ type: 'snapshot', sequence: projection.sequence, data: projection.history, status: projection.status, exitCode: projection.exitCode, signal: projection.signal, runtime: projection.runtime, ptyBackend: projection.ptyBackend }); } const socketWasOpen = this.socket?.readyState === SOCKET_OPEN; - this.ensureConnected().then(() => { if (first && socketWasOpen && set.has(subscriber)) this.send({ t: 'attach', v: 3, s: sessionId }); }).catch((error) => { + this.ensureConnected().then(() => { + const current = this.subscribers.get(sessionId); + if (first && socketWasOpen && current === set && current.size > 0) { + this.send({ t: 'attach', v: 3, s: sessionId }); + } + }).catch((error) => { + if (!set.has(subscriber)) return; handlers.onError?.(error, false); this.scheduleReconnect(); }); @@ -114,6 +121,7 @@ export class TerminalTransport { } if (this.subscribers.size === 0) { this.cancelReconnect(); + this.failures = 0; if (this.socket?.readyState === SOCKET_OPEN) { // Healthy socket: hold it briefly so a tab switch can reattach to it. this.scheduleIdleClose(); @@ -121,6 +129,7 @@ export class TerminalTransport { } // Nothing to reuse, so abandon any dial that is still in flight. this.generation += 1; + this.opening = null; this.closeSocket(); } }; @@ -138,6 +147,7 @@ export class TerminalTransport { dispose(): void { this.disposed = true; this.generation += 1; + this.opening = null; this.subscribers.clear(); this.projections.clear(); if (this.reconnectTimer) clearTimeout(this.reconnectTimer); @@ -155,71 +165,89 @@ export class TerminalTransport { private async ensureConnected(): Promise { if (this.disposed) throw new Error('Terminal runtime changed'); if (this.socket?.readyState === SOCKET_OPEN) return; - if (this.opening && this.openingGeneration === this.generation) { + if (this.opening) { await this.opening; if (this.socket?.readyState === SOCKET_OPEN) return; return this.ensureConnected(); } - if (this.openingGeneration !== this.generation) { - this.opening = null; - this.openingGeneration = null; - } const generation = this.generation; const opening = (async () => { await this.dependencies.refreshAuth(); if (generation !== this.generation || this.disposed) throw new Error('Terminal runtime changed'); await new Promise((resolve, reject) => { - let settled = false; - let pendingSocket: RelayTunnelWebSocket | null = null; - const finish = (error?: Error) => { - if (settled) return; - settled = true; - clearTimeout(timeout); - if (error) reject(error); - else resolve(); - }; - const timeout = setTimeout(() => { - pendingSocket?.close(); - finish(new Error('Terminal connection timed out')); - }, 10_000); - try { - const socket = this.dependencies.openSocket(); - pendingSocket = socket; - socket.binaryType = 'arraybuffer'; - this.socket = socket; - socket.onopen = () => { - if (generation !== this.generation || this.disposed) { socket.close(); finish(new Error('Terminal runtime changed')); return; } - this.failures = 0; - this.send({ t: 'hello', v: 3 }); - for (const sessionId of this.subscribers.keys()) this.send({ t: 'attach', v: 3, s: sessionId }); - this.startKeepalive(); - finish(); + let settled = false; + let opened = false; + let authInvalidated = false; + let pendingSocket: RelayTunnelWebSocket | null = null; + const isCurrentSocket = () => ( + generation === this.generation && + !this.disposed && + pendingSocket !== null && + this.socket === pendingSocket + ); + const invalidatePreOpenAuth = () => { + if (authInvalidated || opened || !isCurrentSocket()) return; + authInvalidated = true; + this.dependencies.clearUrlAuthToken?.(); }; - socket.onmessage = (event) => void this.handleMessage(event.data); - socket.onerror = () => { - finish(new Error('Terminal WebSocket failed')); + const finish = (error?: Error) => { + if (settled) return; + settled = true; + clearTimeout(timeout); + if (error) reject(error); + else resolve(); + }; + const timeout = setTimeout(() => { + invalidatePreOpenAuth(); + pendingSocket?.close(); + finish(new Error('Terminal connection timed out')); + }, 10_000); + try { + const socket = this.dependencies.openSocket(); + pendingSocket = socket; + socket.binaryType = 'arraybuffer'; + this.socket = socket; + socket.onopen = () => { + if (!isCurrentSocket()) { socket.close(); finish(new Error('Terminal runtime changed')); return; } + opened = true; + this.failures = 0; + this.send({ t: 'hello', v: 3 }); + for (const sessionId of this.subscribers.keys()) this.send({ t: 'attach', v: 3, s: sessionId }); + this.startKeepalive(); + finish(); + }; + socket.onmessage = (event) => void this.handleMessage(event.data); + socket.onerror = () => { + const current = isCurrentSocket(); + if (current) invalidatePreOpenAuth(); + finish(new Error('Terminal WebSocket failed')); + if (current && this.subscribers.size > 0) this.scheduleReconnect(); + }; + socket.onclose = () => { + const current = isCurrentSocket(); + if (current) { + this.stopKeepalive(); + // An upgrade rejected before `open` commonly means the cached + // URL-scoped auth token is stale. Retrying it reaches the 8s + // backoff cap instead of minting a fresh token. + invalidatePreOpenAuth(); + } + if (this.socket === socket) this.socket = null; + finish(new Error('Terminal WebSocket closed')); + if (current && this.subscribers.size > 0) this.scheduleReconnect(); + }; + } catch (error) { + finish(error instanceof Error ? error : new Error('Terminal WebSocket failed')); if (!this.disposed && this.subscribers.size > 0) this.scheduleReconnect(); - }; - socket.onclose = () => { - if (this.socket === socket) this.socket = null; - this.stopKeepalive(); - finish(new Error('Terminal WebSocket closed')); - if (!this.disposed && this.subscribers.size > 0) this.scheduleReconnect(); - }; - } catch (error) { - finish(error instanceof Error ? error : new Error('Terminal WebSocket failed')); - if (!this.disposed && this.subscribers.size > 0) this.scheduleReconnect(); - } + } }); })(); this.opening = opening; - this.openingGeneration = generation; try { await opening; } finally { if (this.opening === opening) { this.opening = null; - this.openingGeneration = null; } } } @@ -279,7 +307,7 @@ export class TerminalTransport { if (this.reconnectTimer || this.disposed || this.subscribers.size === 0) return; this.failures += 1; const slow = (typeof document !== 'undefined' && document.visibilityState === 'hidden') || (typeof navigator !== 'undefined' && !navigator.onLine); - const delay = Math.min(500 * 2 ** Math.min(this.failures - 1, 10), slow ? 60_000 : 8_000); + const delay = slow ? 60_000 : Math.min(500 * 2 ** Math.min(this.failures - 1, 10), 8_000); for (const set of this.subscribers.values()) for (const sub of set) sub.handlers.onEvent({ type: 'reconnecting', attempt: this.failures, maxAttempts: Number.POSITIVE_INFINITY }); const wake = () => { if (typeof document !== 'undefined' && document.visibilityState === 'hidden') return; @@ -304,6 +332,7 @@ export class TerminalTransport { this.idleCloseTimer = null; if (this.disposed || this.subscribers.size > 0) return; this.generation += 1; + this.opening = null; this.closeSocket(); }, IDLE_SOCKET_GRACE_MS); } diff --git a/packages/web/server/lib/terminal/DOCUMENTATION.md b/packages/web/server/lib/terminal/DOCUMENTATION.md index fb943ab0..d0b4563a 100644 --- a/packages/web/server/lib/terminal/DOCUMENTATION.md +++ b/packages/web/server/lib/terminal/DOCUMENTATION.md @@ -10,11 +10,12 @@ - `attach` registers a connection for one terminal. One socket may attach to many terminals. - Every attach and reconnect begins with an authoritative `snapshot` containing bounded history and the current sequence. +- A current socket that closes or errors before its initial `open` invalidates its URL-scoped auth token before retrying, so retries mint a fresh token instead of backing off against a rejected upgrade. Hidden or offline clients wait 60 seconds and wake promptly on visibility/online recovery. - `output`, `exit`, and `restarted` carry monotonically increasing per-terminal sequences. Output carries raw live bytes plus replay-safe bytes with terminal query exchanges removed. - Attach registers before capturing the snapshot, buffers concurrent events, drops events represented by the snapshot sequence, then enters live delivery. - `write` always includes the terminal ID; sockets never have mutable single-terminal binding state. - `detach` removes only that attachment. -- Creation carries the active UI appearance. The PTY sets `COLORFGBG` and answers OSC 10, OSC 11, and Mode 2031 queries immediately, including queries emitted before a WebSocket attachment exists. Subscribed TUIs receive a Mode 2031 notification when the appearance changes. +- Creation carries the active UI appearance. The PTY sets `COLORFGBG` and answers OSC 10, OSC 11, Mode 2031, and primary-device-attribute queries immediately, including queries emitted before a WebSocket attachment exists. The DA1 fallback prevents Fish from waiting ten seconds for a renderer that cannot observe or answer its startup query. Subscribed TUIs receive a Mode 2031 notification when the appearance changes. HTTP remains the authenticated command plane for create, resize, appearance updates, restart, close, and force-kill. There is no SSE output or HTTP input compatibility path. diff --git a/packages/web/server/lib/terminal/runtime.js b/packages/web/server/lib/terminal/runtime.js index f717cb7a..04a7135c 100644 --- a/packages/web/server/lib/terminal/runtime.js +++ b/packages/web/server/lib/terminal/runtime.js @@ -145,7 +145,7 @@ export function createTerminalRuntime({ background: session.terminalBackground, foreground: session.terminalForeground, modeEnabled: session.themeModeEnabled, - }); + }, { respondToPrimaryDeviceAttributes: true }); session.pendingThemeControlSequence = theme.pending; session.themeModeEnabled = theme.modeEnabled; for (const response of theme.responses) session.process?.write(response); @@ -331,7 +331,7 @@ export function createTerminalRuntime({ session.process = spawned.process; session.backend = spawned.backend; session.shell = spawned.shell; session.loginShell = spawned.loginShell; session.cwd = cwd; session.cols = cols; session.rows = rows; session.history = ''; session.pendingHistoryControlSequence = ''; session.pendingThemeControlSequence = ''; session.themeModeEnabled = false; session.status = 'running'; session.exitCode = null; session.signal = null; session.eventQueue.length = 0; session.themeMode = themeMode === 'light' ? 'light' : 'dark'; session.terminalBackground = terminalBackground; session.terminalForeground = terminalForeground; - wire(session, spawned.process); void terminateProcess(oldProcess); publish(session, { t: 'restarted', history: '' }); + wire(session, spawned.process); void terminateProcess(oldProcess); publish(session, { t: 'restarted', history: '' }); }); pendingSessionRestarts.set(session.id, restart); try { diff --git a/packages/web/server/lib/terminal/runtime.test.js b/packages/web/server/lib/terminal/runtime.test.js index 4bc21420..8a67c07e 100644 --- a/packages/web/server/lib/terminal/runtime.test.js +++ b/packages/web/server/lib/terminal/runtime.test.js @@ -154,8 +154,8 @@ describe('terminal runtime', () => { expect(harness.processes[0].options.cwd).toBe('/repo'); expect(harness.processes[0].options.env.COLORFGBG).toBe('0;15'); expect(harness.processes[0].options.env.NODE_CHANNEL_FD).toBe(''); - harness.processes[0].emitData('\u001b[?2031h\u001b]10;?\u0007\u001b]11;?\u0007'); - expect(harness.processes[0].writes).toEqual(['\u001b]10;rgb:1b1b/1b1b/1b1b\u001b\\', '\u001b]11;rgb:fafa/f8f8/f0f0\u001b\\']); + harness.processes[0].emitData('\u001b[?2031h\u001b]10;?\u0007\u001b]11;?\u0007\u001b[0c'); + expect(harness.processes[0].writes).toEqual(['\u001b]10;rgb:1b1b/1b1b/1b1b\u001b\\', '\u001b]11;rgb:fafa/f8f8/f0f0\u001b\\', '\u001b[?1;2c']); const appearance = createResponse(); harness.routes.post.get('/api/terminal/:sessionId/appearance')({ params: { sessionId: 'term-1' }, body: { themeMode: 'dark' } }, appearance); diff --git a/packages/web/server/lib/terminal/theme-response.js b/packages/web/server/lib/terminal/theme-response.js index 3738675a..26662610 100644 --- a/packages/web/server/lib/terminal/theme-response.js +++ b/packages/web/server/lib/terminal/theme-response.js @@ -2,11 +2,21 @@ const MODE_SET = '\u001b[?2031h'; const MODE_RESET = '\u001b[?2031l'; const CAPABILITY_QUERY = '\u001b[?2031$p'; const MODE_QUERIES = ['\u001b[?996n', '\u001b[?997n']; +// Fish asks this before an unattached browser terminal can reply. +const PRIMARY_DEVICE_ATTRIBUTE_QUERIES = ['\u001b[c', '\u001b[0c']; +const PRIMARY_DEVICE_ATTRIBUTE_RESPONSE = '\u001b[?1;2c'; const OSC_QUERIES = [10, 11].flatMap((code) => [ { sequence: `\u001b]${code};?\u0007`, code }, { sequence: `\u001b]${code};?\u001b\\`, code }, ]); -const CONTROL_SEQUENCES = [MODE_SET, MODE_RESET, CAPABILITY_QUERY, ...MODE_QUERIES, ...OSC_QUERIES.map(({ sequence }) => sequence)]; +const CONTROL_SEQUENCES = [ + MODE_SET, + MODE_RESET, + CAPABILITY_QUERY, + ...MODE_QUERIES, + ...PRIMARY_DEVICE_ATTRIBUTE_QUERIES, + ...OSC_QUERIES.map(({ sequence }) => sequence), +]; const parseColor = (value) => { if (typeof value !== 'string') return null; @@ -28,7 +38,12 @@ const colorReport = (code, color) => { export const terminalThemeModeReport = (themeMode) => `\u001b[?997;${themeMode === 'light' ? 2 : 1}n`; -export const consumeTerminalThemeQueries = (pending, data, appearance) => { +export const consumeTerminalThemeQueries = ( + pending, + data, + appearance, + { respondToPrimaryDeviceAttributes = false } = {}, +) => { if (!pending && !data.includes('\u001b')) return { pending: '', responses: [], modeEnabled: appearance.modeEnabled === true }; const input = `${pending}${data}`; const responses = []; @@ -56,6 +71,15 @@ export const consumeTerminalThemeQueries = (pending, data, appearance) => { index += modeQuery.length - 1; continue; } + const primaryDeviceAttributeQuery = PRIMARY_DEVICE_ATTRIBUTE_QUERIES.find((query) => input.startsWith(query, index)); + if (primaryDeviceAttributeQuery && respondToPrimaryDeviceAttributes) { + // A shell can ask before any browser terminal is attached. Answer with a + // conservative VT100 DA1 response so Fish does not block startup for its + // ten-second query timeout while waiting for a renderer that cannot see it. + responses.push(PRIMARY_DEVICE_ATTRIBUTE_RESPONSE); + index += primaryDeviceAttributeQuery.length - 1; + continue; + } const oscQuery = OSC_QUERIES.find(({ sequence }) => input.startsWith(sequence, index)); if (oscQuery) { const response = colorReport(oscQuery.code, oscQuery.code === 10 ? appearance.foreground : appearance.background); diff --git a/packages/web/server/lib/terminal/theme-response.test.js b/packages/web/server/lib/terminal/theme-response.test.js index f67fe418..e45fdd9c 100644 --- a/packages/web/server/lib/terminal/theme-response.test.js +++ b/packages/web/server/lib/terminal/theme-response.test.js @@ -44,4 +44,27 @@ describe('terminal theme responses', () => { '\u001b]10;rgb:1b1b/1b1b/1b1b\u001b\\', ]); }); + + test('answers a primary device attribute query when the fallback is enabled', () => { + const attached = consumeTerminalThemeQueries('', '\u001b[0c', lightAppearance); + const unattached = consumeTerminalThemeQueries('', '\u001b[0c', lightAppearance, { + respondToPrimaryDeviceAttributes: true, + }); + + expect(attached.responses).toEqual([]); + expect(unattached.responses).toEqual(['\u001b[?1;2c']); + }); + + test('answers a primary device attribute query split across PTY chunks', () => { + const first = consumeTerminalThemeQueries('', '\u001b[0', lightAppearance, { + respondToPrimaryDeviceAttributes: true, + }); + const second = consumeTerminalThemeQueries(first.pending, 'c', { + ...lightAppearance, + modeEnabled: first.modeEnabled, + }, { respondToPrimaryDeviceAttributes: true }); + + expect(first.pending).toBe('\u001b[0'); + expect(second.responses).toEqual(['\u001b[?1;2c']); + }); }); From 2c52240f8e65feab6d22416c37e47f1dda9e5cf8 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Mon, 3 Aug 2026 12:50:48 +0300 Subject: [PATCH 6/6] fix(sync): route sessions by server-confirmed directory, unstick queued sends Session directory resolution had no precedence contract: the selection-time directory short-circuited every lookup, and a persisted runtime value was consulted before the authoritative record. A worktree session selected before its directory store bootstrapped kept the active-directory fallback, and that guess was persisted, so it survived reloads and restarts. Directory resolution now lives in one module and orders sources by whether the server confirmed the path, not by whether the value is local or synced: authoritative (the child store that holds the session) > server-confirmed selection > worktree attachment/metadata (the requested path, pre-canonical) > remembered. A guessed selection is no longer persisted, remembered, or ranked. Chips read the same resolution the composer used, so queue keys cannot diverge. Queued auto-send could strand an item indefinitely: backoff, missing send configuration, and the recent-abort window all returned without scheduling a wake-up, so the queue only retried when an unrelated status or directory change re-ran the effect. A retry scheduler now wakes it at the earliest known time. A rejected send rolls the optimistic message back while the composer stays silent for transport failures, which makes it indistinguishable from nothing happening. Failures are now recorded to a bounded in-memory log surfaced in the About diagnostics report, alongside a directory-resolution breakdown, plus __opencodeDebug.diagnoseSessionDirectory() and getRecentSendFailures(). Prompted by a report of worktree prompting silently failing. That failure was not reproduced locally, so the diagnostics are what will identify it. --- .../components/chat/QueuedMessageChips.tsx | 10 +- .../hooks/useQueuedMessageAutoSend.test.ts | 49 ++++++ .../ui/src/hooks/useQueuedMessageAutoSend.ts | 65 ++++++- packages/ui/src/lib/debug.ts | 108 +++++++++++- .../ui/src/stores/useMultiRunStore.test.ts | 1 + packages/ui/src/sync/DOCUMENTATION.md | 25 +++ .../sync/__tests__/issue-1637-2270.test.ts | 1 + .../ui/src/sync/__tests__/issue-2039.test.ts | 69 +++++++- packages/ui/src/sync/send-failure-log.ts | 49 ++++++ packages/ui/src/sync/session-actions.test.ts | 1 + packages/ui/src/sync/session-actions.ts | 23 ++- .../sync/session-directory-resolution.test.ts | 131 ++++++++++++++ .../src/sync/session-directory-resolution.ts | 137 +++++++++++++++ packages/ui/src/sync/session-ui-store.ts | 166 +++++++++++++++--- packages/ui/src/sync/sync-refs.ts | 20 +++ 15 files changed, 814 insertions(+), 41 deletions(-) create mode 100644 packages/ui/src/sync/send-failure-log.ts create mode 100644 packages/ui/src/sync/session-directory-resolution.test.ts create mode 100644 packages/ui/src/sync/session-directory-resolution.ts diff --git a/packages/ui/src/components/chat/QueuedMessageChips.tsx b/packages/ui/src/components/chat/QueuedMessageChips.tsx index d49b1ae5..b64a3f4f 100644 --- a/packages/ui/src/components/chat/QueuedMessageChips.tsx +++ b/packages/ui/src/components/chat/QueuedMessageChips.tsx @@ -111,7 +111,15 @@ const EMPTY_QUEUE: QueuedMessage[] = []; export const QueuedMessageChips = memo(({ onEditMessage, onSendMessage }: QueuedMessageChipsProps) => { const { t } = useI18n(); const currentSessionId = useSessionUIStore((state) => state.currentSessionId); - const currentSessionDirectory = useSessionUIStore((state) => state.currentSessionDirectory); + // Must use the same resolution the composer used to build the queue key — + // reading currentSessionDirectory raw can key the chips to a different + // directory than the one the messages were queued under. + const currentSessionDirectory = useSessionUIStore( + React.useCallback( + (state) => (currentSessionId ? state.getDirectoryForSession(currentSessionId) : null), + [currentSessionId], + ), + ); const target = currentSessionId ? createMessageQueueTarget(currentSessionId, currentSessionDirectory) : null; const queueKey = target ? getMessageQueueKey(target) : null; const queuedMessages = useMessageQueueStore( diff --git a/packages/ui/src/hooks/useQueuedMessageAutoSend.test.ts b/packages/ui/src/hooks/useQueuedMessageAutoSend.test.ts index 6675b2a5..70c5eb43 100644 --- a/packages/ui/src/hooks/useQueuedMessageAutoSend.test.ts +++ b/packages/ui/src/hooks/useQueuedMessageAutoSend.test.ts @@ -29,12 +29,61 @@ mock.module('@/sync/session-ui-store', () => ({ import { buildQueuedAutoSendPayload, + createQueuedAutoSendRetryScheduler, getQueuedAutoSendRetryDelayMs, isQueuedAutoSendBackedOff, sendQueuedAutoSendPayload, shouldDispatchQueuedAutoSend, } from './useQueuedMessageAutoSend'; +describe('queued auto-send retry scheduler', () => { + test('wakes the queue when backoff expires', () => { + const callbacks = new Map void>(); + let nextTimer = 0; + let wakeups = 0; + const scheduler = createQueuedAutoSendRetryScheduler( + () => { wakeups += 1; }, + () => 1_000, + (callback, delay) => { + callbacks.set(++nextTimer, callback); + expect(delay).toBe(500); + return nextTimer as unknown as ReturnType; + }, + (timer) => { callbacks.delete(timer as unknown as number); }, + ); + + scheduler.schedule(1_500); + expect(callbacks.size).toBe(1); + callbacks.values().next().value?.(); + expect(wakeups).toBe(1); + }); + + test('keeps the earliest retry and cancels it on dispose', () => { + const callbacks = new Map void>(); + let nextTimer = 0; + const delays: number[] = []; + const scheduler = createQueuedAutoSendRetryScheduler( + () => undefined, + () => 1_000, + (callback, delay) => { + callbacks.set(++nextTimer, callback); + delays.push(delay); + return nextTimer as unknown as ReturnType; + }, + (timer) => { callbacks.delete(timer as unknown as number); }, + ); + + scheduler.schedule(3_000); + scheduler.schedule(4_000); + scheduler.schedule(2_000); + + expect(delays).toEqual([2_000, 1_000]); + expect(callbacks.size).toBe(1); + scheduler.dispose(); + expect(callbacks.size).toBe(0); + }); +}); + describe('shouldDispatchQueuedAutoSend', () => { test('dispatches only after an active session becomes idle', () => { expect(shouldDispatchQueuedAutoSend('busy', 'idle', false)).toBe(true); diff --git a/packages/ui/src/hooks/useQueuedMessageAutoSend.ts b/packages/ui/src/hooks/useQueuedMessageAutoSend.ts index ecb53e6b..bc327ebe 100644 --- a/packages/ui/src/hooks/useQueuedMessageAutoSend.ts +++ b/packages/ui/src/hooks/useQueuedMessageAutoSend.ts @@ -33,12 +33,47 @@ export const isQueuedAutoSendBackedOff = ( now: number, ): boolean => failure !== undefined && failure.messageId === messageId && now < failure.nextAttemptAt; -const hasRecentAbort = (sessionId: string): boolean => { +export const createQueuedAutoSendRetryScheduler = ( + onWake: () => void, + now: () => number = Date.now, + scheduleTimeout: (callback: () => void, delay: number) => ReturnType = setTimeout, + cancelTimeout: (timer: ReturnType) => void = clearTimeout, +) => { + let timer: ReturnType | null = null; + let scheduledAt: number | null = null; + + return { + schedule(retryAt: number) { + if (scheduledAt !== null && scheduledAt <= retryAt) return; + if (timer !== null) cancelTimeout(timer); + scheduledAt = retryAt; + timer = scheduleTimeout(() => { + timer = null; + scheduledAt = null; + onWake(); + }, Math.max(0, retryAt - now())); + }, + dispose() { + if (timer !== null) cancelTimeout(timer); + timer = null; + scheduledAt = null; + }, + }; +}; + +/** + * When the abort window is still open, returns the time it expires so the + * caller can wake the queue then. Returns `null` once sending is allowed + * again — a queued item must not wait for an unrelated state change to be + * retried after the window closes. + */ +const getAbortHoldUntil = (sessionId: string): number | null => { const abortRecord = useSessionUIStore.getState().sessionAbortFlags.get(sessionId); if (!abortRecord) { - return false; + return null; } - return Date.now() - abortRecord.timestamp < RECENT_ABORT_WINDOW_MS; + const holdUntil = abortRecord.timestamp + RECENT_ABORT_WINDOW_MS; + return Date.now() < holdUntil ? holdUntil : null; }; export const buildQueuedAutoSendPayload = (queue: QueuedMessage[]) => { @@ -149,6 +184,13 @@ export function useQueuedMessageAutoSend(enabledOrOptions?: boolean | { enabled? const sendFailuresRef = React.useRef>(new Map()); const previousStatusRef = React.useRef>(new Map()); const autoReviewBlockedSessionsRef = React.useRef>(new Set()); + const [retryTick, setRetryTick] = React.useState(0); + const retryScheduler = React.useMemo( + () => createQueuedAutoSendRetryScheduler(() => setRetryTick((value) => value + 1)), + [], + ); + + React.useEffect(() => () => retryScheduler.dispose(), [retryScheduler]); React.useEffect(() => { if (!enabled) { @@ -164,7 +206,9 @@ export function useQueuedMessageAutoSend(enabledOrOptions?: boolean | { enabled? if (inFlightSessionsRef.current.has(targetKey)) { return; } - if (hasRecentAbort(sessionId)) { + const abortHoldUntil = getAbortHoldUntil(sessionId); + if (abortHoldUntil !== null) { + retryScheduler.schedule(abortHoldUntil); return; } if (useAutoReviewStore.getState().isRunningForSession(sessionId)) { @@ -185,7 +229,8 @@ export function useQueuedMessageAutoSend(enabledOrOptions?: boolean | { enabled? const failure = sendFailuresRef.current.get(targetKey); if (failure && failure.messageId !== payload.queuedMessageId) { sendFailuresRef.current.delete(targetKey); - } else if (isQueuedAutoSendBackedOff(failure, payload.queuedMessageId, Date.now())) { + } else if (failure && isQueuedAutoSendBackedOff(failure, payload.queuedMessageId, Date.now())) { + retryScheduler.schedule(failure.nextAttemptAt); return; } @@ -195,6 +240,10 @@ export function useQueuedMessageAutoSend(enabledOrOptions?: boolean | { enabled? ? captured : resolveSessionSendConfig(sessionId); if (!resolved.providerID || !resolved.modelID) { + // Legacy queues may predate captured send configuration. Config + // hydration is asynchronous, so retry instead of stranding the item + // until an unrelated status or directory update happens. + retryScheduler.schedule(Date.now() + AUTO_SEND_RETRY_BASE_DELAY_MS); return; } @@ -213,11 +262,13 @@ export function useQueuedMessageAutoSend(enabledOrOptions?: boolean | { enabled? console.warn('[queue] queued auto-send failed:', error); const priorFailures = failure?.messageId === payload.queuedMessageId ? failure.failures : 0; const failures = priorFailures + 1; + const nextAttemptAt = Date.now() + getQueuedAutoSendRetryDelayMs(failures); sendFailuresRef.current.set(targetKey, { messageId: payload.queuedMessageId, failures, - nextAttemptAt: Date.now() + getQueuedAutoSendRetryDelayMs(failures), + nextAttemptAt, }); + retryScheduler.schedule(nextAttemptAt); } finally { inFlightSessionsRef.current.delete(targetKey); } @@ -257,5 +308,5 @@ export function useQueuedMessageAutoSend(enabledOrOptions?: boolean | { enabled? }); previousStatusRef.current = nextStatusMap; - }, [enabled, queuedMessages, sessionStatusRecord, autoReviewRuns, currentDirectory]); + }, [enabled, queuedMessages, sessionStatusRecord, autoReviewRuns, currentDirectory, retryTick, retryScheduler]); } diff --git a/packages/ui/src/lib/debug.ts b/packages/ui/src/lib/debug.ts index bafb8272..cece345d 100644 --- a/packages/ui/src/lib/debug.ts +++ b/packages/ui/src/lib/debug.ts @@ -1,12 +1,19 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { useSessionUIStore } from '@/sync/session-ui-store'; +import { useSessionUIStore, getRememberedSessionDirectory } from '@/sync/session-ui-store'; import { useDirectoryStore } from '@/stores/useDirectoryStore'; import { useProjectsStore } from '@/stores/useProjectsStore'; import { opencodeClient } from '@/lib/opencode/client'; import { checkIsGitRepository } from '@/lib/gitApi'; import { streamDebugEnabled } from '@/stores/utils/streamDebug'; import { copyTextToClipboard as copyPlainTextToClipboard } from '@/lib/clipboard'; -import { getSyncSessions, getSyncMessages, getSyncParts } from '@/sync/sync-refs'; +import { getSyncSessions, getSyncMessages, getSyncParts, getAllSyncSessions, getSyncSessionDirectory } from '@/sync/sync-refs'; +import { + describeSessionDirectorySources, + resolveSessionDirectoryFromSources, +} from '@/sync/session-directory-resolution'; +import { useSessionWorktreeStore } from '@/sync/session-worktree-store'; +import { getRecentSendFailures } from '@/sync/send-failure-log'; +import { getAttachedSessionDirectory } from '@/sync/session-worktree-contract'; import { useStreamingStore } from '@/sync/streaming'; import { runtimeFetch } from '@/lib/runtime-fetch'; import { getRegisteredRuntimeAPIs } from '@/contexts/runtimeAPIRegistry'; @@ -375,12 +382,107 @@ export const debugUtils = { openchamber: { settingsInfo, }, + // Empty is a meaningful answer here: it means no prompt was rejected in + // this session, so a "my message disappeared" report is not a rejected + // send and needs a different explanation. + recentSendFailures: getRecentSendFailures(), + currentSessionDirectoryResolution: sessionState.currentSessionId + ? this.diagnoseSessionDirectory(sessionState.currentSessionId) + : null, }; console.log('[DEBUG] App status snapshot:', report); return report; }, + /** + * Prompt sends that were rejected and rolled back in this app session. + * Newest first; empty means no send was rejected. + */ + getRecentSendFailures() { + const failures = getRecentSendFailures(); + if (failures.length === 0) { + console.log('[OK] No prompt sends were rejected in this session.'); + } else { + console.warn(`[ALERT] ${failures.length} rejected prompt send(s):`); + console.table(failures); + } + return failures; + }, + + /** + * Report how a session's directory is resolved, from every source, in + * precedence order. A send is routed by the winning value, so a disagreement + * here explains a prompt that vanishes without an error: it was posted + * against a directory that does not own the session. + */ + diagnoseSessionDirectory(sessionId?: string) { + const sessionState = useSessionUIStore.getState(); + const targetSessionId = sessionId ?? sessionState.currentSessionId; + + if (!targetSessionId) { + console.log('[ERROR] No session selected and no session id passed'); + return null; + } + + const attachment = getAttachedSessionDirectory( + useSessionWorktreeStore.getState().getAttachment(targetSessionId), + ); + const worktreeMetadata = sessionState.worktreeMetadata.get(targetSessionId)?.path ?? null; + const owningStoreDirectory = getSyncSessionDirectory(targetSessionId); + const sessionRecord = getAllSyncSessions().find((session) => session.id === targetSessionId); + const recordDirectory = (sessionRecord as { directory?: string | null } | undefined)?.directory ?? null; + const selected = targetSessionId === sessionState.currentSessionId + ? sessionState.currentSessionDirectory + : null; + + const remembered = getRememberedSessionDirectory(targetSessionId); + + const sources = { + attachment, + worktreeMetadata, + authoritative: owningStoreDirectory ?? recordDirectory, + selected, + remembered: remembered.runtime, + }; + + const resolution = resolveSessionDirectoryFromSources(sources); + const routedDirectory = sessionState.getDirectoryForSession(targetSessionId); + + const report = { + sessionId: targetSessionId, + isCurrentSession: targetSessionId === sessionState.currentSessionId, + routedDirectory, + resolvedFrom: resolution.source, + conflict: resolution.conflict, + sources: describeSessionDirectorySources(sources), + details: { + owningChildStore: owningStoreDirectory, + sessionRecordDirectory: recordDirectory, + sessionIndexed: Boolean(sessionRecord), + currentSessionDirectory: sessionState.currentSessionDirectory, + rememberedForRuntime: remembered.runtime, + persistedAcrossRestarts: remembered.persisted, + activeDirectory: useDirectoryStore.getState().currentDirectory ?? null, + opencodeClientDirectory: opencodeClient.getDirectory() ?? null, + }, + }; + + console.log('[DEBUG] Session directory resolution:', report); + if (resolution.conflict) { + console.warn( + `[ALERT] Directory sources disagree: using "${resolution.directory}" (${resolution.source}) ` + + `while "${resolution.conflict.directory}" came from ${resolution.conflict.source}.`, + ); + } else if (!routedDirectory) { + console.warn('[ALERT] No directory resolved for this session — sends fall back to the active directory.'); + } else { + console.log('[OK] All known sources agree on the session directory.'); + } + + return report; + }, + async buildDiagnosticsReport() { const report = await this.getAppStatus(); return JSON.stringify(report, null, 2); @@ -697,6 +799,8 @@ if (typeof window !== 'undefined') { console.log(' __opencodeDebug.getAllMessages(truncate?) - List all messages (truncate=true for short preview)'); console.log(' __opencodeDebug.truncateMessages(messages) - Truncate long fields in messages array'); console.log(' __opencodeDebug.getAppStatus() - Show app status snapshot'); + console.log(' __opencodeDebug.diagnoseSessionDirectory(sessionId?) - Show how the session directory is resolved'); + console.log(' __opencodeDebug.getRecentSendFailures() - List prompt sends that were rejected and rolled back'); console.log(' __opencodeDebug.checkLastMessage() - Check if last message is problematic'); console.log(' __opencodeDebug.findEmptyMessages() - Find all empty assistant messages'); console.log(' __opencodeDebug.showRetryHelp() - Show instructions for handling empty responses'); diff --git a/packages/ui/src/stores/useMultiRunStore.test.ts b/packages/ui/src/stores/useMultiRunStore.test.ts index b2a1bbb6..c01295f1 100644 --- a/packages/ui/src/stores/useMultiRunStore.test.ts +++ b/packages/ui/src/stores/useMultiRunStore.test.ts @@ -127,6 +127,7 @@ mock.module('./useGlobalSessionsStore', () => ({ })); mock.module('@/sync/sync-refs', () => ({ + getSyncSessionDirectory: () => null, registerSessionDirectory: (sessionID: string, directory: string) => { registeredDirectories.push({ sessionID, directory }); }, diff --git a/packages/ui/src/sync/DOCUMENTATION.md b/packages/ui/src/sync/DOCUMENTATION.md index 93a475ff..78152f32 100644 --- a/packages/ui/src/sync/DOCUMENTATION.md +++ b/packages/ui/src/sync/DOCUMENTATION.md @@ -197,6 +197,30 @@ Directory stores also own session-keyed sidecar notification channels for permis Message sidecar consumers also filter targeted updates by purpose before notifying React. Suspended live-tail text/reasoning changes do not rebuild visible message records, but structural Task session identity changes bypass suspension so a parent can link a newly created subagent immediately. Assistant-only part changes do not rebuild user input history, and targeted updates that preserve authoritative part buckets do not recheck a session that is already renderable. Message replacements, removed final part buckets, and conservative resets always notify. +## Session directory resolution + +`session-directory-resolution.ts` owns the precedence used to answer "which directory does this session belong to". Every send, message fetch, message-queue key, and send-confirmation lookup is routed by that answer, so a wrong value is not a display problem: the prompt is posted against a directory that does not own the session, the request is rejected, and the optimistic message is rolled back with no visible error. + +Precedence, highest authority first: + +The discriminator is whether the server confirmed the path, not whether the value is local or synced. + +| Source | Meaning | +|---|---| +| `authoritative` | The child store that actually holds the session, then its own record | +| `selected` | Server-confirmed directory captured at selection; a guessed one is never passed | +| `attachment` | Worktree attachment recorded by this client; the *requested* path | +| `worktree-metadata` | Worktree captured when the session was created in one; the *requested* path | +| `remembered` | Per-runtime directory persisted across restarts | + +Rules: + +1. `getSyncSessionDirectory()` is the authoritative session→directory mapping: a session lives in exactly the child store for its directory, whether or not the server populated `session.directory`. `null` means "not indexed yet", never "no directory". +2. `attachment` and `worktreeMetadata` hold the worktree path this client asked for, before the server canonicalized it. They are a hint for a session sync has not indexed yet, never a correction of a confirmed directory — otherwise a stale local path re-creates the very mismatch this precedence exists to prevent. +3. Never persist or rank a guessed directory. `selectSession` may fall back to the active directory to keep routing usable, but that value is not written to runtime memory, not written to the last-active snapshot, and not passed as `selected` — a persisted guess outlives the race that produced it and survives reloads and restarts. +4. Components must not read `currentSessionDirectory` to build request or queue keys; use `getDirectoryForSession()` so every consumer resolves identically. +5. A disagreement between sources is logged once per session, and `__opencodeDebug.diagnoseSessionDirectory()` reports every source in precedence order. + ## Session action rules Session actions live in `session-actions.ts` and are the canonical place for SDK-calling session mutations that affect global session lists. @@ -207,6 +231,7 @@ Rules: 2. If an action targets a session by ID, resolve the **session's own directory**. Do not assume the current directory is correct. 3. `session-ui-store.ts` should delegate to `session-actions.ts` for these mutations instead of duplicating SDK calls. 4. Sending after a revert commits the new branch optimistically: remove the reverted tail and marker before inserting the new message, and restore both if the send is rejected. +5. After session creation, the directory returned by the server is authoritative over the requested draft directory. The server may canonicalize a worktree path, and the first prompt must use the same directory identity as the created session. Examples of global-store updates performed in `session-actions.ts`: diff --git a/packages/ui/src/sync/__tests__/issue-1637-2270.test.ts b/packages/ui/src/sync/__tests__/issue-1637-2270.test.ts index 5773f57e..5cd7f3b1 100644 --- a/packages/ui/src/sync/__tests__/issue-1637-2270.test.ts +++ b/packages/ui/src/sync/__tests__/issue-1637-2270.test.ts @@ -42,6 +42,7 @@ mock.module("../session-ui-store", () => ({ })) mock.module("../sync-refs", () => ({ + getSyncSessionDirectory: () => null, registerSessionDirectory: (sessionID: string, directory: string) => { registerSessionDirectoryCalls.push({ sessionID, directory }) }, diff --git a/packages/ui/src/sync/__tests__/issue-2039.test.ts b/packages/ui/src/sync/__tests__/issue-2039.test.ts index 8197823c..ef9f5439 100644 --- a/packages/ui/src/sync/__tests__/issue-2039.test.ts +++ b/packages/ui/src/sync/__tests__/issue-2039.test.ts @@ -4,11 +4,16 @@ import { togglePermissionAutoAccept } from "../../components/chat/permissionAuto const storage = new Map() const createSessionCalls: Array<{ title?: string; directory: string | null; parentID: string | null; metadata?: unknown }> = [] const permissionAutoAcceptCalls: Array<[string, boolean]> = [] +let createdSessionDirectory: string | undefined const getMockCalls = (fn: unknown): unknown[][] => ((fn as { mock?: { calls: unknown[][] } }).mock?.calls ?? []) mock.module("zustand", () => ({ - create: () => (initializer: (set: (patch: unknown | ((state: unknown) => unknown)) => void, get: () => unknown) => Record) => { + create: () => (initializer: ( + set: (patch: unknown | ((state: unknown) => unknown)) => void, + get: () => unknown, + api?: unknown, + ) => Record) => { let state: Record const get = () => state const set = (patch: unknown | ((current: Record) => unknown)) => { @@ -16,7 +21,12 @@ mock.module("zustand", () => ({ state = next && typeof next === "object" ? { ...state, ...(next as Record) } : state } - state = initializer(set, get) + state = initializer(set, get, { + setState: set, + getState: get, + getInitialState: get, + subscribe: () => () => undefined, + } as never) const store = ((selector?: (current: Record) => unknown) => ( typeof selector === "function" ? selector(state) : state @@ -53,6 +63,11 @@ const deferredStorage: Storage = { mock.module("@/stores/utils/safeStorage", () => ({ getDeferredSafeStorage: () => deferredStorage, + createDeferredSafeJSONStorage: () => ({ + getItem: async () => null, + setItem: async () => undefined, + removeItem: async () => undefined, + }), })) mock.module("@/lib/opencode/client", () => ({ @@ -224,15 +239,18 @@ mock.module("../sync-refs", () => ({ getSyncMessages: () => [], getSyncParts: () => [], getAllSyncSessions: () => [], + getSyncSessionDirectory: () => null, })) mock.module("../session-actions", () => ({ createSession: mock(async (title: string | undefined, directory: string | null, parentID: string | null, metadata?: unknown) => { createSessionCalls.push({ title, directory, parentID, metadata }) - return { id: "ses_issue_2039", directory } + return { id: "ses_issue_2039", directory: createdSessionDirectory ?? directory } }), deleteSession: mock(async () => true), + deleteSessions: mock(async () => ({ deletedIds: [], failedIds: [] })), archiveSession: mock(async () => true), + archiveSessions: mock(async () => ({ archivedIds: [], failedIds: [] })), updateSessionTitle: mock(async () => undefined), shareSession: mock(async () => undefined), unshareSession: mock(async () => undefined), @@ -242,6 +260,9 @@ mock.module("../session-actions", () => ({ unrevertSession: mock(async () => undefined), forkFromMessage: mock(async () => undefined), fetchMessagesForSession: mock(async () => undefined), + getSessionLastAssistantModel: () => null, + patchSessionMetadata: mock(async () => undefined), + abortCurrentOperation: mock(async () => undefined), })) const { materializeOpenDraftSession, useSessionUIStore } = await import("../session-ui-store") @@ -298,6 +319,7 @@ describe("issue 2039 draft auto-accept", () => { storage.clear() createSessionCalls.length = 0 permissionAutoAcceptCalls.length = 0 + createdSessionDirectory = undefined useSessionUIStore.setState({ currentSessionId: null, @@ -348,4 +370,45 @@ describe("issue 2039 draft auto-accept", () => { expect(createSessionCalls).toHaveLength(0) expect(permissionAutoAcceptCalls).toHaveLength(0) }) + + test("uses the server-authoritative directory after worktree session creation", async () => { + createdSessionDirectory = "/canonical/worktree" + useSessionUIStore.getState().openNewSessionDraft({ + directoryOverride: "/requested/worktree", + }) + + const result = await materializeOpenDraftSession({ + providerID: "provider", + modelID: "model", + }) + + expect(createSessionCalls[0]?.directory).toBe("/requested/worktree") + expect(result?.directory).toBe("/canonical/worktree") + expect(useSessionUIStore.getState().currentSessionDirectory).toBe("/canonical/worktree") + }) + + test("routes the session by the canonical directory, not the requested worktree path", async () => { + createdSessionDirectory = "/canonical/worktree" + useSessionUIStore.getState().openNewSessionDraft({ + directoryOverride: "/requested/worktree", + }) + + const created = await materializeOpenDraftSession({ + providerID: "provider", + modelID: "model", + }) + const sessionId = created?.sessionId ?? "" + + // The worktree attachment still holds the path this client asked for. The + // directory every send, queue key, and confirmation lookup is routed by + // must be the canonical one the server returned. + useSessionUIStore.getState().setWorktreeMetadata(sessionId, { + path: "/requested/worktree", + projectDirectory: "/repo", + branch: "feature", + label: "feature", + }) + + expect(useSessionUIStore.getState().getDirectoryForSession(sessionId)).toBe("/canonical/worktree") + }) }) diff --git a/packages/ui/src/sync/send-failure-log.ts b/packages/ui/src/sync/send-failure-log.ts new file mode 100644 index 00000000..5bbac589 --- /dev/null +++ b/packages/ui/src/sync/send-failure-log.ts @@ -0,0 +1,49 @@ +/** + * Recent prompt-send failures, kept in memory for diagnostics. + * + * A rejected send rolls the optimistic message back and, for transport-level + * failures, the composer stays silent by design. That makes a misrouted or + * refused prompt indistinguishable from "nothing happened" — the user has + * nothing to report beyond "it disappeared". + * + * This buffer gives the failure somewhere to live until someone asks for it, + * via the About dialog's diagnostics report or `__opencodeDebug`. It is + * in-memory only: never persisted, never sent anywhere, and dropped on reload. + */ + +const MAX_RECORDED_SEND_FAILURES = 20 +const MAX_REASON_LENGTH = 200 + +export type SendFailureRecord = { + at: number + sessionId: string + messageId: string + /** Directory the prompt was routed to — the value under suspicion. */ + directory: string | null + /** HTTP status, or null for a transport failure with no response. */ + status: number | null + /** Whether the send may still have been accepted server-side. */ + ambiguous: boolean + /** Whether a confirmation refetch ran and failed to find the message. */ + confirmationChecked: boolean + reason: string +} + +const records: SendFailureRecord[] = [] + +export function recordSendFailure(record: Omit & { reason: string }): void { + records.push({ + ...record, + reason: record.reason.slice(0, MAX_REASON_LENGTH), + at: Date.now(), + }) + if (records.length > MAX_RECORDED_SEND_FAILURES) { + records.splice(0, records.length - MAX_RECORDED_SEND_FAILURES) + } +} + +/** Newest first. */ +export function getRecentSendFailures(): SendFailureRecord[] { + return [...records].reverse() +} + diff --git a/packages/ui/src/sync/session-actions.test.ts b/packages/ui/src/sync/session-actions.test.ts index 85da9ffc..c9c4325a 100644 --- a/packages/ui/src/sync/session-actions.test.ts +++ b/packages/ui/src/sync/session-actions.test.ts @@ -245,6 +245,7 @@ mock.module("./session-deletion-cleanup", () => ({ })) mock.module("./sync-refs", () => ({ + getSyncSessionDirectory: () => null, registerSessionDirectory: (sessionID: string, directory: string) => { registeredSessionDirectories.push({ sessionID, directory }) }, diff --git a/packages/ui/src/sync/session-actions.ts b/packages/ui/src/sync/session-actions.ts index e310f3a0..5a6eab26 100644 --- a/packages/ui/src/sync/session-actions.ts +++ b/packages/ui/src/sync/session-actions.ts @@ -13,6 +13,7 @@ import { opencodeClient } from "@/lib/opencode/client" import { mergeSessionDirectoryMetadata, resolveGlobalSessionDirectory, useGlobalSessionsStore } from "@/stores/useGlobalSessionsStore" import { useConfigStore } from "@/stores/useConfigStore" import { registerSessionDirectory } from "./sync-refs" +import { recordSendFailure } from "./send-failure-log" import { isSyntheticPart } from "@/lib/messages/synthetic" import { materializeSessionSnapshots } from "./materialization" import { stripMessageDiffSnapshots, stripSessionDiffSnapshots } from "./sanitize" @@ -1176,7 +1177,9 @@ export async function optimisticSend(input: { try { await input.send(messageID) } catch (error) { - const acceptedRecords = isAmbiguousSendFailure(error) + const status = getErrorStatus(error) + const ambiguousFailure = isAmbiguousSendFailure(error) + const acceptedRecords = ambiguousFailure ? await fetchRecentSendConfirmationRecords(input.sessionId, messageID, targetDirectory) : null @@ -1190,6 +1193,24 @@ export async function optimisticSend(input: { return } + // The rollback below makes the user's message disappear with no other + // trace, and the composer intentionally stays silent for transport-level + // failures. Record the failure so the About dialog's diagnostics report can + // answer "it disappeared and nothing happened" with an actual cause. + // `reason` is truncated by the recorder: a rejected send echoes the + // provider/OpenCode response body, which this log has no reason to keep. + const failureRecord = { + sessionId: input.sessionId, + messageId: messageID, + directory: targetDirectory ?? null, + status, + ambiguous: ambiguousFailure, + confirmationChecked: ambiguousFailure, + reason: error instanceof Error ? error.message : String(error), + } + recordSendFailure(failureRecord) + console.warn("[session-actions] prompt send rejected; rolling back optimistic message", failureRecord) + // Rollback via optimistic infrastructure _optimisticRemove({ sessionID: input.sessionId, diff --git a/packages/ui/src/sync/session-directory-resolution.test.ts b/packages/ui/src/sync/session-directory-resolution.test.ts new file mode 100644 index 00000000..c901ca30 --- /dev/null +++ b/packages/ui/src/sync/session-directory-resolution.test.ts @@ -0,0 +1,131 @@ +import { describe, expect, test } from 'bun:test'; + +import { + describeSessionDirectorySources, + resolveSessionDirectoryFromSources, +} from './session-directory-resolution'; + +const WORKTREE = '/repo/.worktrees/feature'; +const MAIN = '/repo'; + +describe('resolveSessionDirectoryFromSources', () => { + test('authoritative directory beats a selection-time fallback', () => { + const resolution = resolveSessionDirectoryFromSources({ + authoritative: WORKTREE, + selected: MAIN, + }); + + expect(resolution.directory).toBe(WORKTREE); + expect(resolution.source).toBe('authoritative'); + expect(resolution.conflict).toEqual({ source: 'selected', directory: MAIN }); + }); + + test('authoritative directory beats a directory persisted across restarts', () => { + const resolution = resolveSessionDirectoryFromSources({ + authoritative: WORKTREE, + remembered: MAIN, + }); + + expect(resolution.directory).toBe(WORKTREE); + expect(resolution.conflict).toEqual({ source: 'remembered', directory: MAIN }); + }); + + test('the indexed directory outranks a locally requested worktree path', () => { + // attachment/worktreeMetadata hold the path this client asked for, before + // the server canonicalized it. Letting them win would route prompts to a + // directory that no child store owns. + const resolution = resolveSessionDirectoryFromSources({ + attachment: '/requested/worktree', + worktreeMetadata: '/requested/worktree', + authoritative: WORKTREE, + }); + + expect(resolution.directory).toBe(WORKTREE); + expect(resolution.source).toBe('authoritative'); + expect(resolution.conflict).toEqual({ source: 'attachment', directory: '/requested/worktree' }); + }); + + test('a worktree attachment is used while the session is not indexed yet', () => { + // A guessed selection is not passed as `selected` at all, so the worktree + // assignment is the best available value during the bootstrap race. + const resolution = resolveSessionDirectoryFromSources({ + authoritative: null, + selected: null, + attachment: WORKTREE, + remembered: MAIN, + }); + + expect(resolution.directory).toBe(WORKTREE); + expect(resolution.source).toBe('attachment'); + expect(resolution.conflict).toEqual({ source: 'remembered', directory: MAIN }); + }); + + test('a server-confirmed selection outranks the requested worktree path', () => { + const resolution = resolveSessionDirectoryFromSources({ + authoritative: null, + selected: '/canonical/worktree', + attachment: '/requested/worktree', + worktreeMetadata: '/requested/worktree', + }); + + expect(resolution.directory).toBe('/canonical/worktree'); + expect(resolution.source).toBe('selected'); + }); + + test('falls back to the selection hint while the session is not indexed yet', () => { + const resolution = resolveSessionDirectoryFromSources({ + authoritative: null, + selected: WORKTREE, + }); + + expect(resolution.directory).toBe(WORKTREE); + expect(resolution.source).toBe('selected'); + expect(resolution.conflict).toBeNull(); + }); + + test('agreeing sources report no conflict', () => { + const resolution = resolveSessionDirectoryFromSources({ + authoritative: WORKTREE, + selected: WORKTREE, + remembered: WORKTREE, + }); + + expect(resolution.conflict).toBeNull(); + }); + + test('reports the first disagreeing source, not the last', () => { + const resolution = resolveSessionDirectoryFromSources({ + authoritative: WORKTREE, + selected: MAIN, + remembered: '/somewhere/else', + }); + + expect(resolution.conflict).toEqual({ source: 'selected', directory: MAIN }); + }); + + test('treats missing and blank values as unknown, never as a directory', () => { + const resolution = resolveSessionDirectoryFromSources({ + attachment: null, + worktreeMetadata: ' ', + authoritative: undefined, + selected: '', + }); + + expect(resolution.directory).toBeNull(); + expect(resolution.source).toBe('none'); + expect(resolution.conflict).toBeNull(); + }); +}); + +describe('describeSessionDirectorySources', () => { + test('lists populated sources in precedence order', () => { + expect(describeSessionDirectorySources({ + remembered: MAIN, + authoritative: WORKTREE, + selected: '', + })).toEqual([ + { source: 'authoritative', directory: WORKTREE }, + { source: 'remembered', directory: MAIN }, + ]); + }); +}); diff --git a/packages/ui/src/sync/session-directory-resolution.ts b/packages/ui/src/sync/session-directory-resolution.ts new file mode 100644 index 00000000..d8069176 --- /dev/null +++ b/packages/ui/src/sync/session-directory-resolution.ts @@ -0,0 +1,137 @@ +/** + * Session → directory resolution precedence. + * + * A session's directory decides which OpenCode project every send, message + * fetch, queue key, and confirmation lookup is routed to. Getting it wrong is + * not a cosmetic problem: the prompt is posted against a directory that does + * not own the session, the send is rejected, and the optimistic message is + * rolled back with no visible error. + * + * The precedence below is deliberate and ordered by authority, not by + * convenience: + * + * The ordering discriminator is **whether the server confirmed the path**, not + * whether the value is local or synced: + * + * 1. `authoritative` — the child store that actually holds the session, then + * the session's own record. Server-backed truth for an indexed session. + * 2. `selected` — the directory captured when the session was selected, but + * only when it came from a server response (the directory `createSession` + * returned, which may be a canonicalized form of what was requested). A + * selection that fell back to the active directory is a guess and is not + * passed here at all. + * 3. `attachment` / `worktreeMetadata` — the worktree this client assigned to + * the session. Both hold the *requested* path, before the server had a + * chance to canonicalize it, so they are a hint for a session sync has not + * indexed yet, never a correction of a confirmed one. + * 4. `remembered` — the per-runtime directory persisted across restarts. Last + * resort: it survives reloads, so a value written from a startup fallback + * would otherwise outlive the race that produced it. + * + * Routing a prompt by an unconfirmed path posts it against a directory that + * does not own the session, and the send is rejected. Moves need no exception: + * a session move updates the owning child store before any client-side value. + */ + +export type SessionDirectorySource = + | 'authoritative' + | 'selected' + | 'attachment' + | 'worktree-metadata' + | 'remembered' + | 'none' + +export type SessionDirectorySources = { + /** Directory of the child store that holds the session, or its own record. */ + authoritative?: string | null + /** Server-confirmed directory captured at selection. Never a guessed one. */ + selected?: string | null + /** Worktree attachment recorded for this session; the requested path. */ + attachment?: string | null + /** Worktree metadata captured when the session was created in a worktree. */ + worktreeMetadata?: string | null + /** Directory persisted for this runtime; may outlive the race that wrote it. */ + remembered?: string | null +} + +export type SessionDirectoryResolution = { + directory: string | null + source: SessionDirectorySource + /** + * Set when a lower-priority source disagrees with the winning one. This is + * the signature of the stale-directory bug: a persisted or selection-time + * fallback pointing at the parent repository while the session lives in a + * worktree. + */ + conflict: { source: SessionDirectorySource; directory: string } | null +} + +const RESOLUTION_ORDER: ReadonlyArray> = [ + 'authoritative', + 'selected', + 'attachment', + 'worktree-metadata', + 'remembered', +] + +const readSource = ( + sources: SessionDirectorySources, + source: Exclude, +): string | null => { + const value = source === 'attachment' + ? sources.attachment + : source === 'worktree-metadata' + ? sources.worktreeMetadata + : source === 'authoritative' + ? sources.authoritative + : source === 'selected' + ? sources.selected + : sources.remembered + if (typeof value !== 'string') return null + const trimmed = value.trim() + return trimmed.length > 0 ? trimmed : null +} + +/** + * Resolve a session directory from every known source, reporting which source + * won and whether a weaker source disagreed. + * + * Callers normalize paths before passing them in; this module only orders + * authority and never rewrites a path. + */ +export const resolveSessionDirectoryFromSources = ( + sources: SessionDirectorySources, +): SessionDirectoryResolution => { + let winner: { source: SessionDirectorySource; directory: string } | null = null + let conflict: { source: SessionDirectorySource; directory: string } | null = null + + for (const source of RESOLUTION_ORDER) { + const directory = readSource(sources, source) + if (!directory) continue + if (!winner) { + winner = { source, directory } + continue + } + if (!conflict && directory !== winner.directory) { + conflict = { source, directory } + } + } + + if (!winner) { + return { directory: null, source: 'none', conflict: null } + } + + return { directory: winner.directory, source: winner.source, conflict } +} + +/** Every source that carries a value, in precedence order. For diagnostics. */ +export const describeSessionDirectorySources = ( + sources: SessionDirectorySources, +): Array<{ source: SessionDirectorySource; directory: string }> => { + const described: Array<{ source: SessionDirectorySource; directory: string }> = [] + for (const source of RESOLUTION_ORDER) { + const directory = readSource(sources, source) + if (directory) described.push({ source, directory }) + } + return described +} diff --git a/packages/ui/src/sync/session-ui-store.ts b/packages/ui/src/sync/session-ui-store.ts index 33618f71..4f65e98b 100644 --- a/packages/ui/src/sync/session-ui-store.ts +++ b/packages/ui/src/sync/session-ui-store.ts @@ -40,7 +40,13 @@ import { getSyncMessages, getSyncParts, getDirectoryState, + getSyncSessionDirectory, } from "./sync-refs" +import { + resolveSessionDirectoryFromSources, + type SessionDirectoryResolution, + type SessionDirectorySources, +} from "./session-directory-resolution" import { markSessionViewed } from "./notification-store" import { setActiveSession } from "./sync-context" import { @@ -73,7 +79,7 @@ import { useSessionWorktreeStore } from "./session-worktree-store" import { getAttachedSessionDirectory } from "./session-worktree-contract" import { setSessionOpener } from "./session-navigation" import { getRuntimeKey } from "@/lib/runtime-switch" -import { clearLastActiveSession, persistLastActiveSession } from "./last-session-cache" +import { clearLastActiveSession, persistLastActiveSession, readLastActiveSession } from "./last-session-cache" import { persistWorktreeTopology, readPersistedWorktreeTopology } from "./worktree-topology-cache" import { rememberRuntimeLiveStatus } from "./runtime-live-memory" @@ -394,23 +400,108 @@ const getAttachmentForSession = (sessionId: string | null | undefined): SessionW return useSessionWorktreeStore.getState().getAttachment(sessionId) } +/** + * Authoritative directory for a session: the child store that holds it, and + * only then the session record's own fields. `null` means "not indexed yet", + * never "no directory" — callers must fall back rather than treat it as empty. + */ +const getAuthoritativeSessionDirectory = (sessionId: string): string | null => { + const owningDirectory = getSyncSessionDirectory(sessionId) + if (owningDirectory) return normalizePath(owningDirectory) + const target = getAllSyncSessions().find((s) => s.id === sessionId) + return target ? resolveDirectoryKey(target) : null +} + +/** + * Directory remembered for a session in this runtime, plus the one persisted + * across restarts. Exported for diagnostics: a stale persisted directory is the + * hardest source to observe and the one that survives reloads, so a report that + * cannot show it cannot rule it out. + */ +export const getRememberedSessionDirectory = (sessionId: string): { + runtime: string | null + persisted: string | null +} => { + const key = runtimeMemoryKey() + const runtimeMemory = runtimeSessionMemory.get(key) + const persisted = readLastActiveSession(key) + return { + runtime: runtimeMemory?.sessionId === sessionId ? normalizePath(runtimeMemory.directory) : null, + persisted: persisted?.sessionId === sessionId ? normalizePath(persisted.directory) : null, + } +} + +/** + * Session whose `currentSessionDirectory` is only the active directory, used + * because the session's own directory was not known at selection time. Such a + * value must never outrank a worktree assignment or reach persistence — it is + * a guess, not a selection. + */ +let guessedSelectionSessionId: string | null = null + +const collectSessionDirectorySources = ( + sessionId: string, + getWtMeta: (id: string) => WorktreeMetadata | undefined, + selected: string | null, +): SessionDirectorySources => ({ + authoritative: getAuthoritativeSessionDirectory(sessionId), + selected: sessionId === guessedSelectionSessionId ? null : normalizePath(selected), + attachment: getAttachedSessionDirectory(getAttachmentForSession(sessionId)), + worktreeMetadata: normalizePath(getWtMeta(sessionId)?.path ?? null), + remembered: getRememberedSessionDirectory(sessionId).runtime, +}) + +/** + * Conflicts already warned about, so a stale directory logs once instead of on + * every keystroke. Keyed by runtime *and* the exact pair of directories: the + * same session ID means a different thing in another runtime, and a conflict + * that reappears after being resolved is news worth logging again. Bounded so + * a long-lived session cannot grow it without limit. + */ +const reportedDirectoryConflicts = new Set() +const MAX_REPORTED_DIRECTORY_CONFLICTS = 200 + +const reportSessionDirectoryConflict = ( + sessionId: string, + resolution: SessionDirectoryResolution, +): void => { + if (!resolution.conflict) return + const conflictKey = JSON.stringify([ + runtimeMemoryKey(), + sessionId, + resolution.directory, + resolution.conflict.source, + resolution.conflict.directory, + ]) + if (reportedDirectoryConflicts.has(conflictKey)) return + if (reportedDirectoryConflicts.size >= MAX_REPORTED_DIRECTORY_CONFLICTS) { + reportedDirectoryConflicts.clear() + } + reportedDirectoryConflicts.add(conflictKey) + console.warn( + "[session-directory] session directory sources disagree; using the higher-authority one. " + + "Run __opencodeDebug.diagnoseSessionDirectory() for the full picture.", + { + sessionId, + using: resolution.source, + directory: resolution.directory, + conflictingSource: resolution.conflict.source, + conflictingDirectory: resolution.conflict.directory, + }, + ) +} + const resolveSessionDirectory = ( sessionId: string | null | undefined, getWtMeta: (id: string) => WorktreeMetadata | undefined, + selected: string | null = null, ): string | null => { if (!sessionId) return null - const attachmentDirectory = getAttachedSessionDirectory(getAttachmentForSession(sessionId)) - if (attachmentDirectory) return attachmentDirectory - const metaPath = getWtMeta(sessionId)?.path - if (typeof metaPath === "string" && metaPath.trim().length > 0) return normalizePath(metaPath) - const runtimeMemory = runtimeSessionMemory.get(runtimeMemoryKey()) - if (runtimeMemory?.sessionId === sessionId && runtimeMemory.directory) { - return normalizePath(runtimeMemory.directory) - } - const sessions = getAllSyncSessions() - const target = sessions.find((s) => s.id === sessionId) - if (!target) return null - return resolveDirectoryKey(target) + const resolution = resolveSessionDirectoryFromSources( + collectSessionDirectorySources(sessionId, getWtMeta, selected), + ) + reportSessionDirectoryConflict(sessionId, resolution) + return resolution.directory } const activateConfigForDirectory = async (directory: string | null | undefined): Promise => { @@ -504,13 +595,18 @@ export async function materializeOpenDraftSession(selection: { const created = await store.createSession(draft.title, draftDirectoryOverride, draft.parentID ?? null) if (!created?.id) throw new Error("Failed to create session") + // The server response is authoritative. It may canonicalize a requested + // worktree path (for example through a symlink or platform path casing). + // Sending with the pre-canonical draft path can target a different + // directory scope than the session that was just created. + const createdDirectory = normalizePath(created.directory ?? draftDirectoryOverride ?? null) + persistDraftTarget({ projectId: draftProjectId, - directory: normalizePath(draftDirectoryOverride ?? created.directory ?? null), + directory: createdDirectory, }) const draftSyntheticParts = draft.syntheticParts - const createdDirectory = normalizePath(draftDirectoryOverride ?? created.directory ?? null) const configState = useConfigStore.getState() void activateConfigForDirectory(createdDirectory).catch((error) => { console.warn("Failed to activate directory after creating session:", error) @@ -604,7 +700,13 @@ export const useSessionUIStore = create()((set, get) => ({ (sid) => get().worktreeMetadata.get(sid), ) const fallbackDir = opencodeClient.getDirectory() ?? directoryState.currentDirectory ?? null - const resolvedDir = (directoryHint ? normalizePath(directoryHint) : null) ?? sessionDir ?? fallbackDir + const knownDir = (directoryHint ? normalizePath(directoryHint) : null) ?? sessionDir + const resolvedDir = knownDir ?? fallbackDir + // `fallbackDir` is the active directory, not this session's directory. It + // keeps routing usable while the owning directory store bootstraps, but it + // must never be remembered: a persisted guess outlives the race that + // produced it and survives reloads and restarts. + const isGuessedDir = knownDir === null const projectsState = useProjectsStore.getState() const sessionProject = resolvedDir ? resolveProjectForSessionDirectory( @@ -617,12 +719,14 @@ export const useSessionUIStore = create()((set, get) => ({ // Set the directory together with the session id so chat hooks read the // same child store that send/SSE events will update during startup races. set({ currentSessionId: id, currentSessionDirectory: id ? resolvedDir ?? null : null }) - writeRuntimeSessionMemory(key, { sessionId: id, directory: resolvedDir ?? null }) + guessedSelectionSessionId = isGuessedDir && id ? id : null + const rememberedDir = isGuessedDir ? null : resolvedDir ?? null + writeRuntimeSessionMemory(key, { sessionId: id, directory: rememberedDir }) // Keep the last NON-null session per runtime across app restarts (cold // mobile launches reopen it after the instance reconnects). Going back to // a draft intentionally does not erase it. if (id) { - persistLastActiveSession(key, { sessionId: id, directory: resolvedDir ?? null }) + persistLastActiveSession(key, { sessionId: id, directory: rememberedDir }) } // Kick off the message fetch on the same tick, before React commits the @@ -1560,16 +1664,19 @@ export const useSessionUIStore = create()((set, get) => ({ }, getDirectoryForSession: (sessionId) => { - if (sessionId === get().currentSessionId && get().currentSessionDirectory) { - return get().currentSessionDirectory - } - const resolved = resolveSessionDirectory(sessionId, (sid) => get().worktreeMetadata.get(sid)) + // The selection-time directory participates in resolution, it does not + // short-circuit it. For a worktree session selected before its directory + // store finished bootstrapping, that value is a startup fallback pointing + // at the parent repository; letting it win would route every send, queue + // key, and send-confirmation lookup to a directory that does not own the + // session. + const selected = sessionId === get().currentSessionId ? get().currentSessionDirectory : null + const resolved = resolveSessionDirectory( + sessionId, + (sid) => get().worktreeMetadata.get(sid), + selected, + ) if (resolved) return resolved - const attachmentDirectory = getAttachedSessionDirectory(getAttachmentForSession(sessionId)) - if (attachmentDirectory) return attachmentDirectory - const sessions = getAllSyncSessions() - const session = sessions.find((s) => s.id === sessionId) - if (session) return resolveDirectoryKey(session) const globalStore = useGlobalSessionsStore.getState() const globalSession = [...globalStore.activeSessions, ...globalStore.archivedSessions] .find((s) => s.id === sessionId) @@ -1634,6 +1741,11 @@ export const useSessionUIStore = create()((set, get) => ({ setSessionDirectory: (sessionId, directory) => { const normalized = normalizePath(directory) + // Callers set this from a confirmed destination (a completed move, a + // created worktree), so the selection is no longer a guess. + if (sessionId === guessedSelectionSessionId) { + guessedSelectionSessionId = null + } if (sessionId === get().currentSessionId) { set({ currentSessionDirectory: normalized }) writeRuntimeSessionMemory(runtimeMemoryKey(), { sessionId, directory: normalized }) diff --git a/packages/ui/src/sync/sync-refs.ts b/packages/ui/src/sync/sync-refs.ts index 9b15f0d3..0d96911c 100644 --- a/packages/ui/src/sync/sync-refs.ts +++ b/packages/ui/src/sync/sync-refs.ts @@ -17,6 +17,7 @@ const configListeners = new Set<(directory: string, config: Config) => void>() let cachedSessionManager: ChildStoreManager | null = null let cachedSessionSlices = new Map() let cachedSessionsById = new Map() +let cachedSessionDirectoryById = new Map() export function setSyncRefs( _sdk: OpencodeClient, @@ -29,6 +30,7 @@ export function setSyncRefs( cachedSessionManager = null cachedSessionSlices = new Map() cachedSessionsById = new Map() + cachedSessionDirectoryById = new Map() } _directory = directory if (registerSessionDirectory) { @@ -103,20 +105,38 @@ export function getAllSyncSessionMap(): ReadonlyMap() const nextSessionsById = new Map() + const nextDirectoriesById = new Map() for (const [directory, store] of stores.children) { const sessions = store.getState().session nextSlices.set(directory, sessions) for (const session of sessions) { if (!session?.id) continue nextSessionsById.set(session.id, session) + nextDirectoriesById.set(session.id, directory) } } cachedSessionManager = stores cachedSessionSlices = nextSlices cachedSessionsById = nextSessionsById + cachedSessionDirectoryById = nextDirectoriesById return cachedSessionsById } +/** + * Directory of the child store that actually holds this session. + * + * This is the authoritative session→directory mapping: a session is present in + * exactly the store for the directory it belongs to, regardless of whether the + * server populated `session.directory` on the record itself. Returns `null` + * when no initialized child store contains the session, which means "unknown", + * never "no directory". + */ +export function getSyncSessionDirectory(sessionId: string): string | null { + if (!sessionId) return null + getAllSyncSessionMap() + return cachedSessionDirectoryById.get(sessionId) ?? null +} + /** Read messages for a session from current directory's child store */ export function getSyncMessages(sessionId: string, directory?: string) { return getDirectoryState(directory)?.message[sessionId] ?? []