v1.0.256
✨ New: Kimi is an API-key engine now
Kimi was the odd one out — Cockpit spawned Moonshot's kimi CLI, parsed its stdout, and guessed the new session id by diffing ~/.kimi before and after. That bought no model picker, no quota readout, no fork, no images, and a run that reported success even when the CLI crashed.
It now runs exactly like DeepSeek: paste a key into the picker in the chat header and pick a mode.
- SDK mode — Claude Agent SDK against
https://api.kimi.com/coding/, sessions under~/.cockpit/kimi/projects - Built-in Agent mode — Cockpit's own loop against the OpenAI-protocol endpoint, sessions under
~/.cockpit/kimi-sessions
Context window and thinking effort come from the model list the picker already fetched, so the SDK stops compacting against Claude's default window and throws away none of the 256K/1M context your plan pays for.
Breaking: pre-existing
~/.kimitranscripts are no longer indexed. The files are left on disk untouched. In exchange, Kimi sessions are forkable — the store is ours now.
✨ New: GLM (Zhipu / BigModel)
Fifth bring-your-own-key engine. Pick GLM from the new-tab dropdown, paste a key from open.bigmodel.cn or z.ai, and both SDK and Built-in Agent modes work end to end.
GLM serves the same account from two regions — open.bigmodel.cn and api.z.ai. A key issued on one authenticates on the other, so the region is routing, not identity, and switching it leaves your sessions resumable. The UI language only seeds the default (English → international, otherwise mainland); once you pick a region explicitly, that choice sticks, because changing a display preference must not silently re-route your API traffic to another country.
The quota button is shared with Kimi and normalises both providers' backwards-reading responses; a pay-as-you-go key with no Coding Plan degrades to a console link instead of an error.
✨ New: Excerpt a single turn into a new session
The scissors button could only keep a prefix — everything from the start of the session up to the chosen turn. Lifting one question-and-answer out of a long session meant dragging all of its context along. There is now a single-turn scope that keeps only that turn, working from either bubble of the pair.
Two things that made fork much less useful than it looked are fixed alongside:
- Fork silently 404'd on five of six engines — it only ever resolved Claude session paths, while the button rendered for all of them and the failure was console-only, so the click read as a dead control. Every store is resolved now, forks land in the engine's own store, and failures raise a toast.
- Forks lost their subagent transcripts and workflow journals. The vendor writes those into a directory named after the session id; fork copied only the jsonl, so every Task and Workflow drill-in in a forked session opened nothing. The artifacts the kept transcript actually references are now carried over — hard-linked, so forking a large session stays instant.
Turn boundaries are also correct now. The splitter had its own rule ("a user row with text"), which read every harness-injected row — background-task notifications, skill bodies, compaction notices — as a new human turn. On a real 700-line session it found 58 turns where there are 21, and excerpting one yielded a five-entry fragment.
✨ New: Independent task on the Claude engine
The independent task toggle — run this turn without the conversation history — used to be a Built-in Agent capability only. It now works on the SDK engines: the turn runs with the transcript stashed aside so the vendor CLI starts with an empty context, then history and the fresh turn are concatenated back with the parent chain re-linked at the seam.
The session id is held constant throughout, so subagent directories, snapshots and titles all stay correct. A crash mid-turn is swept up at server boot. Not available in PTY mode — the interactive CLI holds the conversation itself — where the toggle renders disabled with a tooltip rather than vanishing.
✨ New: Code Map shows what it is building
Opening the Code Map on a cold index used to show a bare "Loading…" for the whole build — measured at 18 seconds on a 7.7k-file project, which reads as a hang. The build now narrates itself: a progress bar with the current phase and the file being parsed, streamed over the /ws/watch socket the file browser already holds.
The file cap also went from 8000 to 15000. A checkout with 8459 source files was stopping at 7998, and the last ~460 files were simply absent from search, callers and the map with nothing in the UI saying so. Note the cost: a 7.7k-file project measures ~0.8 GB resident, and the index is held until the process restarts.
✨ New: Edit the quick-reply phrases
The quick-reply panel in the selection toolbar was a hardcoded list. A pencil in its top-right now opens an editor — one line per panel row, phrases separated by commas (both comma widths split, so a Chinese IME works). Phrases are stored per language in ~/.cockpit/settings.json under quickReplies; "restore defaults" drops that language's entry so it follows the built-ins again.
✨ New: Copy the configured API key
The engine picker only ever held the masked key, so getting the real value back out meant re-issuing one at the provider. There is now a copy button next to Edit and Clear. The plaintext is fetched on click and handed straight to the clipboard — it never lands in component state where a re-render could paint it on screen.
🐛 Fix: provider errors are no longer swallowed
streamText does not throw when a provider fails — it emits an error part and ends. Cockpit did not handle that part, so a first-turn 401 surfaced as the useless "No output generated", and a failure mid tool-loop reported result: success over an empty bubble, entirely silently. This hit every provider running in Built-in Agent mode (Kimi / Ollama / DeepSeek / GLM).
Errors now travel the normal failure path, carry the HTTP status and the provider's own wording, and are persisted to the transcript — so they survive the reconcile at run end and a reload. A bad Kimi key now says [HTTP 401] The API Key appears to be invalid or may have expired.
🐛 Fix: chat bubbles
Two independent sources of stray blank space in the message list. A turn that ended without any assistant content — stopped early, or failed before the first token — left its empty placeholder behind forever as a blank pill. And bubbles were stretching to the height of the hover action buttons beside them, so a one-word message rendered as a tall pill with its text pinned to the top.
🐛 Fix: one bad WebSocket listener no longer starves the rest
Listeners on a shared /ws/watch connection were dispatched with forEach, which aborts on the first callback that throws — and the surrounding try/catch, there for JSON parse errors, hid it completely. Every listener registered after the throwing one was silently never invoked. Dispatch is isolated per listener now and logs instead of vanishing. This is what broke the Code Map progress bar during development: the frames reached the browser and never reached React.
🐛 Fix: page title
An installed PWA window titles itself <manifest name> - <document.title>, so the app name was appearing twice: "OpenCockpit - Cockpit - ai-assistant". A tab now reads just the project name.
📦 Misc: refuse to boot on a Next version mismatch
The published package ships a prebuilt .next-prod but loads Next's server runtime from node_modules at run time. If those drift apart, every request 500s with renderToPipeableStream is not implemented — a symptom that points nowhere near the cause. next is pinned to an exact version, and the build now stamps its Next version into .next-prod/cockpit-build.json so the prod server can cross-check at boot and refuse to start with an actionable message. Builds predating the stamp still boot.
The DeepSeek API key field also picked up a link to platform.deepseek.com/api_keys — the menu asked for a key without saying where to get one.