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.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 ? (
{t('menu.information')}
-
-
- {t('menu.projects')}
-
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<{
>
-
+
+
+
{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