Files and commits
Read and edit the files in a chat session's sandbox, then commit and push the changes yourself. The sandbox must be running, so wake it first with GET /api/projects/{projectId}/preview?sessionId=. A write changes the working tree only, and it stays uncommitted until you commit. A push publishes the branch and leaves the pull request to Open PR. Open PR sends $kosuke:open-pr on a chat session whose Model runs on OpenAI and /kosuke:open-pr on any other. While the session has no Model, or its sandbox lacks the kosuke plugin, it sends a plain message.
| Method | Path | Description |
|---|---|---|
| GET | /api/projects/{projectId}/chat-sessions/{sessionId}/preview/files | List the files in the session's sandbox. Empty when the sandbox is not running. |
| GET | /api/projects/{projectId}/chat-sessions/{sessionId}/preview/files/content?path= | Read one file from the session's sandbox. |
| PUT | /api/projects/{projectId}/chat-sessions/{sessionId}/preview/files/content | Write one file in the session's sandbox. Refused while a turn is running, and for binary content. |
| GET | /api/projects/{projectId}/chat-sessions/{sessionId}/preview/git-state | Read whether the session's sandbox has uncommitted changes, unpushed commits, or commits ahead of its base branch. |
| POST | /api/projects/{projectId}/chat-sessions/{sessionId}/preview/git/commit | Commit the uncommitted changes in the sandbox. Refused while a turn is running. |
| POST | /api/projects/{projectId}/chat-sessions/{sessionId}/preview/git/commit-message | Draft a commit message for the uncommitted changes. Needs a running sandbox. |
| POST | /api/projects/{projectId}/chat-sessions/{sessionId}/preview/git/push | Push the session's branch to GitHub. Refused while a turn is running. |
Read the file tree
GET .../preview/files lists the project's files in the sandbox.
curl -s "https://app.kosuke.ai/api/projects/$PROJECT_ID/chat-sessions/$SESSION_ID/preview/files" \
-H "Authorization: Bearer $KOSUKE_API_KEY"
{
"data": {
"files": [
{
"name": "src",
"type": "directory",
"path": "src",
"children": [
{ "name": "page.tsx", "type": "file", "path": "src/page.tsx", "size": 1843, "lastModified": "2026-09-30T09:14:02.511Z" }
]
}
]
}
}
Paths are relative to the project root. The tree leaves out the folders node_modules, .git, .next, dist, build, coverage, .turbo, .cache, __pycache__, venv and .venv, and the files .DS_Store and Thumbs.db. When the sandbox is not running, files is empty. The CLI command is kosuke sessions files list <projectId> <sessionId>.
Read a file
GET .../preview/files/content returns one file's text.
| Parameter | What it does |
|---|---|
path | Required. The file's path, relative to the project root. |
kosuke sessions files get $PROJECT_ID $SESSION_ID --path src/page.tsx
The answer is { "data": { "content": "..." } }. A path that does not exist, or that leads outside the project, answers 404.
Write a file
PUT .../preview/files/content replaces the content of a file that exists. To add a new file, ask the agent in a message.
| Field | Type | What it does |
|---|---|---|
path | string, up to 2,048 characters | Required. The file's path, relative to the project root. |
content | string, up to 1,048,576 characters | Required. The whole new content. Text only. |
clientId | uuid | Required. A UUID you generate, for example with uuidgen. |
curl -s -X PUT "https://app.kosuke.ai/api/projects/$PROJECT_ID/chat-sessions/$SESSION_ID/preview/files/content" \
-H "Authorization: Bearer $KOSUKE_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"path\": \"README.md\", \"content\": \"# Storefront\\n\", \"clientId\": \"$(uuidgen)\"}"
{ "data": { "path": "README.md", "updatedAt": "2026-09-30T09:31:47.220Z" } }
Members with the session open see the change at once. The change is not committed. A write while a turn runs answers 409 with JOB_ACTIVE, and binary content answers 400. The CLI command is kosuke sessions files write <projectId> <sessionId> --path <path> --content <text> --client-id <uuid>.
Read the git state
GET .../preview/git-state says what is left to commit and push.
{
"data": {
"available": true,
"hasUncommittedChanges": true,
"hasUnpushedCommits": false,
"hasCommitsAheadOfBase": true
}
}
| Field | What it means |
|---|---|
available | false when the sandbox is not running. The other fields are then false too. |
hasUncommittedChanges | The working tree has changes nobody committed. |
hasUnpushedCommits | The branch has commits that are not on GitHub yet. |
hasCommitsAheadOfBase | The branch has commits its pull request's base branch does not have, or the Target Branch before a pull request is open. |
Reading the git state never wakes a sandbox. The CLI command is kosuke sessions git state <projectId> <sessionId>.
Commit
POST .../preview/git/commit stages every change in the working tree and commits it. It wakes a paused sandbox.
| Field | Type | What it does |
|---|---|---|
message | string, 1 to 2,000 characters | Required. The commit message. |
curl -s -X POST "https://app.kosuke.ai/api/projects/$PROJECT_ID/chat-sessions/$SESSION_ID/preview/git/commit" \
-H "Authorization: Bearer $KOSUKE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"message": "docs: rename the project in the README"}'
The answer is { "data": { "sha": "9f3c1e7…" } }. The commit's author is you when you have a GitHub Connection, and Kosuke App otherwise. The commit stays in the sandbox until you push. The CLI command is kosuke sessions git commit <projectId> <sessionId> --message <text>.
Draft a commit message
POST .../preview/git/commit-message takes no body. It returns { "data": { "message": "..." } }, a message written from the uncommitted changes. Nothing is committed. It needs a running sandbox. The CLI command is kosuke sessions git commit-message <projectId> <sessionId>.
Push
POST .../preview/git/push takes no body. It pushes the session's branch to GitHub and answers { "data": { "ok": true } }. It wakes a paused sandbox.
A push opens no pull request. To open one, use Open PR: send $kosuke:open-pr on a chat session whose Model runs on OpenAI and /kosuke:open-pr on any other, as Pull requests and sync shows. When the session already has a pull request, the push adds your commits to it. The CLI command is kosuke sessions git push <projectId> <sessionId>.
Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_ERROR | A field is missing, too long, or clientId is not a UUID. |
| 400 | BAD_REQUEST | A read has no path, the sandbox is not running for a read, a write or a draft message, the session has no sandbox for a commit or a push, the project has no GitHub access for a push, the content is binary, the write failed, or git refused the commit or the push. The message says which. |
| 402 | CREDIT_LIMIT_REACHED | The workspace is blocked on billing. |
| 404 | NOT_FOUND | The project, session or file does not exist. |
| 409 | JOB_ACTIVE | A turn is running. Write, commit or push again once it ends. |
| 503 | SERVICE_UNAVAILABLE | The sandbox could not list the files, read the file or read the git state. Retry. |
Responses and errors has the full table.