GET /v1/providers, GET /v1/providers/{id}/models, and
POST /v1/providers/{id}/switch have shipped since 2026-07-20 (ebc567dec) and
were documented nowhere. The cost was concrete: a desktop integration probing
for model choice on 2026-08-04 tried /v1/models, /v1/runtime/models, and
/v1/runtime/providers, got 404s from all three, and filed 'no way to read the
catalog' and 'no way to know which model values are valid' as runtime gaps —
against a binary where the real route was already live. The API was fine; the
map was blank.
Documents the exact response shapes, the deepseek-cn alias rejection, that an
empty models array means 'not configured here' rather than 'no models', that
the returned ids are what POST /v1/threads accepts, and — most usefully — that
the switch route exists precisely so a GUI does not simulate a provider change
with repeated POST /v1/config writes plus a reload. Also records why there is
no credential-presence field yet and the one-request-per-provider workaround.
No code change. Authored with agent assistance (Claude).
Add explicit local Fleet preparation/start APIs, named role and Workflow metadata, durable privacy-bounded replay/SSE, and per-worker controls. Keep managed launch collision-safe with run-scoped worker identities, effective write-root checks, contention-aware scheduling, and compaction-safe replay epochs. Extend Runtime capabilities, SDK helpers, documentation, and regression coverage while preserving the existing CLI launch path and failing closed on unsupported targets or authority overrides.
Move the existing CLI session envelope and profile/origin slot into codewhale-secrets so CLI, TUI, and Runtime read one secure record. Runtime info now advertises account_session and an authenticated, token-free account receipt with durable IDs, explicit stored scopes, and normalized cached/expired/revoked states. Anonymous probes stay signed out and never read secure storage; local signed-out Work remains valid.
Classify configured API-key values once and reuse that result across runtime resolution, readiness diagnostics, and billing. Exact and whitespace-wrapped keyring sentinels now fall through only to route-bound declarations or an allowed store, while custom routes report an explicit unavailable state.
Add real-TOML runtime regressions, offline doctor/setup parity coverage, and update the credential diagnostic contract without reading durable secrets during ordinary diagnostics.
Report structural credential declarations independently from literal availability so environment, external auth, OAuth, consent, and unprobed secret stores cannot certify Setup or Fleet readiness. Treat only non-empty non-sentinel config values as present, while no-auth and local routes are explicitly not required.
Add the offline source/availability matrix, sentinel and empty-key regressions, readiness assertions, process-level redaction/no-I/O coverage, and document the expanded JSON contract.
Gate release, provider, local, and MCP probes behind explicit flags while keeping ordinary human, JSON, and context diagnostics structural and offline. Only explicit provider and local probes may load workspace dotenv credentials.
Report canonical user paths and symlink-safe metadata-only secret backend state without opening secret files, creating stores, migrating credentials, or constructing an OS keyring. Omit secret-capable URL components, raw MCP commands and arguments, environment/header values, and untrusted live/config failure details.
Test provenance is preserved: all 47 pre-existing secrets tests and helpers remain inline; 4 new secret diagnostic tests share that module env lock through a child seam; 15 new main doctor tests live in main/tests.rs while pre-existing main tests remain in place.
Verified: fmt; diff check; source structure 646051/max 19025; dead-code 482; secrets 51 plus 1 doctest; doctor filters 80 and 89; structural loader tests; isolated binary human/JSON/context/config/update/MCP sentinel smokes without live probes. Exact all-target/all-feature clippy remains blocked by 8 parent-identical diagnostics outside this 13-path concern.
Publish the implemented authorization order and lock its precedence with regression coverage. Preserve the contributor's documentation, translation, and contract-test work.
`GET /v1/sessions/summary` returns rows field-compatible with
`/v1/threads/summary`, and `GET /v1/sessions` now takes its membership and
order from the same shared projection, so the terminal and the dashboard cannot
show different listings of the same store.
`PATCH /v1/sessions/{id}` renames and/or archives through the manager's single
writers and returns a receipt whose `changes` map lists only what actually
moved. It fails closed with `409 Conflict` when the session is open in an
interactive Codewhale process, rather than writing something that process's
next autosave would revert.
`GET /v1/sessions/{id}?peek=true` returns at most twelve entries of at most 400
characters, tool calls and results summarised to a name and a size rather than
inlined, and credential-shaped substrings masked. Bounding and redacting
server-side is the point: the alternative ships a multi-megabyte transcript to
a LAN-reachable browser in order to display twelve lines of it. The payload
carries `"live": false` and deliberately has no turn-status field a client
could mistake for live state.
A `thread/message` turn could not be stopped by anything sent over stdio.
Two things had to be true at once for that, and both were:
The read loop awaited each dispatch to completion before calling
`next_line()` again, so while a turn streamed, no further message was even
read — a cancel could not arrive, let alone be handled. And
`handle_stdio_thread_message` holds the bridge mutex for the whole turn,
so `shutdown` deadlocked against the very turn it meant to stop. The only
way out was killing the process, which takes down every other thread
multiplexed on that bridge.
Keep reading while a turn streams. The turn is polled in a `select!`
alongside `next_line()`, so mid-turn requests are seen. The turn still
owns the writer for its duration, so those requests do not write
immediately: `thread/interrupt` acts at once and queues only its reply,
and everything else queues whole and runs in order once the turn unwinds.
Responses therefore stay whole and ordered.
Give the cancel a path that avoids the bridge. `AppState` gains
`in_flight_turns`, holding the base URL, token, runtime thread id and turn
id copied out when the turn starts. An interrupt POSTs the runtime's
existing `/v1/threads/{id}/turns/{turn_id}/interrupt` from that snapshot,
so it never waits on the mutex the turn holds. Registration covers exactly
the streaming window, so a finished turn never looks cancellable.
`shutdown` now interrupts live turns first, then takes the bridge.
Interrupting an idle thread reports `interrupted: false` rather than
erroring — there was simply nothing to stop.
`run_stdio` is split into a transport-generic `run_stdio_loop` so the loop
can be driven over a duplex pipe. The regression test runs a turn against
a fake runtime that never ends on its own, cancels it mid-stream, and
requires the loop to exit; it fails by timeout if the loop stops reading.
Adds `thread/interrupt` to the capability set (the drift test and
docs/RUNTIME_API.md are updated with it).
Closes#4738
Compare top-level legacy session filenames without reading chat payloads or traversing checkpoint internals. Report incomplete additive migration with bounded filename samples and an exact safe recovery path while preserving explicit CODEWHALE_HOME isolation.
Document the JSON contract and recovery procedure.
Reported by @stream2stream in #4032.
Signed-off-by: Hunter B <hmbown@gmail.com>
Serialize Skill toggles with a cross-process lock, reload and merge the authoritative disk snapshot under that lock, and publish to memory only after atomic persistence succeeds. Refresh Runtime API listings from shared state and fail startup instead of falling back to a pathless no-op store.
Adds stale-store, persistence-failure, refresh, and isolated cross-process regression coverage.
Signed-off-by: Hunter B <hmbown@gmail.com>
Treat the JSONL newline as the durable append marker and document the intentional sequence gap after crash repair. Offload terminal receipt dedupe scans without releasing event ordering, while holding the per-thread projection boundary through terminal persistence, receipt publication, and active-claim cleanup.
Add regressions for valid JSON missing only its commit marker, concurrent dedupe, restart idempotence, and terminal snapshot ordering.
Signed-off-by: Hunter B <hmbown@gmail.com>
Parse durable history on the blocking pool and stream it through backpressured 256-event batches for initial and lag recovery paths. Bound tail replay, preserve predecessor cursors, ignore only unterminated live append tails, and offload projection and duplicate-receipt scans.
Signed-off-by: Hunter B <hmbown@gmail.com>
Claim only an exact pending request, commit a redacted terminal receipt before engine delivery, and supervise settlement past caller cancellation. Restore retry-safe append failures, order terminal cleanup, reject stale IDs, and clear completed-turn attention defensively in the web client.
Signed-off-by: Hunter B <hmbown@gmail.com>
Batch streaming projections behind a per-thread snapshot boundary, make dynamic-tool terminal receipts cancellation- and crash-safe, and reconcile terminal turns whose receipt append was interrupted.
Document durable result acceptance and intentional sequence gaps, with deterministic regression coverage for rollback, restart, repeated failures, and recovery lock ordering.
Signed-off-by: Hunter B <hmbown@gmail.com>
Persist streamed message and reasoning prefixes before advancing the Runtime cursor, and redact request_user_input answers from durable receipts while preserving the model-facing result.
Make client-executed dynamic calls snapshot-authoritative, route-scoped, bounded, and exactly-once across resolution, timeout, and terminal cancellation. Teach the embedded browser client and Runtime API documentation the completed lifecycle.
Signed-off-by: Hunter B <hmbown@gmail.com>
Keep MCP static validation, protocol readiness, and backend health as distinct evidence in human, JSON, and setup output. Require an explicit local-provider probe so ordinary diagnostics cannot wake a desktop-managed daemon.
Refs #4406
Signed-off-by: Hunter B <hmbown@gmail.com>
Independent review of the web session boundary found one lifecycle gap:
when a turn settles through settle_claimed_turn_failure (engine event
stream closes or monitor errors mid-turn), pending user-input prompts
were never drained. Fresh snapshots kept advertising an input card
whose engine channel was dead, and it persisted until process restart.
Mirror the happy terminal path: clear the turn's pending user inputs,
deliver the engine-side cancellation, and emit user_input.canceled
before the terminal turn.completed receipt so connected and reloaded
clients converge on the same truthful state. If the engine is already
evicted, still drop the registrations so snapshots stop advertising
unanswerable prompts.
Also documents the one-time bootstrap capability's sub-second browser
launcher argv window and the process-start-anchored 12h session expiry
in docs/RUNTIME_API.md, and adds MockEngineHandle::close_event_stream
so failure-path tests can close the stream without partial moves.
Covered by failed_turn_cancels_pending_user_input_and_clears_snapshot.
Signed-off-by: Hunter B <hmbown@gmail.com>
RUNTIME_API.md gains the codewhale web transport row, the one-time
loopback bootstrap capability flow, the codewhale_web_session cookie
attributes and 12-hour process-local expiry, and the same-origin
mutation guard, and now names CODEWHALE_RUNTIME_TOKEN as the primary
token env var with DEEPSEEK_RUNTIME_TOKEN as the compatibility alias.
Both changelogs record the embedded client under Unreleased.
Refs #4423.
Signed-off-by: Hunter B <hmbown@gmail.com>
Integrate the underwater TUI, message-first Operate, Fleet and Workflow reliability, expanded model/provider catalog, exact custom-route restoration, docs-first site, localization, packaging, and release metadata for the v0.9.0 candidate.
Harden endpoint-bound credential provenance, approval and goal UX, Fleet attempt fencing and crash recovery, large-workspace mention discovery, Kimi budgeting, and release asset/version gates. Include the stopship Fleet and Workflow fixtures used by release dogfood.
Verified with workspace fmt/check/clippy/tests on Rust 1.88, release-script and npm suites, 18-crate publish dry run, production web build, Docker build check, secret scan, dependency audit, and protected-state hash validation.
Classify the actual StepFun endpoint as PAYG or Step Plan without persisting raw URLs, then carry that non-secret provenance through turn-end hooks, runtime records, usage aggregation, and offline scorecards.
Price only the exact StepFun PAYG fallback route with the official cache rate. Route-blind background and foreign-provider calls now fail closed so subscription quota cannot be reported as token spend.
Validation: cargo fmt --all -- --check; RUSTFLAGS=-D warnings cargo check -p codewhale-tui --bin codewhale-tui --locked; targeted scorecard/pricing/runtime/hook tests; full TUI suite (6408 passed, 2 ignored before the final narrow gate, then gate-specific regression tests passed).
Signed-off-by: Nightt <87569709+nightt5879@users.noreply.github.com>
The provider/model release-smoke matrix mapped `arcee ->
trinity-large-thinking`, but that id is Arcee's LARGE reasoning model (it is
DEFAULT_ARCEE_MODEL), the opposite of the cheap sentinel this matrix wants,
and app-server-smoke.test.sh expects arcee UNMAPPED — so the committed test
failed 3/18. Remove the arcee default so it is unmapped again (fails loud /
overridable via SMOKE_MODEL_ARCEE), which fixes the test and honors the
script's conservative "never guess a model id" design (#3205).
Also resolve a code/docs mismatch: the matrix only ships built-in cheap
defaults for a conservative subset (deepseek, zai, moonshot, openai) and
leaves openrouter, xiaomi-mimo, openai-codex, and arcee unmapped on purpose.
RUNTIME_API.md's "maps each to a cheap model" wording now states which
providers have built-in defaults and that the rest require SMOKE_MODEL_<SLUG>
overrides rather than a guessed id.
`bash scripts/release/app-server-smoke.test.sh` now passes all 18 checks.
Add a diagnostic-only legacy state report to codewhale doctor and doctor --json. The report compares known .deepseek state entries against their .codewhale counterparts and flags unmigrated or dual-root data without creating, moving, or deleting files.
Verification:
- cargo fmt --all --check
- git diff --check
- cargo test -p codewhale-tui --bin codewhale-tui --locked doctor_
Closes#3727
Remove public benchmark docs/scripts and the shipped SWE-bench CLI surface from the CodeWhale repo; benchmark work belongs outside this release repo.
Trim public docs that routed users into private maintainer runbooks, remove stale deleted-doc links, tone down release-facing copy, update CodeWhale crate descriptions, and expose the residue ledger as /debt while keeping quiet legacy dispatch compatibility.
Verification:\n- cargo fmt --all -- --check\n- git diff --check\n- ./scripts/release/check-versions.sh\n- cargo check -p codewhale-tui --bin codewhale-tui --locked\n- cargo test -p codewhale-tui --bin codewhale-tui --locked command_registry\n- cargo test -p codewhale-tui --bin codewhale-tui --locked every_command_alias_dispatches_to_a_handler\n- cargo test -p codewhale-cli --locked
Document that app-server no-auth mode must use a loopback bind and that mobile LAN access should use a token unless explicitly bound to 127.0.0.1 for local-only testing.
Addresses Gemini Code Assist review feedback on PR #3287.
Update the runtime API docs to name the app-server entrypoint's canonical --insecure-no-auth flag while keeping the codewhale serve compatibility aliases on --insecure.
Fixes#3260.
Reported-by: Hmbown <101357273+Hmbown@users.noreply.github.com>
WIP branch for v0.8.61 sub-agent cutover. Removes old lifecycle/tool-agent surface, runtime tag injection, heartbeat scaffolding, and capacity/coherence code, but release readiness is not final: interactive fanout can still freeze the TUI and needs follow-up investigation before shipping.
`codewhale app-server --http`/`--mobile` now serve the full HTTP/SSE runtime API
(/v1/*, SSE, sessions, threads, turns, approvals, usage, fleet, tasks) by
delegating to the same mature server reached via `serve --http`/`--mobile`, which
remain as compatibility aliases. No routes or behavior in
crates/tui/src/runtime_api.rs changed; the CLI reuses the existing sibling-TUI
delegation that `serve` already uses.
- app-server --stdio: unchanged JSON-RPC control transport.
- app-server (bare): unchanged legacy in-process HTTP on :8787.
- AppServerArgs: host/port -> Option (defaults 7878 for --http/--mobile, 8787
legacy); add --http/--mobile/--qr/--workers with clap conflicts; map
--insecure-no-auth onto serve's --insecure when delegating.
- app-server: pin the stdio capability method set with drift tests.
- docs/RUNTIME_API.md: lead with app-server; document benchmark/SDK contract.
- scripts/release/app-server-smoke.sh (+ .test.sh): pre-release headless smoke,
stdio health/capabilities probe (no tokens) plus a secret-safe provider/model
matrix discovered from `codewhale auth list`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Refs #3163.
Adds the @codewhale/runtime-sdk workspace with typed fleet Runtime API helpers, protocol-shaped TypeScript declarations, JSON/SSE event fixture handling, and typed RuntimeCapabilityError failures for create/event-stream endpoints that the Rust API has not exposed yet.
Documents the SDK contract in docs/RUNTIME_API.md and wires npm workspace verification through npm test --workspace @codewhale/runtime-sdk.
Harvests the explainability slice from PR #2971 without changing the public HookEvent constructor shape. Runtime API approval.required frames now carry matched_rule metadata when an execpolicy rule caused the prompt.
Co-authored-by: greyfreedom <11493871+greyfreedom@users.noreply.github.com>
Sweep brand mentions of `DeepSeek TUI` / `deepseek-tui` / bare
`deepseek` (the dispatcher binary) across all user-facing docs to
the new `codewhale` brand. The DeepSeek **provider** integration is
left untouched throughout: env vars (`DEEPSEEK_*`), model IDs
(`deepseek-v4-pro`, `deepseek-v4-flash`, `deepseek-chat`,
`deepseek-reasoner`), the `api.deepseek.com` host, the
`~/.deepseek/` config dir, and the `--provider deepseek` argument
value all keep the legacy spelling.
Anti-scope items deliberately left as the legacy `deepseek-tui`:
- Homebrew tap and formula (`Hmbown/homebrew-deepseek-tui`,
`brew install deepseek-tui`, `scoop install deepseek-tui`). The
tap rename ships separately.
- Docker image (`ghcr.io/hmbown/deepseek-tui`). Image-tag rename
ships separately.
- CNB mirror namespace (`cnb.cool/deepseek-tui.com/DeepSeek-TUI`).
Third-party hosted path.
- Security contact email (`security@deepseek-tui.com`).
- GitHub repo URL (`Hmbown/DeepSeek-TUI`).
New artifact:
- `docs/REBRAND.md` documents what changed, what didn't, the
deprecation window, and migration commands for npm / Cargo /
Homebrew / manual installs.
CHANGELOG entries:
- Root `CHANGELOG.md` and `crates/tui/CHANGELOG.md` both gain a
new `[Unreleased]` section describing the rename and the one-
release deprecation window. Historical entries are untouched.
Issue templates:
- `.github/ISSUE_TEMPLATE/bug_report.md` and `feature_request.md`
refer to "codewhale" / `codewhale --version` instead of the old
brand name in their environment fields.
The rebrand sweep was driven by a perl script with bulk patterns
(`deepseek-tui` -> `codewhale-tui`, `DeepSeek TUI` -> `codewhale`,
bare `deepseek` -> `codewhale` with provider/model/host/env-var/
config-path negative lookbehind/lookahead) followed by targeted
reverts for the anti-scope items above. Output was visually
reviewed file-by-file before committing.
Verified:
- `cargo check --workspace --all-targets --locked` — pass.
- `cargo test --workspace --all-features --locked` — pass (no
test source touched here; suite stayed green to confirm no
doc-from-string assertions broke).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Integrates #856 as a focused runtime API security slice.
Default local behavior remains unchanged. `/v1/*` routes require a token only when `--auth-token` or `DEEPSEEK_RUNTIME_TOKEN` is set, and `/health` remains public for readiness checks.
Co-authored-by: Zhuoran Deng <dengzhuoran9@gmail.com>
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>