From a0c3e197b52fce2d72c3b78f0a0ecde93396bb5a Mon Sep 17 00:00:00 2001 From: d-kimsuon Date: Tue, 5 May 2026 16:14:40 +0900 Subject: [PATCH 01/14] fix(i18n): detect missing translation keys --- src/scripts/check-i18n.ts | 65 ++++++++++++++++++++++++++++++++++- src/web/lib/i18n/resources.ts | 6 ++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/scripts/check-i18n.ts b/src/scripts/check-i18n.ts index 7ba2ce5..901cb01 100644 --- a/src/scripts/check-i18n.ts +++ b/src/scripts/check-i18n.ts @@ -1,7 +1,16 @@ +import { readdirSync, readFileSync, statSync } from 'node:fs'; +import { join, relative } from 'node:path'; + import { defaultLanguage, resources, supportedLanguages } from '../web/lib/i18n/resources.ts'; type FlatTranslations = ReadonlyMap; +type UsedTranslationKey = { + readonly file: string; + readonly key: string; + readonly line: number; +}; + const flattenTranslations = (value: unknown, prefix = ''): FlatTranslations => { if (typeof value === 'string') { return new Map([[prefix, value]]); @@ -32,6 +41,52 @@ const identicalPrimaryKeyValues = (primary: FlatTranslations): readonly string[] .filter(([key, value]) => key === value.trim()) .map(([key]) => key); +const sourceFiles = (directory: string): readonly string[] => { + const entries = readdirSync(directory).sort(); + return entries.flatMap((entry) => { + const path = join(directory, entry); + const stats = statSync(path); + if (stats.isDirectory()) { + return sourceFiles(path); + } + if (/\.[jt]sx?$/.test(entry)) { + return [path]; + } + return []; + }); +}; + +const lineNumberAt = (content: string, index: number): number => + content.slice(0, index).split('\n').length; + +const staticTCallPattern = /(? + files.flatMap((file) => { + const content = readFileSync(file, 'utf8'); + return [...content.matchAll(staticTCallPattern)].map((match) => ({ + file: relative(process.cwd(), file), + key: match[2] ?? '', + line: lineNumberAt(content, match.index ?? 0), + })); + }); + +const missingUsedKeys = ({ + primary, + usedKeys, +}: { + readonly primary: FlatTranslations; + readonly usedKeys: readonly UsedTranslationKey[]; +}): readonly UsedTranslationKey[] => + usedKeys + .filter(({ key }) => !primary.has(key)) + .sort( + (left, right) => + left.key.localeCompare(right.key) || + left.file.localeCompare(right.file) || + left.line - right.line, + ); + const primaryTranslations = flattenTranslations(resources[defaultLanguage].translation); const failures = supportedLanguages.flatMap((language) => { const translations = flattenTranslations(resources[language].translation); @@ -54,7 +109,15 @@ const identicalPrimaryFailures = identicalPrimaryKeyValues(primaryTranslations). (key) => `${defaultLanguage}: primary key and value are identical for '${key}'`, ); -const allFailures = [...failures, ...identicalPrimaryFailures]; +const missingUsedKeyFailures = missingUsedKeys({ + primary: primaryTranslations, + usedKeys: usedTranslationKeys(sourceFiles(join(process.cwd(), 'src/web'))), +}).map( + ({ file, key, line }) => + `used key '${key}' is missing from ${defaultLanguage} resources (${file}:${String(line)})`, +); + +const allFailures = [...failures, ...identicalPrimaryFailures, ...missingUsedKeyFailures]; if (allFailures.length > 0) { console.error(`i18n check failed with ${String(allFailures.length)} issue(s):`); diff --git a/src/web/lib/i18n/resources.ts b/src/web/lib/i18n/resources.ts index e741140..3a592bb 100644 --- a/src/web/lib/i18n/resources.ts +++ b/src/web/lib/i18n/resources.ts @@ -27,6 +27,9 @@ export const resources = { custom: 'カスタム', enabled: '有効', disabled: '無効', + enable: '有効にする', + disable: '無効にする', + saveFailed: '保存に失敗しました。', never: 'なし', }, settings: { @@ -350,6 +353,9 @@ export const resources = { custom: 'Custom', enabled: 'Enabled', disabled: 'Disabled', + enable: 'Enable', + disable: 'Disable', + saveFailed: 'Failed to save.', never: 'Never', }, settings: { From 6592a276db6757d98e512e08d989dca461192393 Mon Sep 17 00:00:00 2001 From: d-kimsuon Date: Tue, 5 May 2026 16:21:39 +0900 Subject: [PATCH 02/14] fix(web): prevent routines menu layout shift --- .../$projectId/project-routines-page.tsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/web/app/projects/$projectId/project-routines-page.tsx b/src/web/app/projects/$projectId/project-routines-page.tsx index f9413be..781b780 100644 --- a/src/web/app/projects/$projectId/project-routines-page.tsx +++ b/src/web/app/projects/$projectId/project-routines-page.tsx @@ -1,6 +1,5 @@ -import type { FC } from 'react'; - import { useSuspenseQuery } from '@tanstack/react-query'; +import { Suspense, type FC } from 'react'; import { useTranslation } from 'react-i18next'; import { fetchAgentProviders, fetchProject, fetchSessions } from '../../../lib/api/acp.ts'; @@ -9,6 +8,16 @@ import { ProjectMenuContent } from './project-menu-content.tsx'; import { agentProvidersQueryKey, projectQueryKey, sessionsQueryKey } from './queries.ts'; import { useLoadSessionDialog } from './use-load-session-dialog.tsx'; +const RoutineSettingsPanelFallback: FC = () => { + const { t } = useTranslation(); + + return ( +
+ {t('common.loading')} +
+ ); +}; + export const ProjectRoutinesPage: FC<{ readonly projectId: string }> = ({ projectId }) => { const { t } = useTranslation(); const { data: projectData } = useSuspenseQuery({ @@ -52,7 +61,9 @@ export const ProjectRoutinesPage: FC<{ readonly projectId: string }> = ({ projec {t('routines.description', { projectName: projectData.project.name })}

- + }> + + ); From a7d848d7cd9e512832e3ca4e575b9b18b6d60339 Mon Sep 17 00:00:00 2001 From: d-kimsuon Date: Tue, 5 May 2026 17:00:19 +0900 Subject: [PATCH 03/14] fix(web): refine session detail ui loading --- .../projects/$projectId/project-chat-page.tsx | 131 ++++++++++++++---- .../$projectId/project-menu-content.tsx | 6 +- src/web/app/settings/settings-panels.tsx | 9 +- 3 files changed, 116 insertions(+), 30 deletions(-) diff --git a/src/web/app/projects/$projectId/project-chat-page.tsx b/src/web/app/projects/$projectId/project-chat-page.tsx index ab6e9d8..7b3c083 100644 --- a/src/web/app/projects/$projectId/project-chat-page.tsx +++ b/src/web/app/projects/$projectId/project-chat-page.tsx @@ -861,7 +861,26 @@ const SlashCommandsLoader: FC<{ return null; }; -const SESSION_MESSAGES_PAGE_SIZE = 100; +const SESSION_MESSAGES_PAGE_SIZE = 50; +const initiallyPrefetchedSessionMessageKeys = new Set(); + +const compareChatMessageOrder = (left: ChatMessage, right: ChatMessage): number => { + const createdAtOrder = left.createdAt.localeCompare(right.createdAt); + return createdAtOrder !== 0 ? createdAtOrder : left.id.localeCompare(right.id); +}; + +const mergeHydratedMessages = ({ + fetched, + local, +}: { + readonly fetched: readonly ChatMessage[]; + readonly local: readonly ChatMessage[]; +}): readonly ChatMessage[] => { + const fetchedIds = new Set(fetched.map((message) => message.id)); + return [...fetched, ...local.filter((message) => !fetchedIds.has(message.id))].toSorted( + compareChatMessageOrder, + ); +}; const SessionMessagesHydrator: FC<{ readonly sessionId: string; @@ -870,6 +889,7 @@ const SessionMessagesHydrator: FC<{ fetchNextPage: () => Promise, hasNextPage: boolean, isFetchingNextPage: boolean, + loadedPageCount: number, ) => void; }> = ({ sessionId, onHydrated, onFetchNextPageReady }) => { const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useSuspenseInfiniteQuery({ @@ -894,9 +914,34 @@ const SessionMessagesHydrator: FC<{ onHydrated(sessionId, allMessages); }, [allMessages, onHydrated, sessionId]); + const fetchNextMessagesPage = useCallback( + () => fetchNextPage({ cancelRefetch: false }), + [fetchNextPage], + ); + useLayoutEffect(() => { - onFetchNextPageReady(fetchNextPage, hasNextPage, isFetchingNextPage); - }, [fetchNextPage, hasNextPage, isFetchingNextPage, onFetchNextPageReady]); + onFetchNextPageReady(fetchNextMessagesPage, hasNextPage, isFetchingNextPage, data.pages.length); + }, [ + data.pages.length, + fetchNextMessagesPage, + hasNextPage, + isFetchingNextPage, + onFetchNextPageReady, + ]); + + useEffect(() => { + const prefetchKey = sessionId; + if ( + data.pages.length !== 1 || + !hasNextPage || + isFetchingNextPage || + initiallyPrefetchedSessionMessageKeys.has(prefetchKey) + ) { + return; + } + initiallyPrefetchedSessionMessageKeys.add(prefetchKey); + void fetchNextMessagesPage(); + }, [data.pages.length, fetchNextMessagesPage, hasNextPage, isFetchingNextPage, sessionId]); return null; }; @@ -988,6 +1033,7 @@ export const ProjectChatPage: FC<{ const attachFileInputRef = useRef(null); const speechRecognitionRef = useRef(null); const chatContentRef = useRef(null); + const olderMessagesPrefetchRef = useRef(null); const scrollAnchorRef = useRef(null); const shouldStickToBottomRef = useRef(true); const lastActiveTranscriptKeyRef = useRef(null); @@ -997,12 +1043,14 @@ export const ProjectChatPage: FC<{ const activeTranscriptKeyRef = useRef(null); const draftSessionRedirectRequestRef = useRef(null); const previousVisibleMessageCountRef = useRef(0); + const [chatViewportElement, setChatViewportElement] = useState(null); const [isChatFollowingTail, setIsChatFollowingTail] = useState(true); const [unreadMessageCount, setUnreadMessageCount] = useState(0); const [messagesBelowScroll, setMessagesBelowScroll] = useState(0); - const fetchNextPageRef = useRef<(() => void) | null>(null); + const fetchNextPageRef = useRef<(() => Promise) | null>(null); const [hasNextPage, setHasNextPage] = useState(false); const [isFetchingNextPage, setIsFetchingNextPage] = useState(false); + const [loadedSessionMessagePageCount, setLoadedSessionMessagePageCount] = useState(0); const fetchingNextPageRef = useRef(false); const onAgentCatalogReady = useCallback((catalog: AgentModelCatalogResponse) => { @@ -1706,17 +1754,12 @@ export const ProjectChatPage: FC<{ ) { return current; } - /** - * 送信中に SSE 由来の再取得で、新メッセージ行が未コミットの古いスナップショット - * (本より短い行数)が届くと、ここで上書きすると履歴を全部失ったように見える。 - * 行は append-only の前提なので、短い取得ではローカルより退けない。 - */ - if (local.length > 0 && messages.length < local.length) { - return current; - } return { ...current, - [targetSessionId]: messages.map((message) => ({ ...message })), + [targetSessionId]: mergeHydratedMessages({ + fetched: messages, + local, + }).map((message) => ({ ...message })), }; }); }, @@ -2368,10 +2411,16 @@ export const ProjectChatPage: FC<{ 'custom'); const handleFetchNextPageReady = useCallback( - (fetchNextPage: () => void, nextPageExists: boolean, nextIsFetchingNextPage: boolean) => { + ( + fetchNextPage: () => Promise, + nextPageExists: boolean, + nextIsFetchingNextPage: boolean, + loadedPageCount: number, + ) => { fetchNextPageRef.current = fetchNextPage; setHasNextPage(nextPageExists); setIsFetchingNextPage(nextIsFetchingNextPage); + setLoadedSessionMessagePageCount(loadedPageCount); fetchingNextPageRef.current = nextIsFetchingNextPage; }, [], @@ -2385,6 +2434,9 @@ export const ProjectChatPage: FC<{ if (target.dataset['slot'] !== 'scroll-area-viewport') { return; } + if (chatViewportElement !== target) { + setChatViewportElement(target); + } const nextIsFollowing = isNearScrollBottom({ clientHeight: target.clientHeight, @@ -2407,14 +2459,40 @@ export const ProjectChatPage: FC<{ Math.max(0, Math.round(visibleTranscript.length * (1 - scrollPercentage))), ); } + }; - // 上端に近づいたら古いメッセージを読み込む - const nearTopThreshold = 800; - if (target.scrollTop < nearTopThreshold && hasNextPage && !fetchingNextPageRef.current) { - fetchingNextPageRef.current = true; - fetchNextPageRef.current?.(); + useEffect(() => { + if ( + sessionId === null || + chatViewportElement === null || + !hasNextPage || + loadedSessionMessagePageCount < 2 + ) { + return; + } + const sentinel = olderMessagesPrefetchRef.current; + if (sentinel === null) { + return; } - }; + + const observer = new IntersectionObserver( + (entries) => { + if (entries.some((entry) => entry.isIntersecting) && !fetchingNextPageRef.current) { + fetchingNextPageRef.current = true; + void fetchNextPageRef.current?.(); + } + }, + { + root: chatViewportElement, + rootMargin: '200px 0px 0px 0px', + threshold: 0, + }, + ); + observer.observe(sentinel); + return () => { + observer.disconnect(); + }; + }, [chatViewportElement, hasNextPage, loadedSessionMessagePageCount, sessionId]); const handleJumpToLatest = () => { shouldStickToBottomRef.current = true; @@ -2635,12 +2713,17 @@ export const ProjectChatPage: FC<{ ) : null} - {isFetchingNextPage && hasNextPage && sessionId !== null && ( + {hasNextPage && sessionId !== null ? (
- - 古いメッセージを読み込み中... + {isFetchingNextPage ? ( + + ) : null} + セッションを読み込み中...
- )} + ) : null} + {loadedSessionMessagePageCount > 1 && hasNextPage ? ( +
+ ) : null} {visibleTranscript.map((message, index) => { const isUser = message.role === 'user'; const displayEvents = filterDisplayableRawEvents(message.rawEvents); diff --git a/src/web/app/projects/$projectId/project-menu-content.tsx b/src/web/app/projects/$projectId/project-menu-content.tsx index 8876b26..c83846f 100644 --- a/src/web/app/projects/$projectId/project-menu-content.tsx +++ b/src/web/app/projects/$projectId/project-menu-content.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react'; import { Link } from '@tanstack/react-router'; -import { CalendarClock, FolderKanban, History, MessageSquare, Plus, Settings } from 'lucide-react'; +import { CalendarClock, History, MessageSquare, Plus, Settings } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import type { SessionSummary } from '../../../../shared/acp.ts'; @@ -84,10 +84,6 @@ export const ProjectMenuContent: FC<{ {t('menu.settings')} - - - {t('menu.projects')} -
diff --git a/src/web/app/settings/settings-panels.tsx b/src/web/app/settings/settings-panels.tsx index ab7a73a..26e6af1 100644 --- a/src/web/app/settings/settings-panels.tsx +++ b/src/web/app/settings/settings-panels.tsx @@ -1875,7 +1875,14 @@ export const LanguageSettingsPanel: FC = () => { value={data.settings.language} > - + + {(value) => { + const selected = languageChoices.find((choice) => choice.value === value); + return selected === undefined + ? t('settings.language.label') + : t(selected.labelKey); + }} + {languageChoices.map((choice) => ( From 4cb622fc7e5fc7948909611c3c01da76412711a5 Mon Sep 17 00:00:00 2001 From: d-kimsuon Date: Tue, 5 May 2026 17:00:47 +0900 Subject: [PATCH 04/14] docs: document sandbox execution --- README.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 552b3e0..5dfd053 100644 --- a/README.md +++ b/README.md @@ -70,10 +70,15 @@ Then clients must send the API key as a bearer token for `/api/*` requests. You - 🛠️ Visual tool viewers: inspect common tool calls such as Bash, Read, Write, and Edit with terminal, file, and diff viewers. - ✅ Tool approval: approve or reject tool-approval requests from supported agents. +- 🧱 Sandboxed execution: optionally run agent CLIs through `srt` sandboxing with global, + project-level, and per-session controls. - 🔔 Notifications: receive task-completion and approval-request notifications. When installed as a PWA, device notifications are available through the service worker. - 🔊 Completion sound: play an audible notification when an agent task finishes. -- ✍️ Efficient prompt input: use agent command completion, file-path completion, and voice input. +- ✍️ Efficient prompt input: use agent command completion, file-path completion, voice input, and + image attachments. +- 🎚️ Model and mode controls: switch supported providers' model/mode from the UI, remember the last + used choices per project/provider, and pin favorite models. - 🧑‍💻 Code review: open a GitHub-like diff viewer, leave line comments, and seamlessly turn them into review instructions for the agent. - 🌿 Git worktree support: start sessions in a selected worktree. Supports `.worktreeinclude`, @@ -128,6 +133,33 @@ See the ACP agent list at https://agentclientprotocol.com/get-started/agents to agent. If the agent you want is not listed, implement an ACP-compatible agent server and register its command as a Custom Provider. +## Sandboxed Agent Execution + +`remote-agent` can launch agent CLIs through `@anthropic-ai/sandbox-runtime` (`srt`) so agent +processes run with explicit filesystem and network rules. + +Sandboxing is opt-in and has three layers: + +1. **Global Settings → Sandbox**: choose which provider presets are allowed to use sandboxing and + define global filesystem/network rules. +2. **Project Settings → Sandbox**: enable sandboxing by default for that project and add project + rules. Relative paths are resolved from the session working directory. +3. **New session toolbar**: toggle sandboxing for an individual session before it starts. + +Filesystem rules support read/write allowlists and denylists. Network policy can be unrestricted +(`none`) or restricted to an allowlist of domains. Project network policy can inherit the global +policy, override it with its own restricted allowlist, or disable network restriction for that +project. + +For agent runtime files, `remote-agent` automatically grants read access to common local agent +configuration directories such as `~/.codex`, `~/.pi`, `~/.claude`, `~/.github`, and `~/.copilot`. +Provider-specific writable runtime directories are added where needed, such as `~/.codex` for Codex +and `~/.pi` for pi-coding-agent. The default write allowlist includes the session working directory +(`.`). + +Sandboxing is a defense-in-depth feature, not a replacement for normal security controls. Keep using +private networks, API key authentication, IP restrictions, and trusted agent credentials. + ## How It Works `remote-agent` keeps ACP sessions on the Node.js server instead of starting local agents directly from the browser. The browser SPA talks to the Hono API/BFF, the server manages ACP-compatible provider processes, and agent output, plans, diffs, terminal data, and approval requests are streamed back to the browser. From 2d1ca5f93ed5fbd4d7ef643d9cd99e4729a268a2 Mon Sep 17 00:00:00 2001 From: d-kimsuon Date: Tue, 5 May 2026 17:00:59 +0900 Subject: [PATCH 05/14] Store send-prompt metadata in user messages --- src/server/acp/services/session-store.test.ts | 138 ++++++++++++++++++ src/server/acp/services/session-store.ts | 29 ++++ src/shared/acp.ts | 12 ++ .../projects/$projectId/project-chat-page.tsx | 5 + .../transcript-display.pure.test.ts | 53 +++++++ .../$projectId/transcript-display.pure.ts | 33 ++++- 6 files changed, 269 insertions(+), 1 deletion(-) diff --git a/src/server/acp/services/session-store.test.ts b/src/server/acp/services/session-store.test.ts index bc897ab..5486a42 100644 --- a/src/server/acp/services/session-store.test.ts +++ b/src/server/acp/services/session-store.test.ts @@ -596,6 +596,144 @@ describe('createSessionStore', () => { expect(restoredMessages.map((message) => message.text)).toEqual(['ping', 'pong']); }); + test('stores send-prompt selection metadata on user messages', async () => { + const database = createMemoryDatabase(); + disposableClients.push(database.client); + + const store = createSessionStore({ + database, + resolveCommand: () => Promise.resolve('/bin/codex'), + createProvider: () => ({ + cleanup: () => {}, + initSession: () => + Promise.resolve({ + sessionId: 'session-msgs-meta', + modes: { + currentModeId: 'full-access', + availableModes: [{ id: 'full-access', name: 'full-access' }], + }, + models: { + currentModelId: 'gpt-5-codex', + availableModels: [ + { modelId: 'gpt-5-codex', name: 'GPT-5 Codex' }, + { modelId: 'gpt-5.4-codex-spark', name: 'gpt-5.4-codex-spark' }, + ], + }, + }), + languageModel: stubLanguageModel, + setMode: async () => {}, + setModel: async () => {}, + tools: {}, + }), + promptCollector: () => + Promise.resolve({ + text: 'pong', + rawEvents: [], + alreadyPersisted: false, + assistantSegmentMessages: [], + }), + }); + + await store.createSession({ + projectId: null, + preset: codexPreset, + command: 'npx', + args: ['-y', '@zed-industries/codex-acp'], + cwd: process.cwd(), + }); + + await store.sendPrompt('session-msgs-meta', { + prompt: 'ping', + attachmentIds: [], + modelId: 'gpt-5.4-codex-spark', + modeId: 'full-access', + }); + + const userMessage = (await store.listMessages('session-msgs-meta')).find( + (entry) => entry.role === 'user', + ); + if (userMessage?.rawJson.type !== 'user') { + throw new Error('expected user message'); + } + expect(userMessage.rawJson).toEqual( + expect.objectContaining({ + metadata: { + source: 'send-prompt', + presetId: 'codex', + modelId: 'gpt-5.4-codex-spark', + modelName: 'gpt-5.4-codex-spark', + modeId: 'full-access', + modeName: 'full-access', + }, + }), + ); + }); + + test('stores current session model/mode when send-prompt overrides are omitted', async () => { + const database = createMemoryDatabase(); + disposableClients.push(database.client); + + const store = createSessionStore({ + database, + resolveCommand: () => Promise.resolve('/bin/codex'), + createProvider: () => ({ + cleanup: () => {}, + initSession: () => + Promise.resolve({ + sessionId: 'session-msgs-meta-inherit', + modes: { + currentModeId: 'full-access', + availableModes: [{ id: 'full-access', name: 'full-access' }], + }, + models: { + currentModelId: 'gpt-5-codex', + availableModels: [{ modelId: 'gpt-5-codex', name: 'GPT-5 Codex' }], + }, + }), + languageModel: stubLanguageModel, + setMode: async () => {}, + setModel: async () => {}, + tools: {}, + }), + promptCollector: () => + Promise.resolve({ + text: 'pong', + rawEvents: [], + alreadyPersisted: false, + assistantSegmentMessages: [], + }), + }); + + await store.createSession({ + projectId: null, + preset: codexPreset, + command: 'npx', + args: ['-y', '@zed-industries/codex-acp'], + cwd: process.cwd(), + }); + + await store.sendPrompt('session-msgs-meta-inherit', { prompt: 'ping', attachmentIds: [] }); + + const userMessage = (await store.listMessages('session-msgs-meta-inherit')).find( + (entry) => entry.role === 'user', + ); + if (userMessage?.rawJson.type !== 'user') { + throw new Error('expected user message'); + } + expect(userMessage.rawJson).toEqual( + expect.objectContaining({ + metadata: { + source: 'send-prompt', + presetId: 'codex', + modelId: 'gpt-5-codex', + modelName: 'GPT-5 Codex', + modeId: 'full-access', + modeName: 'full-access', + }, + }), + ); + }); + test('persists image attachments on the user message as base64 image blocks', async () => { const database = createMemoryDatabase(); disposableClients.push(database.client); diff --git a/src/server/acp/services/session-store.ts b/src/server/acp/services/session-store.ts index af56b16..8c9d4d9 100644 --- a/src/server/acp/services/session-store.ts +++ b/src/server/acp/services/session-store.ts @@ -1026,6 +1026,7 @@ export const createSessionStore = ({ type: 'user', role: 'user', text, + metadata: metadataJson === '{}' ? undefined : JSON.parse(metadataJson), attachments: [...attachments], createdAt: t, } @@ -1680,6 +1681,17 @@ export const createSessionStore = ({ return session; }; + const normalizeSelectionId = (value: string | null | undefined): string | null => + value === null || value === undefined || value.length === 0 ? null : value; + + const lookupModelName = ( + entry: { readonly availableModels: readonly ModelOption[] }, + id: string, + ) => entry.availableModels.find((model) => model.id === id)?.name ?? id; + + const lookupModeName = (entry: { readonly availableModes: readonly ModeOption[] }, id: string) => + entry.availableModes.find((mode) => mode.id === id)?.name ?? id; + const sendPrompt = async ( sessionId: string, request: SendMessageRequest, @@ -1697,6 +1709,22 @@ export const createSessionStore = ({ const effectivePrompt = attachmentPromptPlan.promptText; const promptMessages = await attachmentPromptMessagesFromPlan(attachmentPromptPlan); const userAttachments = userAttachmentsFromPromptPlan(attachmentPromptPlan); + const selectedModelId = + normalizeSelectionId(request.modelId) ?? + normalizeSelectionId(entry.session.currentModelId) ?? + null; + const selectedModeId = + normalizeSelectionId(request.modeId) ?? + normalizeSelectionId(entry.session.currentModeId) ?? + null; + const selectionMetadata = { + source: 'send-prompt' as const, + presetId: entry.session.presetId, + modelId: selectedModelId, + modelName: selectedModelId === null ? null : lookupModelName(entry.session, selectedModelId), + modeId: selectedModeId, + modeName: selectedModeId === null ? null : lookupModeName(entry.session, selectedModeId), + }; await persistSession(entry.session); await persistMessage({ @@ -1706,6 +1734,7 @@ export const createSessionStore = ({ text: request.prompt, rawEvents: [], kind: 'user', + metadataJson: JSON.stringify(selectionMetadata), attachments: userAttachments, }), }); diff --git a/src/shared/acp.ts b/src/shared/acp.ts index 81151b6..7490b13 100644 --- a/src/shared/acp.ts +++ b/src/shared/acp.ts @@ -247,6 +247,7 @@ export const userRawSchema = object({ type: literal('user'), role: literal('user'), text: string(), + metadata: optional(unknown()), attachments: optional(array(userAttachmentSchema)), promptPlan: optional(unknown()), }); @@ -1251,6 +1252,17 @@ export const chatMessageSchema = object({ export type ChatMessage = InferOutput; +export const sendPromptSelectionMetadataSchema = object({ + source: literal('send-prompt'), + presetId: optional(nullable(pipe(string(), trim()))), + modelId: optional(nullable(pipe(string(), trim()))), + modelName: optional(nullable(pipe(string(), trim()))), + modeId: optional(nullable(pipe(string(), trim()))), + modeName: optional(nullable(pipe(string(), trim()))), +}); + +export type SendPromptSelectionMetadata = InferOutput; + export const messageResponseSchema = object({ session: sessionSummarySchema, text: string(), diff --git a/src/web/app/projects/$projectId/project-chat-page.tsx b/src/web/app/projects/$projectId/project-chat-page.tsx index ab6e9d8..3e86ab3 100644 --- a/src/web/app/projects/$projectId/project-chat-page.tsx +++ b/src/web/app/projects/$projectId/project-chat-page.tsx @@ -166,6 +166,7 @@ import { appendRichPromptText } from './rich-prompt-editor.pure.ts'; import { RichPromptEditor } from './rich-prompt-editor.tsx'; import { filterDisplayableRawEvents, + formatUserMessageSelectionMetadata, isToolOnlyTranscriptMessage, shouldDisplayTranscriptMessage, } from './transcript-display.pure.ts'; @@ -423,8 +424,12 @@ const TranscriptMessageBody: FC<{ readonly cwd: string; readonly message: ChatMe if (message.role === 'user') { const text = message.rawJson.type === 'user' ? message.rawJson.text : message.text; const attachments = message.rawJson.type === 'user' ? (message.rawJson.attachments ?? []) : []; + const selectionMetadata = formatUserMessageSelectionMetadata(message); return (
+ {selectionMetadata === null ? null : ( +
{selectionMetadata}
+ )} {text.trim().length === 0 ? null : {text}}
diff --git a/src/web/app/projects/$projectId/transcript-display.pure.test.ts b/src/web/app/projects/$projectId/transcript-display.pure.test.ts index 04968a5..03e27ca 100644 --- a/src/web/app/projects/$projectId/transcript-display.pure.test.ts +++ b/src/web/app/projects/$projectId/transcript-display.pure.test.ts @@ -4,6 +4,7 @@ import type { ChatMessage, RawEvent } from '../../../../shared/acp'; import { filterDisplayableRawEvents, + formatUserMessageSelectionMetadata, isToolOnlyTranscriptMessage, shouldDisplayTranscriptMessage, } from './transcript-display.pure.ts'; @@ -48,6 +49,58 @@ describe('filterDisplayableRawEvents', () => { }); }); +describe('formatUserMessageSelectionMetadata', () => { + test('send-prompt 選択情報を期待フォーマットで返す', () => { + const message: ChatMessage = { + id: 'user-1', + role: 'user', + kind: 'user', + rawJson: { + schemaVersion: 1, + type: 'user', + role: 'user', + text: 'hello', + metadata: { + source: 'send-prompt', + presetId: 'codex', + modelId: 'gpt-5.4-codex-spark', + modelName: 'gpt-5.4-codex-spark', + modeId: 'full-access', + modeName: 'full-access', + }, + createdAt: '2025-01-01T00:00:00.000Z', + }, + textForSearch: '', + text: 'hello', + rawEvents: [], + createdAt: '2025-01-01T00:00:00.000Z', + }; + expect(formatUserMessageSelectionMetadata(message)).toBe( + 'codex / gpt-5.4-codex-spark / full-access', + ); + }); + + test('metadata が無い user メッセージでは null', () => { + const message: ChatMessage = { + id: 'user-2', + role: 'user', + kind: 'user', + rawJson: { + schemaVersion: 1, + type: 'user', + role: 'user', + text: 'hello', + createdAt: '2025-01-01T00:00:00.000Z', + }, + textForSearch: '', + text: 'hello', + rawEvents: [], + createdAt: '2025-01-01T00:00:00.000Z', + }; + expect(formatUserMessageSelectionMetadata(message)).toBeNull(); + }); +}); + describe('shouldDisplayTranscriptMessage', () => { test('隠し種別の assistant は出さない', () => { for (const kind of [ diff --git a/src/web/app/projects/$projectId/transcript-display.pure.ts b/src/web/app/projects/$projectId/transcript-display.pure.ts index dba0a08..ef4bfd6 100644 --- a/src/web/app/projects/$projectId/transcript-display.pure.ts +++ b/src/web/app/projects/$projectId/transcript-display.pure.ts @@ -1,5 +1,11 @@ -import type { ChatMessage, ChatMessageKind, RawEvent } from '../../../../shared/acp'; +import { safeParse } from 'valibot'; +import { + sendPromptSelectionMetadataSchema, + type ChatMessage, + type ChatMessageKind, + type RawEvent, +} from '../../../../shared/acp'; import { planRawEventsForRender } from './acp-event-plan.pure.ts'; /** @@ -60,3 +66,28 @@ export const isToolOnlyTranscriptMessage = ( } return planRawEventsForRender(displayableEvents).every((item) => item.type === 'tool'); }; + +const resolveSelectionMetadataFromMessage = (message: ChatMessage) => { + if (message.role !== 'user' || message.rawJson.type !== 'user') { + return null; + } + if (message.rawJson.metadata === undefined) { + return null; + } + const parsed = safeParse(sendPromptSelectionMetadataSchema, message.rawJson.metadata); + return parsed.success ? parsed.output : null; +}; + +export const formatUserMessageSelectionMetadata = (message: ChatMessage): string | null => { + const metadata = resolveSelectionMetadataFromMessage(message); + if (metadata === null || metadata.source !== 'send-prompt') { + return null; + } + const preset = metadata.presetId?.trim(); + const model = metadata.modelName?.trim() ?? metadata.modelId?.trim(); + const mode = metadata.modeName?.trim() ?? metadata.modeId?.trim(); + const chunks: string[] = [preset, model, mode].filter((value): value is string => { + return value !== undefined && value !== null && value.length > 0; + }); + return chunks.length > 0 ? chunks.join(' / ') : null; +}; From 86657bbeca22129cd155537a02cebeeb7e86d2af Mon Sep 17 00:00:00 2001 From: d-kimsuon Date: Tue, 5 May 2026 17:16:37 +0900 Subject: [PATCH 06/14] chore: bug fixes --- package.json | 4 +- pnpm-lock.yaml | 225 +++++++++++------- src/server/routes.ts | 2 + src/shared/acp.ts | 1 + .../projects/$projectId/project-chat-page.tsx | 33 ++- .../$projectId/project-menu-content.tsx | 23 +- src/web/components/app-menu.tsx | 10 +- src/web/lib/i18n/resources.ts | 22 ++ src/web/routeTree.gen.ts | 21 ++ src/web/routes/information.tsx | 123 ++++++++++ .../routes/projects/$projectId/settings.tsx | 14 +- src/web/routes/settings.tsx | 22 +- 12 files changed, 395 insertions(+), 105 deletions(-) create mode 100644 src/web/routes/information.tsx diff --git a/package.json b/package.json index e7a7c02..6e91d54 100644 --- a/package.json +++ b/package.json @@ -50,9 +50,9 @@ "test": "vitest run" }, "dependencies": { - "@agentclientprotocol/claude-agent-acp": "0.31.4", + "@agentclientprotocol/claude-agent-acp": "0.32.0", "@anthropic-ai/sandbox-runtime": "0.0.49", - "@zed-industries/codex-acp": "0.12.0", + "@zed-industries/codex-acp": "0.13.0", "pi-acp": "0.0.26" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6b610f..624b1ce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,14 +17,14 @@ importers: .: dependencies: '@agentclientprotocol/claude-agent-acp': - specifier: 0.31.4 - version: 0.31.4 + specifier: 0.32.0 + version: 0.32.0 '@anthropic-ai/sandbox-runtime': specifier: 0.0.49 version: 0.0.49 '@zed-industries/codex-acp': - specifier: 0.12.0 - version: 0.12.0 + specifier: 0.13.0 + version: 0.13.0 pi-acp: specifier: 0.0.26 version: 0.0.26 @@ -227,8 +227,8 @@ importers: packages: - '@agentclientprotocol/claude-agent-acp@0.31.4': - resolution: {integrity: sha512-Ge2qzNN7vXQje0H+xoPhcRToubgdkgpY/YoqNSeJGpx8S90V/uposdsE+OSgIA+4nHcUEbgV9OmCiIqpyEsA9g==} + '@agentclientprotocol/claude-agent-acp@0.32.0': + resolution: {integrity: sha512-3WIaD1bTmIciqHdeU97oeNajOG9H+ctloXnQ+R/T563C2CM8u1K7QsNqqgqR2F+Cn8NVBkXdHRvAMtUHglLzAw==} hasBin: true '@agentclientprotocol/sdk@0.12.0': @@ -258,56 +258,66 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/provider-utils@4.0.26': + resolution: {integrity: sha512-CsKNLKsOpvPujRlIYvoz+Ybw+kGn7J4/fIZa/58+R7iWLLfwn6ifE2G6Yq8K9XvH/I/3bzaDAJ3NhRwEMsLBKQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider@3.0.10': + resolution: {integrity: sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw==} + engines: {node: '>=18'} + '@ai-sdk/provider@3.0.9': resolution: {integrity: sha512-/ngMKqKdL9dSlY/eQ3NFDzzFyw0Hix+cbFFlyuKEKcOgpHdBt/spKUvX/i0wGrDLFPYJeVvv3N0j92LxWRL7yQ==} engines: {node: '>=18'} - '@anthropic-ai/claude-agent-sdk-darwin-arm64@0.2.121': - resolution: {integrity: sha512-zVHcXvx6Hl/glDcOCH+EyNx4KPE9cMGLk42eEBSZe014tAN5W8bwM/By08iM6dxijnpH0NQRNNEAW+BryWzuDg==} + '@anthropic-ai/claude-agent-sdk-darwin-arm64@0.2.126': + resolution: {integrity: sha512-JFlJBbeAlx7Ic5s4lGUN9SppobryXk/lIqPCvhp6KrJTQIerh3MIBzxsVIJ0MaDut7jVni/oYgsvDni7NIyqHA==} cpu: [arm64] os: [darwin] - '@anthropic-ai/claude-agent-sdk-darwin-x64@0.2.121': - resolution: {integrity: sha512-lIXdqKj+bpfDxCk/eU1F1TXNqsIsLTRrkUG/wx19WIGZ8gLUmmVSveUKGlNegTs7S6evMvuezprJzDJT4TcvPA==} + '@anthropic-ai/claude-agent-sdk-darwin-x64@0.2.126': + resolution: {integrity: sha512-J8BpMj16NK9FUaG3HnHSivyp4Xww9DKWHiC8QSHT9oiT8pH5IG7nl0jxyjIq/lY79evlTY+ubgDVWlMUhUAN/g==} cpu: [x64] os: [darwin] - '@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.2.121': - resolution: {integrity: sha512-4XaGK+dRBYy7krln7BrDG0WsdE6ejUSgHjWHlUGXoubFfZUvls4GSahLcYjJBArLi4dLnxKw8zEuiQguPAIbrw==} + '@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.2.126': + resolution: {integrity: sha512-GO0BnIUw3LQ3XAy+nipAabkN0GwQGPhHB6ITI4XLoR99fLHB3TA6WfyvTf0fnpxd25A+c/+UsAoxz4zBQaHlhA==} cpu: [arm64] os: [linux] libc: [musl] - '@anthropic-ai/claude-agent-sdk-linux-arm64@0.2.121': - resolution: {integrity: sha512-AQSnJzaiFvQpUPfO1tWLvsHgb6KNar4QYEQ/5/sk1itfgr3Fx9gxTreq43wX7AXSvkBX1QlDaP1aR1sfM/g/lQ==} + '@anthropic-ai/claude-agent-sdk-linux-arm64@0.2.126': + resolution: {integrity: sha512-LM+mnfQsgI+1i5mYZwIPDDf14NGBu5wbhzm5U8P11dCa2p8sXmKoWpkbO16BFM2NxeW44I/RXCxE5qFsbz4zcg==} cpu: [arm64] os: [linux] libc: [glibc] - '@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.2.121': - resolution: {integrity: sha512-sQoGIgzLlBRrwizxsCV/lbaEuxXom/cfOwlDtQ2HnS1IzDDSjSf5d5pugpWItkOyXBWcHzMUu731WTTutvd/BQ==} + '@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.2.126': + resolution: {integrity: sha512-ByJGO0+mu7EplxSFSCIHd7QWsXdrF3qgtzQ177o/j+oSppLoqR1ot5ktf8aw5oR3CC5lFHg4tqd6TnneQpEoIg==} cpu: [x64] os: [linux] libc: [musl] - '@anthropic-ai/claude-agent-sdk-linux-x64@0.2.121': - resolution: {integrity: sha512-DJUgpm7au086WaQV/S7BGOt2M8D90spGZRizT3twYsacf1BxzK1qsXqB/Pw1lUjPy6pI107pml/TaPzWuS/Vzg==} + '@anthropic-ai/claude-agent-sdk-linux-x64@0.2.126': + resolution: {integrity: sha512-yaOTDcYCdscxC0LKg9w8IwSa5g+993WggFZJBTZpqvflA2+WMQeTarDnKlsFTCw9XUZkL8XZeBALYJGx0HutuA==} cpu: [x64] os: [linux] libc: [glibc] - '@anthropic-ai/claude-agent-sdk-win32-arm64@0.2.121': - resolution: {integrity: sha512-6n/NHkHxs0/lCJX3XPADjo1EFzXBf0IwYz/nyzJGBCDJjGKmgTe0i8eYBr/hviwt1/OPeK7dmVzVSVl6EL9Azg==} + '@anthropic-ai/claude-agent-sdk-win32-arm64@0.2.126': + resolution: {integrity: sha512-gv3MOsOBkCx3LajOOIjD7AKsOtz/qNHsS2oshGt2GVoy7JA3XbCDeCetDjM6SorV4SE+7F/IH0UJdXe5ejI/Zg==} cpu: [arm64] os: [win32] - '@anthropic-ai/claude-agent-sdk-win32-x64@0.2.121': - resolution: {integrity: sha512-v2/R918/t94cCwc6rmbxk+UYeQPtF2oBLtQAk+cT0M60hvqmCZO2noyZx5uTp8TQncOlG4MkINIeNY2yfmWSoQ==} + '@anthropic-ai/claude-agent-sdk-win32-x64@0.2.126': + resolution: {integrity: sha512-oRV75HwyoOd1/t5+kipAM2g62CaElpKGvSBx3Ys4lCwCiFUyOnmet/O+hRXENsY6ShDeQZEcJL2UWljr2d5NQw==} cpu: [x64] os: [win32] - '@anthropic-ai/claude-agent-sdk@0.2.121': - resolution: {integrity: sha512-hwZNYTkGLKVixd/V/OCJwfH/SdfxZXGV0m6wvy5EBq6qfB+lvJTRz/MSOSa7dHqo4/F7zJY68crEEca68Wrxpw==} + '@anthropic-ai/claude-agent-sdk@0.2.126': + resolution: {integrity: sha512-4ZrVu0XUEwNG6wxvsLgppRAmSfAf3oeEMEUPhgazb0AXUUe/7W8MxwZKJWOffqSLWaNYzOt3ZCIL7NJY6toqWw==} engines: {node: '>=18.0.0'} peerDependencies: zod: ^4.0.0 @@ -411,6 +421,10 @@ packages: resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.29.3': + resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==} + engines: {node: '>=6.9.0'} + '@babel/core@7.29.0': resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} engines: {node: '>=6.9.0'} @@ -437,6 +451,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-create-class-features-plugin@7.29.3': + resolution: {integrity: sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-create-regexp-features-plugin@7.28.5': resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==} engines: {node: '>=6.9.0'} @@ -546,6 +566,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.3': + resolution: {integrity: sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} engines: {node: '>=6.9.0'} @@ -906,8 +932,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/preset-env@7.29.2': - resolution: {integrity: sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==} + '@babel/preset-env@7.29.3': + resolution: {integrity: sha512-ySZypNLAIH1ClygLDQzVMoGQRViATnkHkYYV6TcNDz+8+jwZCdsguGvsb3EY5d9wyWyhmF1iSuFM0Yh5XPnqSA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -2470,44 +2496,44 @@ packages: '@vitest/utils@4.1.5': resolution: {integrity: sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==} - '@zed-industries/codex-acp-darwin-arm64@0.12.0': - resolution: {integrity: sha512-RvTXH21sLpswEo8xLeQXcA/uWZauyNP1y+WI6b355+/o7sQ5wrvBkxt+NyhaJXJIQvbfdpl04LND4cmM+DTcNg==} + '@zed-industries/codex-acp-darwin-arm64@0.13.0': + resolution: {integrity: sha512-SNJbpxOD1b98pK1Qw2pZjFJbfYBICheRs3mYvLMgHABehdypaeYKnEmEGp3Bu/gUT6JFAtOPRtaU+sfxKzgCvw==} cpu: [arm64] os: [darwin] hasBin: true - '@zed-industries/codex-acp-darwin-x64@0.12.0': - resolution: {integrity: sha512-N7EhrUTioix3L21qnm6kZzAESc+B7Mac+/uW3khn/UQe7fJJ7u1ojbgMPDdGo/8Xm6HBBXgak2NOj7mJ+NNXSw==} + '@zed-industries/codex-acp-darwin-x64@0.13.0': + resolution: {integrity: sha512-R5CQi2mmi9Nk2P6t48T5JoOQx0jWnP9DzLf5jcTnCLqk1tsg9XtASpLBtsedll9MesBax6aflDvz+0dyWW+3Mw==} cpu: [x64] os: [darwin] hasBin: true - '@zed-industries/codex-acp-linux-arm64@0.12.0': - resolution: {integrity: sha512-Kq35FclgZiSMBKyf80PnCvvJ3xfMjZIkPJXpci35U/VqXVQelhHCwYWwA3waTxvW07tNHxsehv1eQICz7wZdVQ==} + '@zed-industries/codex-acp-linux-arm64@0.13.0': + resolution: {integrity: sha512-Z3f2D94SOgy+BVFEIWxoR64IQB+d4/zgjHB1oeBS5yAYKaX7Wv3W6x+XGktDx+KnfD7c9vSSdFdknI6cZ8hO7g==} cpu: [arm64] os: [linux] hasBin: true - '@zed-industries/codex-acp-linux-x64@0.12.0': - resolution: {integrity: sha512-twmX9noSqfgWgVkGG1dd9u20Pxp8vNRXggvJ61RQSrNYITGuqHil2F3ViYICZoXyr9w1gok28bWG5DU2d9adPg==} + '@zed-industries/codex-acp-linux-x64@0.13.0': + resolution: {integrity: sha512-sWNfyeuwEHPo6DSbcjklnBr7M8+MWd2b9oVbIqgwxryTPpm0ZPF3U28PWR3/vGxS5UmhGiZIShe9tqx8FsvvBg==} cpu: [x64] os: [linux] hasBin: true - '@zed-industries/codex-acp-win32-arm64@0.12.0': - resolution: {integrity: sha512-VoFsTIrQopO917x2EpxYXm3jTIoSknCbzP76FwX9uOThlRms+M+fHWJ4kJttOPpeofz1ulAS3vPVMQ3WNlvnhw==} + '@zed-industries/codex-acp-win32-arm64@0.13.0': + resolution: {integrity: sha512-oxd6IF5dVHsa7zLnK1VAClzGADqn4N9TVSPb+3X4CqnOs4y4M9JPHSEEPiRYF44ibDJTWR+9EZ673djRYEGraw==} cpu: [arm64] os: [win32] hasBin: true - '@zed-industries/codex-acp-win32-x64@0.12.0': - resolution: {integrity: sha512-HImgXGIYgW6Wxr3rylrHS7Dzs35zvcQQB7eqAEWZ2Lj+3AxP/7TViW9KkjS+PTPnVWqpTkz0hYDQhk63Ruw3JA==} + '@zed-industries/codex-acp-win32-x64@0.13.0': + resolution: {integrity: sha512-675+tZlhzDMBJUrgiTnbcCMB15MQ8B0Ih/GmzB9MqW/FDFJqOFjXe4P+M7joePzQqa7QYwf36le50sDokXDrew==} cpu: [x64] os: [win32] hasBin: true - '@zed-industries/codex-acp@0.12.0': - resolution: {integrity: sha512-0d7gRzOiYTgDmIyh783mCcq50h3mdOg/TtKdLfBIghOLushpQRwhuLjKK8Q9hxZfNlPL0Ua56DoPjnsW8amf8g==} + '@zed-industries/codex-acp@0.13.0': + resolution: {integrity: sha512-Ep3gINMVB8qQL3kozJxEzG4YP7NmWUb5s+8yu8tQ7YSPfaIPXBIQQmO5sQk2Uu2av+gIC2EchbwaSSG3Mo17YQ==} hasBin: true abort-controller@3.0.0: @@ -5802,10 +5828,10 @@ packages: snapshots: - '@agentclientprotocol/claude-agent-acp@0.31.4': + '@agentclientprotocol/claude-agent-acp@0.32.0': dependencies: '@agentclientprotocol/sdk': 0.21.0(zod@4.4.1) - '@anthropic-ai/claude-agent-sdk': 0.2.121(zod@4.4.1) + '@anthropic-ai/claude-agent-sdk': 0.2.126(zod@4.4.1) zod: 4.4.1 transitivePeerDependencies: - '@cfworker/json-schema' @@ -5837,48 +5863,59 @@ snapshots: eventsource-parser: 3.0.8 zod: 4.4.1 + '@ai-sdk/provider-utils@4.0.26(zod@4.4.1)': + dependencies: + '@ai-sdk/provider': 3.0.10 + '@standard-schema/spec': 1.1.0 + eventsource-parser: 3.0.8 + zod: 4.4.1 + + '@ai-sdk/provider@3.0.10': + dependencies: + json-schema: 0.4.0 + '@ai-sdk/provider@3.0.9': dependencies: json-schema: 0.4.0 - '@anthropic-ai/claude-agent-sdk-darwin-arm64@0.2.121': + '@anthropic-ai/claude-agent-sdk-darwin-arm64@0.2.126': optional: true - '@anthropic-ai/claude-agent-sdk-darwin-x64@0.2.121': + '@anthropic-ai/claude-agent-sdk-darwin-x64@0.2.126': optional: true - '@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.2.121': + '@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.2.126': optional: true - '@anthropic-ai/claude-agent-sdk-linux-arm64@0.2.121': + '@anthropic-ai/claude-agent-sdk-linux-arm64@0.2.126': optional: true - '@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.2.121': + '@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.2.126': optional: true - '@anthropic-ai/claude-agent-sdk-linux-x64@0.2.121': + '@anthropic-ai/claude-agent-sdk-linux-x64@0.2.126': optional: true - '@anthropic-ai/claude-agent-sdk-win32-arm64@0.2.121': + '@anthropic-ai/claude-agent-sdk-win32-arm64@0.2.126': optional: true - '@anthropic-ai/claude-agent-sdk-win32-x64@0.2.121': + '@anthropic-ai/claude-agent-sdk-win32-x64@0.2.126': optional: true - '@anthropic-ai/claude-agent-sdk@0.2.121(zod@4.4.1)': + '@anthropic-ai/claude-agent-sdk@0.2.126(zod@4.4.1)': dependencies: '@anthropic-ai/sdk': 0.91.1(zod@4.4.1) '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.1) zod: 4.4.1 optionalDependencies: - '@anthropic-ai/claude-agent-sdk-darwin-arm64': 0.2.121 - '@anthropic-ai/claude-agent-sdk-darwin-x64': 0.2.121 - '@anthropic-ai/claude-agent-sdk-linux-arm64': 0.2.121 - '@anthropic-ai/claude-agent-sdk-linux-arm64-musl': 0.2.121 - '@anthropic-ai/claude-agent-sdk-linux-x64': 0.2.121 - '@anthropic-ai/claude-agent-sdk-linux-x64-musl': 0.2.121 - '@anthropic-ai/claude-agent-sdk-win32-arm64': 0.2.121 - '@anthropic-ai/claude-agent-sdk-win32-x64': 0.2.121 + '@anthropic-ai/claude-agent-sdk-darwin-arm64': 0.2.126 + '@anthropic-ai/claude-agent-sdk-darwin-x64': 0.2.126 + '@anthropic-ai/claude-agent-sdk-linux-arm64': 0.2.126 + '@anthropic-ai/claude-agent-sdk-linux-arm64-musl': 0.2.126 + '@anthropic-ai/claude-agent-sdk-linux-x64': 0.2.126 + '@anthropic-ai/claude-agent-sdk-linux-x64-musl': 0.2.126 + '@anthropic-ai/claude-agent-sdk-win32-arm64': 0.2.126 + '@anthropic-ai/claude-agent-sdk-win32-x64': 0.2.126 transitivePeerDependencies: - '@cfworker/json-schema' - supports-color @@ -6053,6 +6090,8 @@ snapshots: '@babel/compat-data@7.29.0': {} + '@babel/compat-data@7.29.3': {} + '@babel/core@7.29.0': dependencies: '@babel/code-frame': 7.29.0 @@ -6115,6 +6154,19 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-create-class-features-plugin@7.29.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.29.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -6238,6 +6290,14 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -6321,7 +6381,7 @@ snapshots: '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color @@ -6329,7 +6389,7 @@ snapshots: '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color @@ -6532,7 +6592,7 @@ snapshots: '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color @@ -6541,7 +6601,7 @@ snapshots: dependencies: '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color @@ -6629,9 +6689,9 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 - '@babel/preset-env@7.29.2(@babel/core@7.29.0)': + '@babel/preset-env@7.29.3(@babel/core@7.29.0)': dependencies: - '@babel/compat-data': 7.29.0 + '@babel/compat-data': 7.29.3 '@babel/core': 7.29.0 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 @@ -6639,6 +6699,7 @@ snapshots: '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.29.0) '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.29.0) '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.3(@babel/core@7.29.0) '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.29.0) '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.29.0) '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0) @@ -7159,8 +7220,8 @@ snapshots: '@mcpc-tech/acp-ai-provider@0.3.3': dependencies: '@agentclientprotocol/sdk': 0.14.1(zod@4.4.1) - '@ai-sdk/provider': 3.0.9 - '@ai-sdk/provider-utils': 4.0.24(zod@4.4.1) + '@ai-sdk/provider': 3.0.10 + '@ai-sdk/provider-utils': 4.0.26(zod@4.4.1) '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.1) ai: 6.0.170(zod@4.4.1) zod: 4.4.1 @@ -7929,32 +7990,32 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 - '@zed-industries/codex-acp-darwin-arm64@0.12.0': + '@zed-industries/codex-acp-darwin-arm64@0.13.0': optional: true - '@zed-industries/codex-acp-darwin-x64@0.12.0': + '@zed-industries/codex-acp-darwin-x64@0.13.0': optional: true - '@zed-industries/codex-acp-linux-arm64@0.12.0': + '@zed-industries/codex-acp-linux-arm64@0.13.0': optional: true - '@zed-industries/codex-acp-linux-x64@0.12.0': + '@zed-industries/codex-acp-linux-x64@0.13.0': optional: true - '@zed-industries/codex-acp-win32-arm64@0.12.0': + '@zed-industries/codex-acp-win32-arm64@0.13.0': optional: true - '@zed-industries/codex-acp-win32-x64@0.12.0': + '@zed-industries/codex-acp-win32-x64@0.13.0': optional: true - '@zed-industries/codex-acp@0.12.0': + '@zed-industries/codex-acp@0.13.0': optionalDependencies: - '@zed-industries/codex-acp-darwin-arm64': 0.12.0 - '@zed-industries/codex-acp-darwin-x64': 0.12.0 - '@zed-industries/codex-acp-linux-arm64': 0.12.0 - '@zed-industries/codex-acp-linux-x64': 0.12.0 - '@zed-industries/codex-acp-win32-arm64': 0.12.0 - '@zed-industries/codex-acp-win32-x64': 0.12.0 + '@zed-industries/codex-acp-darwin-arm64': 0.13.0 + '@zed-industries/codex-acp-darwin-x64': 0.13.0 + '@zed-industries/codex-acp-linux-arm64': 0.13.0 + '@zed-industries/codex-acp-linux-x64': 0.13.0 + '@zed-industries/codex-acp-win32-arm64': 0.13.0 + '@zed-industries/codex-acp-win32-x64': 0.13.0 abort-controller@3.0.0: dependencies: @@ -8061,7 +8122,7 @@ snapshots: babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.0): dependencies: - '@babel/compat-data': 7.29.0 + '@babel/compat-data': 7.29.3 '@babel/core': 7.29.0 '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) semver: 6.3.1 @@ -11397,7 +11458,7 @@ snapshots: dependencies: '@apideck/better-ajv-errors': 0.3.7(ajv@8.20.0) '@babel/core': 7.29.0 - '@babel/preset-env': 7.29.2(@babel/core@7.29.0) + '@babel/preset-env': 7.29.3(@babel/core@7.29.0) '@babel/runtime': 7.29.2 '@rollup/plugin-babel': 5.3.1(@babel/core@7.29.0)(rollup@2.80.0) '@rollup/plugin-node-resolve': 15.3.1(rollup@2.80.0) diff --git a/src/server/routes.ts b/src/server/routes.ts index 20e2acb..2b0199f 100644 --- a/src/server/routes.ts +++ b/src/server/routes.ts @@ -2,6 +2,7 @@ import { Hono } from 'hono'; import { describeRoute } from 'hono-openapi'; import { parse } from 'valibot'; +import pkg from '../../package.json' with { type: 'json' }; import { appInfoSchema } from '../shared/acp.ts'; import { agentPresets } from './acp/presets.ts'; import { acpRoutes } from './acp/routes.ts'; @@ -24,6 +25,7 @@ export const routes = new Hono() (c) => { const response = parse(appInfoSchema, { appName: 'Remote Agent', + version: pkg.version, workingDirectory: process.cwd(), projectsFilePath: getProjectsFilePath(), agentPresets, diff --git a/src/shared/acp.ts b/src/shared/acp.ts index 81151b6..e412d54 100644 --- a/src/shared/acp.ts +++ b/src/shared/acp.ts @@ -512,6 +512,7 @@ export const parsePersistedMessageRaw = (value: unknown) => export const appInfoSchema = object({ appName: pipe(string(), trim()), + version: pipe(string(), trim()), workingDirectory: pipe(string(), trim()), projectsFilePath: pipe(string(), trim()), agentPresets: array(agentPresetSchema), diff --git a/src/web/app/projects/$projectId/project-chat-page.tsx b/src/web/app/projects/$projectId/project-chat-page.tsx index ab6e9d8..da0072d 100644 --- a/src/web/app/projects/$projectId/project-chat-page.tsx +++ b/src/web/app/projects/$projectId/project-chat-page.tsx @@ -987,6 +987,7 @@ export const ProjectChatPage: FC<{ const preparingScopeKeysRef = useRef(new Set()); const attachFileInputRef = useRef(null); const speechRecognitionRef = useRef(null); + const speechListeningDesiredRef = useRef(false); const chatContentRef = useRef(null); const scrollAnchorRef = useRef(null); const shouldStickToBottomRef = useRef(true); @@ -1044,6 +1045,7 @@ export const ProjectChatPage: FC<{ useEffect(() => { return () => { + speechListeningDesiredRef.current = false; speechRecognitionRef.current?.abort(); }; }, []); @@ -1851,6 +1853,7 @@ export const ProjectChatPage: FC<{ } const currentRecognition = speechRecognitionRef.current; if (isListeningToSpeech) { + speechListeningDesiredRef.current = false; currentRecognition?.stop(); setIsListeningToSpeech(false); return; @@ -1863,7 +1866,7 @@ export const ProjectChatPage: FC<{ } const recognition = new SpeechRecognition(); - recognition.continuous = false; + recognition.continuous = true; recognition.interimResults = false; recognition.lang = navigator.language.length > 0 ? navigator.language : 'ja-JP'; recognition.onresult = (event) => { @@ -1876,22 +1879,39 @@ export const ProjectChatPage: FC<{ ); }; recognition.onerror = (event) => { + if (event.error === 'aborted' || event.error === 'no-speech') { + return; + } + speechListeningDesiredRef.current = false; setIsListeningToSpeech(false); speechRecognitionRef.current = null; - if (event.error !== 'aborted') { - toast.error(event.message.length > 0 ? event.message : t('chat.voiceFailed')); - } + toast.error(event.message.length > 0 ? event.message : t('chat.voiceFailed')); }; recognition.onend = () => { - setIsListeningToSpeech(false); - speechRecognitionRef.current = null; + if (speechRecognitionRef.current !== recognition) { + return; + } + if (!speechListeningDesiredRef.current) { + setIsListeningToSpeech(false); + speechRecognitionRef.current = null; + return; + } + try { + recognition.start(); + } catch { + speechListeningDesiredRef.current = false; + setIsListeningToSpeech(false); + speechRecognitionRef.current = null; + } }; speechRecognitionRef.current = recognition; + speechListeningDesiredRef.current = true; setIsListeningToSpeech(true); try { recognition.start(); } catch { + speechListeningDesiredRef.current = false; speechRecognitionRef.current = null; setIsListeningToSpeech(false); toast.error(t('chat.voiceStartFailed')); @@ -2143,6 +2163,7 @@ export const ProjectChatPage: FC<{ shouldStickToBottomRef.current = true; setIsChatFollowingTail(true); addAwaitingAssistantTranscriptKeys(requestAwaitingTranscriptKeys); + speechListeningDesiredRef.current = false; speechRecognitionRef.current?.stop(); setIsListeningToSpeech(false); replacePrompt(''); diff --git a/src/web/app/projects/$projectId/project-menu-content.tsx b/src/web/app/projects/$projectId/project-menu-content.tsx index 8876b26..fd8f199 100644 --- a/src/web/app/projects/$projectId/project-menu-content.tsx +++ b/src/web/app/projects/$projectId/project-menu-content.tsx @@ -1,7 +1,15 @@ import type { FC } from 'react'; import { Link } from '@tanstack/react-router'; -import { CalendarClock, FolderKanban, History, MessageSquare, Plus, Settings } from 'lucide-react'; +import { + CalendarClock, + FolderKanban, + History, + Info, + MessageSquare, + Plus, + Settings, +} from 'lucide-react'; import { useTranslation } from 'react-i18next'; import type { SessionSummary } from '../../../../shared/acp.ts'; @@ -78,12 +86,21 @@ export const ProjectMenuContent: FC<{ {t('menu.settings')} + + + {t('menu.information')} + {t('menu.projects')} diff --git a/src/web/components/app-menu.tsx b/src/web/components/app-menu.tsx index fe6597c..d6b6e63 100644 --- a/src/web/components/app-menu.tsx +++ b/src/web/components/app-menu.tsx @@ -1,5 +1,5 @@ import { Link } from '@tanstack/react-router'; -import { FolderKanban, Menu, PanelLeftClose, Settings, X } from 'lucide-react'; +import { FolderKanban, Info, Menu, PanelLeftClose, Settings, X } from 'lucide-react'; import { createContext, useCallback, @@ -56,6 +56,14 @@ const DefaultMenuContent: FC<{ readonly closeMobileMenu: () => void }> = ({ clos {t('menu.settings')} + + + {t('menu.information')} +
); }; diff --git a/src/web/lib/i18n/resources.ts b/src/web/lib/i18n/resources.ts index 3a592bb..f91e5da 100644 --- a/src/web/lib/i18n/resources.ts +++ b/src/web/lib/i18n/resources.ts @@ -53,6 +53,7 @@ export const resources = { sessions: 'セッションリスト', routines: 'Routines', settings: '設定', + information: 'Information', openMenu: 'メニューを開く', closeMenu: 'メニューを閉じる', collapseMenu: 'メニューを折りたたむ', @@ -61,6 +62,16 @@ export const resources = { noSessionsYet: 'まだセッションがありません。', viewing: '表示中', }, + information: { + title: 'Information', + remoteAgent: 'Remote Agent', + version: 'バージョン', + connectionStatus: '疎通ステータス', + apiReachable: 'API と疎通できています。', + workingDirectory: 'Working directory', + projectsFilePath: 'Projects file', + agentPresets: 'Agent presets', + }, notifications: { title: '通知', ariaLabel_unread: '通知、未読{{count}}件', @@ -379,6 +390,7 @@ export const resources = { sessions: 'Sessions', routines: 'Routines', settings: 'Settings', + information: 'Information', openMenu: 'Open menu', closeMenu: 'Close menu', collapseMenu: 'Collapse menu', @@ -387,6 +399,16 @@ export const resources = { noSessionsYet: 'No sessions yet.', viewing: 'Viewing', }, + information: { + title: 'Information', + remoteAgent: 'Remote Agent', + version: 'Version', + connectionStatus: 'Connection status', + apiReachable: 'API is reachable.', + workingDirectory: 'Working directory', + projectsFilePath: 'Projects file', + agentPresets: 'Agent presets', + }, notifications: { title: 'Notifications', ariaLabel_unread: 'Notifications, {{count}} unread', diff --git a/src/web/routeTree.gen.ts b/src/web/routeTree.gen.ts index e5e230e..c1b3de6 100644 --- a/src/web/routeTree.gen.ts +++ b/src/web/routeTree.gen.ts @@ -11,6 +11,7 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as SettingsRouteImport } from './routes/settings' import { Route as ProjectsRouteImport } from './routes/projects' +import { Route as InformationRouteImport } from './routes/information' import { Route as IndexRouteImport } from './routes/index' import { Route as ProjectsIndexRouteImport } from './routes/projects/index' import { Route as ProjectsProjectIdRouteImport } from './routes/projects/$projectId' @@ -29,6 +30,11 @@ const ProjectsRoute = ProjectsRouteImport.update({ path: '/projects', getParentRoute: () => rootRouteImport, } as any) +const InformationRoute = InformationRouteImport.update({ + id: '/information', + path: '/information', + getParentRoute: () => rootRouteImport, +} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', @@ -70,6 +76,7 @@ const ProjectsProjectIdRoutinesRoute = export interface FileRoutesByFullPath { '/': typeof IndexRoute + '/information': typeof InformationRoute '/projects': typeof ProjectsRouteWithChildren '/settings': typeof SettingsRoute '/projects/$projectId': typeof ProjectsProjectIdRouteWithChildren @@ -81,6 +88,7 @@ export interface FileRoutesByFullPath { } export interface FileRoutesByTo { '/': typeof IndexRoute + '/information': typeof InformationRoute '/settings': typeof SettingsRoute '/projects': typeof ProjectsIndexRoute '/projects/$projectId/routines': typeof ProjectsProjectIdRoutinesRoute @@ -91,6 +99,7 @@ export interface FileRoutesByTo { export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute + '/information': typeof InformationRoute '/projects': typeof ProjectsRouteWithChildren '/settings': typeof SettingsRoute '/projects/$projectId': typeof ProjectsProjectIdRouteWithChildren @@ -104,6 +113,7 @@ export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' + | '/information' | '/projects' | '/settings' | '/projects/$projectId' @@ -115,6 +125,7 @@ export interface FileRouteTypes { fileRoutesByTo: FileRoutesByTo to: | '/' + | '/information' | '/settings' | '/projects' | '/projects/$projectId/routines' @@ -124,6 +135,7 @@ export interface FileRouteTypes { id: | '__root__' | '/' + | '/information' | '/projects' | '/settings' | '/projects/$projectId' @@ -136,6 +148,7 @@ export interface FileRouteTypes { } export interface RootRouteChildren { IndexRoute: typeof IndexRoute + InformationRoute: typeof InformationRoute ProjectsRoute: typeof ProjectsRouteWithChildren SettingsRoute: typeof SettingsRoute } @@ -156,6 +169,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ProjectsRouteImport parentRoute: typeof rootRouteImport } + '/information': { + id: '/information' + path: '/information' + fullPath: '/information' + preLoaderRoute: typeof InformationRouteImport + parentRoute: typeof rootRouteImport + } '/': { id: '/' path: '/' @@ -241,6 +261,7 @@ const ProjectsRouteWithChildren = ProjectsRoute._addFileChildren( const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, + InformationRoute: InformationRoute, ProjectsRoute: ProjectsRouteWithChildren, SettingsRoute: SettingsRoute, } diff --git a/src/web/routes/information.tsx b/src/web/routes/information.tsx new file mode 100644 index 0000000..88dd2bd --- /dev/null +++ b/src/web/routes/information.tsx @@ -0,0 +1,123 @@ +import { useSuspenseQuery } from '@tanstack/react-query'; +import { createFileRoute } from '@tanstack/react-router'; +import { CheckCircle2, Info, Server } from 'lucide-react'; +import { Suspense, type FC } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { ProjectMenuContent } from '../app/projects/$projectId/project-menu-content.tsx'; +import { + agentProvidersQueryKey, + appInfoQueryKey, + projectQueryKey, + sessionsQueryKey, +} from '../app/projects/$projectId/queries.ts'; +import { useLoadSessionDialog } from '../app/projects/$projectId/use-load-session-dialog.tsx'; +import { fetchAgentProviders, fetchAppInfo, fetchProject, fetchSessions } from '../lib/api/acp.ts'; + +type InformationSearch = { + readonly projectId?: string; +}; + +const ProjectInformationMenu: FC<{ readonly projectId: string }> = ({ projectId }) => { + const { data: projectData } = useSuspenseQuery({ + queryKey: projectQueryKey(projectId), + queryFn: () => fetchProject(projectId), + }); + const { data: sessionsData } = useSuspenseQuery({ + queryKey: sessionsQueryKey, + queryFn: fetchSessions, + }); + const { data: providerData } = useSuspenseQuery({ + queryKey: agentProvidersQueryKey, + queryFn: fetchAgentProviders, + }); + const projectSessions = sessionsData.sessions.filter( + (session) => session.projectId === projectId, + ); + const { canLoadSessions, dialog, openLoadSessionDialog } = useLoadSessionDialog({ + projectId, + providers: providerData.providers, + workingDirectory: projectData.project.workingDirectory, + }); + + return ( + <> + + {dialog} + + ); +}; + +const InformationRoute: FC = () => { + const { t } = useTranslation(); + const search = Route.useSearch(); + const { data: appInfo } = useSuspenseQuery({ + queryKey: appInfoQueryKey, + queryFn: fetchAppInfo, + }); + + return ( +
+ {search.projectId === undefined ? null : ( + + + + )} +
+
+
+ + {t('information.title')} +
+

{appInfo.appName}

+
+ +
+
+
+ +
+

{t('information.connectionStatus')}

+

{t('information.apiReachable')}

+
+
+
+ +
+
+ +

{t('information.remoteAgent')}

+
+
+
{t('information.version')}
+
{appInfo.version}
+
{t('information.workingDirectory')}
+
{appInfo.workingDirectory}
+
{t('information.projectsFilePath')}
+
{appInfo.projectsFilePath}
+
{t('information.agentPresets')}
+
{appInfo.agentPresets.length}
+
+
+
+
+
+ ); +}; + +export const Route = createFileRoute('/information')({ + component: InformationRoute, + validateSearch: (search: Record): InformationSearch => { + const projectId = search['projectId']; + return { + projectId: typeof projectId === 'string' && projectId.length > 0 ? projectId : undefined, + }; + }, +}); diff --git a/src/web/routes/projects/$projectId/settings.tsx b/src/web/routes/projects/$projectId/settings.tsx index aed74a7..0d53f59 100644 --- a/src/web/routes/projects/$projectId/settings.tsx +++ b/src/web/routes/projects/$projectId/settings.tsx @@ -1,15 +1,9 @@ -import { createFileRoute } from '@tanstack/react-router'; -import { Suspense } from 'react'; +import { createFileRoute, Navigate } from '@tanstack/react-router'; +import { type FC } from 'react'; -import { ProjectSettingsPage } from '../../../app/projects/$projectId/project-settings-page.tsx'; - -const ProjectSettingsRoute = () => { +const ProjectSettingsRoute: FC = () => { const { projectId } = Route.useParams(); - return ( - - - - ); + return ; }; export const Route = createFileRoute('/projects/$projectId/settings')({ diff --git a/src/web/routes/settings.tsx b/src/web/routes/settings.tsx index 6267931..f0182d6 100644 --- a/src/web/routes/settings.tsx +++ b/src/web/routes/settings.tsx @@ -1,11 +1,25 @@ import { createFileRoute } from '@tanstack/react-router'; -import { type FC } from 'react'; +import { Suspense, type FC } from 'react'; import { useTranslation } from 'react-i18next'; +import { ProjectSettingsPage } from '../app/projects/$projectId/project-settings-page.tsx'; import { SettingsPage } from '../app/settings/settings-page.tsx'; +type SettingsSearch = { + readonly projectId?: string; +}; + const SettingsRoute: FC = () => { const { t } = useTranslation(); + const search = Route.useSearch(); + + if (search.projectId !== undefined) { + return ( + + + + ); + } return (
@@ -23,4 +37,10 @@ const SettingsRoute: FC = () => { export const Route = createFileRoute('/settings')({ component: SettingsRoute, + validateSearch: (search: Record): SettingsSearch => { + const projectId = search['projectId']; + return { + projectId: typeof projectId === 'string' && projectId.length > 0 ? projectId : undefined, + }; + }, }); From 725d921f7b3b2ac533c6f321c35c2eebce86e2cd Mon Sep 17 00:00:00 2001 From: d-kimsuon Date: Tue, 5 May 2026 18:01:55 +0900 Subject: [PATCH 07/14] perf(acp): stream message diffs over sse --- src/server/acp/services/session-store.ts | 30 ++- src/server/acp/services/sse-broadcast.ts | 7 + src/shared/acp.ts | 23 +-- .../projects/$projectId/project-chat-page.tsx | 7 - src/web/lib/api/acp-sse-cache.pure.test.ts | 184 ++++++++++-------- src/web/lib/api/acp-sse-cache.pure.ts | 137 +++++++++---- src/web/lib/api/useAcpSseCacheSync.ts | 39 +--- 7 files changed, 244 insertions(+), 183 deletions(-) diff --git a/src/server/acp/services/session-store.ts b/src/server/acp/services/session-store.ts index 8c9d4d9..621de7f 100644 --- a/src/server/acp/services/session-store.ts +++ b/src/server/acp/services/session-store.ts @@ -78,7 +78,7 @@ import { requestUserPermission, } from './permission-request-store.ts'; import { resolveSandboxLaunchConfig } from './sandbox-launch.ts'; -import { emitAcpSse } from './sse-broadcast.ts'; +import { emitAcpSse, nextAcpSseSequence } from './sse-broadcast.ts'; type SessionProvider = Pick< ACPProvider, @@ -970,7 +970,12 @@ export const createSessionStore = ({ rawJson: JSON.stringify(message.rawJson), createdAt: created, }); - emitAcpSse({ type: 'session_messages_updated', sessionId }); + emitAcpSse({ + type: 'message-add', + sessionId, + sequence: nextAcpSseSequence(), + message, + }); }; const hasStoredMessages = async (sessionId: string): Promise => { @@ -1194,7 +1199,8 @@ export const createSessionStore = ({ ), ); if (input.notify !== 'none') { - emitAcpSse({ type: 'session_messages_updated', sessionId: input.sessionId }); + // Stream content is delivered via message-delta events. Final persistence updates are + // intentionally not invalidation-driven. } }, }; @@ -1794,12 +1800,15 @@ export const createSessionStore = ({ return; } emitAcpSse({ - type: 'session_text_delta', + type: 'message-delta', sessionId: deltaSessionId, + sequence: nextAcpSseSequence(), + deltaIndex: + message.rawJson.type === 'assistant_text' ? (message.rawJson.deltaCount ?? 0) : 0, messageId: message.id, streamPartId: message.streamPartId, - delta, - text: message.text, + kind: 'assistant_text', + contentDelta: delta, createdAt: message.createdAt, updatedAt: message.updatedAt ?? message.createdAt, metadataJson: message.metadataJson, @@ -1810,12 +1819,15 @@ export const createSessionStore = ({ return; } emitAcpSse({ - type: 'session_reasoning_delta', + type: 'message-delta', sessionId: deltaSessionId, + sequence: nextAcpSseSequence(), + deltaIndex: + message.rawJson.type === 'reasoning' ? (message.rawJson.deltaCount ?? 0) : 0, messageId: message.id, streamPartId: message.streamPartId, - delta, - text: message.text, + kind: 'reasoning', + contentDelta: delta, createdAt: message.createdAt, updatedAt: message.updatedAt ?? message.createdAt, metadataJson: message.metadataJson, diff --git a/src/server/acp/services/sse-broadcast.ts b/src/server/acp/services/sse-broadcast.ts index 0aa9490..1dc2517 100644 --- a/src/server/acp/services/sse-broadcast.ts +++ b/src/server/acp/services/sse-broadcast.ts @@ -3,6 +3,13 @@ import { parse } from 'valibot'; import { acpSseEventSchema, type AcpSseEvent } from '../../../shared/acp.ts'; const subscribers = new Set<(line: string) => void>(); +let nextSequence = 1; + +export const nextAcpSseSequence = (): number => { + const sequence = nextSequence; + nextSequence += 1; + return sequence; +}; export const subscribeAcpSse = (onLine: (line: string) => void): (() => void) => { subscribers.add(onLine); diff --git a/src/shared/acp.ts b/src/shared/acp.ts index de2d191..8737de4 100644 --- a/src/shared/acp.ts +++ b/src/shared/acp.ts @@ -1297,31 +1297,24 @@ export const acpSseEventSchema = union([ status: optional(sessionStatusSchema), }), object({ - type: literal('session_text_delta'), + type: literal('message-add'), sessionId: pipe(string(), trim()), - messageId: pipe(string(), trim()), - streamPartId: pipe(string(), trim()), - delta: string(), - text: string(), - createdAt: pipe(string(), trim()), - updatedAt: pipe(string(), trim()), - metadataJson: optional(nullable(pipe(string(), trim()))), + sequence: number(), + message: chatMessageSchema, }), object({ - type: literal('session_reasoning_delta'), + type: literal('message-delta'), sessionId: pipe(string(), trim()), + sequence: number(), + deltaIndex: number(), messageId: pipe(string(), trim()), streamPartId: pipe(string(), trim()), - delta: string(), - text: string(), + kind: union([literal('assistant_text'), literal('reasoning')]), + contentDelta: string(), createdAt: pipe(string(), trim()), updatedAt: pipe(string(), trim()), metadataJson: optional(nullable(pipe(string(), trim()))), }), - object({ - type: literal('session_messages_updated'), - sessionId: pipe(string(), trim()), - }), object({ type: literal('session_removed'), sessionId: pipe(string(), trim()), diff --git a/src/web/app/projects/$projectId/project-chat-page.tsx b/src/web/app/projects/$projectId/project-chat-page.tsx index 8d2c039..67158f3 100644 --- a/src/web/app/projects/$projectId/project-chat-page.tsx +++ b/src/web/app/projects/$projectId/project-chat-page.tsx @@ -2157,7 +2157,6 @@ export const ProjectChatPage: FC<{ upsertSessionInCache(response.session); void queryClient.invalidateQueries({ queryKey: sessionsQueryKey }); void queryClient.invalidateQueries({ queryKey: acpPermissionRequestsQueryKey }); - void queryClient.invalidateQueries({ queryKey: sessionMessagesQueryKey(targetSessionId) }); } catch { queryClient.setQueryData(sessionsQueryKey, previousSessions); } @@ -2305,9 +2304,6 @@ export const ProjectChatPage: FC<{ } void queryClient.invalidateQueries({ queryKey: sessionsQueryKey }); void queryClient.invalidateQueries({ queryKey: projectSettingsQueryKey(projectId) }); - void queryClient.invalidateQueries({ - queryKey: sessionMessagesQueryKey(response.session.sessionId), - }); setPendingTuningModelId(null); setPendingTuningModeId(null); removeAwaitingAssistantTranscriptKeys(requestAwaitingTranscriptKeys); @@ -2377,9 +2373,6 @@ export const ProjectChatPage: FC<{ void queryClient.invalidateQueries({ queryKey: sessionsQueryKey }); void queryClient.invalidateQueries({ queryKey: projectSettingsQueryKey(projectId) }); - void queryClient.invalidateQueries({ - queryKey: sessionMessagesQueryKey(resolvedActiveSessionId), - }); setPendingTuningModelId(null); setPendingTuningModeId(null); removeAwaitingAssistantTranscriptKeys(requestAwaitingTranscriptKeys); diff --git a/src/web/lib/api/acp-sse-cache.pure.test.ts b/src/web/lib/api/acp-sse-cache.pure.test.ts index 29e8e7c..5e551ee 100644 --- a/src/web/lib/api/acp-sse-cache.pure.test.ts +++ b/src/web/lib/api/acp-sse-cache.pure.test.ts @@ -7,15 +7,17 @@ import type { SessionMessagesResponse, } from '../../../shared/acp.ts'; -import { applySessionStreamDeltaToMessages, newPermissionRequests } from './acp-sse-cache.pure.ts'; +import { applySessionMessageEventToMessages, newPermissionRequests } from './acp-sse-cache.pure.ts'; const baseTextDelta = { - type: 'session_text_delta', + type: 'message-delta', sessionId: 'session-1', + sequence: 2, + deltaIndex: 2, messageId: 'message-1', streamPartId: 'turn-1::text-1', - delta: 'lo', - text: 'hello', + kind: 'assistant_text', + contentDelta: 'lo', createdAt: '2026-04-28T00:00:00.000Z', updatedAt: '2026-04-28T00:00:01.000Z', } satisfies AcpSseEvent; @@ -31,6 +33,7 @@ const baseMessage = { streamPartId: 'turn-1::text-1', providerStreamId: 'turn-1::text-1', text: 'hel', + deltaCount: 1, createdAt: '2026-04-28T00:00:00.000Z', }, textForSearch: 'hel', @@ -42,51 +45,52 @@ const baseMessage = { } satisfies ChatMessage; const baseReasoningDelta = { - type: 'session_reasoning_delta', + type: 'message-delta', sessionId: 'session-1', + sequence: 3, + deltaIndex: 1, messageId: 'reasoning-1', streamPartId: 'turn-1::reasoning-1', - delta: 'inking', - text: 'thinking', + kind: 'reasoning', + contentDelta: 'thinking', createdAt: '2026-04-28T00:00:02.000Z', updatedAt: '2026-04-28T00:00:03.000Z', } satisfies AcpSseEvent; -describe('applySessionStreamDeltaToMessages', () => { - test('replaces the matching assistant text with the server snapshot', () => { +const userMessage = { + id: 'user-1', + role: 'user', + kind: 'user', + rawJson: { + schemaVersion: 1, + type: 'user', + role: 'user', + text: 'prompt', + attachments: [], + createdAt: '2026-04-28T00:00:00.000Z', + }, + textForSearch: 'prompt', + text: 'prompt', + rawEvents: [], + createdAt: '2026-04-28T00:00:00.000Z', + updatedAt: '2026-04-28T00:00:00.000Z', + streamPartId: null, +} satisfies ChatMessage; + +describe('applySessionMessageEventToMessages', () => { + test('appends a delta to the matching assistant text', () => { const current = { - messages: [ - { - id: 'user-1', - role: 'user', - kind: 'user', - rawJson: { - schemaVersion: 1, - type: 'user', - role: 'user', - text: 'prompt', - attachments: [], - createdAt: '2026-04-28T00:00:00.000Z', - }, - textForSearch: 'prompt', - text: 'prompt', - rawEvents: [], - createdAt: '2026-04-28T00:00:00.000Z', - updatedAt: '2026-04-28T00:00:00.000Z', - streamPartId: null, - }, - baseMessage, - ], + messages: [userMessage, baseMessage], pageInfo: { hasMoreBefore: false, beforeCursor: null }, meta: { totalMessageCount: 2 }, } satisfies SessionMessagesResponse; - expect(applySessionStreamDeltaToMessages(current, baseTextDelta)).toEqual({ + expect(applySessionMessageEventToMessages(current, baseTextDelta)).toEqual({ messages: [ - current.messages[0], + userMessage, { ...baseMessage, - rawJson: { ...baseMessage.rawJson, text: 'hello' }, + rawJson: { ...baseMessage.rawJson, text: 'hello', deltaCount: 2 }, textForSearch: 'hello', text: 'hello', updatedAt: '2026-04-28T00:00:01.000Z', @@ -97,36 +101,33 @@ describe('applySessionStreamDeltaToMessages', () => { }); }); - test('appends a text message when the start row was fetched before the delta patch', () => { + test('discards duplicate or older deltas by per-message deltaIndex', () => { const current = { messages: [ { - id: 'user-1', - role: 'user', - kind: 'user', - rawJson: { - schemaVersion: 1, - type: 'user', - role: 'user', - text: 'prompt', - attachments: [], - createdAt: '2026-04-28T00:00:00.000Z', - }, - textForSearch: 'prompt', - text: 'prompt', - rawEvents: [], - createdAt: '2026-04-28T00:00:00.000Z', - updatedAt: '2026-04-28T00:00:00.000Z', - streamPartId: null, + ...baseMessage, + rawJson: { ...baseMessage.rawJson, text: 'hello', deltaCount: 2 }, + text: 'hello', + textForSearch: 'hello', }, ], pageInfo: { hasMoreBefore: false, beforeCursor: null }, meta: { totalMessageCount: 1 }, } satisfies SessionMessagesResponse; - expect(applySessionStreamDeltaToMessages(current, baseTextDelta)).toEqual({ + expect(applySessionMessageEventToMessages(current, baseTextDelta)).toBe(current); + }); + + test('appends a text message when the start row was not fetched before the delta patch', () => { + const current = { + messages: [userMessage], + pageInfo: { hasMoreBefore: false, beforeCursor: null }, + meta: { totalMessageCount: 1 }, + } satisfies SessionMessagesResponse; + + expect(applySessionMessageEventToMessages(current, baseTextDelta)).toEqual({ messages: [ - current.messages[0], + userMessage, { id: 'message-1', role: 'assistant', @@ -137,11 +138,12 @@ describe('applySessionStreamDeltaToMessages', () => { role: 'assistant', streamPartId: 'turn-1::text-1', providerStreamId: 'turn-1::text-1', - text: 'hello', + text: 'lo', + deltaCount: 2, createdAt: '2026-04-28T00:00:00.000Z', }, - textForSearch: 'hello', - text: 'hello', + textForSearch: 'lo', + text: 'lo', rawEvents: [], createdAt: '2026-04-28T00:00:00.000Z', updatedAt: '2026-04-28T00:00:01.000Z', @@ -155,7 +157,7 @@ describe('applySessionStreamDeltaToMessages', () => { }); test('creates a text message when the cache has not been loaded yet', () => { - expect(applySessionStreamDeltaToMessages(undefined, baseTextDelta)).toEqual({ + expect(applySessionMessageEventToMessages(undefined, baseTextDelta)).toEqual({ messages: [ { id: 'message-1', @@ -167,11 +169,12 @@ describe('applySessionStreamDeltaToMessages', () => { role: 'assistant', streamPartId: 'turn-1::text-1', providerStreamId: 'turn-1::text-1', - text: 'hello', + text: 'lo', + deltaCount: 2, createdAt: '2026-04-28T00:00:00.000Z', }, - textForSearch: 'hello', - text: 'hello', + textForSearch: 'lo', + text: 'lo', rawEvents: [], createdAt: '2026-04-28T00:00:00.000Z', updatedAt: '2026-04-28T00:00:01.000Z', @@ -185,7 +188,7 @@ describe('applySessionStreamDeltaToMessages', () => { }); test('creates a reasoning message when a reasoning delta arrives before cache hydration', () => { - expect(applySessionStreamDeltaToMessages(undefined, baseReasoningDelta)).toEqual({ + expect(applySessionMessageEventToMessages(undefined, baseReasoningDelta)).toEqual({ messages: [ { id: 'reasoning-1', @@ -198,6 +201,7 @@ describe('applySessionStreamDeltaToMessages', () => { streamPartId: 'turn-1::reasoning-1', providerStreamId: 'turn-1::reasoning-1', text: 'thinking', + deltaCount: 1, createdAt: '2026-04-28T00:00:02.000Z', }, textForSearch: 'thinking', @@ -213,32 +217,52 @@ describe('applySessionStreamDeltaToMessages', () => { meta: { totalMessageCount: 1 }, }); }); + + test('upserts message-add events by id', () => { + const event = { + type: 'message-add', + sessionId: 'session-1', + sequence: 1, + message: userMessage, + } satisfies AcpSseEvent; + + expect(applySessionMessageEventToMessages(undefined, event)).toEqual({ + messages: [userMessage], + pageInfo: { hasMoreBefore: false, beforeCursor: null }, + meta: { totalMessageCount: 1 }, + }); + + const current = { + messages: [userMessage], + pageInfo: { hasMoreBefore: false, beforeCursor: null }, + meta: { totalMessageCount: 1 }, + } satisfies SessionMessagesResponse; + + expect(applySessionMessageEventToMessages(current, event)).toEqual(current); + }); }); describe('newPermissionRequests', () => { const request = { id: 'request-1', sessionId: 'session-1', - toolCallId: 'tool-1', - title: 'Allow shell command', - kind: 'tool', - rawInputText: null, - options: [{ id: 'allow', kind: 'allow_once', name: 'Allow' }], - createdAt: '2026-04-30T00:00:00.000Z', + toolCallId: 'tool-call-1', + title: 'Approve command', + kind: 'tool_call', + rawInputText: '{"foo":"bar"}', + options: [], + createdAt: '2026-04-29T00:00:00.000Z', } satisfies AcpPermissionRequest; - test('returns only unseen permission requests', () => { + test('returns requests whose ids were not known', () => { + expect(newPermissionRequests({ current: [request], knownRequestIds: new Set() })).toEqual([ + request, + ]); + }); + + test('filters requests whose ids were already known', () => { expect( - newPermissionRequests({ - current: [ - request, - { - ...request, - id: 'request-2', - }, - ], - knownRequestIds: new Set(['request-1']), - }).map((entry) => entry.id), - ).toEqual(['request-2']); + newPermissionRequests({ current: [request], knownRequestIds: new Set(['request-1']) }), + ).toEqual([]); }); }); diff --git a/src/web/lib/api/acp-sse-cache.pure.ts b/src/web/lib/api/acp-sse-cache.pure.ts index da0c5cd..ac0f715 100644 --- a/src/web/lib/api/acp-sse-cache.pure.ts +++ b/src/web/lib/api/acp-sse-cache.pure.ts @@ -5,34 +5,36 @@ import type { SessionMessagesResponse, } from '../../../shared/acp.ts'; -type SessionStreamDeltaEvent = Extract< - AcpSseEvent, - { type: 'session_text_delta' | 'session_reasoning_delta' } ->; - -const messageKindFromDelta = ( - event: SessionStreamDeltaEvent, -): Extract => - event.type === 'session_text_delta' ? 'assistant_text' : 'reasoning'; +type MessageAddEvent = Extract; +type MessageDeltaEvent = Extract; +type MessageEvent = MessageAddEvent | MessageDeltaEvent; -const messageMatchesDelta = (message: ChatMessage, event: SessionStreamDeltaEvent): boolean => +const messageMatchesDelta = (message: ChatMessage, event: MessageDeltaEvent): boolean => message.id === event.messageId || message.streamPartId === event.streamPartId; -const messageFromDelta = (event: SessionStreamDeltaEvent): ChatMessage => ({ +const deltaCountFromMessage = (message: ChatMessage): number => + message.rawJson.type === 'assistant_text' || + message.rawJson.type === 'reasoning' || + message.rawJson.type === 'tool_input' + ? (message.rawJson.deltaCount ?? 0) + : 0; + +const messageFromDelta = (event: MessageDeltaEvent): ChatMessage => ({ id: event.messageId, role: 'assistant', - kind: messageKindFromDelta(event), + kind: event.kind, rawJson: { schemaVersion: 1, - type: messageKindFromDelta(event), + type: event.kind, role: 'assistant', streamPartId: event.streamPartId, providerStreamId: event.streamPartId, - text: event.text, + text: event.contentDelta, + deltaCount: event.deltaIndex, createdAt: event.createdAt, }, - textForSearch: event.text, - text: event.text, + textForSearch: event.contentDelta, + text: event.contentDelta, rawEvents: [], createdAt: event.createdAt, updatedAt: event.updatedAt, @@ -40,35 +42,78 @@ const messageFromDelta = (event: SessionStreamDeltaEvent): ChatMessage => ({ metadataJson: event.metadataJson ?? undefined, }); -const mergeTextDeltaMessage = ( - message: ChatMessage, - event: SessionStreamDeltaEvent, -): ChatMessage => ({ - ...message, - kind: message.kind ?? messageKindFromDelta(event), - rawJson: - message.rawJson.type === 'assistant_text' || message.rawJson.type === 'reasoning' - ? { ...message.rawJson, text: event.text } - : messageFromDelta(event).rawJson, - textForSearch: event.text, - text: event.text, - updatedAt: event.updatedAt, - streamPartId: message.streamPartId ?? event.streamPartId, - metadataJson: event.metadataJson ?? message.metadataJson, +const mergeDeltaMessage = (message: ChatMessage, event: MessageDeltaEvent): ChatMessage => { + if (event.deltaIndex <= deltaCountFromMessage(message)) { + return message; + } + + const text = `${message.text}${event.contentDelta}`; + return { + ...message, + kind: message.kind ?? event.kind, + rawJson: + message.rawJson.type === 'assistant_text' || message.rawJson.type === 'reasoning' + ? { ...message.rawJson, text, deltaCount: event.deltaIndex } + : messageFromDelta(event).rawJson, + textForSearch: text, + text, + updatedAt: event.updatedAt, + streamPartId: message.streamPartId ?? event.streamPartId, + metadataJson: event.metadataJson ?? message.metadataJson, + }; +}; + +const mergeAddMessage = (current: ChatMessage, incoming: ChatMessage): ChatMessage => { + const currentDeltaCount = deltaCountFromMessage(current); + const incomingDeltaCount = deltaCountFromMessage(incoming); + if (currentDeltaCount > incomingDeltaCount) { + return current; + } + return incoming; +}; + +const emptyMessagesResponse = (message: ChatMessage): SessionMessagesResponse => ({ + messages: [message], + pageInfo: { hasMoreBefore: false, beforeCursor: null }, + meta: { totalMessageCount: 1 }, }); -export const applySessionStreamDeltaToMessages = ( +const applyMessageAddToMessages = ( current: SessionMessagesResponse | undefined, - event: SessionStreamDeltaEvent, + event: MessageAddEvent, ): SessionMessagesResponse => { if (current === undefined) { + return emptyMessagesResponse(event.message); + } + + const matched = current.messages.some((message) => message.id === event.message.id); + if (!matched) { return { - messages: [messageFromDelta(event)], - pageInfo: { hasMoreBefore: false, beforeCursor: null }, - meta: { totalMessageCount: 1 }, + ...current, + messages: [...current.messages, event.message], + meta: { + ...current.meta, + totalMessageCount: current.meta.totalMessageCount + 1, + }, }; } + const messages = current.messages.map((message) => + message.id === event.message.id ? mergeAddMessage(message, event.message) : message, + ); + return messages.every((message, index) => message === current.messages[index]) + ? current + : { ...current, messages }; +}; + +const applyMessageDeltaToMessages = ( + current: SessionMessagesResponse | undefined, + event: MessageDeltaEvent, +): SessionMessagesResponse => { + if (current === undefined) { + return emptyMessagesResponse(messageFromDelta(event)); + } + const matched = current.messages.some((message) => messageMatchesDelta(message, event)); if (!matched) { return { @@ -81,14 +126,22 @@ export const applySessionStreamDeltaToMessages = ( }; } - return { - ...current, - messages: current.messages.map((message) => - messageMatchesDelta(message, event) ? mergeTextDeltaMessage(message, event) : message, - ), - }; + const messages = current.messages.map((message) => + messageMatchesDelta(message, event) ? mergeDeltaMessage(message, event) : message, + ); + return messages.every((message, index) => message === current.messages[index]) + ? current + : { ...current, messages }; }; +export const applySessionMessageEventToMessages = ( + current: SessionMessagesResponse | undefined, + event: MessageEvent, +): SessionMessagesResponse => + event.type === 'message-add' + ? applyMessageAddToMessages(current, event) + : applyMessageDeltaToMessages(current, event); + export const newPermissionRequests = ({ current, knownRequestIds, diff --git a/src/web/lib/api/useAcpSseCacheSync.ts b/src/web/lib/api/useAcpSseCacheSync.ts index 0c5fef1..189caf5 100644 --- a/src/web/lib/api/useAcpSseCacheSync.ts +++ b/src/web/lib/api/useAcpSseCacheSync.ts @@ -31,25 +31,23 @@ import { } from '../../pwa/notifications.ts'; import { playTaskCompletionSound } from '../../pwa/task-completion-sound.ts'; import { dispatchAcpSseBrowserEvent } from './acp-sse-browser-event.ts'; -import { applySessionStreamDeltaToMessages, newPermissionRequests } from './acp-sse-cache.pure.ts'; +import { applySessionMessageEventToMessages, newPermissionRequests } from './acp-sse-cache.pure.ts'; import { fetchAcpPermissionRequests, fetchProjects, fetchSessions } from './acp.ts'; import { acpSseUrl } from './client.ts'; -/** ACP のストリーミングで SSE が連打されるため、invalidate の間隔を空ける */ -const ACP_SSE_INVALIDATE_DEBOUNCE_MS = 300; +/** ACP の非メッセージ系 SSE が連打されるため、fetch の間隔を空ける */ +const ACP_SSE_FETCH_DEBOUNCE_MS = 300; -const applyTextDelta = (queryClient: QueryClient, event: AcpSseEvent): boolean => { - if (event.type !== 'session_text_delta' && event.type !== 'session_reasoning_delta') { +const applyMessageEvent = (queryClient: QueryClient, event: AcpSseEvent): boolean => { + if (event.type !== 'message-add' && event.type !== 'message-delta') { return false; } - // 従来の flat query に適用 queryClient.setQueryData( sessionMessagesQueryKey(event.sessionId), - (current) => applySessionStreamDeltaToMessages(current, event), + (current) => applySessionMessageEventToMessages(current, event), ); - // infinite query の先頭ページ(最新メッセージ)にも適用 queryClient.setQueriesData>( { queryKey: sessionMessagesInfiniteQueryKey(event.sessionId) }, (current) => { @@ -60,7 +58,7 @@ const applyTextDelta = (queryClient: QueryClient, event: AcpSseEvent): boolean = if (firstPage === undefined) { return current; } - const updatedFirst = applySessionStreamDeltaToMessages(firstPage, event); + const updatedFirst = applySessionMessageEventToMessages(firstPage, event); if (updatedFirst === firstPage) { return current; } @@ -77,7 +75,6 @@ const applyTextDelta = (queryClient: QueryClient, event: AcpSseEvent): boolean = const mergeEventToPending = ( pending: { needSessionsList: boolean; - messageSessionIds: Set; pausedSessionIds: Set; removedSessionIds: Set; catalogUpdates: Set; @@ -101,11 +98,6 @@ const mergeEventToPending = ( knownStatuses.delete(event.sessionId); return; } - if (event.type === 'session_messages_updated') { - pending.needSessionsList = true; - pending.messageSessionIds.add(event.sessionId); - return; - } if (event.type === 'session_updated') { pending.needSessionsList = true; if (event.status !== undefined) { @@ -251,7 +243,6 @@ const flushPending = async ( queryClient: QueryClient, pending: { needSessionsList: boolean; - messageSessionIds: Set; pausedSessionIds: Set; removedSessionIds: Set; catalogUpdates: Set; @@ -262,14 +253,12 @@ const flushPending = async ( ): Promise => { const work = { needSessionsList: pending.needSessionsList, - messageSessionIds: new Set(pending.messageSessionIds), pausedSessionIds: new Set(pending.pausedSessionIds), removedSessionIds: new Set(pending.removedSessionIds), catalogUpdates: new Set(pending.catalogUpdates), permissionRequestsUpdated: pending.permissionRequestsUpdated, }; pending.needSessionsList = false; - pending.messageSessionIds.clear(); pending.pausedSessionIds.clear(); pending.removedSessionIds.clear(); pending.catalogUpdates.clear(); @@ -299,14 +288,6 @@ const flushPending = async ( knownStatuses.set(session.sessionId, session.status); } } - for (const sessionId of work.messageSessionIds) { - const queryKey = sessionMessagesQueryKey(sessionId); - void queryClient.invalidateQueries({ queryKey, refetchType: 'all' }); - void queryClient.invalidateQueries({ - queryKey: sessionMessagesInfiniteQueryKey(sessionId), - refetchType: 'all', - }); - } for (const key of work.catalogUpdates) { if (key.length > 0) { void queryClient.invalidateQueries({ queryKey: ['acp', 'agent-model-catalog'] }); @@ -355,7 +336,6 @@ export const useAcpSseCacheSync = (): void => { const knownStatuses = knownStatusesRef.current; const pending = { needSessionsList: false, - messageSessionIds: new Set(), pausedSessionIds: new Set(), removedSessionIds: new Set(), catalogUpdates: new Set(), @@ -382,7 +362,7 @@ export const useAcpSseCacheSync = (): void => { knownStatuses, knownPermissionRequestIdsRef.current, ); - }, ACP_SSE_INVALIDATE_DEBOUNCE_MS); + }, ACP_SSE_FETCH_DEBOUNCE_MS); }; const source = new EventSource(acpSseUrl(), { withCredentials: false }); @@ -397,7 +377,7 @@ export const useAcpSseCacheSync = (): void => { return; } dispatchAcpSseBrowserEvent(event); - if (applyTextDelta(queryClient, event)) { + if (applyMessageEvent(queryClient, event)) { return; } mergeEventToPending(pending, event, knownStatuses, statusFromCache); @@ -414,7 +394,6 @@ export const useAcpSseCacheSync = (): void => { } const hasWork = pending.needSessionsList || - pending.messageSessionIds.size > 0 || pending.pausedSessionIds.size > 0 || pending.removedSessionIds.size > 0 || pending.catalogUpdates.size > 0 || From 6c198789fc348f634efe4fcb7025bc630cf68dab Mon Sep 17 00:00:00 2001 From: d-kimsuon Date: Tue, 5 May 2026 22:03:45 +0900 Subject: [PATCH 08/14] fix(chat): stabilize session transcript updates --- src/server/acp/routes.ts | 2 +- src/server/acp/services/session-store.test.ts | 41 +- src/server/acp/services/session-store.ts | 94 ++++- .../projects/$projectId/project-chat-page.tsx | 397 +++++++++++++----- .../$projectId/project-menu-content.tsx | 14 +- src/web/lib/api/useAcpSseCacheSync.ts | 12 +- src/web/lib/i18n/resources.ts | 8 +- 7 files changed, 410 insertions(+), 158 deletions(-) diff --git a/src/server/acp/routes.ts b/src/server/acp/routes.ts index f21e03e..961bd22 100644 --- a/src/server/acp/routes.ts +++ b/src/server/acp/routes.ts @@ -898,7 +898,7 @@ export const acpRoutes = new Hono() const beforeParam = c.req.query('before'); const view = viewParam === 'raw' ? 'raw' : 'transcript'; - const limit = limitParam !== undefined ? Math.min(Number(limitParam), 1000) : 200; + const limit = limitParam !== undefined ? Math.min(Number(limitParam), 10_000) : 200; const before = beforeParam !== undefined && beforeParam.includes('__') ? (() => { diff --git a/src/server/acp/services/session-store.test.ts b/src/server/acp/services/session-store.test.ts index 5486a42..b59f46f 100644 --- a/src/server/acp/services/session-store.test.ts +++ b/src/server/acp/services/session-store.test.ts @@ -1719,16 +1719,21 @@ describe('createSessionStore', () => { availableCommands: [{ name: 'review', description: 'Review current changes' }], }, }); + const toolUpdate = { + sessionUpdate: 'tool_call_update' as const, + toolCallId: 'tool-1', + title: 'Edit file', + kind: 'edit' as const, + status: 'in_progress' as const, + locations: [{ path: 'src/app.ts', line: 12 }], + }; installedHandler({ sessionId: 'session-updates', - update: { - sessionUpdate: 'tool_call_update', - toolCallId: 'tool-1', - title: 'Edit file', - kind: 'edit', - status: 'in_progress', - locations: [{ path: 'src/app.ts', line: 12 }], - }, + update: toolUpdate, + }); + installedHandler({ + sessionId: 'session-updates', + update: toolUpdate, }); await new Promise((resolve) => { @@ -1742,19 +1747,17 @@ describe('createSessionStore', () => { }); const messages = await store.listMessages('session-updates'); expect(messages.map((message) => message.kind)).toContain('raw_meta'); + const toolUpdateText = JSON.stringify({ + toolCallId: 'tool-1', + title: 'Edit file', + kind: 'edit', + status: 'in_progress', + locations: [{ path: 'src/app.ts', line: 12 }], + }); expect(messages.map((message) => message.text)).toEqual( - expect.arrayContaining([ - 'context 1200/4000 tokens (30%)', - '/review', - JSON.stringify({ - toolCallId: 'tool-1', - title: 'Edit file', - kind: 'edit', - status: 'in_progress', - locations: [{ path: 'src/app.ts', line: 12 }], - }), - ]), + expect.arrayContaining(['context 1200/4000 tokens (30%)', '/review', toolUpdateText]), ); + expect(messages.filter((message) => message.text === toolUpdateText)).toHaveLength(1); }); test('cancels a running prompt and persists an abort message', async () => { diff --git a/src/server/acp/services/session-store.ts b/src/server/acp/services/session-store.ts index 621de7f..0860616 100644 --- a/src/server/acp/services/session-store.ts +++ b/src/server/acp/services/session-store.ts @@ -654,6 +654,7 @@ export const createSessionStore = ({ installAcpProviderToolResultPatch(); const runtimeSessions = new Map(); + const lastPersistedToolUpdateTextByKey = new Map(); const emitSessionUpdated = (session: SessionSummary): void => { emitAcpSse({ @@ -978,6 +979,45 @@ export const createSessionStore = ({ }); }; + const persistMutableMessage = async ({ + id, + sessionId, + message, + }: { + readonly id: string; + readonly sessionId: string; + readonly message: ChatMessage; + }): Promise => { + const created = message.createdAt; + const kind: ChatMessageKind = + message.kind ?? (message.role === 'user' ? 'user' : 'legacy_assistant_turn'); + await database.db + .insert(sessionMessagesTable) + .values({ + id, + sessionId, + kind, + textForSearch: message.textForSearch ?? message.text, + rawJson: JSON.stringify(message.rawJson), + createdAt: created, + }) + .onConflictDoUpdate({ + target: sessionMessagesTable.id, + set: { + kind, + textForSearch: message.textForSearch ?? message.text, + rawJson: JSON.stringify(message.rawJson), + createdAt: created, + }, + }); + emitAcpSse({ + type: 'message-add', + sessionId, + sequence: nextAcpSseSequence(), + message: { ...message, id }, + }); + }; + const hasStoredMessages = async (sessionId: string): Promise => { const rows = await database.db .select({ id: sessionMessagesTable.id }) @@ -1105,23 +1145,49 @@ export const createSessionStore = ({ return; } + const text = sessionUpdateText(notification); + if ( + notification.update.sessionUpdate === 'tool_call' || + notification.update.sessionUpdate === 'tool_call_update' + ) { + const dedupeKey = `${notification.sessionId}:${notification.update.toolCallId}`; + if (lastPersistedToolUpdateTextByKey.get(dedupeKey) === text) { + return; + } + lastPersistedToolUpdateTextByKey.set(dedupeKey, text); + } + const rawText = JSON.stringify(notification.update); + const message = buildMessage({ + role: 'assistant', + text, + rawEvents: [ + { + type: 'streamPart', + partType: updateType, + text, + rawText, + }, + ], + kind: 'raw_meta', + metadataJson: JSON.stringify({ acpSessionUpdate: notification.update }), + }); + + if ( + notification.update.sessionUpdate === 'tool_call' || + notification.update.sessionUpdate === 'tool_call_update' + ) { + await persistMutableMessage({ + id: `raw-meta:tool:${notification.sessionId}:${notification.update.toolCallId}`, + sessionId: notification.sessionId, + message, + }); + return; + } + await persistMessage({ sessionId: notification.sessionId, - message: buildMessage({ - role: 'assistant', - text: sessionUpdateText(notification), - rawEvents: [ - { - type: 'streamPart', - partType: updateType, - text: sessionUpdateText(notification), - rawText, - }, - ], - kind: 'raw_meta', - metadataJson: JSON.stringify({ acpSessionUpdate: notification.update }), - }), + message, }); }; diff --git a/src/web/app/projects/$projectId/project-chat-page.tsx b/src/web/app/projects/$projectId/project-chat-page.tsx index 67158f3..d30765b 100644 --- a/src/web/app/projects/$projectId/project-chat-page.tsx +++ b/src/web/app/projects/$projectId/project-chat-page.tsx @@ -1,9 +1,4 @@ -import { - useMutation, - useQueryClient, - useSuspenseInfiniteQuery, - useSuspenseQuery, -} from '@tanstack/react-query'; +import { useMutation, useQueryClient, useSuspenseQuery } from '@tanstack/react-query'; import { Link, useNavigate } from '@tanstack/react-router'; import { ArrowDown, @@ -63,7 +58,6 @@ import { Badge } from '../../../components/ui/badge.tsx'; import { Button } from '../../../components/ui/button.tsx'; import { Input } from '../../../components/ui/input.tsx'; import { Label } from '../../../components/ui/label.tsx'; -import { ScrollArea } from '../../../components/ui/scroll-area.tsx'; import { Select, SelectContent, @@ -75,6 +69,7 @@ import { SelectValue, } from '../../../components/ui/select.tsx'; import { ACP_SSE_BROWSER_EVENT } from '../../../lib/api/acp-sse-browser-event.ts'; +import { applySessionMessageEventToMessages } from '../../../lib/api/acp-sse-cache.pure.ts'; import { cancelSessionRequest, createProjectWorktreeRequest, @@ -867,24 +862,151 @@ const SlashCommandsLoader: FC<{ }; const SESSION_MESSAGES_PAGE_SIZE = 50; -const initiallyPrefetchedSessionMessageKeys = new Set(); const compareChatMessageOrder = (left: ChatMessage, right: ChatMessage): number => { const createdAtOrder = left.createdAt.localeCompare(right.createdAt); return createdAtOrder !== 0 ? createdAtOrder : left.id.localeCompare(right.id); }; +const OPTIMISTIC_USER_MESSAGE_DUPLICATE_WINDOW_MS = 2 * 60 * 1000; + +const timestampMillisOrNull = (value: string): number | null => { + const time = Date.parse(value); + return Number.isNaN(time) ? null : time; +}; + +const userAttachmentIdentitySignature = (message: ChatMessage): string => { + if (message.rawJson.type !== 'user') { + return ''; + } + return (message.rawJson.attachments ?? []) + .map((attachment) => + [ + attachment.attachmentId ?? '', + attachment.name ?? '', + String(attachment.sizeInBytes ?? ''), + attachment.source.type, + attachment.source.media_type, + ].join('\u0000'), + ) + .join('\u0001'); +}; + +const isOptimisticUserMessageDuplicate = ({ + fetched, + local, +}: { + readonly fetched: ChatMessage; + readonly local: ChatMessage; +}): boolean => { + if (fetched.role !== 'user' || local.role !== 'user') { + return false; + } + if (fetched.text !== local.text) { + return false; + } + if (userAttachmentIdentitySignature(fetched) !== userAttachmentIdentitySignature(local)) { + return false; + } + + const fetchedTime = timestampMillisOrNull(fetched.createdAt); + const localTime = timestampMillisOrNull(local.createdAt); + if (fetchedTime === null || localTime === null) { + return false; + } + return Math.abs(fetchedTime - localTime) <= OPTIMISTIC_USER_MESSAGE_DUPLICATE_WINDOW_MS; +}; + const mergeHydratedMessages = ({ fetched, local, + optimisticUserMessageIds, }: { readonly fetched: readonly ChatMessage[]; readonly local: readonly ChatMessage[]; + readonly optimisticUserMessageIds: ReadonlySet; }): readonly ChatMessage[] => { const fetchedIds = new Set(fetched.map((message) => message.id)); - return [...fetched, ...local.filter((message) => !fetchedIds.has(message.id))].toSorted( - compareChatMessageOrder, - ); + return [ + ...fetched, + ...local.filter( + (message) => + !fetchedIds.has(message.id) && + (!optimisticUserMessageIds.has(message.id) || + !fetched.some((fetchedMessage) => + isOptimisticUserMessageDuplicate({ fetched: fetchedMessage, local: message }), + )), + ), + ].toSorted(compareChatMessageOrder); +}; + +const REMAINING_SESSION_MESSAGES_PAGE_SIZE = 100; + +const ChunkedRemainingSessionMessagesLoader: FC<{ + readonly before: string; + readonly latestMessages: readonly ChatMessage[]; + readonly sessionId: string; + readonly onBeforeHydrateRemaining: () => void; + readonly onHydrated: (sessionId: string, messages: readonly ChatMessage[]) => void; + readonly onReady: () => void; +}> = ({ before, latestMessages, onBeforeHydrateRemaining, onHydrated, onReady, sessionId }) => { + const [cursor, setCursor] = useState(before); + const [loadedPages, setLoadedPages] = useState([]); + const processedCursorsRef = useRef(new Set()); + const capturedAnchorRef = useRef(false); + + useLayoutEffect(() => { + setCursor(before); + setLoadedPages([]); + processedCursorsRef.current = new Set(); + capturedAnchorRef.current = false; + }, [before, sessionId]); + + const { data } = useSuspenseQuery({ + queryKey: [...sessionMessagesInfiniteQueryKey(sessionId), 'remaining-chunk', cursor], + queryFn: () => + fetchSessionMessages(sessionId, { + view: 'transcript', + limit: REMAINING_SESSION_MESSAGES_PAGE_SIZE, + before: cursor, + }), + }); + + useLayoutEffect(() => { + if (processedCursorsRef.current.has(cursor)) { + return; + } + processedCursorsRef.current.add(cursor); + if (!capturedAnchorRef.current) { + capturedAnchorRef.current = true; + onBeforeHydrateRemaining(); + } + + const nextLoadedPages = [...loadedPages, data]; + onHydrated(sessionId, [ + ...[...nextLoadedPages].reverse().flatMap((page) => page.messages), + ...latestMessages, + ]); + + const nextCursor = data.pageInfo.beforeCursor ?? null; + if (data.pageInfo.hasMoreBefore && nextCursor !== null) { + setLoadedPages(nextLoadedPages); + setCursor(nextCursor); + return; + } + onReady(); + }, [ + cursor, + data, + latestMessages, + loadedPages, + onBeforeHydrateRemaining, + onHydrated, + onReady, + sessionId, + ]); + + return null; }; const SessionMessagesHydrator: FC<{ @@ -896,59 +1018,54 @@ const SessionMessagesHydrator: FC<{ isFetchingNextPage: boolean, loadedPageCount: number, ) => void; -}> = ({ sessionId, onHydrated, onFetchNextPageReady }) => { - const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useSuspenseInfiniteQuery({ - queryKey: sessionMessagesInfiniteQueryKey(sessionId), - queryFn: ({ pageParam }) => + readonly onBeforeFetchRemaining: () => void; +}> = ({ sessionId, onBeforeFetchRemaining, onHydrated, onFetchNextPageReady }) => { + const [remainingLoaded, setRemainingLoaded] = useState(false); + const latestMessagesQuery = useSuspenseQuery({ + queryKey: [...sessionMessagesInfiniteQueryKey(sessionId), 'latest'], + queryFn: () => fetchSessionMessages(sessionId, { view: 'transcript', limit: SESSION_MESSAGES_PAGE_SIZE, - before: pageParam ?? undefined, }), - initialPageParam: null as string | null, - getNextPageParam: (lastPage) => - lastPage.pageInfo.hasMoreBefore ? lastPage.pageInfo.beforeCursor : undefined, }); - - const allMessages = useMemo( - () => [...data.pages].reverse().flatMap((page) => page.messages), - [data.pages], - ); + const beforeCursor = latestMessagesQuery.data.pageInfo.beforeCursor ?? null; + const shouldFetchRemaining = + latestMessagesQuery.data.pageInfo.hasMoreBefore && beforeCursor !== null; useLayoutEffect(() => { - onHydrated(sessionId, allMessages); - }, [allMessages, onHydrated, sessionId]); - - const fetchNextMessagesPage = useCallback( - () => fetchNextPage({ cancelRefetch: false }), - [fetchNextPage], - ); + setRemainingLoaded(false); + }, [sessionId, beforeCursor]); useLayoutEffect(() => { - onFetchNextPageReady(fetchNextMessagesPage, hasNextPage, isFetchingNextPage, data.pages.length); - }, [ - data.pages.length, - fetchNextMessagesPage, - hasNextPage, - isFetchingNextPage, - onFetchNextPageReady, - ]); + onHydrated(sessionId, latestMessagesQuery.data.messages); + }, [latestMessagesQuery.data.messages, onHydrated, sessionId]); - useEffect(() => { - const prefetchKey = sessionId; - if ( - data.pages.length !== 1 || - !hasNextPage || - isFetchingNextPage || - initiallyPrefetchedSessionMessageKeys.has(prefetchKey) - ) { - return; - } - initiallyPrefetchedSessionMessageKeys.add(prefetchKey); - void fetchNextMessagesPage(); - }, [data.pages.length, fetchNextMessagesPage, hasNextPage, isFetchingNextPage, sessionId]); + const noopFetchNextPage = useCallback(async () => {}, []); - return null; + useLayoutEffect(() => { + onFetchNextPageReady( + noopFetchNextPage, + shouldFetchRemaining && !remainingLoaded, + shouldFetchRemaining && !remainingLoaded, + remainingLoaded ? 2 : 1, + ); + }, [noopFetchNextPage, onFetchNextPageReady, remainingLoaded, shouldFetchRemaining]); + + return shouldFetchRemaining && !remainingLoaded ? ( + + { + setRemainingLoaded(true); + }} + sessionId={sessionId} + /> + + ) : null; }; export const ProjectChatPage: FC<{ @@ -1011,6 +1128,7 @@ export const ProjectChatPage: FC<{ value: '', }); const [transcripts, setTranscripts] = useState({}); + const optimisticUserMessageIdsRef = useRef(new Set()); const [attachedFiles, setAttachedFiles] = useState([]); const [awaitingAssistantTranscriptKeys, setAwaitingAssistantTranscriptKeys] = useState< readonly string[] @@ -1039,7 +1157,6 @@ export const ProjectChatPage: FC<{ const speechRecognitionRef = useRef(null); const speechListeningDesiredRef = useRef(false); const chatContentRef = useRef(null); - const olderMessagesPrefetchRef = useRef(null); const scrollAnchorRef = useRef(null); const shouldStickToBottomRef = useRef(true); const lastActiveTranscriptKeyRef = useRef(null); @@ -1056,8 +1173,10 @@ export const ProjectChatPage: FC<{ const fetchNextPageRef = useRef<(() => Promise) | null>(null); const [hasNextPage, setHasNextPage] = useState(false); const [isFetchingNextPage, setIsFetchingNextPage] = useState(false); - const [loadedSessionMessagePageCount, setLoadedSessionMessagePageCount] = useState(0); - const fetchingNextPageRef = useRef(false); + const pendingPrependAnchorRef = useRef<{ + readonly messageId: string; + readonly top: number; + } | null>(null); const onAgentCatalogReady = useCallback((catalog: AgentModelCatalogResponse) => { setProbedModelCatalog(catalog); @@ -1727,15 +1846,52 @@ export const ProjectChatPage: FC<{ setAwaitingAssistantTranscriptKeys((current) => current.includes(nextSessionId) ? current : [...current, nextSessionId], ); - setTranscripts((current) => - current[nextSessionId] === undefined && draftSessionRedirectRequestRef.current !== null - ? moveTranscript({ - from: draftSessionRedirectRequestRef.current.draftTranscriptKey, - to: nextSessionId, - transcripts: current, - }) - : current, - ); + setTranscripts((current) => { + const moved = + current[nextSessionId] === undefined && draftSessionRedirectRequestRef.current !== null + ? moveTranscript({ + from: draftSessionRedirectRequestRef.current.draftTranscriptKey, + to: nextSessionId, + transcripts: current, + }) + : current; + + if (sseEvent.type !== 'message-add' && sseEvent.type !== 'message-delta') { + return moved; + } + + const local = moved[nextSessionId] ?? []; + const patchedFromEvent = applySessionMessageEventToMessages( + { + messages: [...local], + pageInfo: { hasMoreBefore: false, beforeCursor: null }, + meta: { totalMessageCount: local.length }, + }, + sseEvent, + ).messages; + const patched = + sseEvent.type === 'message-add' && sseEvent.message.role === 'user' + ? patchedFromEvent.filter((message) => { + const shouldDrop = + optimisticUserMessageIdsRef.current.has(message.id) && + isOptimisticUserMessageDuplicate({ fetched: sseEvent.message, local: message }); + if (shouldDrop) { + optimisticUserMessageIdsRef.current.delete(message.id); + } + return !shouldDrop; + }) + : patchedFromEvent; + if ( + patched.length === local.length && + patched.every((message, index) => message === local[index]) + ) { + return moved; + } + return { + ...moved, + [nextSessionId]: patched.map((message) => ({ ...message })), + }; + }); navigateToStartedDraftSession(nextSessionId); }; @@ -1761,17 +1917,61 @@ export const ProjectChatPage: FC<{ ) { return current; } + const merged = mergeHydratedMessages({ + fetched: messages, + local, + optimisticUserMessageIds: optimisticUserMessageIdsRef.current, + }); + if ( + merged.length === local.length && + merged.every((message, index) => { + const currentMessage = local[index]; + return ( + currentMessage !== undefined && + currentMessage.id === message.id && + currentMessage.updatedAt === message.updatedAt && + currentMessage.text === message.text + ); + }) + ) { + return current; + } return { ...current, - [targetSessionId]: mergeHydratedMessages({ - fetched: messages, - local, - }).map((message) => ({ ...message })), + [targetSessionId]: merged.map((message) => ({ ...message })), }; }); }, [awaitingAssistantTranscriptKeys, isAssistantRequestPending], ); + const handleBeforeFetchRemainingMessages = useCallback(() => { + const viewport = chatViewportElement; + if ( + viewport === null || + shouldStickToBottomRef.current || + pendingPrependAnchorRef.current !== null + ) { + return; + } + + const viewportTop = viewport.getBoundingClientRect().top; + const messageElements = [...viewport.querySelectorAll('[data-message-id]')]; + const anchorElement = + messageElements.find((element) => element.getBoundingClientRect().bottom >= viewportTop) ?? + messageElements[0]; + if (anchorElement === undefined) { + return; + } + const messageId = anchorElement.dataset['messageId']; + if (messageId === undefined) { + return; + } + pendingPrependAnchorRef.current = { + messageId, + top: anchorElement.getBoundingClientRect().top, + }; + }, [chatViewportElement]); + const attachmentNames = attachedFiles.map((attachment) => attachment.name); const canSendPrompt = useCallback( (value: string) => @@ -2204,6 +2404,7 @@ export const ProjectChatPage: FC<{ kind: 'user', attachments: userAttachmentsFromUploaded(attachedFiles), }); + optimisticUserMessageIdsRef.current.add(userMessage.id); const initialTranscriptKey = activeTranscriptKey; let requestAwaitingTranscriptKeys: readonly string[] = [initialTranscriptKey]; @@ -2434,13 +2635,10 @@ export const ProjectChatPage: FC<{ fetchNextPage: () => Promise, nextPageExists: boolean, nextIsFetchingNextPage: boolean, - loadedPageCount: number, ) => { fetchNextPageRef.current = fetchNextPage; setHasNextPage(nextPageExists); setIsFetchingNextPage(nextIsFetchingNextPage); - setLoadedSessionMessagePageCount(loadedPageCount); - fetchingNextPageRef.current = nextIsFetchingNextPage; }, [], ); @@ -2480,38 +2678,23 @@ export const ProjectChatPage: FC<{ } }; - useEffect(() => { - if ( - sessionId === null || - chatViewportElement === null || - !hasNextPage || - loadedSessionMessagePageCount < 2 - ) { + useLayoutEffect(() => { + const pendingAnchor = pendingPrependAnchorRef.current; + const viewport = chatViewportElement; + if (pendingAnchor === null || viewport === null) { return; } - const sentinel = olderMessagesPrefetchRef.current; - if (sentinel === null) { + + const anchorElement = [...viewport.querySelectorAll('[data-message-id]')].find( + (element) => element.dataset['messageId'] === pendingAnchor.messageId, + ); + if (anchorElement === undefined) { return; } - const observer = new IntersectionObserver( - (entries) => { - if (entries.some((entry) => entry.isIntersecting) && !fetchingNextPageRef.current) { - fetchingNextPageRef.current = true; - void fetchNextPageRef.current?.(); - } - }, - { - root: chatViewportElement, - rootMargin: '200px 0px 0px 0px', - threshold: 0, - }, - ); - observer.observe(sentinel); - return () => { - observer.disconnect(); - }; - }, [chatViewportElement, hasNextPage, loadedSessionMessagePageCount, sessionId]); + pendingPrependAnchorRef.current = null; + viewport.scrollTop += anchorElement.getBoundingClientRect().top - pendingAnchor.top; + }, [chatViewportElement, visibleTranscript.length]); const handleJumpToLatest = () => { shouldStickToBottomRef.current = true; @@ -2536,6 +2719,7 @@ export const ProjectChatPage: FC<{ const didSessionChange = lastActiveTranscriptKeyRef.current !== activeTranscriptKey; if (didSessionChange) { lastActiveTranscriptKeyRef.current = activeTranscriptKey; + pendingPrependAnchorRef.current = null; shouldStickToBottomRef.current = true; setIsChatFollowingTail(true); setUnreadMessageCount(0); @@ -2573,6 +2757,7 @@ export const ProjectChatPage: FC<{
-
) : null} - {loadedSessionMessagePageCount > 1 && hasNextPage ? ( -
- ) : null} {visibleTranscript.map((message, index) => { const isUser = message.role === 'user'; const displayEvents = filterDisplayableRawEvents(message.rawEvents); @@ -2758,10 +2942,11 @@ export const ProjectChatPage: FC<{ return (
- +
{shouldShowScrollBanner ? (
diff --git a/src/web/lib/api/useAcpSseCacheSync.ts b/src/web/lib/api/useAcpSseCacheSync.ts index 189caf5..da771c3 100644 --- a/src/web/lib/api/useAcpSseCacheSync.ts +++ b/src/web/lib/api/useAcpSseCacheSync.ts @@ -48,10 +48,20 @@ const applyMessageEvent = (queryClient: QueryClient, event: AcpSseEvent): boolea (current) => applySessionMessageEventToMessages(current, event), ); + queryClient.setQueriesData( + { queryKey: sessionMessagesInfiniteQueryKey(event.sessionId) }, + (current) => { + if (current === undefined || !Array.isArray(current.messages)) { + return current; + } + return applySessionMessageEventToMessages(current, event); + }, + ); + queryClient.setQueriesData>( { queryKey: sessionMessagesInfiniteQueryKey(event.sessionId) }, (current) => { - if (current === undefined) { + if (current === undefined || !Array.isArray(current.pages)) { return current; } const firstPage = current.pages[0]; diff --git a/src/web/lib/i18n/resources.ts b/src/web/lib/i18n/resources.ts index f91e5da..4b110c4 100644 --- a/src/web/lib/i18n/resources.ts +++ b/src/web/lib/i18n/resources.ts @@ -53,7 +53,7 @@ export const resources = { sessions: 'セッションリスト', routines: 'Routines', settings: '設定', - information: 'Information', + information: 'About Remote Agent', openMenu: 'メニューを開く', closeMenu: 'メニューを閉じる', collapseMenu: 'メニューを折りたたむ', @@ -63,7 +63,7 @@ export const resources = { viewing: '表示中', }, information: { - title: 'Information', + title: 'About Remote Agent', remoteAgent: 'Remote Agent', version: 'バージョン', connectionStatus: '疎通ステータス', @@ -390,7 +390,7 @@ export const resources = { sessions: 'Sessions', routines: 'Routines', settings: 'Settings', - information: 'Information', + information: 'About Remote Agent', openMenu: 'Open menu', closeMenu: 'Close menu', collapseMenu: 'Collapse menu', @@ -400,7 +400,7 @@ export const resources = { viewing: 'Viewing', }, information: { - title: 'Information', + title: 'About Remote Agent', remoteAgent: 'Remote Agent', version: 'Version', connectionStatus: 'Connection status', From c0d3828f141add465230c958882f6bf645350e4c Mon Sep 17 00:00:00 2001 From: d-kimsuon Date: Wed, 6 May 2026 03:16:42 +0900 Subject: [PATCH 09/14] fix(web): add active indicators to menu tabs and improve project routing - Add left indicator bar + background highlight to active menu items (both global DefaultMenuContent and project-scoped ProjectMenuContent) - Show project selector on /settings?projectId= and /information?projectId= - Navigate to correct path when switching projects from settings/information - Link header logo to /projects - Fix menu flash during Suspense by showing loading on project-scoped routes - i18n: simplify menu labels (sessions, routines, about) --- .../$projectId/project-menu-content.tsx | 37 ++++++++++-- src/web/components/app-header.tsx | 57 +++++++++++++----- src/web/components/app-menu.tsx | 59 +++++++++++++++++-- src/web/lib/i18n/resources.ts | 8 +-- 4 files changed, 129 insertions(+), 32 deletions(-) diff --git a/src/web/app/projects/$projectId/project-menu-content.tsx b/src/web/app/projects/$projectId/project-menu-content.tsx index fe0dc1b..2261ed3 100644 --- a/src/web/app/projects/$projectId/project-menu-content.tsx +++ b/src/web/app/projects/$projectId/project-menu-content.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react'; -import { Link } from '@tanstack/react-router'; +import { Link, useLocation } from '@tanstack/react-router'; import { CalendarClock, History, Info, MessageSquare, Plus, Settings } from 'lucide-react'; import { useTranslation } from 'react-i18next'; @@ -21,7 +21,7 @@ import { } from './project-session-list.pure.ts'; const menuLinkClassName = - 'flex h-10 items-center gap-2 rounded-lg px-3 text-sm font-medium text-sidebar-foreground transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground'; + 'flex h-10 relative items-center gap-2 rounded-lg px-3 text-sm font-medium text-sidebar-foreground transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground'; const formatDateTime = (iso: string): string => new Intl.DateTimeFormat('ja-JP', { @@ -49,17 +49,33 @@ export const ProjectMenuContent: FC<{ const { t } = useTranslation(); const closeAppMenu = useCloseAppMenu(); const sortedSessions = sortSessionsNewestFirst(sessions); + const location = useLocation(); + const isSessionsActive = location.pathname.endsWith('/sessions'); + const isRoutinesActive = location.pathname.endsWith('/routines'); + const isSettingsActive = + location.pathname === '/settings' && + (location.search as { projectId?: string }).projectId === projectId; + const isInformationActive = + location.pathname === '/information' && + (location.search as { projectId?: string }).projectId === projectId; return (
+ {isSessionsActive && ( + + )} {t('menu.sessions')} @@ -67,29 +83,38 @@ export const ProjectMenuContent: FC<{ {sessionCount} + {isRoutinesActive && ( + + )} {t('menu.routines')} + {isSettingsActive && ( + + )} {t('menu.settings')} + {isInformationActive && ( + + )} {t('menu.information')} diff --git a/src/web/components/app-header.tsx b/src/web/components/app-header.tsx index 8c211aa..07babac 100644 --- a/src/web/components/app-header.tsx +++ b/src/web/components/app-header.tsx @@ -21,12 +21,28 @@ import { useNotificationCenter, } from '@/web/pwa/notification-center'; -const currentProjectIdFromPath = (pathname: string): string | null => { +const currentProjectIdFromPath = (pathname: string, search: unknown): string | null => { const match = /^\/projects\/([^/]+)/.exec(pathname); - return match?.[1] ?? null; + if (match !== null && match[1] !== undefined) { + return match[1]; + } + + if (pathname === '/settings' || pathname === '/information') { + if ( + typeof search === 'object' && + search !== null && + 'projectId' in search && + typeof search['projectId'] === 'string' && + search['projectId'].length > 0 + ) { + return search['projectId']; + } + } + + return null; }; -type ProjectPathTarget = 'chat' | 'routines' | 'sessions' | 'settings'; +type ProjectPathTarget = 'chat' | 'routines' | 'sessions' | 'settings' | 'information'; const projectPathTargetFromPath = (pathname: string): ProjectPathTarget => { if (pathname.endsWith('/sessions')) { @@ -35,9 +51,12 @@ const projectPathTargetFromPath = (pathname: string): ProjectPathTarget => { if (pathname.endsWith('/routines')) { return 'routines'; } - if (pathname.endsWith('/settings')) { + if (pathname === '/settings' || pathname.endsWith('/settings')) { return 'settings'; } + if (pathname === '/information' || pathname.endsWith('/information')) { + return 'information'; + } return 'chat'; }; @@ -174,7 +193,7 @@ export const AppHeader: FC<{ const { t } = useTranslation(); const location = useLocation(); const navigate = useNavigate(); - const currentProjectId = currentProjectIdFromPath(location.pathname); + const currentProjectId = currentProjectIdFromPath(location.pathname, location.search); const { data } = useSuspenseQuery({ queryKey: projectsQueryKey, queryFn: fetchProjects, @@ -194,7 +213,9 @@ export const AppHeader: FC<{ > - Remote Agent + + Remote Agent + {currentProjectId === null ? (

{t('common.appName')}

@@ -207,17 +228,21 @@ export const AppHeader: FC<{ if (nextProjectId === null || nextProjectId === currentProjectId) { return; } - void navigate({ - to: - projectPathTarget === 'sessions' - ? '/projects/$projectId/sessions' - : projectPathTarget === 'routines' - ? '/projects/$projectId/routines' - : projectPathTarget === 'settings' - ? '/projects/$projectId/settings' + if (projectPathTarget === 'settings') { + void navigate({ to: '/settings', search: { projectId: nextProjectId } }); + } else if (projectPathTarget === 'information') { + void navigate({ to: '/information', search: { projectId: nextProjectId } }); + } else { + void navigate({ + to: + projectPathTarget === 'sessions' + ? '/projects/$projectId/sessions' + : projectPathTarget === 'routines' + ? '/projects/$projectId/routines' : '/projects/$projectId', - params: { projectId: nextProjectId }, - }); + params: { projectId: nextProjectId }, + }); + } }} value={currentProjectId} > diff --git a/src/web/components/app-menu.tsx b/src/web/components/app-menu.tsx index d6b6e63..39c5162 100644 --- a/src/web/components/app-menu.tsx +++ b/src/web/components/app-menu.tsx @@ -1,4 +1,4 @@ -import { Link } from '@tanstack/react-router'; +import { Link, useLocation, useRouterState } from '@tanstack/react-router'; import { FolderKanban, Info, Menu, PanelLeftClose, Settings, X } from 'lucide-react'; import { createContext, @@ -36,31 +36,48 @@ const maxDesktopMenuWidth = 520; const clampDesktopMenuWidth = (width: number): number => Math.min(maxDesktopMenuWidth, Math.max(minDesktopMenuWidth, width)); +const menuLinkClassName = + 'flex h-9 relative items-center gap-2 rounded-lg px-3 text-sm font-medium text-sidebar-foreground transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground'; + const DefaultMenuContent: FC<{ readonly closeMobileMenu: () => void }> = ({ closeMobileMenu }) => { const { t } = useTranslation(); + const location = useLocation(); + const isProjectsActive = location.pathname.startsWith('/projects'); + const isSettingsActive = location.pathname === '/settings'; + const isInformationActive = location.pathname === '/information'; + return (
+ {isProjectsActive && ( + + )} {t('menu.projects')} + {isSettingsActive && ( + + )} {t('menu.settings')} + {isInformationActive && ( + + )} {t('menu.information')} @@ -72,9 +89,17 @@ const AppMenuBody: FC<{ readonly closeMobileMenu: () => void; readonly hasCustomContent: boolean; readonly isMobile: boolean; + readonly isProjectScopedRoute: boolean; readonly setTarget: (target: HTMLDivElement | null) => void; readonly onCollapse?: () => void; -}> = ({ closeMobileMenu, hasCustomContent, isMobile, onCollapse, setTarget }) => { +}> = ({ + closeMobileMenu, + hasCustomContent, + isMobile, + isProjectScopedRoute, + onCollapse, + setTarget, +}) => { const { t } = useTranslation(); return ( @@ -114,7 +139,13 @@ const AppMenuBody: FC<{ )}
- {hasCustomContent ? null : } + {hasCustomContent ? null : isProjectScopedRoute ? ( +
+

{t('common.loading')}

+
+ ) : ( + + )}
); @@ -161,6 +192,20 @@ export const AppMenuLayout: FC<{ readonly children: ReactNode }> = ({ children } const [desktopExpanded, setDesktopExpanded] = useState(true); const [desktopMenuWidth, setDesktopMenuWidth] = useState(300); const [mobileOpen, setMobileOpen] = useState(false); + const location = useRouterState({ select: (s) => s.location }); + const isProjectScopedRoute = useMemo(() => { + if (location.pathname.startsWith('/projects/')) return true; + const search = location.search as Record | undefined; + const projectId = search?.['projectId']; + if ( + (location.pathname === '/settings' || location.pathname === '/information') && + typeof projectId === 'string' && + projectId.length > 0 + ) { + return true; + } + return false; + }, [location.pathname, location.search]); const desktopResizeStartRef = useRef<{ readonly startWidth: number; readonly startX: number; @@ -222,6 +267,7 @@ export const AppMenuLayout: FC<{ readonly children: ReactNode }> = ({ children } closeMobileMenu={closeMobileMenu} hasCustomContent={hasCustomContent} isMobile={false} + isProjectScopedRoute={isProjectScopedRoute} onCollapse={() => { setDesktopExpanded(false); }} @@ -262,6 +308,7 @@ export const AppMenuLayout: FC<{ readonly children: ReactNode }> = ({ children } closeMobileMenu={closeMobileMenu} hasCustomContent={hasCustomContent} isMobile + isProjectScopedRoute={isProjectScopedRoute} setTarget={setMobileTarget} />
diff --git a/src/web/lib/i18n/resources.ts b/src/web/lib/i18n/resources.ts index 4b110c4..d1ea4a1 100644 --- a/src/web/lib/i18n/resources.ts +++ b/src/web/lib/i18n/resources.ts @@ -50,10 +50,10 @@ export const resources = { menu: { title: 'MENU', projects: 'プロジェクト', - sessions: 'セッションリスト', - routines: 'Routines', + sessions: 'セッション', + routines: 'ルーティン', settings: '設定', - information: 'About Remote Agent', + information: 'About', openMenu: 'メニューを開く', closeMenu: 'メニューを閉じる', collapseMenu: 'メニューを折りたたむ', @@ -390,7 +390,7 @@ export const resources = { sessions: 'Sessions', routines: 'Routines', settings: 'Settings', - information: 'About Remote Agent', + information: 'About', openMenu: 'Open menu', closeMenu: 'Close menu', collapseMenu: 'Collapse menu', From d842567dbede1d02596f5b25b1b8b048fb231e89 Mon Sep 17 00:00:00 2001 From: d-kimsuon Date: Wed, 6 May 2026 03:53:08 +0900 Subject: [PATCH 10/14] feat: add non-interactive release options --- scripts/release.ts | 164 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 141 insertions(+), 23 deletions(-) diff --git a/scripts/release.ts b/scripts/release.ts index 194fbb2..94e17e6 100644 --- a/scripts/release.ts +++ b/scripts/release.ts @@ -26,6 +26,59 @@ const readGitConfig = (key: string): string => { } }; +type CliOptions = { + readonly yes: boolean; + readonly version: string | undefined; +}; + +const parseCliArgs = (args: readonly string[]): CliOptions => { + let yes = false; + let version: string | undefined; + + for (let index = 0; index < args.length; index += 1) { + const arg = args[index]; + + if (arg === undefined) { + continue; + } + + if (arg === '-y' || arg === '--yes') { + yes = true; + continue; + } + + if (arg === '--version') { + const value = args[index + 1]; + if (value === undefined || value.startsWith('-')) { + console.error('x --version requires a value.'); + process.exit(1); + } + version = value; + index += 1; + continue; + } + + if (arg.startsWith('--version=')) { + version = arg.slice('--version='.length); + continue; + } + + if (arg === '-h' || arg === '--help') { + console.log( + 'Usage: pnpm release [-y|--yes] [--version patch|minor|major|beta|x.y.z[-tag.n]]', + ); + process.exit(0); + } + + console.error(`x Unknown argument: ${arg}`); + process.exit(1); + } + + return { yes, version }; +}; + +const cliOptions = parseCliArgs(process.argv.slice(2)); + const pkgPath = path.join(root, 'package.json'); const parsedPackageJson: unknown = JSON.parse(readFileSync(pkgPath, 'utf-8')); @@ -110,34 +163,99 @@ const bumpChoices = (v: string): { name: string; value: string }[] => { ]; }; -const { version } = await inquirer.prompt<{ version: string }>([ - { - type: 'rawlist', - name: 'version', - message: 'Select release version:', - choices: [...bumpChoices(current), { name: 'Custom', value: 'custom' }], - }, -]); +type VersionResolveResult = + | { readonly type: 'ok'; readonly version: string } + | { readonly type: 'error'; readonly message: string }; + +const semverPattern = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/; + +const resolveVersion = (versionSpec: string, fromVersion: string): VersionResolveResult => { + const { major, minor, patch, pre } = parseVersion(fromVersion); + const nextPatch = `${major}.${minor}.${patch + 1}`; + + if (versionSpec === 'patch') { + return { + type: 'ok', + version: pre === undefined ? nextPatch : `${major}.${minor}.${patch}`, + }; + } + + if (versionSpec === 'minor') { + return { type: 'ok', version: `${major}.${minor + 1}.0` }; + } + + if (versionSpec === 'major') { + return { type: 'ok', version: `${major + 1}.0.0` }; + } + + if (versionSpec === 'beta') { + if (pre === undefined) { + return { type: 'ok', version: `${nextPatch}-beta.0` }; + } + + const preParts = pre.split('.'); + const preTag = preParts[0] ?? 'beta'; + const preNum = Number(preParts[1] ?? 0); + return { type: 'ok', version: `${major}.${minor}.${patch}-${preTag}.${preNum + 1}` }; + } + + if (semverPattern.test(versionSpec)) { + return { type: 'ok', version: versionSpec }; + } + + return { + type: 'error', + message: + 'Unsupported --version value. Use patch, minor, major, beta, or an explicit semver like 1.2.3-beta.0.', + }; +}; + +const promptVersion = async (): Promise => { + const { version } = await inquirer.prompt<{ version: string }>([ + { + type: 'rawlist', + name: 'version', + message: 'Select release version:', + choices: [...bumpChoices(current), { name: 'Custom', value: 'custom' }], + }, + ]); + + if (version !== 'custom') { + return version; + } + + const { custom } = await inquirer.prompt<{ custom: string }>([ + { type: 'input', name: 'custom', message: 'Enter version:' }, + ]); + return custom; +}; const nextVersion = - version === 'custom' - ? ( - await inquirer.prompt<{ custom: string }>([ - { type: 'input', name: 'custom', message: 'Enter version:' }, - ]) - ).custom - : version; + cliOptions.version === undefined + ? await promptVersion() + : (() => { + const result = resolveVersion(cliOptions.version, current); + if (result.type === 'error') { + console.error(`x ${result.message}`); + process.exit(1); + } + return result.version; + })(); const tag = `v${nextVersion}`; -const { confirmed } = await inquirer.prompt<{ confirmed: boolean }>([ - { - type: 'confirm', - name: 'confirmed', - message: `Release ${tag}? This will commit, tag (signed), and push.`, - default: false, - }, -]); +const confirmed = cliOptions.yes + ? true + : ( + await inquirer.prompt<{ confirmed: boolean }>([ + { + type: 'confirm', + name: 'confirmed', + message: `Release ${tag}? This will commit, tag (signed), and push.`, + default: false, + }, + ]) + ).confirmed; if (!confirmed) { console.log('Aborted.'); From 3e0c11ce77c570d21de4585ca0ed0fc2fd1c92e2 Mon Sep 17 00:00:00 2001 From: d-kimsuon Date: Wed, 6 May 2026 03:53:30 +0900 Subject: [PATCH 11/14] chore: release v0.0.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6e91d54..1f2f0ef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kimuson/remote-agent", - "version": "0.0.5", + "version": "0.0.6", "description": "A CLI that serves a web UI and bridge server for interacting with ACP-compatible agents", "license": "MIT", "repository": { From d23d65cc7d675873ef1ac606dc86e49633126cf8 Mon Sep 17 00:00:00 2001 From: d-kimsuon Date: Wed, 6 May 2026 03:56:26 +0900 Subject: [PATCH 12/14] docs: add release skill --- .agents/skills/release/SKILL.md | 153 ++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 .agents/skills/release/SKILL.md diff --git a/.agents/skills/release/SKILL.md b/.agents/skills/release/SKILL.md new file mode 100644 index 0000000..f9adda4 --- /dev/null +++ b/.agents/skills/release/SKILL.md @@ -0,0 +1,153 @@ +--- +name: release +description: Run the remote-agent release flow end-to-end. Use when the user asks to release patch, minor, major, beta, or an explicit semver; includes non-interactive release execution, GitHub Actions monitoring with gh, draft release note cleanup, and publishing the GitHub Release. +--- + +# remote-agent Release + +Use this skill to perform an end-to-end release for this repository. + +## Inputs + +Interpret the user's argument as the release version spec: + +- `patch`, `minor`, `major`, `beta` +- or an explicit semver such as `0.1.0` / `0.1.0-beta.0` + +If no version spec is present, ask the user which one to use. + +## Preconditions + +1. Work from the repository root. +2. Inspect the current branch and working tree: + +```bash +git branch --show-current +git status --short +``` + +3. If there are unrelated uncommitted changes, stop and ask the user how to handle them. +4. If release-related changes were just made, commit them before running release because `scripts/release.ts` requires a clean working tree. +5. The release script requires SSH signing config: + +```bash +git config --get gpg.format +git config --get commit.gpgsign +git config --get tag.gpgsign +``` + +Expected values are `ssh`, `true`, `true`. + +## Release command + +Run the non-interactive release: + +```bash +VERSION_SPEC="patch" # replace with the user's requested spec +pnpm release -y --version "$VERSION_SPEC" +``` + +The script runs audit/build/license/gatecheck/test/pack checks, updates `package.json`, creates a signed release commit, creates a signed tag, and pushes commits and tags. + +## If push fails because the branch has no upstream + +The release commit and tag may already exist locally. Push the current branch with upstream, then push tags: + +```bash +BRANCH="$(git branch --show-current)" +git push --set-upstream origin "$BRANCH" +git push --tags +``` + +Do not rerun `pnpm release` unless the local release commit/tag were removed or the previous run failed before creating them. + +## Monitor GitHub Actions + +After tags are pushed, find and watch the Release workflow run: + +```bash +gh run list --workflow Release --limit 5 +RUN_ID="" +gh run watch "$RUN_ID" --exit-status +``` + +A reusable one-liner variant: + +```bash +TAG="v0.0.0"; RUN_ID="$(gh run list --workflow Release --limit 20 --json databaseId,headBranch,event --jq ".[] | select(.headBranch == \"$TAG\" and .event == \"push\") | .databaseId" | head -n 1)"; test -n "$RUN_ID" && gh run watch "$RUN_ID" --exit-status +``` + +If the workflow fails, inspect logs before taking corrective action: + +```bash +gh run view "$RUN_ID" --log-failed +``` + +## Verify publish + +After the workflow succeeds: + +```bash +TAG="v0.0.0" # replace +npm view @kimuson/remote-agent version +gh release view "$TAG" --json tagName,name,isDraft,isPrerelease,url +``` + +## Rewrite and publish GitHub Release + +The workflow creates a draft release. Inspect the generated notes: + +```bash +TAG="v0.0.0" # replace +gh release view "$TAG" --json body --jq .body +``` + +Rewrite the notes into a concise human-facing changelog. Keep the language consistent with previous releases, usually English. A simple structure is: + +```markdown +## Highlights + +- ... + +## Changes + +### Features + +- ... + +### Fixes + +- ... + +### Performance + +- ... + +[View changes on GitHub](https://github.com/d-kimuson/remote-agent/compare/vPREV...vNEXT) +``` + +Publish the draft with the rewritten notes: + +```bash +TAG="v0.0.0" # replace +NOTES_FILE="/tmp/remote-agent-$TAG-release-notes.md" +$EDITOR "$NOTES_FILE" # or write the file with the agent's file tool +gh release edit "$TAG" --notes-file "$NOTES_FILE" --draft=false +``` + +Confirm it is public: + +```bash +gh release view "$TAG" --json tagName,name,isDraft,isPrerelease,url +``` + +## Final report + +Report: + +- released tag/version +- local release command used +- GitHub Actions run ID and result +- npm version verification result +- GitHub Release URL and draft/public state +- any follow-up commits created for release automation or skill updates From a07b66806a9a3cd2827457e0bc9513564d6cf3f5 Mon Sep 17 00:00:00 2001 From: d-kimsuon Date: Wed, 6 May 2026 04:06:34 +0900 Subject: [PATCH 13/14] docs: add release note guideline --- .agents/skills/release/SKILL.md | 27 ++----- docs/release-note-guideline.md | 134 ++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 20 deletions(-) create mode 100644 docs/release-note-guideline.md diff --git a/.agents/skills/release/SKILL.md b/.agents/skills/release/SKILL.md index f9adda4..34662dd 100644 --- a/.agents/skills/release/SKILL.md +++ b/.agents/skills/release/SKILL.md @@ -102,30 +102,17 @@ TAG="v0.0.0" # replace gh release view "$TAG" --json body --jq .body ``` -Rewrite the notes into a concise human-facing changelog. Keep the language consistent with previous releases, usually English. A simple structure is: +Rewrite the notes according to `docs/release-note-guideline.md`. The generated draft is based on commit logs, so categories and wording may be wrong from the remote-agent product perspective. Remove internal-only updates, move entries to the correct category, merge intermediate same-release fixes into their related feature, and rewrite commit-message phrasing into release-note prose. -```markdown -## Highlights +For a second-pass review before publishing, delegate a focused review to another agent: -- ... - -## Changes - -### Features - -- ... - -### Fixes - -- ... - -### Performance - -- ... - -[View changes on GitHub](https://github.com/d-kimuson/remote-agent/compare/vPREV...vNEXT) +```bash +RELEASE_URL="https://github.com/d-kimuson/remote-agent/releases/tag/v0.0.0" # replace +pi -p "Read docs/release-note-guideline.md, review the Release Note at $RELEASE_URL, and identify concrete changes that should be made. Do not edit files or GitHub releases; only report findings." ``` +Apply the review findings when they are consistent with the guideline. + Publish the draft with the rewritten notes: ```bash diff --git a/docs/release-note-guideline.md b/docs/release-note-guideline.md new file mode 100644 index 0000000..e756348 --- /dev/null +++ b/docs/release-note-guideline.md @@ -0,0 +1,134 @@ +# Release Note Guideline + +This guideline is for agents reviewing and rewriting GitHub Release notes for remote-agent. + +## Background + +Release notes are initially generated automatically from commit logs. Treat them as a draft, not as the final source of truth. + +Because they come from commits: + +- Conventional Commit types such as `feat`, `fix`, and `perf` may not match the actual user-facing release-note category. +- Internal implementation details may be included even when they are not useful to remote-agent users. +- Commit-message phrasing may be too terse, too technical, or written from a developer workflow perspective. +- Intermediate commits may appear as fixes even though the feature they fix has never been released before. + +## Review Perspective + +Review the draft from the perspective of the released remote-agent software, not from the perspective of the repository commit history. + +Ask: + +- What changed for users or operators of remote-agent? +- Is this an actual product feature, bug fix, performance improvement, or maintenance-only change? +- Would this entry help someone decide whether to upgrade or understand what changed? + +## Categorization Rules + +### Features + +Use `Features` for new or expanded remote-agent capabilities visible to users, operators, or API consumers. + +Do not classify development workflow improvements as product features. For example, adding lefthook-based commit checks may be a `feat` commit, but it is not a remote-agent software feature and usually should not appear in release notes. + +### Fixes + +Use `Fixes` for bugs that existed in a previously released version and are now corrected. + +Be careful with fix commits that only repair a feature introduced earlier in the same unreleased cycle. If the broken state was never released to users, do not list it as a bug fix. Instead: + +- merge it into the related feature entry, or +- omit it if it is only an implementation correction. + +Do not list CI fixes, release automation fixes, test fixes, lint fixes, or development-tool fixes as product bug fixes unless they directly affect the published package or user-visible behavior. + +### Performance + +Use `Performance` only for changes that improve runtime behavior, responsiveness, resource usage, or scalability of remote-agent. + +Do not use it for build-time, CI-time, or developer-tool performance unless that is relevant to users of the published software. + +### Maintenance / Internal Changes + +Most internal changes should be omitted from release notes, including: + +- CI changes +- test-only updates +- lint/format/tooling configuration +- refactors with no user-visible impact +- release automation changes +- dependency updates with no notable user-facing impact +- documentation-only changes that do not change product behavior + +Include such changes only when they are important for users, operators, or package consumers to know. + +## Rewriting Rules + +The generated draft uses commit-message text. Rewrite entries into release-note prose. + +Good release-note entries should be: + +- user-facing or operator-facing +- concise but understandable +- written in past tense or neutral descriptive style +- free of commit-message prefixes, implementation noise, and unnecessary scopes +- grouped under the category that best reflects the released product behavior + +Avoid: + +- raw commit-message phrasing such as “add x”, “fix y”, or “refactor z” when it reads like a developer task +- mentioning commit hashes or authors unless intentionally preserving generated metadata +- exposing temporary bugs that were introduced and fixed before the release +- listing every small internal commit separately + +## Suggested Review Workflow + +1. Read the generated release note draft. +2. Inspect the commit list between the previous tag and the new tag when needed: + +```bash +git log --oneline vPREVIOUS..vNEXT +``` + +3. Identify entries that should be: + - kept as-is + - rewritten + - moved to another category + - merged with another entry + - removed entirely +4. Produce a concrete review report before editing. +5. Rewrite the release notes based on that report. + +For a second-pass review, ask another agent to review the draft against this guideline, for example: + +```bash +pi -p 'Read docs/release-note-guideline.md, review the Release Note at , and identify concrete changes that should be made. Do not edit files or GitHub releases; only report findings.' +``` + +## Final Shape + +A typical release note should look like: + +```markdown +## Highlights + +- Summarize the most important user-facing changes. + +## Changes + +### Features + +- Describe product capabilities added or expanded. + +### Fixes + +- Describe user-visible bugs fixed from previous releases. + +### Performance + +- Describe runtime performance improvements. + +[View changes on GitHub](https://github.com/d-kimuson/remote-agent/compare/vPREVIOUS...vNEXT) +``` + +Omit empty sections. From 6347083d7b03866f5fac70aaec5734571d7d0cc7 Mon Sep 17 00:00:00 2001 From: d-kimsuon Date: Wed, 6 May 2026 04:23:57 +0900 Subject: [PATCH 14/14] docs: update generated openapi spec --- docs/openapi.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/openapi.json b/docs/openapi.json index 2c242e5..baef9dd 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -1 +1 @@ -{"openapi":"3.1.0","info":{"title":"Remote Agent API","description":"API for Remote Agent BFF","version":"0.0.0"},"paths":{"/api/info":{"get":{"operationId":"getApiInfo","summary":"Get application info","responses":{"200":{"description":"Application info","content":{"application/json":{"schema":{"type":"object","properties":{"appName":{"type":"string"},"workingDirectory":{"type":"string"},"projectsFilePath":{"type":"string"},"agentPresets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"authMethodId":{"type":"string"},"modelSelectLabel":{"type":"string"},"modeSelectLabel":{"type":"string"}},"required":["id","label","description","command","args"]}}},"required":["appName","workingDirectory","projectsFilePath","agentPresets"]}}}}}}},"/api/attachments/ingest":{"post":{"operationId":"postApiAttachmentsIngest","summary":"Ingest browser attachments","responses":{"201":{"description":"Ingested attachments","content":{"application/json":{"schema":{"type":"object","properties":{"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["attachments"]}}}},"400":{"description":"Attachment ingest error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/filesystem/tree":{"get":{"operationId":"getApiFilesystemTree","summary":"Get filesystem tree","responses":{"200":{"description":"Filesystem tree","content":{"application/json":{"schema":{"type":"object","properties":{"root":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string"},"kind":{"anyOf":[{"const":"directory"},{"const":"file"}]},"children":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string"},"kind":{"anyOf":[{"const":"directory"},{"const":"file"}]}},"required":["name","path","kind"]}}},"required":["name","path","kind"]}},"required":["root"]}}}},"400":{"description":"Directory read error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"in":"query","name":"root","schema":{"type":"string"}}]}},"/api/filesystem/directory-listing":{"get":{"operationId":"getApiFilesystemDirectoryListing","summary":"List directory entries for navigation","responses":{"200":{"description":"Directory listing","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string"},"type":{"anyOf":[{"const":"directory"},{"const":"file"}]}},"required":["name","path","type"]}},"currentPath":{"type":"string"}},"required":["entries","currentPath"]}}}},"400":{"description":"Directory read error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"in":"query","name":"currentPath","schema":{"type":"string"}},{"in":"query","name":"showHidden","schema":{"type":"string"}}]}},"/api/filesystem/file-completion":{"get":{"operationId":"getApiFilesystemFileCompletion","summary":"List project files for prompt completion","responses":{"200":{"description":"File completion entries","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string"},"type":{"anyOf":[{"const":"directory"},{"const":"file"}]}},"required":["name","path","type"]}},"basePath":{"type":"string"},"projectPath":{"type":"string"}},"required":["entries","basePath","projectPath"]}}}},"400":{"description":"File completion error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"in":"query","name":"projectId","schema":{"type":"string"},"required":true},{"in":"query","name":"basePath","schema":{"type":"string"}}]}},"/api/projects":{"get":{"operationId":"getApiProjects","summary":"List projects","responses":{"200":{"description":"Projects","content":{"application/json":{"schema":{"type":"object","properties":{"projects":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"workingDirectory":{"type":"string"},"worktreeSetupScript":{"type":"string"}},"required":["id","name","workingDirectory"]}}},"required":["projects"]}}}}}},"post":{"operationId":"postApiProjects","summary":"Create project","responses":{"201":{"description":"Created project","content":{"application/json":{"schema":{"type":"object","properties":{"project":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"workingDirectory":{"type":"string"},"worktreeSetupScript":{"type":"string"}},"required":["id","name","workingDirectory"]}},"required":["project"]}}}},"400":{"description":"Project creation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"workingDirectory":{"type":"string"}},"required":["name","workingDirectory"]}}}}}},"/api/projects/{projectId}":{"get":{"operationId":"getApiProjectsByProjectId","summary":"Get project","responses":{"200":{"description":"Project","content":{"application/json":{"schema":{"type":"object","properties":{"project":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"workingDirectory":{"type":"string"},"worktreeSetupScript":{"type":"string"}},"required":["id","name","workingDirectory"]}},"required":["project"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"projectId","required":true}]}},"/api/projects/{projectId}/worktrees":{"post":{"operationId":"postApiProjectsByProjectIdWorktrees","summary":"Create project worktree","responses":{"201":{"description":"Created project worktree","content":{"application/json":{"schema":{"type":"object","properties":{"worktree":{"type":"object","properties":{"projectId":{"type":"string"},"name":{"type":"string"},"path":{"type":"string"},"branchName":{"type":"string"},"baseRef":{"type":"string"},"createdAt":{"type":"string"}},"required":["projectId","name","path","branchName","baseRef","createdAt"]}},"required":["worktree"]}}}},"400":{"description":"Project worktree creation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"branchName":{"type":"string"},"baseRef":{"type":"string"}},"required":["name"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"projectId","required":true}]}},"/api/projects/{projectId}/git/revisions":{"get":{"operationId":"getApiProjectsByProjectIdGitRevisions","summary":"Get project git revisions","responses":{"200":{"description":"Git revisions","content":{"application/json":{"schema":{"type":"object","properties":{"refs":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"type":{"anyOf":[{"const":"branch"},{"const":"commit"},{"const":"head"},{"const":"working"}]},"displayName":{"type":"string"},"sha":{"type":"string"}},"required":["name","type","displayName"]}}},"required":["refs"]}}}},"400":{"description":"Git revisions error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"in":"query","name":"cwd","schema":{"anyOf":[{"type":"string"},{"type":"null"}]},"required":true},{"schema":{"type":"string"},"in":"path","name":"projectId","required":true}]}},"/api/projects/{projectId}/git/diff":{"post":{"operationId":"postApiProjectsByProjectIdGitDiff","summary":"Get project git diff","responses":{"200":{"description":"Git diff","content":{"application/json":{"schema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"object","properties":{"filename":{"type":"string"},"oldFilename":{"type":"string"},"isNew":{"type":"boolean"},"isDeleted":{"type":"boolean"},"isRenamed":{"type":"boolean"},"isBinary":{"type":"boolean"},"hunks":{"type":"array","items":{"type":"object","properties":{"oldStart":{"type":"number"},"newStart":{"type":"number"},"lines":{"type":"array","items":{"type":"object","properties":{"type":{"anyOf":[{"const":"added"},{"const":"deleted"},{"const":"unchanged"},{"const":"hunk"},{"const":"context"}]},"oldLineNumber":{"type":"number"},"newLineNumber":{"type":"number"},"content":{"type":"string"}},"required":["type","content"]}}},"required":["oldStart","newStart","lines"]}},"linesAdded":{"type":"number"},"linesDeleted":{"type":"number"}},"required":["filename","isNew","isDeleted","isRenamed","isBinary","hunks","linesAdded","linesDeleted"]}},"summary":{"type":"object","properties":{"totalFiles":{"type":"number"},"totalAdditions":{"type":"number"},"totalDeletions":{"type":"number"}},"required":["totalFiles","totalAdditions","totalDeletions"]}},"required":["files","summary"]}}}},"400":{"description":"Git diff error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fromRef":{"type":"string"},"toRef":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["fromRef","toRef","cwd"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"projectId","required":true}]}},"/api/projects/{projectId}/settings":{"get":{"operationId":"getApiProjectsByProjectIdSettings","summary":"Get project settings","responses":{"200":{"description":"Project settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"projectId":{"type":"string"},"modelPreferences":{"type":"array","items":{"type":"object","properties":{"presetId":{"type":"string"},"modelId":{"type":"string"},"isFavorite":{"type":"boolean"},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["presetId","modelId","isFavorite","lastUsedAt","updatedAt"]}},"modePreferences":{"type":"array","items":{"type":"object","properties":{"presetId":{"type":"string"},"modeId":{"type":"string"},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["presetId","modeId","lastUsedAt","updatedAt"]}},"worktreeSetupScript":{"type":"string"},"sandbox":{"type":"object","properties":{"enabled":{"type":"boolean"},"filesystem":{"type":"object","properties":{"allowRead":{"type":"array","items":{"type":"string"}},"denyRead":{"type":"array","items":{"type":"string"}},"allowWrite":{"type":"array","items":{"type":"string"}},"denyWrite":{"type":"array","items":{"type":"string"}}},"required":["allowRead","denyRead","allowWrite","denyWrite"]},"network":{"type":"object","properties":{"mode":{"anyOf":[{"const":"inherit"},{"const":"restrict"},{"const":"none"}]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["mode","allowedDomains"]}},"required":["enabled","filesystem","network"]}},"required":["projectId","modelPreferences","modePreferences","worktreeSetupScript","sandbox"]}},"required":["settings"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"projectId","required":true}]},"patch":{"operationId":"patchApiProjectsByProjectIdSettings","summary":"Update project settings","responses":{"200":{"description":"Project settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"projectId":{"type":"string"},"modelPreferences":{"type":"array","items":{"type":"object","properties":{"presetId":{"type":"string"},"modelId":{"type":"string"},"isFavorite":{"type":"boolean"},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["presetId","modelId","isFavorite","lastUsedAt","updatedAt"]}},"modePreferences":{"type":"array","items":{"type":"object","properties":{"presetId":{"type":"string"},"modeId":{"type":"string"},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["presetId","modeId","lastUsedAt","updatedAt"]}},"worktreeSetupScript":{"type":"string"},"sandbox":{"type":"object","properties":{"enabled":{"type":"boolean"},"filesystem":{"type":"object","properties":{"allowRead":{"type":"array","items":{"type":"string"}},"denyRead":{"type":"array","items":{"type":"string"}},"allowWrite":{"type":"array","items":{"type":"string"}},"denyWrite":{"type":"array","items":{"type":"string"}}},"required":["allowRead","denyRead","allowWrite","denyWrite"]},"network":{"type":"object","properties":{"mode":{"anyOf":[{"const":"inherit"},{"const":"restrict"},{"const":"none"}]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["mode","allowedDomains"]}},"required":["enabled","filesystem","network"]}},"required":["projectId","modelPreferences","modePreferences","worktreeSetupScript","sandbox"]}},"required":["settings"]}}}},"400":{"description":"Project settings update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"worktreeSetupScript":{"type":"string"},"sandbox":{"type":"object","properties":{"enabled":{"type":"boolean"},"filesystem":{"type":"object","properties":{"allowRead":{"type":"array","items":{"type":"string"}},"denyRead":{"type":"array","items":{"type":"string"}},"allowWrite":{"type":"array","items":{"type":"string"}},"denyWrite":{"type":"array","items":{"type":"string"}}},"required":["allowRead","denyRead","allowWrite","denyWrite"]},"network":{"type":"object","properties":{"mode":{"anyOf":[{"const":"inherit"},{"const":"restrict"},{"const":"none"}]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["mode","allowedDomains"]}},"required":["enabled","filesystem","network"]}},"required":["name","worktreeSetupScript","sandbox"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"projectId","required":true}]}},"/api/projects/{projectId}/model-preferences":{"patch":{"operationId":"patchApiProjectsByProjectIdModelPreferences","summary":"Update project model preferences","responses":{"200":{"description":"Project settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"projectId":{"type":"string"},"modelPreferences":{"type":"array","items":{"type":"object","properties":{"presetId":{"type":"string"},"modelId":{"type":"string"},"isFavorite":{"type":"boolean"},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["presetId","modelId","isFavorite","lastUsedAt","updatedAt"]}},"modePreferences":{"type":"array","items":{"type":"object","properties":{"presetId":{"type":"string"},"modeId":{"type":"string"},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["presetId","modeId","lastUsedAt","updatedAt"]}},"worktreeSetupScript":{"type":"string"},"sandbox":{"type":"object","properties":{"enabled":{"type":"boolean"},"filesystem":{"type":"object","properties":{"allowRead":{"type":"array","items":{"type":"string"}},"denyRead":{"type":"array","items":{"type":"string"}},"allowWrite":{"type":"array","items":{"type":"string"}},"denyWrite":{"type":"array","items":{"type":"string"}}},"required":["allowRead","denyRead","allowWrite","denyWrite"]},"network":{"type":"object","properties":{"mode":{"anyOf":[{"const":"inherit"},{"const":"restrict"},{"const":"none"}]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["mode","allowedDomains"]}},"required":["enabled","filesystem","network"]}},"required":["projectId","modelPreferences","modePreferences","worktreeSetupScript","sandbox"]}},"required":["settings"]}}}},"400":{"description":"Project settings update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"presetId":{"type":"string"},"modelId":{"type":"string"},"isFavorite":{"type":"boolean"},"markLastUsed":{"type":"boolean"}},"required":["presetId","modelId"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"projectId","required":true}]}},"/api/projects/{projectId}/mode-preferences":{"patch":{"operationId":"patchApiProjectsByProjectIdModePreferences","summary":"Update project mode preferences","responses":{"200":{"description":"Project settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"projectId":{"type":"string"},"modelPreferences":{"type":"array","items":{"type":"object","properties":{"presetId":{"type":"string"},"modelId":{"type":"string"},"isFavorite":{"type":"boolean"},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["presetId","modelId","isFavorite","lastUsedAt","updatedAt"]}},"modePreferences":{"type":"array","items":{"type":"object","properties":{"presetId":{"type":"string"},"modeId":{"type":"string"},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["presetId","modeId","lastUsedAt","updatedAt"]}},"worktreeSetupScript":{"type":"string"},"sandbox":{"type":"object","properties":{"enabled":{"type":"boolean"},"filesystem":{"type":"object","properties":{"allowRead":{"type":"array","items":{"type":"string"}},"denyRead":{"type":"array","items":{"type":"string"}},"allowWrite":{"type":"array","items":{"type":"string"}},"denyWrite":{"type":"array","items":{"type":"string"}}},"required":["allowRead","denyRead","allowWrite","denyWrite"]},"network":{"type":"object","properties":{"mode":{"anyOf":[{"const":"inherit"},{"const":"restrict"},{"const":"none"}]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["mode","allowedDomains"]}},"required":["enabled","filesystem","network"]}},"required":["projectId","modelPreferences","modePreferences","worktreeSetupScript","sandbox"]}},"required":["settings"]}}}},"400":{"description":"Project settings update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"presetId":{"type":"string"},"modeId":{"type":"string"},"markLastUsed":{"type":"boolean"}},"required":["presetId","modeId"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"projectId","required":true}]}},"/api/routines":{"get":{"operationId":"getApiRoutines","summary":"List routines","responses":{"200":{"description":"Routines","content":{"application/json":{"schema":{"type":"object","properties":{"routines":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"const":"cron"},"config":{"type":"object","properties":{"cronExpression":{"type":"string"}},"required":["cronExpression"]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","enabled","kind","config","sendConfig","createdAt","updatedAt","lastRunAt","nextRunAt","lastError"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"const":"scheduled"},"config":{"type":"object","properties":{"runAt":{"type":"string"}},"required":["runAt"]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","enabled","kind","config","sendConfig","createdAt","updatedAt","lastRunAt","nextRunAt","lastError"]}]}}},"required":["routines"]}}}}}},"post":{"operationId":"postApiRoutines","summary":"Create routine","responses":{"201":{"description":"Routines","content":{"application/json":{"schema":{"type":"object","properties":{"routines":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"const":"cron"},"config":{"type":"object","properties":{"cronExpression":{"type":"string"}},"required":["cronExpression"]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","enabled","kind","config","sendConfig","createdAt","updatedAt","lastRunAt","nextRunAt","lastError"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"const":"scheduled"},"config":{"type":"object","properties":{"runAt":{"type":"string"}},"required":["runAt"]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","enabled","kind","config","sendConfig","createdAt","updatedAt","lastRunAt","nextRunAt","lastError"]}]}}},"required":["routines"]}}}},"400":{"description":"Routine creation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"anyOf":[{"const":"cron"},{"const":"scheduled"}]},"config":{"anyOf":[{"type":"object","properties":{"cronExpression":{"type":"string"}},"required":["cronExpression"]},{"type":"object","properties":{"runAt":{"type":"string"}},"required":["runAt"]}]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]}},"required":["name","kind","config","sendConfig"]}}}}}},"/api/routines/{routineId}":{"patch":{"operationId":"patchApiRoutinesByRoutineId","summary":"Update routine","responses":{"200":{"description":"Routines","content":{"application/json":{"schema":{"type":"object","properties":{"routines":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"const":"cron"},"config":{"type":"object","properties":{"cronExpression":{"type":"string"}},"required":["cronExpression"]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","enabled","kind","config","sendConfig","createdAt","updatedAt","lastRunAt","nextRunAt","lastError"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"const":"scheduled"},"config":{"type":"object","properties":{"runAt":{"type":"string"}},"required":["runAt"]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","enabled","kind","config","sendConfig","createdAt","updatedAt","lastRunAt","nextRunAt","lastError"]}]}}},"required":["routines"]}}}},"400":{"description":"Routine update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Routine not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"anyOf":[{"const":"cron"},{"const":"scheduled"}]},"config":{"anyOf":[{"type":"object","properties":{"cronExpression":{"type":"string"}},"required":["cronExpression"]},{"type":"object","properties":{"runAt":{"type":"string"}},"required":["runAt"]}]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]}},"required":[]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"routineId","required":true}]},"delete":{"operationId":"deleteApiRoutinesByRoutineId","summary":"Delete routine","responses":{"200":{"description":"Routines","content":{"application/json":{"schema":{"type":"object","properties":{"routines":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"const":"cron"},"config":{"type":"object","properties":{"cronExpression":{"type":"string"}},"required":["cronExpression"]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","enabled","kind","config","sendConfig","createdAt","updatedAt","lastRunAt","nextRunAt","lastError"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"const":"scheduled"},"config":{"type":"object","properties":{"runAt":{"type":"string"}},"required":["runAt"]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","enabled","kind","config","sendConfig","createdAt","updatedAt","lastRunAt","nextRunAt","lastError"]}]}}},"required":["routines"]}}}},"404":{"description":"Routine not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"routineId","required":true}]}},"/api/settings":{"get":{"operationId":"getApiSettings","summary":"Get application settings","responses":{"200":{"description":"Application settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"language":{"anyOf":[{"const":"ja"},{"const":"en"}]},"submitKeyBinding":{"anyOf":[{"const":"mod-enter"},{"const":"enter"}]},"sandbox":{"type":"object","properties":{"enabledProviderIds":{"type":"array","items":{"type":"string"}},"filesystem":{"type":"object","properties":{"allowRead":{"type":"array","items":{"type":"string"}},"denyRead":{"type":"array","items":{"type":"string"}},"allowWrite":{"type":"array","items":{"type":"string"}},"denyWrite":{"type":"array","items":{"type":"string"}}},"required":["allowRead","denyRead","allowWrite","denyWrite"]},"network":{"type":"object","properties":{"mode":{"anyOf":[{"const":"restrict"},{"const":"none"}]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["mode","allowedDomains"]}},"required":["enabledProviderIds","filesystem","network"]}},"required":["language","submitKeyBinding","sandbox"]}},"required":["settings"]}}}}}},"patch":{"operationId":"patchApiSettings","summary":"Update application settings","responses":{"200":{"description":"Application settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"language":{"anyOf":[{"const":"ja"},{"const":"en"}]},"submitKeyBinding":{"anyOf":[{"const":"mod-enter"},{"const":"enter"}]},"sandbox":{"type":"object","properties":{"enabledProviderIds":{"type":"array","items":{"type":"string"}},"filesystem":{"type":"object","properties":{"allowRead":{"type":"array","items":{"type":"string"}},"denyRead":{"type":"array","items":{"type":"string"}},"allowWrite":{"type":"array","items":{"type":"string"}},"denyWrite":{"type":"array","items":{"type":"string"}}},"required":["allowRead","denyRead","allowWrite","denyWrite"]},"network":{"type":"object","properties":{"mode":{"anyOf":[{"const":"restrict"},{"const":"none"}]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["mode","allowedDomains"]}},"required":["enabledProviderIds","filesystem","network"]}},"required":["language","submitKeyBinding","sandbox"]}},"required":["settings"]}}}},"400":{"description":"Application settings update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"language":{"anyOf":[{"const":"ja"},{"const":"en"}]},"submitKeyBinding":{"anyOf":[{"const":"mod-enter"},{"const":"enter"}]},"sandbox":{"type":"object","properties":{"enabledProviderIds":{"type":"array","items":{"type":"string"}},"filesystem":{"type":"object","properties":{"allowRead":{"type":"array","items":{"type":"string"}},"denyRead":{"type":"array","items":{"type":"string"}},"allowWrite":{"type":"array","items":{"type":"string"}},"denyWrite":{"type":"array","items":{"type":"string"}}},"required":["allowRead","denyRead","allowWrite","denyWrite"]},"network":{"type":"object","properties":{"mode":{"anyOf":[{"const":"restrict"},{"const":"none"}]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["mode","allowedDomains"]}},"required":["enabledProviderIds","filesystem","network"]}},"required":["language","submitKeyBinding","sandbox"]}}}}}},"/api/setup":{"get":{"operationId":"getApiSetup","summary":"Get application setup state","responses":{"200":{"description":"Application setup state","content":{"application/json":{"schema":{"type":"object","properties":{"setup":{"type":"object","properties":{"initialSetupCompleted":{"type":"boolean"},"completedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["initialSetupCompleted","completedAt"]}},"required":["setup"]}}}}}}},"/api/setup/complete":{"post":{"operationId":"postApiSetupComplete","summary":"Mark initial application setup completed","responses":{"200":{"description":"Application setup state","content":{"application/json":{"schema":{"type":"object","properties":{"setup":{"type":"object","properties":{"initialSetupCompleted":{"type":"boolean"},"completedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["initialSetupCompleted","completedAt"]}},"required":["setup"]}}}},"400":{"description":"Application setup completion error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/acp/sse":{"get":{"operationId":"getApiAcpSse","summary":"Subscribe to ACP session updates (Server-Sent Events, JSON in each data line)","responses":{"200":{"description":"Event stream"}}}},"/api/acp/permissions":{"get":{"operationId":"getApiAcpPermissions","summary":"List pending ACP permission requests","responses":{"200":{"description":"ACP permission requests","content":{"application/json":{"schema":{"type":"object","properties":{"requests":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sessionId":{"type":"string"},"toolCallId":{"type":"string"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawInputText":{"anyOf":[{"type":"string"},{"type":"null"}]},"options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"anyOf":[{"const":"allow_once"},{"const":"allow_always"},{"const":"reject_once"},{"const":"reject_always"}]},"name":{"type":"string"}},"required":["id","kind","name"]}},"createdAt":{"type":"string"}},"required":["id","sessionId","toolCallId","title","kind","rawInputText","options","createdAt"]}}},"required":["requests"]}}}}}}},"/api/acp/permissions/{requestId}/resolve":{"post":{"operationId":"postApiAcpPermissionsByRequestIdResolve","summary":"Resolve a pending ACP permission request","responses":{"200":{"description":"ACP permission requests","content":{"application/json":{"schema":{"type":"object","properties":{"requests":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sessionId":{"type":"string"},"toolCallId":{"type":"string"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawInputText":{"anyOf":[{"type":"string"},{"type":"null"}]},"options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"anyOf":[{"const":"allow_once"},{"const":"allow_always"},{"const":"reject_once"},{"const":"reject_always"}]},"name":{"type":"string"}},"required":["id","kind","name"]}},"createdAt":{"type":"string"}},"required":["id","sessionId","toolCallId","title","kind","rawInputText","options","createdAt"]}}},"required":["requests"]}}}},"404":{"description":"ACP permission request not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"optionId":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["optionId"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"requestId","required":true}]}},"/api/acp/providers":{"get":{"operationId":"getApiAcpProviders","summary":"List ACP provider presets and enabled state","responses":{"200":{"description":"ACP providers","content":{"application/json":{"schema":{"type":"object","properties":{"providers":{"type":"array","items":{"type":"object","properties":{"preset":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"authMethodId":{"type":"string"},"modelSelectLabel":{"type":"string"},"modeSelectLabel":{"type":"string"}},"required":["id","label","description","command","args"]},"enabled":{"type":"boolean"},"enabledAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"catalogSummary":{"anyOf":[{"type":"object","properties":{"availableModelCount":{"type":"number"},"availableModeCount":{"type":"number"},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]},"refreshedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["availableModelCount","availableModeCount","currentModelId","currentModeId","lastError","refreshedAt"]},{"type":"null"}]}},"required":["preset","enabled","enabledAt","updatedAt"]}}},"required":["providers"]}}}}}}},"/api/acp/providers/custom":{"post":{"operationId":"postApiAcpProvidersCustom","summary":"Create a custom ACP provider","responses":{"201":{"description":"ACP providers","content":{"application/json":{"schema":{"type":"object","properties":{"providers":{"type":"array","items":{"type":"object","properties":{"preset":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"authMethodId":{"type":"string"},"modelSelectLabel":{"type":"string"},"modeSelectLabel":{"type":"string"}},"required":["id","label","description","command","args"]},"enabled":{"type":"boolean"},"enabledAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"catalogSummary":{"anyOf":[{"type":"object","properties":{"availableModelCount":{"type":"number"},"availableModeCount":{"type":"number"},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]},"refreshedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["availableModelCount","availableModeCount","currentModelId","currentModeId","lastError","refreshedAt"]},{"type":"null"}]}},"required":["preset","enabled","enabledAt","updatedAt"]}}},"required":["providers"]}}}},"400":{"description":"Custom ACP provider create error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"commandText":{"type":"string"}},"required":["name","commandText"]}}}}}},"/api/acp/providers/custom/{providerId}":{"patch":{"operationId":"patchApiAcpProvidersCustomByProviderId","summary":"Update a custom ACP provider","responses":{"200":{"description":"ACP providers","content":{"application/json":{"schema":{"type":"object","properties":{"providers":{"type":"array","items":{"type":"object","properties":{"preset":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"authMethodId":{"type":"string"},"modelSelectLabel":{"type":"string"},"modeSelectLabel":{"type":"string"}},"required":["id","label","description","command","args"]},"enabled":{"type":"boolean"},"enabledAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"catalogSummary":{"anyOf":[{"type":"object","properties":{"availableModelCount":{"type":"number"},"availableModeCount":{"type":"number"},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]},"refreshedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["availableModelCount","availableModeCount","currentModelId","currentModeId","lastError","refreshedAt"]},{"type":"null"}]}},"required":["preset","enabled","enabledAt","updatedAt"]}}},"required":["providers"]}}}},"400":{"description":"Custom ACP provider update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"commandText":{"type":"string"}},"required":["name","commandText"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"providerId","required":true}]},"delete":{"operationId":"deleteApiAcpProvidersCustomByProviderId","summary":"Delete a custom ACP provider","responses":{"200":{"description":"ACP providers","content":{"application/json":{"schema":{"type":"object","properties":{"providers":{"type":"array","items":{"type":"object","properties":{"preset":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"authMethodId":{"type":"string"},"modelSelectLabel":{"type":"string"},"modeSelectLabel":{"type":"string"}},"required":["id","label","description","command","args"]},"enabled":{"type":"boolean"},"enabledAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"catalogSummary":{"anyOf":[{"type":"object","properties":{"availableModelCount":{"type":"number"},"availableModeCount":{"type":"number"},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]},"refreshedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["availableModelCount","availableModeCount","currentModelId","currentModeId","lastError","refreshedAt"]},{"type":"null"}]}},"required":["preset","enabled","enabledAt","updatedAt"]}}},"required":["providers"]}}}},"400":{"description":"Custom ACP provider delete error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"providerId","required":true}]}},"/api/acp/providers/{presetId}":{"patch":{"operationId":"patchApiAcpProvidersByPresetId","summary":"Enable or disable an ACP provider preset","responses":{"200":{"description":"ACP providers","content":{"application/json":{"schema":{"type":"object","properties":{"providers":{"type":"array","items":{"type":"object","properties":{"preset":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"authMethodId":{"type":"string"},"modelSelectLabel":{"type":"string"},"modeSelectLabel":{"type":"string"}},"required":["id","label","description","command","args"]},"enabled":{"type":"boolean"},"enabledAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"catalogSummary":{"anyOf":[{"type":"object","properties":{"availableModelCount":{"type":"number"},"availableModeCount":{"type":"number"},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]},"refreshedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["availableModelCount","availableModeCount","currentModelId","currentModeId","lastError","refreshedAt"]},{"type":"null"}]}},"required":["preset","enabled","enabledAt","updatedAt"]}}},"required":["providers"]}}}},"400":{"description":"ACP provider update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"}},"required":["enabled"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"presetId","required":true}]}},"/api/acp/providers/{presetId}/check":{"post":{"operationId":"postApiAcpProvidersByPresetIdCheck","summary":"Check ACP provider connectivity and refresh catalog","responses":{"200":{"description":"Model/mode options","content":{"application/json":{"schema":{"type":"object","properties":{"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["availableModels","availableModes","currentModelId","currentModeId","lastError"]}}}},"400":{"description":"ACP provider check error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["cwd"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"presetId","required":true}]}},"/api/acp/agent/model-catalog":{"get":{"operationId":"getApiAcpAgentModelCatalog","summary":"Probe agent for model and mode list (ephemeral initSession)","responses":{"200":{"description":"Model/mode options","content":{"application/json":{"schema":{"type":"object","properties":{"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["availableModels","availableModes","currentModelId","currentModeId","lastError"]}}}},"400":{"description":"Model catalog error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"in":"query","name":"projectId","schema":{"type":"string"},"required":true},{"in":"query","name":"presetId","schema":{"type":"string","default":"codex"}}]}},"/api/acp/agent/slash-commands":{"get":{"operationId":"getApiAcpAgentSlashCommands","summary":"Probe agent for slash command list (ephemeral ACP session)","responses":{"200":{"description":"Slash command options","content":{"application/json":{"schema":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"inputHint":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["name","description","inputHint"]}},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["commands","lastError"]}}}},"400":{"description":"Slash command probe error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"in":"query","name":"projectId","schema":{"type":"string"},"required":true},{"in":"query","name":"presetId","schema":{"type":"string","default":"codex"}}]}},"/api/acp/agent/prepare":{"post":{"operationId":"postApiAcpAgentPrepare","summary":"Start an ACP provider session in the background for a draft chat","responses":{"202":{"description":"Prepared session handle","content":{"application/json":{"schema":{"type":"object","properties":{"prepareId":{"type":"string"}},"required":["prepareId"]}}}},"400":{"description":"ACP prepare error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"sandboxEnabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["projectId","presetId","cwd"]}}}}}},"/api/acp/sessions":{"get":{"operationId":"getApiAcpSessions","summary":"List ACP sessions","responses":{"200":{"description":"ACP sessions","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]}}},"required":["sessions"]}}}}}},"post":{"operationId":"postApiAcpSessions","summary":"Create ACP session","responses":{"201":{"description":"Created ACP session","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]}},"required":["session"]}}}},"400":{"description":"ACP session creation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"anyOf":[{"type":"string"},{"type":"null"}]},"argsText":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"sandboxEnabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["projectId","presetId","command","cwd"]}}}}}},"/api/acp/sessions/discover":{"get":{"operationId":"getApiAcpSessionsDiscover","summary":"Discover resumable ACP sessions","responses":{"200":{"description":"Resumable ACP sessions","content":{"application/json":{"schema":{"type":"object","properties":{"capability":{"type":"object","properties":{"loadSession":{"type":"boolean"},"listSessions":{"type":"boolean"},"resumeSession":{"type":"boolean"},"canLoadIntoProvider":{"type":"boolean"},"fallbackReason":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["loadSession","listSessions","resumeSession","canLoadIntoProvider","fallbackReason"]},"sessions":{"type":"array","items":{"type":"object","properties":{"sessionId":{"type":"string"},"cwd":{"type":"string"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"loadable":{"type":"boolean"}},"required":["sessionId","cwd","title","updatedAt","loadable"]}}},"required":["capability","sessions"]}}}},"400":{"description":"ACP session discovery error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"in":"query","name":"projectId","schema":{"anyOf":[{"type":"string"},{"type":"null"}]},"required":true},{"in":"query","name":"presetId","schema":{"anyOf":[{"type":"string"},{"type":"null"}]},"required":true},{"in":"query","name":"cwd","schema":{"anyOf":[{"type":"string"},{"type":"null"}]},"required":true}]}},"/api/acp/sessions/load":{"post":{"operationId":"postApiAcpSessionsLoad","summary":"Load existing ACP session","responses":{"201":{"description":"Loaded ACP session","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]}},"required":["session"]}}}},"400":{"description":"ACP session load error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"sessionId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["projectId","presetId","sessionId","cwd","title","updatedAt"]}}}}}},"/api/acp/sessions/{sessionId}/cancel":{"post":{"operationId":"postApiAcpSessionsBySessionIdCancel","summary":"Cancel running ACP session turn","responses":{"200":{"description":"Cancelled ACP session","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]},"cancelled":{"type":"boolean"}},"required":["session","cancelled"]}}}},"400":{"description":"ACP session cancel error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"sessionId","required":true}]}},"/api/acp/sessions/{sessionId}/stop":{"post":{"operationId":"postApiAcpSessionsBySessionIdStop","summary":"Stop paused ACP session runtime","responses":{"200":{"description":"Stopped ACP session","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]}},"required":["session"]}}}},"400":{"description":"ACP session stop error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"sessionId","required":true}]}},"/api/acp/sessions/prepared/{prepareId}/messages":{"post":{"operationId":"postApiAcpSessionsPreparedByPrepareIdMessages","summary":"Send message to a prepared ACP session","responses":{"200":{"description":"ACP message response","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]},"text":{"type":"string"},"rawEvents":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"const":"plan"},"entries":{"type":"array","items":{"type":"string"}},"rawText":{"type":"string"}},"required":["type","entries","rawText"]},{"type":"object","properties":{"type":{"const":"diff"},"path":{"type":"string"},"oldText":{"anyOf":[{"type":"string"},{"type":"null"}]},"newText":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawText":{"type":"string"}},"required":["type","path","oldText","newText","rawText"]},{"type":"object","properties":{"type":{"const":"terminal"},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","terminalId","text","rawText"]},{"type":"object","properties":{"type":{"const":"reasoning"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolCall"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"inputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","inputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolResult"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"outputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","outputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolError"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"errorText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","errorText","rawText"]},{"type":"object","properties":{"type":{"const":"streamPart"},"partType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","partType","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolInput"},"streamId":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","streamId","text","rawText"]}]}},"assistantSegmentMessages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"role":{"anyOf":[{"const":"user"},{"const":"assistant"}]},"kind":{"anyOf":[{"const":"user"},{"const":"legacy_assistant_turn"},{"const":"assistant_text"},{"const":"reasoning"},{"const":"tool_input"},{"const":"tool_call"},{"const":"tool_result"},{"const":"tool_error"},{"const":"tool_output_denied"},{"const":"tool_approval_request"},{"const":"source"},{"const":"file"},{"const":"stream_start"},{"const":"stream_finish"},{"const":"step_start"},{"const":"step_finish"},{"const":"abort"},{"const":"stream_error"},{"const":"raw_meta"},{"const":"x-error"}]},"rawJson":{"anyOf":[{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"user"},"createdAt":{"type":"string"},"type":{"const":"user"},"text":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"const":"image"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]},"attachmentId":{"type":"string"},"name":{"type":"string"},"sizeInBytes":{"type":"number"}},"required":["type","source"]}},"promptPlan":{}},"required":["schemaVersion","role","createdAt","type","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"assistant_text"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"reasoning"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_input"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"toolName":{"anyOf":[{"type":"string"},{"type":"null"}]},"providerExecuted":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"dynamic":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"legacy_assistant_turn"},"text":{"type":"string"},"rawEvents":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"const":"plan"},"entries":{"type":"array","items":{"type":"string"}},"rawText":{"type":"string"}},"required":["type","entries","rawText"]},{"type":"object","properties":{"type":{"const":"diff"},"path":{"type":"string"},"oldText":{"anyOf":[{"type":"string"},{"type":"null"}]},"newText":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawText":{"type":"string"}},"required":["type","path","oldText","newText","rawText"]},{"type":"object","properties":{"type":{"const":"terminal"},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","terminalId","text","rawText"]},{"type":"object","properties":{"type":{"const":"reasoning"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolCall"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"inputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","inputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolResult"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"outputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","outputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolError"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"errorText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","errorText","rawText"]},{"type":"object","properties":{"type":{"const":"streamPart"},"partType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","partType","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolInput"},"streamId":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","streamId","text","rawText"]}]}},"metadata":{}},"required":["schemaVersion","role","createdAt","type","text","rawEvents"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_call"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_result"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_error"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_output_denied"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_approval_request"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"source"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"file"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_start"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_finish"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"step_start"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"step_finish"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"abort"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_error"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"raw_meta"},"text":{"type":"string"},"part":{}},"required":["schemaVersion","role","createdAt","type","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"type":{"const":"x-error"},"role":{"const":"assistant"},"sourceKind":{"type":"string"},"errorMessage":{"type":"string"},"rawJsonText":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}},"createdAt":{"type":"string"}},"required":["schemaVersion","type","role","sourceKind","errorMessage","rawJsonText","issues","createdAt"]}]},"textForSearch":{"type":"string"},"text":{"type":"string"},"rawEvents":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"const":"plan"},"entries":{"type":"array","items":{"type":"string"}},"rawText":{"type":"string"}},"required":["type","entries","rawText"]},{"type":"object","properties":{"type":{"const":"diff"},"path":{"type":"string"},"oldText":{"anyOf":[{"type":"string"},{"type":"null"}]},"newText":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawText":{"type":"string"}},"required":["type","path","oldText","newText","rawText"]},{"type":"object","properties":{"type":{"const":"terminal"},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","terminalId","text","rawText"]},{"type":"object","properties":{"type":{"const":"reasoning"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolCall"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"inputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","inputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolResult"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"outputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","outputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolError"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"errorText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","errorText","rawText"]},{"type":"object","properties":{"type":{"const":"streamPart"},"partType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","partType","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolInput"},"streamId":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","streamId","text","rawText"]}]}},"createdAt":{"type":"string"},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"streamPartId":{"anyOf":[{"type":"string"},{"type":"null"}]},"metadataJson":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","role","rawJson","text","rawEvents","createdAt"]}}},"required":["session","text","rawEvents"]}}}},"400":{"description":"ACP prepared message error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string"},"attachmentIds":{"type":"array","items":{"type":"string"}},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["prompt"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"prepareId","required":true}]}},"/api/acp/sessions/{sessionId}":{"patch":{"operationId":"patchApiAcpSessionsBySessionId","summary":"Update ACP session","responses":{"200":{"description":"Updated ACP session","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]}},"required":["session"]}}}},"400":{"description":"ACP session update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":[]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"sessionId","required":true}]},"delete":{"operationId":"deleteApiAcpSessionsBySessionId","summary":"Delete ACP session","responses":{"200":{"description":"Delete result","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"sessionId","required":true}]}},"/api/acp/sessions/{sessionId}/config-options":{"patch":{"operationId":"patchApiAcpSessionsBySessionIdConfigOptions","summary":"Update ACP session generic config option","responses":{"200":{"description":"Updated ACP session","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]}},"required":["session"]}}}},"400":{"description":"ACP session config option update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"configId":{"type":"string"},"value":{"type":"string"}},"required":["configId","value"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"sessionId","required":true}]}},"/api/acp/sessions/{sessionId}/messages":{"get":{"operationId":"getApiAcpSessionsBySessionIdMessages","summary":"List ACP session messages","responses":{"200":{"description":"ACP session messages","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"role":{"anyOf":[{"const":"user"},{"const":"assistant"}]},"kind":{"anyOf":[{"const":"user"},{"const":"legacy_assistant_turn"},{"const":"assistant_text"},{"const":"reasoning"},{"const":"tool_input"},{"const":"tool_call"},{"const":"tool_result"},{"const":"tool_error"},{"const":"tool_output_denied"},{"const":"tool_approval_request"},{"const":"source"},{"const":"file"},{"const":"stream_start"},{"const":"stream_finish"},{"const":"step_start"},{"const":"step_finish"},{"const":"abort"},{"const":"stream_error"},{"const":"raw_meta"},{"const":"x-error"}]},"rawJson":{"anyOf":[{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"user"},"createdAt":{"type":"string"},"type":{"const":"user"},"text":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"const":"image"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]},"attachmentId":{"type":"string"},"name":{"type":"string"},"sizeInBytes":{"type":"number"}},"required":["type","source"]}},"promptPlan":{}},"required":["schemaVersion","role","createdAt","type","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"assistant_text"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"reasoning"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_input"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"toolName":{"anyOf":[{"type":"string"},{"type":"null"}]},"providerExecuted":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"dynamic":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"legacy_assistant_turn"},"text":{"type":"string"},"rawEvents":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"const":"plan"},"entries":{"type":"array","items":{"type":"string"}},"rawText":{"type":"string"}},"required":["type","entries","rawText"]},{"type":"object","properties":{"type":{"const":"diff"},"path":{"type":"string"},"oldText":{"anyOf":[{"type":"string"},{"type":"null"}]},"newText":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawText":{"type":"string"}},"required":["type","path","oldText","newText","rawText"]},{"type":"object","properties":{"type":{"const":"terminal"},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","terminalId","text","rawText"]},{"type":"object","properties":{"type":{"const":"reasoning"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolCall"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"inputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","inputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolResult"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"outputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","outputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolError"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"errorText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","errorText","rawText"]},{"type":"object","properties":{"type":{"const":"streamPart"},"partType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","partType","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolInput"},"streamId":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","streamId","text","rawText"]}]}},"metadata":{}},"required":["schemaVersion","role","createdAt","type","text","rawEvents"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_call"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_result"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_error"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_output_denied"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_approval_request"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"source"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"file"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_start"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_finish"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"step_start"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"step_finish"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"abort"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_error"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"raw_meta"},"text":{"type":"string"},"part":{}},"required":["schemaVersion","role","createdAt","type","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"type":{"const":"x-error"},"role":{"const":"assistant"},"sourceKind":{"type":"string"},"errorMessage":{"type":"string"},"rawJsonText":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}},"createdAt":{"type":"string"}},"required":["schemaVersion","type","role","sourceKind","errorMessage","rawJsonText","issues","createdAt"]}]},"textForSearch":{"type":"string"},"text":{"type":"string"},"rawEvents":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"const":"plan"},"entries":{"type":"array","items":{"type":"string"}},"rawText":{"type":"string"}},"required":["type","entries","rawText"]},{"type":"object","properties":{"type":{"const":"diff"},"path":{"type":"string"},"oldText":{"anyOf":[{"type":"string"},{"type":"null"}]},"newText":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawText":{"type":"string"}},"required":["type","path","oldText","newText","rawText"]},{"type":"object","properties":{"type":{"const":"terminal"},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","terminalId","text","rawText"]},{"type":"object","properties":{"type":{"const":"reasoning"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolCall"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"inputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","inputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolResult"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"outputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","outputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolError"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"errorText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","errorText","rawText"]},{"type":"object","properties":{"type":{"const":"streamPart"},"partType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","partType","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolInput"},"streamId":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","streamId","text","rawText"]}]}},"createdAt":{"type":"string"},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"streamPartId":{"anyOf":[{"type":"string"},{"type":"null"}]},"metadataJson":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","role","rawJson","text","rawEvents","createdAt"]}},"pageInfo":{"type":"object","properties":{"hasMoreBefore":{"type":"boolean"},"beforeCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["hasMoreBefore","beforeCursor"]},"meta":{"type":"object","properties":{"totalMessageCount":{"type":"number"}},"required":["totalMessageCount"]}},"required":["messages","pageInfo","meta"]}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"sessionId","required":true}]},"post":{"operationId":"postApiAcpSessionsBySessionIdMessages","summary":"Send message to ACP session","responses":{"200":{"description":"ACP message response","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]},"text":{"type":"string"},"rawEvents":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"const":"plan"},"entries":{"type":"array","items":{"type":"string"}},"rawText":{"type":"string"}},"required":["type","entries","rawText"]},{"type":"object","properties":{"type":{"const":"diff"},"path":{"type":"string"},"oldText":{"anyOf":[{"type":"string"},{"type":"null"}]},"newText":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawText":{"type":"string"}},"required":["type","path","oldText","newText","rawText"]},{"type":"object","properties":{"type":{"const":"terminal"},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","terminalId","text","rawText"]},{"type":"object","properties":{"type":{"const":"reasoning"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolCall"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"inputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","inputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolResult"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"outputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","outputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolError"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"errorText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","errorText","rawText"]},{"type":"object","properties":{"type":{"const":"streamPart"},"partType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","partType","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolInput"},"streamId":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","streamId","text","rawText"]}]}},"assistantSegmentMessages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"role":{"anyOf":[{"const":"user"},{"const":"assistant"}]},"kind":{"anyOf":[{"const":"user"},{"const":"legacy_assistant_turn"},{"const":"assistant_text"},{"const":"reasoning"},{"const":"tool_input"},{"const":"tool_call"},{"const":"tool_result"},{"const":"tool_error"},{"const":"tool_output_denied"},{"const":"tool_approval_request"},{"const":"source"},{"const":"file"},{"const":"stream_start"},{"const":"stream_finish"},{"const":"step_start"},{"const":"step_finish"},{"const":"abort"},{"const":"stream_error"},{"const":"raw_meta"},{"const":"x-error"}]},"rawJson":{"anyOf":[{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"user"},"createdAt":{"type":"string"},"type":{"const":"user"},"text":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"const":"image"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]},"attachmentId":{"type":"string"},"name":{"type":"string"},"sizeInBytes":{"type":"number"}},"required":["type","source"]}},"promptPlan":{}},"required":["schemaVersion","role","createdAt","type","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"assistant_text"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"reasoning"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_input"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"toolName":{"anyOf":[{"type":"string"},{"type":"null"}]},"providerExecuted":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"dynamic":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"legacy_assistant_turn"},"text":{"type":"string"},"rawEvents":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"const":"plan"},"entries":{"type":"array","items":{"type":"string"}},"rawText":{"type":"string"}},"required":["type","entries","rawText"]},{"type":"object","properties":{"type":{"const":"diff"},"path":{"type":"string"},"oldText":{"anyOf":[{"type":"string"},{"type":"null"}]},"newText":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawText":{"type":"string"}},"required":["type","path","oldText","newText","rawText"]},{"type":"object","properties":{"type":{"const":"terminal"},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","terminalId","text","rawText"]},{"type":"object","properties":{"type":{"const":"reasoning"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolCall"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"inputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","inputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolResult"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"outputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","outputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolError"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"errorText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","errorText","rawText"]},{"type":"object","properties":{"type":{"const":"streamPart"},"partType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","partType","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolInput"},"streamId":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","streamId","text","rawText"]}]}},"metadata":{}},"required":["schemaVersion","role","createdAt","type","text","rawEvents"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_call"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_result"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_error"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_output_denied"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_approval_request"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"source"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"file"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_start"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_finish"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"step_start"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"step_finish"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"abort"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_error"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"raw_meta"},"text":{"type":"string"},"part":{}},"required":["schemaVersion","role","createdAt","type","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"type":{"const":"x-error"},"role":{"const":"assistant"},"sourceKind":{"type":"string"},"errorMessage":{"type":"string"},"rawJsonText":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}},"createdAt":{"type":"string"}},"required":["schemaVersion","type","role","sourceKind","errorMessage","rawJsonText","issues","createdAt"]}]},"textForSearch":{"type":"string"},"text":{"type":"string"},"rawEvents":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"const":"plan"},"entries":{"type":"array","items":{"type":"string"}},"rawText":{"type":"string"}},"required":["type","entries","rawText"]},{"type":"object","properties":{"type":{"const":"diff"},"path":{"type":"string"},"oldText":{"anyOf":[{"type":"string"},{"type":"null"}]},"newText":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawText":{"type":"string"}},"required":["type","path","oldText","newText","rawText"]},{"type":"object","properties":{"type":{"const":"terminal"},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","terminalId","text","rawText"]},{"type":"object","properties":{"type":{"const":"reasoning"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolCall"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"inputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","inputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolResult"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"outputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","outputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolError"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"errorText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","errorText","rawText"]},{"type":"object","properties":{"type":{"const":"streamPart"},"partType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","partType","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolInput"},"streamId":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","streamId","text","rawText"]}]}},"createdAt":{"type":"string"},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"streamPartId":{"anyOf":[{"type":"string"},{"type":"null"}]},"metadataJson":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","role","rawJson","text","rawEvents","createdAt"]}}},"required":["session","text","rawEvents"]}}}},"400":{"description":"ACP message error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string"},"attachmentIds":{"type":"array","items":{"type":"string"}},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["prompt"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"sessionId","required":true}]}}},"components":{}} \ No newline at end of file +{"openapi":"3.1.0","info":{"title":"Remote Agent API","description":"API for Remote Agent BFF","version":"0.0.0"},"paths":{"/api/info":{"get":{"operationId":"getApiInfo","summary":"Get application info","responses":{"200":{"description":"Application info","content":{"application/json":{"schema":{"type":"object","properties":{"appName":{"type":"string"},"version":{"type":"string"},"workingDirectory":{"type":"string"},"projectsFilePath":{"type":"string"},"agentPresets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"authMethodId":{"type":"string"},"modelSelectLabel":{"type":"string"},"modeSelectLabel":{"type":"string"}},"required":["id","label","description","command","args"]}}},"required":["appName","version","workingDirectory","projectsFilePath","agentPresets"]}}}}}}},"/api/attachments/ingest":{"post":{"operationId":"postApiAttachmentsIngest","summary":"Ingest browser attachments","responses":{"201":{"description":"Ingested attachments","content":{"application/json":{"schema":{"type":"object","properties":{"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["attachments"]}}}},"400":{"description":"Attachment ingest error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/filesystem/tree":{"get":{"operationId":"getApiFilesystemTree","summary":"Get filesystem tree","responses":{"200":{"description":"Filesystem tree","content":{"application/json":{"schema":{"type":"object","properties":{"root":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string"},"kind":{"anyOf":[{"const":"directory"},{"const":"file"}]},"children":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string"},"kind":{"anyOf":[{"const":"directory"},{"const":"file"}]}},"required":["name","path","kind"]}}},"required":["name","path","kind"]}},"required":["root"]}}}},"400":{"description":"Directory read error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"in":"query","name":"root","schema":{"type":"string"}}]}},"/api/filesystem/directory-listing":{"get":{"operationId":"getApiFilesystemDirectoryListing","summary":"List directory entries for navigation","responses":{"200":{"description":"Directory listing","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string"},"type":{"anyOf":[{"const":"directory"},{"const":"file"}]}},"required":["name","path","type"]}},"currentPath":{"type":"string"}},"required":["entries","currentPath"]}}}},"400":{"description":"Directory read error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"in":"query","name":"currentPath","schema":{"type":"string"}},{"in":"query","name":"showHidden","schema":{"type":"string"}}]}},"/api/filesystem/file-completion":{"get":{"operationId":"getApiFilesystemFileCompletion","summary":"List project files for prompt completion","responses":{"200":{"description":"File completion entries","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string"},"type":{"anyOf":[{"const":"directory"},{"const":"file"}]}},"required":["name","path","type"]}},"basePath":{"type":"string"},"projectPath":{"type":"string"}},"required":["entries","basePath","projectPath"]}}}},"400":{"description":"File completion error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"in":"query","name":"projectId","schema":{"type":"string"},"required":true},{"in":"query","name":"basePath","schema":{"type":"string"}}]}},"/api/projects":{"get":{"operationId":"getApiProjects","summary":"List projects","responses":{"200":{"description":"Projects","content":{"application/json":{"schema":{"type":"object","properties":{"projects":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"workingDirectory":{"type":"string"},"worktreeSetupScript":{"type":"string"}},"required":["id","name","workingDirectory"]}}},"required":["projects"]}}}}}},"post":{"operationId":"postApiProjects","summary":"Create project","responses":{"201":{"description":"Created project","content":{"application/json":{"schema":{"type":"object","properties":{"project":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"workingDirectory":{"type":"string"},"worktreeSetupScript":{"type":"string"}},"required":["id","name","workingDirectory"]}},"required":["project"]}}}},"400":{"description":"Project creation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"workingDirectory":{"type":"string"}},"required":["name","workingDirectory"]}}}}}},"/api/projects/{projectId}":{"get":{"operationId":"getApiProjectsByProjectId","summary":"Get project","responses":{"200":{"description":"Project","content":{"application/json":{"schema":{"type":"object","properties":{"project":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"workingDirectory":{"type":"string"},"worktreeSetupScript":{"type":"string"}},"required":["id","name","workingDirectory"]}},"required":["project"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"projectId","required":true}]}},"/api/projects/{projectId}/worktrees":{"post":{"operationId":"postApiProjectsByProjectIdWorktrees","summary":"Create project worktree","responses":{"201":{"description":"Created project worktree","content":{"application/json":{"schema":{"type":"object","properties":{"worktree":{"type":"object","properties":{"projectId":{"type":"string"},"name":{"type":"string"},"path":{"type":"string"},"branchName":{"type":"string"},"baseRef":{"type":"string"},"createdAt":{"type":"string"}},"required":["projectId","name","path","branchName","baseRef","createdAt"]}},"required":["worktree"]}}}},"400":{"description":"Project worktree creation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"branchName":{"type":"string"},"baseRef":{"type":"string"}},"required":["name"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"projectId","required":true}]}},"/api/projects/{projectId}/git/revisions":{"get":{"operationId":"getApiProjectsByProjectIdGitRevisions","summary":"Get project git revisions","responses":{"200":{"description":"Git revisions","content":{"application/json":{"schema":{"type":"object","properties":{"refs":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"type":{"anyOf":[{"const":"branch"},{"const":"commit"},{"const":"head"},{"const":"working"}]},"displayName":{"type":"string"},"sha":{"type":"string"}},"required":["name","type","displayName"]}}},"required":["refs"]}}}},"400":{"description":"Git revisions error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"in":"query","name":"cwd","schema":{"anyOf":[{"type":"string"},{"type":"null"}]},"required":true},{"schema":{"type":"string"},"in":"path","name":"projectId","required":true}]}},"/api/projects/{projectId}/git/diff":{"post":{"operationId":"postApiProjectsByProjectIdGitDiff","summary":"Get project git diff","responses":{"200":{"description":"Git diff","content":{"application/json":{"schema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"object","properties":{"filename":{"type":"string"},"oldFilename":{"type":"string"},"isNew":{"type":"boolean"},"isDeleted":{"type":"boolean"},"isRenamed":{"type":"boolean"},"isBinary":{"type":"boolean"},"hunks":{"type":"array","items":{"type":"object","properties":{"oldStart":{"type":"number"},"newStart":{"type":"number"},"lines":{"type":"array","items":{"type":"object","properties":{"type":{"anyOf":[{"const":"added"},{"const":"deleted"},{"const":"unchanged"},{"const":"hunk"},{"const":"context"}]},"oldLineNumber":{"type":"number"},"newLineNumber":{"type":"number"},"content":{"type":"string"}},"required":["type","content"]}}},"required":["oldStart","newStart","lines"]}},"linesAdded":{"type":"number"},"linesDeleted":{"type":"number"}},"required":["filename","isNew","isDeleted","isRenamed","isBinary","hunks","linesAdded","linesDeleted"]}},"summary":{"type":"object","properties":{"totalFiles":{"type":"number"},"totalAdditions":{"type":"number"},"totalDeletions":{"type":"number"}},"required":["totalFiles","totalAdditions","totalDeletions"]}},"required":["files","summary"]}}}},"400":{"description":"Git diff error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fromRef":{"type":"string"},"toRef":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["fromRef","toRef","cwd"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"projectId","required":true}]}},"/api/projects/{projectId}/settings":{"get":{"operationId":"getApiProjectsByProjectIdSettings","summary":"Get project settings","responses":{"200":{"description":"Project settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"projectId":{"type":"string"},"modelPreferences":{"type":"array","items":{"type":"object","properties":{"presetId":{"type":"string"},"modelId":{"type":"string"},"isFavorite":{"type":"boolean"},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["presetId","modelId","isFavorite","lastUsedAt","updatedAt"]}},"modePreferences":{"type":"array","items":{"type":"object","properties":{"presetId":{"type":"string"},"modeId":{"type":"string"},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["presetId","modeId","lastUsedAt","updatedAt"]}},"worktreeSetupScript":{"type":"string"},"sandbox":{"type":"object","properties":{"enabled":{"type":"boolean"},"filesystem":{"type":"object","properties":{"allowRead":{"type":"array","items":{"type":"string"}},"denyRead":{"type":"array","items":{"type":"string"}},"allowWrite":{"type":"array","items":{"type":"string"}},"denyWrite":{"type":"array","items":{"type":"string"}}},"required":["allowRead","denyRead","allowWrite","denyWrite"]},"network":{"type":"object","properties":{"mode":{"anyOf":[{"const":"inherit"},{"const":"restrict"},{"const":"none"}]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["mode","allowedDomains"]}},"required":["enabled","filesystem","network"]}},"required":["projectId","modelPreferences","modePreferences","worktreeSetupScript","sandbox"]}},"required":["settings"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"projectId","required":true}]},"patch":{"operationId":"patchApiProjectsByProjectIdSettings","summary":"Update project settings","responses":{"200":{"description":"Project settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"projectId":{"type":"string"},"modelPreferences":{"type":"array","items":{"type":"object","properties":{"presetId":{"type":"string"},"modelId":{"type":"string"},"isFavorite":{"type":"boolean"},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["presetId","modelId","isFavorite","lastUsedAt","updatedAt"]}},"modePreferences":{"type":"array","items":{"type":"object","properties":{"presetId":{"type":"string"},"modeId":{"type":"string"},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["presetId","modeId","lastUsedAt","updatedAt"]}},"worktreeSetupScript":{"type":"string"},"sandbox":{"type":"object","properties":{"enabled":{"type":"boolean"},"filesystem":{"type":"object","properties":{"allowRead":{"type":"array","items":{"type":"string"}},"denyRead":{"type":"array","items":{"type":"string"}},"allowWrite":{"type":"array","items":{"type":"string"}},"denyWrite":{"type":"array","items":{"type":"string"}}},"required":["allowRead","denyRead","allowWrite","denyWrite"]},"network":{"type":"object","properties":{"mode":{"anyOf":[{"const":"inherit"},{"const":"restrict"},{"const":"none"}]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["mode","allowedDomains"]}},"required":["enabled","filesystem","network"]}},"required":["projectId","modelPreferences","modePreferences","worktreeSetupScript","sandbox"]}},"required":["settings"]}}}},"400":{"description":"Project settings update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"worktreeSetupScript":{"type":"string"},"sandbox":{"type":"object","properties":{"enabled":{"type":"boolean"},"filesystem":{"type":"object","properties":{"allowRead":{"type":"array","items":{"type":"string"}},"denyRead":{"type":"array","items":{"type":"string"}},"allowWrite":{"type":"array","items":{"type":"string"}},"denyWrite":{"type":"array","items":{"type":"string"}}},"required":["allowRead","denyRead","allowWrite","denyWrite"]},"network":{"type":"object","properties":{"mode":{"anyOf":[{"const":"inherit"},{"const":"restrict"},{"const":"none"}]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["mode","allowedDomains"]}},"required":["enabled","filesystem","network"]}},"required":["name","worktreeSetupScript","sandbox"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"projectId","required":true}]}},"/api/projects/{projectId}/model-preferences":{"patch":{"operationId":"patchApiProjectsByProjectIdModelPreferences","summary":"Update project model preferences","responses":{"200":{"description":"Project settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"projectId":{"type":"string"},"modelPreferences":{"type":"array","items":{"type":"object","properties":{"presetId":{"type":"string"},"modelId":{"type":"string"},"isFavorite":{"type":"boolean"},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["presetId","modelId","isFavorite","lastUsedAt","updatedAt"]}},"modePreferences":{"type":"array","items":{"type":"object","properties":{"presetId":{"type":"string"},"modeId":{"type":"string"},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["presetId","modeId","lastUsedAt","updatedAt"]}},"worktreeSetupScript":{"type":"string"},"sandbox":{"type":"object","properties":{"enabled":{"type":"boolean"},"filesystem":{"type":"object","properties":{"allowRead":{"type":"array","items":{"type":"string"}},"denyRead":{"type":"array","items":{"type":"string"}},"allowWrite":{"type":"array","items":{"type":"string"}},"denyWrite":{"type":"array","items":{"type":"string"}}},"required":["allowRead","denyRead","allowWrite","denyWrite"]},"network":{"type":"object","properties":{"mode":{"anyOf":[{"const":"inherit"},{"const":"restrict"},{"const":"none"}]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["mode","allowedDomains"]}},"required":["enabled","filesystem","network"]}},"required":["projectId","modelPreferences","modePreferences","worktreeSetupScript","sandbox"]}},"required":["settings"]}}}},"400":{"description":"Project settings update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"presetId":{"type":"string"},"modelId":{"type":"string"},"isFavorite":{"type":"boolean"},"markLastUsed":{"type":"boolean"}},"required":["presetId","modelId"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"projectId","required":true}]}},"/api/projects/{projectId}/mode-preferences":{"patch":{"operationId":"patchApiProjectsByProjectIdModePreferences","summary":"Update project mode preferences","responses":{"200":{"description":"Project settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"projectId":{"type":"string"},"modelPreferences":{"type":"array","items":{"type":"object","properties":{"presetId":{"type":"string"},"modelId":{"type":"string"},"isFavorite":{"type":"boolean"},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["presetId","modelId","isFavorite","lastUsedAt","updatedAt"]}},"modePreferences":{"type":"array","items":{"type":"object","properties":{"presetId":{"type":"string"},"modeId":{"type":"string"},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["presetId","modeId","lastUsedAt","updatedAt"]}},"worktreeSetupScript":{"type":"string"},"sandbox":{"type":"object","properties":{"enabled":{"type":"boolean"},"filesystem":{"type":"object","properties":{"allowRead":{"type":"array","items":{"type":"string"}},"denyRead":{"type":"array","items":{"type":"string"}},"allowWrite":{"type":"array","items":{"type":"string"}},"denyWrite":{"type":"array","items":{"type":"string"}}},"required":["allowRead","denyRead","allowWrite","denyWrite"]},"network":{"type":"object","properties":{"mode":{"anyOf":[{"const":"inherit"},{"const":"restrict"},{"const":"none"}]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["mode","allowedDomains"]}},"required":["enabled","filesystem","network"]}},"required":["projectId","modelPreferences","modePreferences","worktreeSetupScript","sandbox"]}},"required":["settings"]}}}},"400":{"description":"Project settings update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"presetId":{"type":"string"},"modeId":{"type":"string"},"markLastUsed":{"type":"boolean"}},"required":["presetId","modeId"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"projectId","required":true}]}},"/api/routines":{"get":{"operationId":"getApiRoutines","summary":"List routines","responses":{"200":{"description":"Routines","content":{"application/json":{"schema":{"type":"object","properties":{"routines":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"const":"cron"},"config":{"type":"object","properties":{"cronExpression":{"type":"string"}},"required":["cronExpression"]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","enabled","kind","config","sendConfig","createdAt","updatedAt","lastRunAt","nextRunAt","lastError"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"const":"scheduled"},"config":{"type":"object","properties":{"runAt":{"type":"string"}},"required":["runAt"]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","enabled","kind","config","sendConfig","createdAt","updatedAt","lastRunAt","nextRunAt","lastError"]}]}}},"required":["routines"]}}}}}},"post":{"operationId":"postApiRoutines","summary":"Create routine","responses":{"201":{"description":"Routines","content":{"application/json":{"schema":{"type":"object","properties":{"routines":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"const":"cron"},"config":{"type":"object","properties":{"cronExpression":{"type":"string"}},"required":["cronExpression"]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","enabled","kind","config","sendConfig","createdAt","updatedAt","lastRunAt","nextRunAt","lastError"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"const":"scheduled"},"config":{"type":"object","properties":{"runAt":{"type":"string"}},"required":["runAt"]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","enabled","kind","config","sendConfig","createdAt","updatedAt","lastRunAt","nextRunAt","lastError"]}]}}},"required":["routines"]}}}},"400":{"description":"Routine creation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"anyOf":[{"const":"cron"},{"const":"scheduled"}]},"config":{"anyOf":[{"type":"object","properties":{"cronExpression":{"type":"string"}},"required":["cronExpression"]},{"type":"object","properties":{"runAt":{"type":"string"}},"required":["runAt"]}]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]}},"required":["name","kind","config","sendConfig"]}}}}}},"/api/routines/{routineId}":{"patch":{"operationId":"patchApiRoutinesByRoutineId","summary":"Update routine","responses":{"200":{"description":"Routines","content":{"application/json":{"schema":{"type":"object","properties":{"routines":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"const":"cron"},"config":{"type":"object","properties":{"cronExpression":{"type":"string"}},"required":["cronExpression"]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","enabled","kind","config","sendConfig","createdAt","updatedAt","lastRunAt","nextRunAt","lastError"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"const":"scheduled"},"config":{"type":"object","properties":{"runAt":{"type":"string"}},"required":["runAt"]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","enabled","kind","config","sendConfig","createdAt","updatedAt","lastRunAt","nextRunAt","lastError"]}]}}},"required":["routines"]}}}},"400":{"description":"Routine update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Routine not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"anyOf":[{"const":"cron"},{"const":"scheduled"}]},"config":{"anyOf":[{"type":"object","properties":{"cronExpression":{"type":"string"}},"required":["cronExpression"]},{"type":"object","properties":{"runAt":{"type":"string"}},"required":["runAt"]}]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]}},"required":[]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"routineId","required":true}]},"delete":{"operationId":"deleteApiRoutinesByRoutineId","summary":"Delete routine","responses":{"200":{"description":"Routines","content":{"application/json":{"schema":{"type":"object","properties":{"routines":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"const":"cron"},"config":{"type":"object","properties":{"cronExpression":{"type":"string"}},"required":["cronExpression"]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","enabled","kind","config","sendConfig","createdAt","updatedAt","lastRunAt","nextRunAt","lastError"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"kind":{"const":"scheduled"},"config":{"type":"object","properties":{"runAt":{"type":"string"}},"required":["runAt"]},"sendConfig":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"prompt":{"type":"string"},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}}},"required":["projectId","presetId","cwd","prompt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"lastRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextRunAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","enabled","kind","config","sendConfig","createdAt","updatedAt","lastRunAt","nextRunAt","lastError"]}]}}},"required":["routines"]}}}},"404":{"description":"Routine not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"routineId","required":true}]}},"/api/settings":{"get":{"operationId":"getApiSettings","summary":"Get application settings","responses":{"200":{"description":"Application settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"language":{"anyOf":[{"const":"ja"},{"const":"en"}]},"submitKeyBinding":{"anyOf":[{"const":"mod-enter"},{"const":"enter"}]},"sandbox":{"type":"object","properties":{"enabledProviderIds":{"type":"array","items":{"type":"string"}},"filesystem":{"type":"object","properties":{"allowRead":{"type":"array","items":{"type":"string"}},"denyRead":{"type":"array","items":{"type":"string"}},"allowWrite":{"type":"array","items":{"type":"string"}},"denyWrite":{"type":"array","items":{"type":"string"}}},"required":["allowRead","denyRead","allowWrite","denyWrite"]},"network":{"type":"object","properties":{"mode":{"anyOf":[{"const":"restrict"},{"const":"none"}]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["mode","allowedDomains"]}},"required":["enabledProviderIds","filesystem","network"]}},"required":["language","submitKeyBinding","sandbox"]}},"required":["settings"]}}}}}},"patch":{"operationId":"patchApiSettings","summary":"Update application settings","responses":{"200":{"description":"Application settings","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"language":{"anyOf":[{"const":"ja"},{"const":"en"}]},"submitKeyBinding":{"anyOf":[{"const":"mod-enter"},{"const":"enter"}]},"sandbox":{"type":"object","properties":{"enabledProviderIds":{"type":"array","items":{"type":"string"}},"filesystem":{"type":"object","properties":{"allowRead":{"type":"array","items":{"type":"string"}},"denyRead":{"type":"array","items":{"type":"string"}},"allowWrite":{"type":"array","items":{"type":"string"}},"denyWrite":{"type":"array","items":{"type":"string"}}},"required":["allowRead","denyRead","allowWrite","denyWrite"]},"network":{"type":"object","properties":{"mode":{"anyOf":[{"const":"restrict"},{"const":"none"}]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["mode","allowedDomains"]}},"required":["enabledProviderIds","filesystem","network"]}},"required":["language","submitKeyBinding","sandbox"]}},"required":["settings"]}}}},"400":{"description":"Application settings update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"language":{"anyOf":[{"const":"ja"},{"const":"en"}]},"submitKeyBinding":{"anyOf":[{"const":"mod-enter"},{"const":"enter"}]},"sandbox":{"type":"object","properties":{"enabledProviderIds":{"type":"array","items":{"type":"string"}},"filesystem":{"type":"object","properties":{"allowRead":{"type":"array","items":{"type":"string"}},"denyRead":{"type":"array","items":{"type":"string"}},"allowWrite":{"type":"array","items":{"type":"string"}},"denyWrite":{"type":"array","items":{"type":"string"}}},"required":["allowRead","denyRead","allowWrite","denyWrite"]},"network":{"type":"object","properties":{"mode":{"anyOf":[{"const":"restrict"},{"const":"none"}]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["mode","allowedDomains"]}},"required":["enabledProviderIds","filesystem","network"]}},"required":["language","submitKeyBinding","sandbox"]}}}}}},"/api/setup":{"get":{"operationId":"getApiSetup","summary":"Get application setup state","responses":{"200":{"description":"Application setup state","content":{"application/json":{"schema":{"type":"object","properties":{"setup":{"type":"object","properties":{"initialSetupCompleted":{"type":"boolean"},"completedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["initialSetupCompleted","completedAt"]}},"required":["setup"]}}}}}}},"/api/setup/complete":{"post":{"operationId":"postApiSetupComplete","summary":"Mark initial application setup completed","responses":{"200":{"description":"Application setup state","content":{"application/json":{"schema":{"type":"object","properties":{"setup":{"type":"object","properties":{"initialSetupCompleted":{"type":"boolean"},"completedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["initialSetupCompleted","completedAt"]}},"required":["setup"]}}}},"400":{"description":"Application setup completion error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/acp/sse":{"get":{"operationId":"getApiAcpSse","summary":"Subscribe to ACP session updates (Server-Sent Events, JSON in each data line)","responses":{"200":{"description":"Event stream"}}}},"/api/acp/permissions":{"get":{"operationId":"getApiAcpPermissions","summary":"List pending ACP permission requests","responses":{"200":{"description":"ACP permission requests","content":{"application/json":{"schema":{"type":"object","properties":{"requests":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sessionId":{"type":"string"},"toolCallId":{"type":"string"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawInputText":{"anyOf":[{"type":"string"},{"type":"null"}]},"options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"anyOf":[{"const":"allow_once"},{"const":"allow_always"},{"const":"reject_once"},{"const":"reject_always"}]},"name":{"type":"string"}},"required":["id","kind","name"]}},"createdAt":{"type":"string"}},"required":["id","sessionId","toolCallId","title","kind","rawInputText","options","createdAt"]}}},"required":["requests"]}}}}}}},"/api/acp/permissions/{requestId}/resolve":{"post":{"operationId":"postApiAcpPermissionsByRequestIdResolve","summary":"Resolve a pending ACP permission request","responses":{"200":{"description":"ACP permission requests","content":{"application/json":{"schema":{"type":"object","properties":{"requests":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sessionId":{"type":"string"},"toolCallId":{"type":"string"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawInputText":{"anyOf":[{"type":"string"},{"type":"null"}]},"options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"anyOf":[{"const":"allow_once"},{"const":"allow_always"},{"const":"reject_once"},{"const":"reject_always"}]},"name":{"type":"string"}},"required":["id","kind","name"]}},"createdAt":{"type":"string"}},"required":["id","sessionId","toolCallId","title","kind","rawInputText","options","createdAt"]}}},"required":["requests"]}}}},"404":{"description":"ACP permission request not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"optionId":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["optionId"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"requestId","required":true}]}},"/api/acp/providers":{"get":{"operationId":"getApiAcpProviders","summary":"List ACP provider presets and enabled state","responses":{"200":{"description":"ACP providers","content":{"application/json":{"schema":{"type":"object","properties":{"providers":{"type":"array","items":{"type":"object","properties":{"preset":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"authMethodId":{"type":"string"},"modelSelectLabel":{"type":"string"},"modeSelectLabel":{"type":"string"}},"required":["id","label","description","command","args"]},"enabled":{"type":"boolean"},"enabledAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"catalogSummary":{"anyOf":[{"type":"object","properties":{"availableModelCount":{"type":"number"},"availableModeCount":{"type":"number"},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]},"refreshedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["availableModelCount","availableModeCount","currentModelId","currentModeId","lastError","refreshedAt"]},{"type":"null"}]}},"required":["preset","enabled","enabledAt","updatedAt"]}}},"required":["providers"]}}}}}}},"/api/acp/providers/custom":{"post":{"operationId":"postApiAcpProvidersCustom","summary":"Create a custom ACP provider","responses":{"201":{"description":"ACP providers","content":{"application/json":{"schema":{"type":"object","properties":{"providers":{"type":"array","items":{"type":"object","properties":{"preset":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"authMethodId":{"type":"string"},"modelSelectLabel":{"type":"string"},"modeSelectLabel":{"type":"string"}},"required":["id","label","description","command","args"]},"enabled":{"type":"boolean"},"enabledAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"catalogSummary":{"anyOf":[{"type":"object","properties":{"availableModelCount":{"type":"number"},"availableModeCount":{"type":"number"},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]},"refreshedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["availableModelCount","availableModeCount","currentModelId","currentModeId","lastError","refreshedAt"]},{"type":"null"}]}},"required":["preset","enabled","enabledAt","updatedAt"]}}},"required":["providers"]}}}},"400":{"description":"Custom ACP provider create error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"commandText":{"type":"string"}},"required":["name","commandText"]}}}}}},"/api/acp/providers/custom/{providerId}":{"patch":{"operationId":"patchApiAcpProvidersCustomByProviderId","summary":"Update a custom ACP provider","responses":{"200":{"description":"ACP providers","content":{"application/json":{"schema":{"type":"object","properties":{"providers":{"type":"array","items":{"type":"object","properties":{"preset":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"authMethodId":{"type":"string"},"modelSelectLabel":{"type":"string"},"modeSelectLabel":{"type":"string"}},"required":["id","label","description","command","args"]},"enabled":{"type":"boolean"},"enabledAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"catalogSummary":{"anyOf":[{"type":"object","properties":{"availableModelCount":{"type":"number"},"availableModeCount":{"type":"number"},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]},"refreshedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["availableModelCount","availableModeCount","currentModelId","currentModeId","lastError","refreshedAt"]},{"type":"null"}]}},"required":["preset","enabled","enabledAt","updatedAt"]}}},"required":["providers"]}}}},"400":{"description":"Custom ACP provider update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"commandText":{"type":"string"}},"required":["name","commandText"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"providerId","required":true}]},"delete":{"operationId":"deleteApiAcpProvidersCustomByProviderId","summary":"Delete a custom ACP provider","responses":{"200":{"description":"ACP providers","content":{"application/json":{"schema":{"type":"object","properties":{"providers":{"type":"array","items":{"type":"object","properties":{"preset":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"authMethodId":{"type":"string"},"modelSelectLabel":{"type":"string"},"modeSelectLabel":{"type":"string"}},"required":["id","label","description","command","args"]},"enabled":{"type":"boolean"},"enabledAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"catalogSummary":{"anyOf":[{"type":"object","properties":{"availableModelCount":{"type":"number"},"availableModeCount":{"type":"number"},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]},"refreshedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["availableModelCount","availableModeCount","currentModelId","currentModeId","lastError","refreshedAt"]},{"type":"null"}]}},"required":["preset","enabled","enabledAt","updatedAt"]}}},"required":["providers"]}}}},"400":{"description":"Custom ACP provider delete error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"providerId","required":true}]}},"/api/acp/providers/{presetId}":{"patch":{"operationId":"patchApiAcpProvidersByPresetId","summary":"Enable or disable an ACP provider preset","responses":{"200":{"description":"ACP providers","content":{"application/json":{"schema":{"type":"object","properties":{"providers":{"type":"array","items":{"type":"object","properties":{"preset":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"authMethodId":{"type":"string"},"modelSelectLabel":{"type":"string"},"modeSelectLabel":{"type":"string"}},"required":["id","label","description","command","args"]},"enabled":{"type":"boolean"},"enabledAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"catalogSummary":{"anyOf":[{"type":"object","properties":{"availableModelCount":{"type":"number"},"availableModeCount":{"type":"number"},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]},"refreshedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["availableModelCount","availableModeCount","currentModelId","currentModeId","lastError","refreshedAt"]},{"type":"null"}]}},"required":["preset","enabled","enabledAt","updatedAt"]}}},"required":["providers"]}}}},"400":{"description":"ACP provider update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"}},"required":["enabled"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"presetId","required":true}]}},"/api/acp/providers/{presetId}/check":{"post":{"operationId":"postApiAcpProvidersByPresetIdCheck","summary":"Check ACP provider connectivity and refresh catalog","responses":{"200":{"description":"Model/mode options","content":{"application/json":{"schema":{"type":"object","properties":{"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["availableModels","availableModes","currentModelId","currentModeId","lastError"]}}}},"400":{"description":"ACP provider check error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["cwd"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"presetId","required":true}]}},"/api/acp/agent/model-catalog":{"get":{"operationId":"getApiAcpAgentModelCatalog","summary":"Probe agent for model and mode list (ephemeral initSession)","responses":{"200":{"description":"Model/mode options","content":{"application/json":{"schema":{"type":"object","properties":{"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["availableModels","availableModes","currentModelId","currentModeId","lastError"]}}}},"400":{"description":"Model catalog error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"in":"query","name":"projectId","schema":{"type":"string"},"required":true},{"in":"query","name":"presetId","schema":{"type":"string","default":"codex"}}]}},"/api/acp/agent/slash-commands":{"get":{"operationId":"getApiAcpAgentSlashCommands","summary":"Probe agent for slash command list (ephemeral ACP session)","responses":{"200":{"description":"Slash command options","content":{"application/json":{"schema":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"inputHint":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["name","description","inputHint"]}},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["commands","lastError"]}}}},"400":{"description":"Slash command probe error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"in":"query","name":"projectId","schema":{"type":"string"},"required":true},{"in":"query","name":"presetId","schema":{"type":"string","default":"codex"}}]}},"/api/acp/agent/prepare":{"post":{"operationId":"postApiAcpAgentPrepare","summary":"Start an ACP provider session in the background for a draft chat","responses":{"202":{"description":"Prepared session handle","content":{"application/json":{"schema":{"type":"object","properties":{"prepareId":{"type":"string"}},"required":["prepareId"]}}}},"400":{"description":"ACP prepare error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"sandboxEnabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["projectId","presetId","cwd"]}}}}}},"/api/acp/sessions":{"get":{"operationId":"getApiAcpSessions","summary":"List ACP sessions","responses":{"200":{"description":"ACP sessions","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]}}},"required":["sessions"]}}}}}},"post":{"operationId":"postApiAcpSessions","summary":"Create ACP session","responses":{"201":{"description":"Created ACP session","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]}},"required":["session"]}}}},"400":{"description":"ACP session creation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"anyOf":[{"type":"string"},{"type":"null"}]},"argsText":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"sandboxEnabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["projectId","presetId","command","cwd"]}}}}}},"/api/acp/sessions/discover":{"get":{"operationId":"getApiAcpSessionsDiscover","summary":"Discover resumable ACP sessions","responses":{"200":{"description":"Resumable ACP sessions","content":{"application/json":{"schema":{"type":"object","properties":{"capability":{"type":"object","properties":{"loadSession":{"type":"boolean"},"listSessions":{"type":"boolean"},"resumeSession":{"type":"boolean"},"canLoadIntoProvider":{"type":"boolean"},"fallbackReason":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["loadSession","listSessions","resumeSession","canLoadIntoProvider","fallbackReason"]},"sessions":{"type":"array","items":{"type":"object","properties":{"sessionId":{"type":"string"},"cwd":{"type":"string"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"loadable":{"type":"boolean"}},"required":["sessionId","cwd","title","updatedAt","loadable"]}}},"required":["capability","sessions"]}}}},"400":{"description":"ACP session discovery error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"in":"query","name":"projectId","schema":{"anyOf":[{"type":"string"},{"type":"null"}]},"required":true},{"in":"query","name":"presetId","schema":{"anyOf":[{"type":"string"},{"type":"null"}]},"required":true},{"in":"query","name":"cwd","schema":{"anyOf":[{"type":"string"},{"type":"null"}]},"required":true}]}},"/api/acp/sessions/load":{"post":{"operationId":"postApiAcpSessionsLoad","summary":"Load existing ACP session","responses":{"201":{"description":"Loaded ACP session","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]}},"required":["session"]}}}},"400":{"description":"ACP session load error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"sessionId":{"type":"string"},"cwd":{"anyOf":[{"type":"string"},{"type":"null"}]},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["projectId","presetId","sessionId","cwd","title","updatedAt"]}}}}}},"/api/acp/sessions/{sessionId}/cancel":{"post":{"operationId":"postApiAcpSessionsBySessionIdCancel","summary":"Cancel running ACP session turn","responses":{"200":{"description":"Cancelled ACP session","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]},"cancelled":{"type":"boolean"}},"required":["session","cancelled"]}}}},"400":{"description":"ACP session cancel error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"sessionId","required":true}]}},"/api/acp/sessions/{sessionId}/stop":{"post":{"operationId":"postApiAcpSessionsBySessionIdStop","summary":"Stop paused ACP session runtime","responses":{"200":{"description":"Stopped ACP session","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]}},"required":["session"]}}}},"400":{"description":"ACP session stop error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"sessionId","required":true}]}},"/api/acp/sessions/prepared/{prepareId}/messages":{"post":{"operationId":"postApiAcpSessionsPreparedByPrepareIdMessages","summary":"Send message to a prepared ACP session","responses":{"200":{"description":"ACP message response","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]},"text":{"type":"string"},"rawEvents":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"const":"plan"},"entries":{"type":"array","items":{"type":"string"}},"rawText":{"type":"string"}},"required":["type","entries","rawText"]},{"type":"object","properties":{"type":{"const":"diff"},"path":{"type":"string"},"oldText":{"anyOf":[{"type":"string"},{"type":"null"}]},"newText":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawText":{"type":"string"}},"required":["type","path","oldText","newText","rawText"]},{"type":"object","properties":{"type":{"const":"terminal"},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","terminalId","text","rawText"]},{"type":"object","properties":{"type":{"const":"reasoning"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolCall"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"inputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","inputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolResult"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"outputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","outputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolError"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"errorText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","errorText","rawText"]},{"type":"object","properties":{"type":{"const":"streamPart"},"partType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","partType","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolInput"},"streamId":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","streamId","text","rawText"]}]}},"assistantSegmentMessages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"role":{"anyOf":[{"const":"user"},{"const":"assistant"}]},"kind":{"anyOf":[{"const":"user"},{"const":"legacy_assistant_turn"},{"const":"assistant_text"},{"const":"reasoning"},{"const":"tool_input"},{"const":"tool_call"},{"const":"tool_result"},{"const":"tool_error"},{"const":"tool_output_denied"},{"const":"tool_approval_request"},{"const":"source"},{"const":"file"},{"const":"stream_start"},{"const":"stream_finish"},{"const":"step_start"},{"const":"step_finish"},{"const":"abort"},{"const":"stream_error"},{"const":"raw_meta"},{"const":"x-error"}]},"rawJson":{"anyOf":[{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"user"},"createdAt":{"type":"string"},"type":{"const":"user"},"text":{"type":"string"},"metadata":{},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"const":"image"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]},"attachmentId":{"type":"string"},"name":{"type":"string"},"sizeInBytes":{"type":"number"}},"required":["type","source"]}},"promptPlan":{}},"required":["schemaVersion","role","createdAt","type","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"assistant_text"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"reasoning"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_input"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"toolName":{"anyOf":[{"type":"string"},{"type":"null"}]},"providerExecuted":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"dynamic":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"legacy_assistant_turn"},"text":{"type":"string"},"rawEvents":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"const":"plan"},"entries":{"type":"array","items":{"type":"string"}},"rawText":{"type":"string"}},"required":["type","entries","rawText"]},{"type":"object","properties":{"type":{"const":"diff"},"path":{"type":"string"},"oldText":{"anyOf":[{"type":"string"},{"type":"null"}]},"newText":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawText":{"type":"string"}},"required":["type","path","oldText","newText","rawText"]},{"type":"object","properties":{"type":{"const":"terminal"},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","terminalId","text","rawText"]},{"type":"object","properties":{"type":{"const":"reasoning"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolCall"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"inputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","inputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolResult"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"outputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","outputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolError"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"errorText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","errorText","rawText"]},{"type":"object","properties":{"type":{"const":"streamPart"},"partType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","partType","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolInput"},"streamId":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","streamId","text","rawText"]}]}},"metadata":{}},"required":["schemaVersion","role","createdAt","type","text","rawEvents"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_call"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_result"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_error"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_output_denied"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_approval_request"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"source"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"file"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_start"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_finish"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"step_start"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"step_finish"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"abort"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_error"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"raw_meta"},"text":{"type":"string"},"part":{}},"required":["schemaVersion","role","createdAt","type","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"type":{"const":"x-error"},"role":{"const":"assistant"},"sourceKind":{"type":"string"},"errorMessage":{"type":"string"},"rawJsonText":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}},"createdAt":{"type":"string"}},"required":["schemaVersion","type","role","sourceKind","errorMessage","rawJsonText","issues","createdAt"]}]},"textForSearch":{"type":"string"},"text":{"type":"string"},"rawEvents":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"const":"plan"},"entries":{"type":"array","items":{"type":"string"}},"rawText":{"type":"string"}},"required":["type","entries","rawText"]},{"type":"object","properties":{"type":{"const":"diff"},"path":{"type":"string"},"oldText":{"anyOf":[{"type":"string"},{"type":"null"}]},"newText":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawText":{"type":"string"}},"required":["type","path","oldText","newText","rawText"]},{"type":"object","properties":{"type":{"const":"terminal"},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","terminalId","text","rawText"]},{"type":"object","properties":{"type":{"const":"reasoning"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolCall"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"inputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","inputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolResult"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"outputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","outputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolError"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"errorText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","errorText","rawText"]},{"type":"object","properties":{"type":{"const":"streamPart"},"partType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","partType","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolInput"},"streamId":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","streamId","text","rawText"]}]}},"createdAt":{"type":"string"},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"streamPartId":{"anyOf":[{"type":"string"},{"type":"null"}]},"metadataJson":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","role","rawJson","text","rawEvents","createdAt"]}}},"required":["session","text","rawEvents"]}}}},"400":{"description":"ACP prepared message error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string"},"attachmentIds":{"type":"array","items":{"type":"string"}},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["prompt"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"prepareId","required":true}]}},"/api/acp/sessions/{sessionId}":{"patch":{"operationId":"patchApiAcpSessionsBySessionId","summary":"Update ACP session","responses":{"200":{"description":"Updated ACP session","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]}},"required":["session"]}}}},"400":{"description":"ACP session update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":[]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"sessionId","required":true}]},"delete":{"operationId":"deleteApiAcpSessionsBySessionId","summary":"Delete ACP session","responses":{"200":{"description":"Delete result","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"sessionId","required":true}]}},"/api/acp/sessions/{sessionId}/config-options":{"patch":{"operationId":"patchApiAcpSessionsBySessionIdConfigOptions","summary":"Update ACP session generic config option","responses":{"200":{"description":"Updated ACP session","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]}},"required":["session"]}}}},"400":{"description":"ACP session config option update error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"configId":{"type":"string"},"value":{"type":"string"}},"required":["configId","value"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"sessionId","required":true}]}},"/api/acp/sessions/{sessionId}/messages":{"get":{"operationId":"getApiAcpSessionsBySessionIdMessages","summary":"List ACP session messages","responses":{"200":{"description":"ACP session messages","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"role":{"anyOf":[{"const":"user"},{"const":"assistant"}]},"kind":{"anyOf":[{"const":"user"},{"const":"legacy_assistant_turn"},{"const":"assistant_text"},{"const":"reasoning"},{"const":"tool_input"},{"const":"tool_call"},{"const":"tool_result"},{"const":"tool_error"},{"const":"tool_output_denied"},{"const":"tool_approval_request"},{"const":"source"},{"const":"file"},{"const":"stream_start"},{"const":"stream_finish"},{"const":"step_start"},{"const":"step_finish"},{"const":"abort"},{"const":"stream_error"},{"const":"raw_meta"},{"const":"x-error"}]},"rawJson":{"anyOf":[{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"user"},"createdAt":{"type":"string"},"type":{"const":"user"},"text":{"type":"string"},"metadata":{},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"const":"image"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]},"attachmentId":{"type":"string"},"name":{"type":"string"},"sizeInBytes":{"type":"number"}},"required":["type","source"]}},"promptPlan":{}},"required":["schemaVersion","role","createdAt","type","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"assistant_text"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"reasoning"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_input"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"toolName":{"anyOf":[{"type":"string"},{"type":"null"}]},"providerExecuted":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"dynamic":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"legacy_assistant_turn"},"text":{"type":"string"},"rawEvents":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"const":"plan"},"entries":{"type":"array","items":{"type":"string"}},"rawText":{"type":"string"}},"required":["type","entries","rawText"]},{"type":"object","properties":{"type":{"const":"diff"},"path":{"type":"string"},"oldText":{"anyOf":[{"type":"string"},{"type":"null"}]},"newText":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawText":{"type":"string"}},"required":["type","path","oldText","newText","rawText"]},{"type":"object","properties":{"type":{"const":"terminal"},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","terminalId","text","rawText"]},{"type":"object","properties":{"type":{"const":"reasoning"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolCall"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"inputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","inputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolResult"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"outputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","outputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolError"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"errorText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","errorText","rawText"]},{"type":"object","properties":{"type":{"const":"streamPart"},"partType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","partType","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolInput"},"streamId":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","streamId","text","rawText"]}]}},"metadata":{}},"required":["schemaVersion","role","createdAt","type","text","rawEvents"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_call"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_result"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_error"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_output_denied"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_approval_request"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"source"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"file"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_start"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_finish"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"step_start"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"step_finish"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"abort"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_error"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"raw_meta"},"text":{"type":"string"},"part":{}},"required":["schemaVersion","role","createdAt","type","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"type":{"const":"x-error"},"role":{"const":"assistant"},"sourceKind":{"type":"string"},"errorMessage":{"type":"string"},"rawJsonText":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}},"createdAt":{"type":"string"}},"required":["schemaVersion","type","role","sourceKind","errorMessage","rawJsonText","issues","createdAt"]}]},"textForSearch":{"type":"string"},"text":{"type":"string"},"rawEvents":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"const":"plan"},"entries":{"type":"array","items":{"type":"string"}},"rawText":{"type":"string"}},"required":["type","entries","rawText"]},{"type":"object","properties":{"type":{"const":"diff"},"path":{"type":"string"},"oldText":{"anyOf":[{"type":"string"},{"type":"null"}]},"newText":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawText":{"type":"string"}},"required":["type","path","oldText","newText","rawText"]},{"type":"object","properties":{"type":{"const":"terminal"},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","terminalId","text","rawText"]},{"type":"object","properties":{"type":{"const":"reasoning"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolCall"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"inputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","inputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolResult"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"outputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","outputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolError"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"errorText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","errorText","rawText"]},{"type":"object","properties":{"type":{"const":"streamPart"},"partType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","partType","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolInput"},"streamId":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","streamId","text","rawText"]}]}},"createdAt":{"type":"string"},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"streamPartId":{"anyOf":[{"type":"string"},{"type":"null"}]},"metadataJson":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","role","rawJson","text","rawEvents","createdAt"]}},"pageInfo":{"type":"object","properties":{"hasMoreBefore":{"type":"boolean"},"beforeCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["hasMoreBefore","beforeCursor"]},"meta":{"type":"object","properties":{"totalMessageCount":{"type":"number"}},"required":["totalMessageCount"]}},"required":["messages","pageInfo","meta"]}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"sessionId","required":true}]},"post":{"operationId":"postApiAcpSessionsBySessionIdMessages","summary":"Send message to ACP session","responses":{"200":{"description":"ACP message response","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"sessionId":{"type":"string"},"origin":{"anyOf":[{"const":"new"},{"const":"loaded"}]},"status":{"anyOf":[{"const":"running"},{"const":"paused"},{"const":"inactive"}]},"projectId":{"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"anyOf":[{"type":"string"},{"type":"null"}]},"command":{"type":"string"},"args":{"type":"array","items":{"type":"string"}},"cwd":{"type":"string"},"createdAt":{"type":"string"},"isActive":{"type":"boolean"},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"firstUserMessagePreview":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentModelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"availableModes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"availableModels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","description"]}},"configOptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentValue":{"type":"string"},"values":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["value","name","description"]}}},"required":["id","name","category","description","currentValue","values"]}}},"required":["sessionId","origin","status","projectId","presetId","command","args","cwd","createdAt","isActive","title","firstUserMessagePreview","updatedAt","currentModeId","currentModelId","availableModes","availableModels","configOptions"]},"text":{"type":"string"},"rawEvents":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"const":"plan"},"entries":{"type":"array","items":{"type":"string"}},"rawText":{"type":"string"}},"required":["type","entries","rawText"]},{"type":"object","properties":{"type":{"const":"diff"},"path":{"type":"string"},"oldText":{"anyOf":[{"type":"string"},{"type":"null"}]},"newText":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawText":{"type":"string"}},"required":["type","path","oldText","newText","rawText"]},{"type":"object","properties":{"type":{"const":"terminal"},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","terminalId","text","rawText"]},{"type":"object","properties":{"type":{"const":"reasoning"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolCall"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"inputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","inputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolResult"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"outputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","outputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolError"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"errorText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","errorText","rawText"]},{"type":"object","properties":{"type":{"const":"streamPart"},"partType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","partType","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolInput"},"streamId":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","streamId","text","rawText"]}]}},"assistantSegmentMessages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"role":{"anyOf":[{"const":"user"},{"const":"assistant"}]},"kind":{"anyOf":[{"const":"user"},{"const":"legacy_assistant_turn"},{"const":"assistant_text"},{"const":"reasoning"},{"const":"tool_input"},{"const":"tool_call"},{"const":"tool_result"},{"const":"tool_error"},{"const":"tool_output_denied"},{"const":"tool_approval_request"},{"const":"source"},{"const":"file"},{"const":"stream_start"},{"const":"stream_finish"},{"const":"step_start"},{"const":"step_finish"},{"const":"abort"},{"const":"stream_error"},{"const":"raw_meta"},{"const":"x-error"}]},"rawJson":{"anyOf":[{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"user"},"createdAt":{"type":"string"},"type":{"const":"user"},"text":{"type":"string"},"metadata":{},"attachments":{"type":"array","items":{"type":"object","properties":{"type":{"const":"image"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]},"attachmentId":{"type":"string"},"name":{"type":"string"},"sizeInBytes":{"type":"number"}},"required":["type","source"]}},"promptPlan":{}},"required":["schemaVersion","role","createdAt","type","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"assistant_text"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"reasoning"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_input"},"streamPartId":{"type":"string"},"providerStreamId":{"type":"string"},"text":{"type":"string"},"toolName":{"anyOf":[{"type":"string"},{"type":"null"}]},"providerExecuted":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"dynamic":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"parts":{"type":"object","properties":{"start":{},"end":{}},"required":[]},"deltaCount":{"type":"number"},"endedAt":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","streamPartId","providerStreamId","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"legacy_assistant_turn"},"text":{"type":"string"},"rawEvents":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"const":"plan"},"entries":{"type":"array","items":{"type":"string"}},"rawText":{"type":"string"}},"required":["type","entries","rawText"]},{"type":"object","properties":{"type":{"const":"diff"},"path":{"type":"string"},"oldText":{"anyOf":[{"type":"string"},{"type":"null"}]},"newText":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawText":{"type":"string"}},"required":["type","path","oldText","newText","rawText"]},{"type":"object","properties":{"type":{"const":"terminal"},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","terminalId","text","rawText"]},{"type":"object","properties":{"type":{"const":"reasoning"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolCall"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"inputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","inputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolResult"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"outputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","outputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolError"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"errorText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","errorText","rawText"]},{"type":"object","properties":{"type":{"const":"streamPart"},"partType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","partType","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolInput"},"streamId":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","streamId","text","rawText"]}]}},"metadata":{}},"required":["schemaVersion","role","createdAt","type","text","rawEvents"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_call"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_result"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_error"},"part":{},"text":{"type":"string"},"toolCallId":{"type":"string"},"toolName":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part","toolCallId","toolName"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_output_denied"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"tool_approval_request"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"source"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"file"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_start"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_finish"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"step_start"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"step_finish"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"abort"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"stream_error"},"part":{},"text":{"type":"string"}},"required":["schemaVersion","role","createdAt","type","part"]},{"type":"object","properties":{"schemaVersion":{"const":1},"role":{"const":"assistant"},"createdAt":{"type":"string"},"type":{"const":"raw_meta"},"text":{"type":"string"},"part":{}},"required":["schemaVersion","role","createdAt","type","text"]},{"type":"object","properties":{"schemaVersion":{"const":1},"type":{"const":"x-error"},"role":{"const":"assistant"},"sourceKind":{"type":"string"},"errorMessage":{"type":"string"},"rawJsonText":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}},"createdAt":{"type":"string"}},"required":["schemaVersion","type","role","sourceKind","errorMessage","rawJsonText","issues","createdAt"]}]},"textForSearch":{"type":"string"},"text":{"type":"string"},"rawEvents":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"const":"plan"},"entries":{"type":"array","items":{"type":"string"}},"rawText":{"type":"string"}},"required":["type","entries","rawText"]},{"type":"object","properties":{"type":{"const":"diff"},"path":{"type":"string"},"oldText":{"anyOf":[{"type":"string"},{"type":"null"}]},"newText":{"anyOf":[{"type":"string"},{"type":"null"}]},"rawText":{"type":"string"}},"required":["type","path","oldText","newText","rawText"]},{"type":"object","properties":{"type":{"const":"terminal"},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","terminalId","text","rawText"]},{"type":"object","properties":{"type":{"const":"reasoning"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolCall"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"inputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","inputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolResult"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"outputText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","outputText","rawText"]},{"type":"object","properties":{"type":{"const":"toolError"},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"errorText":{"type":"string"},"rawText":{"type":"string"}},"required":["type","toolCallId","toolName","errorText","rawText"]},{"type":"object","properties":{"type":{"const":"streamPart"},"partType":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","partType","text","rawText"]},{"type":"object","properties":{"type":{"const":"toolInput"},"streamId":{"type":"string"},"text":{"type":"string"},"rawText":{"type":"string"}},"required":["type","streamId","text","rawText"]}]}},"createdAt":{"type":"string"},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"streamPartId":{"anyOf":[{"type":"string"},{"type":"null"}]},"metadataJson":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","role","rawJson","text","rawEvents","createdAt"]}}},"required":["session","text","rawEvents"]}}}},"400":{"description":"ACP message error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string"},"attachmentIds":{"type":"array","items":{"type":"string"}},"attachments":{"type":"array","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"name":{"type":"string"},"mediaType":{"type":"string"},"sizeInBytes":{"type":"number"},"source":{"type":"object","properties":{"type":{"const":"base64"},"media_type":{"type":"string"},"data":{"type":"string"}},"required":["type","media_type","data"]}},"required":["attachmentId","name","mediaType","sizeInBytes"]}},"modelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"modeId":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["prompt"]}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"sessionId","required":true}]}}},"components":{}} \ No newline at end of file