diff --git a/.agents/skills/release/SKILL.md b/.agents/skills/release/SKILL.md new file mode 100644 index 0000000..34662dd --- /dev/null +++ b/.agents/skills/release/SKILL.md @@ -0,0 +1,140 @@ +--- +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 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. + +For a second-pass review before publishing, delegate a focused review to another agent: + +```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 +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 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. 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 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. diff --git a/package.json b/package.json index e7a7c02..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": { @@ -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/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.'); 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/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 bc897ab..b59f46f 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); @@ -1581,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) => { @@ -1604,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 af56b16..0860616 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, @@ -654,6 +654,7 @@ export const createSessionStore = ({ installAcpProviderToolResultPatch(); const runtimeSessions = new Map(); + const lastPersistedToolUpdateTextByKey = new Map(); const emitSessionUpdated = (session: SessionSummary): void => { emitAcpSse({ @@ -970,7 +971,51 @@ 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 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 => { @@ -1026,6 +1071,7 @@ export const createSessionStore = ({ type: 'user', role: 'user', text, + metadata: metadataJson === '{}' ? undefined : JSON.parse(metadataJson), attachments: [...attachments], createdAt: t, } @@ -1099,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, }); }; @@ -1193,7 +1265,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. } }, }; @@ -1680,6 +1753,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 +1781,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 +1806,7 @@ export const createSessionStore = ({ text: request.prompt, rawEvents: [], kind: 'user', + metadataJson: JSON.stringify(selectionMetadata), attachments: userAttachments, }), }); @@ -1765,12 +1866,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, @@ -1781,12 +1885,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/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..8737de4 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()), }); @@ -512,6 +513,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), @@ -1251,6 +1253,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(), @@ -1284,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 ab6e9d8..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, @@ -166,6 +161,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 +419,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}}
@@ -861,7 +861,153 @@ const SlashCommandsLoader: FC<{ return null; }; -const SESSION_MESSAGES_PAGE_SIZE = 100; +const SESSION_MESSAGES_PAGE_SIZE = 50; + +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) && + (!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<{ readonly sessionId: string; @@ -870,35 +1016,56 @@ const SessionMessagesHydrator: FC<{ fetchNextPage: () => Promise, hasNextPage: boolean, 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]); + setRemainingLoaded(false); + }, [sessionId, beforeCursor]); useLayoutEffect(() => { - onFetchNextPageReady(fetchNextPage, hasNextPage, isFetchingNextPage); - }, [fetchNextPage, hasNextPage, isFetchingNextPage, onFetchNextPageReady]); + onHydrated(sessionId, latestMessagesQuery.data.messages); + }, [latestMessagesQuery.data.messages, onHydrated, sessionId]); - return null; + const noopFetchNextPage = useCallback(async () => {}, []); + + 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<{ @@ -961,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[] @@ -987,6 +1155,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); @@ -997,13 +1166,17 @@ 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 fetchingNextPageRef = useRef(false); + const pendingPrependAnchorRef = useRef<{ + readonly messageId: string; + readonly top: number; + } | null>(null); const onAgentCatalogReady = useCallback((catalog: AgentModelCatalogResponse) => { setProbedModelCatalog(catalog); @@ -1044,6 +1217,7 @@ export const ProjectChatPage: FC<{ useEffect(() => { return () => { + speechListeningDesiredRef.current = false; speechRecognitionRef.current?.abort(); }; }, []); @@ -1672,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); }; @@ -1706,22 +1917,61 @@ export const ProjectChatPage: FC<{ ) { return current; } - /** - * 送信中に SSE 由来の再取得で、新メッセージ行が未コミットの古いスナップショット - * (本より短い行数)が届くと、ここで上書きすると履歴を全部失ったように見える。 - * 行は append-only の前提なので、短い取得ではローカルより退けない。 - */ - if (local.length > 0 && messages.length < local.length) { + 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]: messages.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) => @@ -1851,6 +2101,7 @@ export const ProjectChatPage: FC<{ } const currentRecognition = speechRecognitionRef.current; if (isListeningToSpeech) { + speechListeningDesiredRef.current = false; currentRecognition?.stop(); setIsListeningToSpeech(false); return; @@ -1863,7 +2114,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 +2127,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')); @@ -2089,7 +2357,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); } @@ -2137,12 +2404,14 @@ export const ProjectChatPage: FC<{ kind: 'user', attachments: userAttachmentsFromUploaded(attachedFiles), }); + optimisticUserMessageIdsRef.current.add(userMessage.id); const initialTranscriptKey = activeTranscriptKey; let requestAwaitingTranscriptKeys: readonly string[] = [initialTranscriptKey]; shouldStickToBottomRef.current = true; setIsChatFollowingTail(true); addAwaitingAssistantTranscriptKeys(requestAwaitingTranscriptKeys); + speechListeningDesiredRef.current = false; speechRecognitionRef.current?.stop(); setIsListeningToSpeech(false); replacePrompt(''); @@ -2236,9 +2505,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); @@ -2308,9 +2574,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); @@ -2368,11 +2631,14 @@ export const ProjectChatPage: FC<{ 'custom'); const handleFetchNextPageReady = useCallback( - (fetchNextPage: () => void, nextPageExists: boolean, nextIsFetchingNextPage: boolean) => { + ( + fetchNextPage: () => Promise, + nextPageExists: boolean, + nextIsFetchingNextPage: boolean, + ) => { fetchNextPageRef.current = fetchNextPage; setHasNextPage(nextPageExists); setIsFetchingNextPage(nextIsFetchingNextPage); - fetchingNextPageRef.current = nextIsFetchingNextPage; }, [], ); @@ -2385,6 +2651,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 +2676,25 @@ 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?.(); + useLayoutEffect(() => { + const pendingAnchor = pendingPrependAnchorRef.current; + const viewport = chatViewportElement; + if (pendingAnchor === null || viewport === null) { + return; + } + + const anchorElement = [...viewport.querySelectorAll('[data-message-id]')].find( + (element) => element.dataset['messageId'] === pendingAnchor.messageId, + ); + if (anchorElement === undefined) { + return; } - }; + + pendingPrependAnchorRef.current = null; + viewport.scrollTop += anchorElement.getBoundingClientRect().top - pendingAnchor.top; + }, [chatViewportElement, visibleTranscript.length]); const handleJumpToLatest = () => { shouldStickToBottomRef.current = true; @@ -2439,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); @@ -2476,6 +2757,7 @@ export const ProjectChatPage: FC<{
-
) : null} - {isFetchingNextPage && hasNextPage && sessionId !== null && ( + {hasNextPage && sessionId !== null ? (
- - 古いメッセージを読み込み中... + {isFetchingNextPage ? ( + + ) : null} + セッションを読み込み中...
- )} + ) : null} {visibleTranscript.map((message, index) => { const isUser = message.role === 'user'; const displayEvents = filterDisplayableRawEvents(message.rawEvents); @@ -2656,10 +2942,11 @@ export const ProjectChatPage: FC<{ return (
- +
{shouldShowScrollBanner ? (
); 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; +}; 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) => ( diff --git a/src/web/components/app-header.tsx b/src/web/components/app-header.tsx index 8c211aa..07babac 100644 --- a/src/web/components/app-header.tsx +++ b/src/web/components/app-header.tsx @@ -21,12 +21,28 @@ import { useNotificationCenter, } from '@/web/pwa/notification-center'; -const currentProjectIdFromPath = (pathname: string): string | null => { +const currentProjectIdFromPath = (pathname: string, search: unknown): string | null => { const match = /^\/projects\/([^/]+)/.exec(pathname); - return match?.[1] ?? null; + if (match !== null && match[1] !== undefined) { + return match[1]; + } + + if (pathname === '/settings' || pathname === '/information') { + if ( + typeof search === 'object' && + search !== null && + 'projectId' in search && + typeof search['projectId'] === 'string' && + search['projectId'].length > 0 + ) { + return search['projectId']; + } + } + + return null; }; -type ProjectPathTarget = 'chat' | 'routines' | 'sessions' | 'settings'; +type ProjectPathTarget = 'chat' | 'routines' | 'sessions' | 'settings' | 'information'; const projectPathTargetFromPath = (pathname: string): ProjectPathTarget => { if (pathname.endsWith('/sessions')) { @@ -35,9 +51,12 @@ const projectPathTargetFromPath = (pathname: string): ProjectPathTarget => { if (pathname.endsWith('/routines')) { return 'routines'; } - if (pathname.endsWith('/settings')) { + if (pathname === '/settings' || pathname.endsWith('/settings')) { return 'settings'; } + if (pathname === '/information' || pathname.endsWith('/information')) { + return 'information'; + } return 'chat'; }; @@ -174,7 +193,7 @@ export const AppHeader: FC<{ const { t } = useTranslation(); const location = useLocation(); const navigate = useNavigate(); - const currentProjectId = currentProjectIdFromPath(location.pathname); + const currentProjectId = currentProjectIdFromPath(location.pathname, location.search); const { data } = useSuspenseQuery({ queryKey: projectsQueryKey, queryFn: fetchProjects, @@ -194,7 +213,9 @@ export const AppHeader: FC<{ > - Remote Agent + + Remote Agent + {currentProjectId === null ? (

{t('common.appName')}

@@ -207,17 +228,21 @@ export const AppHeader: FC<{ if (nextProjectId === null || nextProjectId === currentProjectId) { return; } - void navigate({ - to: - projectPathTarget === 'sessions' - ? '/projects/$projectId/sessions' - : projectPathTarget === 'routines' - ? '/projects/$projectId/routines' - : projectPathTarget === 'settings' - ? '/projects/$projectId/settings' + if (projectPathTarget === 'settings') { + void navigate({ to: '/settings', search: { projectId: nextProjectId } }); + } else if (projectPathTarget === 'information') { + void navigate({ to: '/information', search: { projectId: nextProjectId } }); + } else { + void navigate({ + to: + projectPathTarget === 'sessions' + ? '/projects/$projectId/sessions' + : projectPathTarget === 'routines' + ? '/projects/$projectId/routines' : '/projects/$projectId', - params: { projectId: nextProjectId }, - }); + params: { projectId: nextProjectId }, + }); + } }} value={currentProjectId} > diff --git a/src/web/components/app-menu.tsx b/src/web/components/app-menu.tsx index fe6597c..39c5162 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 { Link, useLocation, useRouterState } from '@tanstack/react-router'; +import { FolderKanban, Info, Menu, PanelLeftClose, Settings, X } from 'lucide-react'; import { createContext, useCallback, @@ -36,26 +36,51 @@ 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')} +
); }; @@ -64,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 ( @@ -106,7 +139,13 @@ const AppMenuBody: FC<{ )}
- {hasCustomContent ? null : } + {hasCustomContent ? null : isProjectScopedRoute ? ( +
+

{t('common.loading')}

+
+ ) : ( + + )}
); @@ -153,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; @@ -214,6 +267,7 @@ export const AppMenuLayout: FC<{ readonly children: ReactNode }> = ({ children } closeMobileMenu={closeMobileMenu} hasCustomContent={hasCustomContent} isMobile={false} + isProjectScopedRoute={isProjectScopedRoute} onCollapse={() => { setDesktopExpanded(false); }} @@ -254,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/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..da771c3 100644 --- a/src/web/lib/api/useAcpSseCacheSync.ts +++ b/src/web/lib/api/useAcpSseCacheSync.ts @@ -31,36 +31,44 @@ 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), + ); + + queryClient.setQueriesData( + { queryKey: sessionMessagesInfiniteQueryKey(event.sessionId) }, + (current) => { + if (current === undefined || !Array.isArray(current.messages)) { + return current; + } + return applySessionMessageEventToMessages(current, event); + }, ); - // infinite query の先頭ページ(最新メッセージ)にも適用 queryClient.setQueriesData>( { queryKey: sessionMessagesInfiniteQueryKey(event.sessionId) }, (current) => { - if (current === undefined) { + if (current === undefined || !Array.isArray(current.pages)) { return current; } const firstPage = current.pages[0]; if (firstPage === undefined) { return current; } - const updatedFirst = applySessionStreamDeltaToMessages(firstPage, event); + const updatedFirst = applySessionMessageEventToMessages(firstPage, event); if (updatedFirst === firstPage) { return current; } @@ -77,7 +85,6 @@ const applyTextDelta = (queryClient: QueryClient, event: AcpSseEvent): boolean = const mergeEventToPending = ( pending: { needSessionsList: boolean; - messageSessionIds: Set; pausedSessionIds: Set; removedSessionIds: Set; catalogUpdates: Set; @@ -101,11 +108,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 +253,6 @@ const flushPending = async ( queryClient: QueryClient, pending: { needSessionsList: boolean; - messageSessionIds: Set; pausedSessionIds: Set; removedSessionIds: Set; catalogUpdates: Set; @@ -262,14 +263,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 +298,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 +346,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 +372,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 +387,7 @@ export const useAcpSseCacheSync = (): void => { return; } dispatchAcpSseBrowserEvent(event); - if (applyTextDelta(queryClient, event)) { + if (applyMessageEvent(queryClient, event)) { return; } mergeEventToPending(pending, event, knownStatuses, statusFromCache); @@ -414,7 +404,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 || diff --git a/src/web/lib/i18n/resources.ts b/src/web/lib/i18n/resources.ts index e741140..d1ea4a1 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: { @@ -47,9 +50,10 @@ export const resources = { menu: { title: 'MENU', projects: 'プロジェクト', - sessions: 'セッションリスト', - routines: 'Routines', + sessions: 'セッション', + routines: 'ルーティン', settings: '設定', + information: 'About', openMenu: 'メニューを開く', closeMenu: 'メニューを閉じる', collapseMenu: 'メニューを折りたたむ', @@ -58,6 +62,16 @@ export const resources = { noSessionsYet: 'まだセッションがありません。', viewing: '表示中', }, + information: { + title: 'About Remote Agent', + remoteAgent: 'Remote Agent', + version: 'バージョン', + connectionStatus: '疎通ステータス', + apiReachable: 'API と疎通できています。', + workingDirectory: 'Working directory', + projectsFilePath: 'Projects file', + agentPresets: 'Agent presets', + }, notifications: { title: '通知', ariaLabel_unread: '通知、未読{{count}}件', @@ -350,6 +364,9 @@ export const resources = { custom: 'Custom', enabled: 'Enabled', disabled: 'Disabled', + enable: 'Enable', + disable: 'Disable', + saveFailed: 'Failed to save.', never: 'Never', }, settings: { @@ -373,6 +390,7 @@ export const resources = { sessions: 'Sessions', routines: 'Routines', settings: 'Settings', + information: 'About', openMenu: 'Open menu', closeMenu: 'Close menu', collapseMenu: 'Collapse menu', @@ -381,6 +399,16 @@ export const resources = { noSessionsYet: 'No sessions yet.', viewing: 'Viewing', }, + information: { + title: 'About Remote Agent', + remoteAgent: 'Remote Agent', + version: 'Version', + connectionStatus: 'Connection status', + apiReachable: 'API is reachable.', + workingDirectory: 'Working directory', + projectsFilePath: 'Projects file', + agentPresets: 'Agent presets', + }, notifications: { title: 'Notifications', ariaLabel_unread: 'Notifications, {{count}} unread', diff --git a/src/web/routeTree.gen.ts b/src/web/routeTree.gen.ts index e5e230e..c1b3de6 100644 --- a/src/web/routeTree.gen.ts +++ b/src/web/routeTree.gen.ts @@ -11,6 +11,7 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as SettingsRouteImport } from './routes/settings' import { Route as ProjectsRouteImport } from './routes/projects' +import { Route as InformationRouteImport } from './routes/information' import { Route as IndexRouteImport } from './routes/index' import { Route as ProjectsIndexRouteImport } from './routes/projects/index' import { Route as ProjectsProjectIdRouteImport } from './routes/projects/$projectId' @@ -29,6 +30,11 @@ const ProjectsRoute = ProjectsRouteImport.update({ path: '/projects', getParentRoute: () => rootRouteImport, } as any) +const InformationRoute = InformationRouteImport.update({ + id: '/information', + path: '/information', + getParentRoute: () => rootRouteImport, +} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', @@ -70,6 +76,7 @@ const ProjectsProjectIdRoutinesRoute = export interface FileRoutesByFullPath { '/': typeof IndexRoute + '/information': typeof InformationRoute '/projects': typeof ProjectsRouteWithChildren '/settings': typeof SettingsRoute '/projects/$projectId': typeof ProjectsProjectIdRouteWithChildren @@ -81,6 +88,7 @@ export interface FileRoutesByFullPath { } export interface FileRoutesByTo { '/': typeof IndexRoute + '/information': typeof InformationRoute '/settings': typeof SettingsRoute '/projects': typeof ProjectsIndexRoute '/projects/$projectId/routines': typeof ProjectsProjectIdRoutinesRoute @@ -91,6 +99,7 @@ export interface FileRoutesByTo { export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute + '/information': typeof InformationRoute '/projects': typeof ProjectsRouteWithChildren '/settings': typeof SettingsRoute '/projects/$projectId': typeof ProjectsProjectIdRouteWithChildren @@ -104,6 +113,7 @@ export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' + | '/information' | '/projects' | '/settings' | '/projects/$projectId' @@ -115,6 +125,7 @@ export interface FileRouteTypes { fileRoutesByTo: FileRoutesByTo to: | '/' + | '/information' | '/settings' | '/projects' | '/projects/$projectId/routines' @@ -124,6 +135,7 @@ export interface FileRouteTypes { id: | '__root__' | '/' + | '/information' | '/projects' | '/settings' | '/projects/$projectId' @@ -136,6 +148,7 @@ export interface FileRouteTypes { } export interface RootRouteChildren { IndexRoute: typeof IndexRoute + InformationRoute: typeof InformationRoute ProjectsRoute: typeof ProjectsRouteWithChildren SettingsRoute: typeof SettingsRoute } @@ -156,6 +169,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ProjectsRouteImport parentRoute: typeof rootRouteImport } + '/information': { + id: '/information' + path: '/information' + fullPath: '/information' + preLoaderRoute: typeof InformationRouteImport + parentRoute: typeof rootRouteImport + } '/': { id: '/' path: '/' @@ -241,6 +261,7 @@ const ProjectsRouteWithChildren = ProjectsRoute._addFileChildren( const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, + InformationRoute: InformationRoute, ProjectsRoute: ProjectsRouteWithChildren, SettingsRoute: SettingsRoute, } diff --git a/src/web/routes/information.tsx b/src/web/routes/information.tsx new file mode 100644 index 0000000..88dd2bd --- /dev/null +++ b/src/web/routes/information.tsx @@ -0,0 +1,123 @@ +import { useSuspenseQuery } from '@tanstack/react-query'; +import { createFileRoute } from '@tanstack/react-router'; +import { CheckCircle2, Info, Server } from 'lucide-react'; +import { Suspense, type FC } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { ProjectMenuContent } from '../app/projects/$projectId/project-menu-content.tsx'; +import { + agentProvidersQueryKey, + appInfoQueryKey, + projectQueryKey, + sessionsQueryKey, +} from '../app/projects/$projectId/queries.ts'; +import { useLoadSessionDialog } from '../app/projects/$projectId/use-load-session-dialog.tsx'; +import { fetchAgentProviders, fetchAppInfo, fetchProject, fetchSessions } from '../lib/api/acp.ts'; + +type InformationSearch = { + readonly projectId?: string; +}; + +const ProjectInformationMenu: FC<{ readonly projectId: string }> = ({ projectId }) => { + const { data: projectData } = useSuspenseQuery({ + queryKey: projectQueryKey(projectId), + queryFn: () => fetchProject(projectId), + }); + const { data: sessionsData } = useSuspenseQuery({ + queryKey: sessionsQueryKey, + queryFn: fetchSessions, + }); + const { data: providerData } = useSuspenseQuery({ + queryKey: agentProvidersQueryKey, + queryFn: fetchAgentProviders, + }); + const projectSessions = sessionsData.sessions.filter( + (session) => session.projectId === projectId, + ); + const { canLoadSessions, dialog, openLoadSessionDialog } = useLoadSessionDialog({ + projectId, + providers: providerData.providers, + workingDirectory: projectData.project.workingDirectory, + }); + + return ( + <> + + {dialog} + + ); +}; + +const InformationRoute: FC = () => { + const { t } = useTranslation(); + const search = Route.useSearch(); + const { data: appInfo } = useSuspenseQuery({ + queryKey: appInfoQueryKey, + queryFn: fetchAppInfo, + }); + + return ( +
+ {search.projectId === undefined ? null : ( + + + + )} +
+
+
+ + {t('information.title')} +
+

{appInfo.appName}

+
+ +
+
+
+ +
+

{t('information.connectionStatus')}

+

{t('information.apiReachable')}

+
+
+
+ +
+
+ +

{t('information.remoteAgent')}

+
+
+
{t('information.version')}
+
{appInfo.version}
+
{t('information.workingDirectory')}
+
{appInfo.workingDirectory}
+
{t('information.projectsFilePath')}
+
{appInfo.projectsFilePath}
+
{t('information.agentPresets')}
+
{appInfo.agentPresets.length}
+
+
+
+
+
+ ); +}; + +export const Route = createFileRoute('/information')({ + component: InformationRoute, + validateSearch: (search: Record): InformationSearch => { + const projectId = search['projectId']; + return { + projectId: typeof projectId === 'string' && projectId.length > 0 ? projectId : undefined, + }; + }, +}); diff --git a/src/web/routes/projects/$projectId/settings.tsx b/src/web/routes/projects/$projectId/settings.tsx index aed74a7..0d53f59 100644 --- a/src/web/routes/projects/$projectId/settings.tsx +++ b/src/web/routes/projects/$projectId/settings.tsx @@ -1,15 +1,9 @@ -import { createFileRoute } from '@tanstack/react-router'; -import { Suspense } from 'react'; +import { createFileRoute, Navigate } from '@tanstack/react-router'; +import { type FC } from 'react'; -import { ProjectSettingsPage } from '../../../app/projects/$projectId/project-settings-page.tsx'; - -const ProjectSettingsRoute = () => { +const ProjectSettingsRoute: FC = () => { const { projectId } = Route.useParams(); - return ( - - - - ); + return ; }; export const Route = createFileRoute('/projects/$projectId/settings')({ diff --git a/src/web/routes/settings.tsx b/src/web/routes/settings.tsx index 6267931..f0182d6 100644 --- a/src/web/routes/settings.tsx +++ b/src/web/routes/settings.tsx @@ -1,11 +1,25 @@ import { createFileRoute } from '@tanstack/react-router'; -import { type FC } from 'react'; +import { Suspense, type FC } from 'react'; import { useTranslation } from 'react-i18next'; +import { ProjectSettingsPage } from '../app/projects/$projectId/project-settings-page.tsx'; import { SettingsPage } from '../app/settings/settings-page.tsx'; +type SettingsSearch = { + readonly projectId?: string; +}; + const SettingsRoute: FC = () => { const { t } = useTranslation(); + const search = Route.useSearch(); + + if (search.projectId !== undefined) { + return ( + + + + ); + } return (
@@ -23,4 +37,10 @@ const SettingsRoute: FC = () => { export const Route = createFileRoute('/settings')({ component: SettingsRoute, + validateSearch: (search: Record): SettingsSearch => { + const projectId = search['projectId']; + return { + projectId: typeof projectId === 'string' && projectId.length > 0 ? projectId : undefined, + }; + }, });