Bridge work to unblock whalescale-desktop's Settings/Composer/Archived-chats flows without requiring a daemon recompile per dev-port or client-side aggregation. #561 / whalescale#255 — CORS allow-list configurable * Add `[runtime_api] cors_origins` config field, `--cors-origin URL` (repeatable) flag on `deepseek serve --http`, and `DEEPSEEK_CORS_ORIGINS` env var. User entries stack on top of the built-in defaults (localhost:3000, localhost:1420, tauri://localhost). Resolution preserves first-seen order and drops empty/duplicate values; invalid HeaderValues log a warning and are skipped. * Refactor `cors_layer()` to read merged origins from `RuntimeApiState`. #562 / whalescale#256 — `PATCH /v1/threads/{id}` accepts the full editable field set * Extend `UpdateThreadRequest` with `allow_shell`, `trust_mode`, `auto_approve`, `model`, `mode`, `title`, `system_prompt`. Each is optional; missing means no change. Empty-string clears `title`/ `system_prompt`. Empty `model`/`mode` rejected with 400. * Add `title: Option<String>` to `ThreadRecord` (additive, no schema bump per documented criteria — old readers ignore the field without misinterpretation). `list_threads_summary` now returns the user-set title when present, falling back to the derived input-summary title. * `thread.updated` event payload now carries a `changes` map with only the fields that actually changed. #563 / whalescale#260 — list-archived-only filter * New `archived_only=true` query param on `GET /v1/threads` and `GET /v1/threads/summary`. Backed by a new `ThreadListFilter` enum (`ActiveOnly` | `IncludeArchived` | `ArchivedOnly`). `archived_only` takes precedence over `include_archived`. Default behavior unchanged. #564 / whalescale#261 — `GET /v1/usage` aggregation * New `RuntimeThreadManager::aggregate_usage` walks all threads/turns, filters by inclusive `since`/`until` RFC 3339 bounds, accumulates token totals + cost (via `pricing::calculate_turn_cost_from_usage`), and groups by `day` (default), `model`, `provider`, or `thread`. * New `GET /v1/usage` route. `since`/`until`/`group_by` query params, `since > until` and unknown `group_by` rejected with 400. Empty time ranges yield empty `buckets` (never 404). 5 new tests cover preflight Allow-Origin echoing for both default and extra origins, the extended PATCH field set + clear-by-empty + 400 paths, the archived_only filter on list + summary endpoints, and the /v1/usage envelope + validation errors. Existing 13 runtime_api tests continue to pass; the parity gates and full workspace test suite are clean. `docs/RUNTIME_API.md` and `config.example.toml` updated to document the new params, body shape, endpoint, and CORS knob. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+79
-3
@@ -114,14 +114,35 @@ there is no `[app_server]` config section.
|
||||
- `POST /v1/sessions/{id}/resume-thread`
|
||||
|
||||
**Threads** (durable runtime data model)
|
||||
- `GET /v1/threads?limit=50&include_archived=false`
|
||||
- `GET /v1/threads/summary?limit=50&search=<optional>&include_archived=false`
|
||||
- `GET /v1/threads?limit=50&include_archived=false&archived_only=false`
|
||||
- `GET /v1/threads/summary?limit=50&search=<optional>&include_archived=false&archived_only=false`
|
||||
- `POST /v1/threads`
|
||||
- `GET /v1/threads/{id}`
|
||||
- `PATCH /v1/threads/{id}` (currently supports `{ "archived": true|false }`)
|
||||
- `PATCH /v1/threads/{id}` (see body shape below)
|
||||
- `POST /v1/threads/{id}/resume`
|
||||
- `POST /v1/threads/{id}/fork`
|
||||
|
||||
`archived_only=true` returns archived threads only (mutually overrides
|
||||
`include_archived`). Default behavior is unchanged: `include_archived=false`
|
||||
and `archived_only=false` returns active threads. Added in v0.8.10 (#563).
|
||||
|
||||
`PATCH /v1/threads/{id}` body — every field is optional, missing means
|
||||
"no change". At least one field must be present. `title` and `system_prompt`
|
||||
accept an empty string to clear a previously-set value. Added in v0.8.10 (#562):
|
||||
|
||||
```json
|
||||
{
|
||||
"archived": true,
|
||||
"allow_shell": false,
|
||||
"trust_mode": false,
|
||||
"auto_approve": false,
|
||||
"model": "deepseek-v4-pro",
|
||||
"mode": "agent",
|
||||
"title": "User-set thread title",
|
||||
"system_prompt": "You are a useful assistant."
|
||||
}
|
||||
```
|
||||
|
||||
**Turns** (within a thread)
|
||||
- `POST /v1/threads/{id}/turns`
|
||||
- `POST /v1/threads/{id}/turns/{turn_id}/steer`
|
||||
@@ -157,6 +178,42 @@ there is no `[app_server]` config section.
|
||||
- `GET /v1/apps/mcp/servers`
|
||||
- `GET /v1/apps/mcp/tools?server=<optional>`
|
||||
|
||||
**Usage** (token/cost aggregation across threads)
|
||||
- `GET /v1/usage?since=<rfc3339>&until=<rfc3339>&group_by=<day|model|provider|thread>`
|
||||
|
||||
`since` / `until` are inclusive RFC 3339 timestamps and may be omitted (no
|
||||
bound). `group_by` defaults to `day`. Buckets are sorted by ascending key.
|
||||
Empty time ranges produce empty `buckets` (never a 404). Cost is computed via
|
||||
the model→pricing map; turns whose model has no pricing entry contribute
|
||||
tokens but `0.0` cost. Added in v0.8.10 (#564).
|
||||
|
||||
```json
|
||||
{
|
||||
"since": "2026-04-01T00:00:00Z",
|
||||
"until": "2026-04-30T23:59:59Z",
|
||||
"group_by": "day",
|
||||
"totals": {
|
||||
"input_tokens": 12345,
|
||||
"output_tokens": 6789,
|
||||
"cached_tokens": 0,
|
||||
"reasoning_tokens": 0,
|
||||
"cost_usd": 0.012,
|
||||
"turns": 42
|
||||
},
|
||||
"buckets": [
|
||||
{
|
||||
"key": "2026-04-30",
|
||||
"input_tokens": 1234,
|
||||
"output_tokens": 678,
|
||||
"cached_tokens": 0,
|
||||
"reasoning_tokens": 0,
|
||||
"cost_usd": 0.001,
|
||||
"turns": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Runtime data model
|
||||
|
||||
The runtime uses a durable Thread/Turn/Item lifecycle.
|
||||
@@ -226,6 +283,25 @@ Common event names: `thread.started`, `thread.forked`, `turn.started`,
|
||||
- **Capability responses** never leak secrets, file contents, or session
|
||||
message bodies. They report *metadata*: presence, counts, status flags.
|
||||
|
||||
### CORS allow-list
|
||||
|
||||
The runtime API ships with a built-in dev-origin allow-list:
|
||||
`http://localhost:3000`, `http://127.0.0.1:3000`, `http://localhost:1420`,
|
||||
`http://127.0.0.1:1420`, `tauri://localhost`. To add additional origins (e.g.
|
||||
when developing a UI on Vite's default `:5173`), use any of:
|
||||
|
||||
- CLI flag (repeatable): `deepseek serve --http --cors-origin http://localhost:5173`
|
||||
- Env var (comma-separated): `DEEPSEEK_CORS_ORIGINS="http://localhost:5173,http://localhost:8080"`
|
||||
- Config (`~/.deepseek/config.toml`):
|
||||
```toml
|
||||
[runtime_api]
|
||||
cors_origins = ["http://localhost:5173"]
|
||||
```
|
||||
|
||||
User-supplied origins **stack on top of** the built-in defaults; they do not
|
||||
replace them. Wildcard origins are not supported — the explicit allow-list
|
||||
model is preserved. Added in v0.8.10 (#561).
|
||||
|
||||
## Session lifecycle (native UI supervision)
|
||||
|
||||
| Operation | Endpoint |
|
||||
|
||||
Reference in New Issue
Block a user