codewhale integrations dsh … connects a user's existing official DeepSeek
Harness installation (dsh, npm @deepseek-ai/dsh) to their Codewhale setup.
DSH stays an integrated harness surface. Codewhale remains the owner of Fleet
configuration, provider/model selection, permissions, credentials, and
lifecycle authority; DSH is not a second Fleet scheduler and never an
authority bypass.
Verified against dsh 0.1.0-rc.6 (the latest published release at the time
of writing). DSH is a developer preview that warns of compatibility-breaking
changes; a newer dsh is reported as stale-version (launchable, unverified),
an older one or one without --patch as incompatible.
Codewhale uses only DSH's documented seams:
| Seam | How Codewhale uses it |
|---|---|
dsh --version / dsh --help |
read-only detection (never initializes a profile) |
$DSH_HOME (or ~/.dsh) |
read-only inventory: profile names, settings.yaml top-level namespaces, whether .credentials.yaml exists and is 0600. Values are never read. |
--patch <file> overlay |
Codewhale writes one overlay under its own home and passes it at launch |
DSH_PERMISSION_MODE env |
mirrors the Codewhale permission posture |
--profile web / --profile headless |
the two shipped DSH profiles; DSH initializes them itself on first launch (its own documented behavior) |
Codewhale writes only under $CODEWHALE_HOME/integrations/dsh/ (plus,
with the opt-in plugin path below, whatever dsh plugin itself writes into
the dedicated codewhale DSH profile):
codewhale.patch.yml— the overlay. Identity only: provider route, model, base URL, and (native DeepSeek route)reasoningEffort. For every non-native route it declares acodewhale-<provider>route on DSH'sllm-pi-aiadapter, naming that route's own wire dialect underapi:(openai-completions,openai-responses, oranthropic-messages) andapiKeyEnvnaming the provider's canonical environment variable — the name, never the value. Keyless local routes (loopback Ollama / LM Studio / vLLM / SGLang) carry no credential reference.receipt.json— the current connection record plus an append-only history ofconnect/update/disable/enable/removeevents with the overlay SHA-256, dsh version,$DSH_HOME, mapped identity, permission mode, and timestamps (seedocs/RECEIPTS.md). Every event is also appended to$CODEWHALE_HOME/audit.log.bundle/— only afterinstall-bundle; see below. The Codewhale palette (skin) and the ambient ocean scene live here, in the bundle's client half — no stylesheet is exported.
Codewhale never:
- copies, prints, or embeds API keys, OAuth documents, environment secrets,
prompts, or filesystem contents (a
--api-key/keyring credential Codewhale itself materialized into the process is stripped from the launched child; a key the user exported in their own shell is left alone); - writes to
$DSH_HOME(settings, credentials, profiles, sessions); - edits installed
@deepseek-ai/dshpackage files; - switches to a cloud model or broadens permissions silently. Codewhale
read-only→ DSHread-only; anything else →workspace-write;danger-full-accessonly with--allow-full-accessand a Codewhale full-access posture (sandbox_mode = "danger-full-access"/ yolo).
| State | Meaning | Launch |
|---|---|---|
not-installed |
dsh not on PATH |
refused |
offline |
dsh exists but --version failed |
refused |
incompatible |
older than 0.1.0-rc.6 or no --patch |
refused |
detected |
usable dsh, no Codewhale overlay | refused (connect first) |
connected |
overlay matches the current Codewhale route | allowed |
stale-config |
route changed, overlay edited outside Codewhale, or missing | refused (update) |
stale-version |
connected, but dsh is newer than verified | allowed, unverified |
disabled |
overlay kept, launches refused | refused (enable) |
status, plan, /setup tools (Tools and MCP step) and codewhale doctor
are side-effect free.
codewhale integrations dsh status [--json]
codewhale integrations dsh plan [--profile web|headless] [--allow-full-access] [--skin] [--json]
codewhale integrations dsh connect [--profile web|headless] [--allow-full-access] [--skin] [--yes]
codewhale integrations dsh update [--profile …] [--allow-full-access] [--skin true|false] [--ocean true|false] [--yes]
codewhale integrations dsh launch [--profile web|headless] [--dry-run] [-- <dsh app args>]
codewhale integrations dsh disable
codewhale integrations dsh enable
codewhale integrations dsh remove [--yes]
codewhale integrations dsh install-bundle [--app web|headless] [--yes]
codewhale integrations dsh remove-bundle [--yes]connect, update, and remove print the exact plan (files, identity,
permission mode, disclosures, and the overlay text) and require confirmation
(--yes when stdin is not a terminal). launch runs
DSH_PERMISSION_MODE=<mode> dsh --profile <p> --patch <overlay> … in the
Codewhale workspace with the user's own $DSH_HOME, so their credentials,
sessions, and profiles remain theirs.
- DSH layers the user's
settings.yamlsections (agent-default-model,llm-deepseek,llm-pi-ai) over the overlay per field. If those sections exist, DSH's saved selection can shadow the pinned identity until it is cleared in DSH;status/planlist them. - Reasoning tiers are mapped only for the native DeepSeek route
(
off|high|max); hand-declared routes send no effort parameter. - Wire dialects are carried, never approximated: a Chat Completions route
declares
api: openai-completions, an OpenAI Responses route (e.g. the defaultdeepseek/deepseek-v4-flash) declaresapi: openai-responses, and an Anthropic Messages route declaresapi: anthropic-messages. This follows the installed adapters' own declarations (verified against@deepseek-ai/dsh@0.1.0-rc.6):@deepseek-ai/dsh-llm-deepseek— thedeepseek-officialroute — speaks chat completions only (its single wire call posts to<baseURL>/chat/completions, with no protocol switch), while@deepseek-ai/dsh-llm-pi-ai's hand-declared route schema accepts exactlyopenai-completions | openai-responses | anthropic-messagesforapi:. So DeepSeek chat routes ride the native adapter (with reasoning tiers), and every other dialect — including DeepSeek's own Responses-dialect models — rides a hand-declaredcodewhale-*pi-ai route in its own dialect. - What is refused: base URLs that embed credentials (userinfo or
query/fragment material) are never copied into the overlay;
planfails naming the currentprovider/modeland the reason, andstatusshows carry-ability for the current route beforeplanis ever run.
--patch is Codewhale's default because it needs nothing but the launcher.
The documented DSH plugin mechanism is available as an explicit opt-in:
codewhale integrations dsh install-bundle [--app web|headless] [--yes]
codewhale integrations dsh remove-bundle [--yes]install-bundle requires an existing connection and pnpm on PATH (dsh
shells out to it); without pnpm the status reads
plugin path: not available: pnpm missing … and the command refuses. It:
- materializes an npm-shaped bundle package under
$CODEWHALE_HOME/integrations/dsh/bundle/—package.json(codewhale-dsh-bundle, private, MIT, version<codewhale version>+dsh.<patch sha12>,"dsh": {"bundle": {"patch": "./cordis.patch.yml"}}),cordis.patch.yml(the identity overlay, plus one trailing skin insert row when the skin is on — see below),README.md,NOTICE.md(DSH MIT notice retained), and, with the skin on,lib/index.js+lib/client.js(the palette plugin, with the ocean scene spliced in unless--ocean false); - runs the documented
dsh plugin --profile codewhale add <path>twice: first for DSH's own shipped app bundle (@deepseek-ai/dsh-web-appordsh-headless, linked from the installed launcher so the profile can boot; no network), then for the Codewhale bundle so its rows patch last. DSH creates the dedicated profile$DSH_HOME/profiles/codewhale(package.jsonwithlink:dependencies,pnpm-lock.yaml,node_moduleslinks). The user'sweb/headlessprofiles are never touched; - records an
install_bundlereceipt (profile dir, bundle dir, package version, patch SHA-256, app bundle source, pnpm version, SHA-256 digest of thedsh pluginoutput — the output text itself is not stored).
Afterwards dsh --profile codewhale alone carries the identity (verified with
dsh --profile codewhale --dump-config), and launch prefers that profile
without --patch; launch --profile web|headless still uses the overlay.
Because the profile dependency is a link: to the Codewhale-owned directory,
update regenerates cordis.patch.yml (and the skin files) in place — no
pnpm run. Stale detection covers the bundle: a modified or missing bundle
patch, a bundle that no longer matches the overlay, a lib/client.js that
is missing, modified, present while the receipt says the skin is off, or
carrying/lacking the ocean scene against the receipt's ocean decision, or
a profile manifest that stopped listing codewhale-dsh-bundle all report
stale-config.
remove-bundle runs dsh plugin --profile codewhale remove codewhale-dsh-bundle and deletes only the Codewhale-owned bundle files. The
profile directory itself (and the app bundle link dsh recorded there) is
DSH-owned and is left in place; the receipt says so. remove refuses while a
bundle is installed.
DSH 0.1.0-rc.6 has one documented token-level theming seam:
ThemeService.overrideTokens(source, tokens) in
@deepseek-ai/dsh-client-ui-theme, which stacks a partial --dsw-alias-*
layer over the active theme (per-token, later layers win) and returns a
disposer. That is the mechanism the Codewhale skin uses. It is applied only
through the bundle profile (dsh --profile codewhale); the --patch
overlay never carries skin code, so launch --profile web|headless stays
overlay-only and stock-themed.
install-bundle turns the skin on by default. With the skin on, the
bundle is a dual-face DSH plugin:
package.jsongains"dsh": {"client": {"platform": "web", "immediately": true, "inject": ["@deepseek-ai/dsh-client-ui-theme"]}}and"exports": {".": …, "./client": …, "./package.json": …}(Node exports maps are exhaustive; the loader imports the bare name and dsh-client-modules resolves<name>/package.json);lib/index.jsis a no-op Node cordis entry (so the row mounts) andlib/client.jsis a plainwindow.__ModuleLoader__.load({ id, factory })script whose factory callsctx.theme.overrideTokens("codewhale-dsh-bundle", TOKENS)insidectx.effectand returns the disposer (inject: ["theme"]defers it until the theme service exists);cordis.patch.ymlends with- insert: [{ id: codewhale-skin, name: codewhale-dsh-bundle }]after the identity rows.
TOKENS is a bounded map of --dsw-alias-* names (backgrounds, borders,
brand, buttons, labels, error/success/warn states, code blocks, scrollbar,
toast, tooltip) onto light/dark values rendered from the TUI's real palette
(crates/tui/src/palette, Blue Stage dark and light) — palette constants
only, no user data or environment. The receipt records skin: true|false
and skin_sha256 (SHA-256 of the rendered TOKENS JSON); package.json
carries the same hash under codewhale.skin_sha256.
The skin mounts a small plugin-owned lockup in the top-right corner that says
WHALE BROTHERS, CODEWHALE, and × DEEPSEEK HARNESS. It is additive: it
registers through DSH's frame-wide shell.overlay slot and does not replace or
rewrite DeepSeek Harness branding or controls. The lockup uses the active skin
tokens, ignores pointer input, collapses to a compact whale mark below 760 px,
and is removed with the client plugin.
package.json records the generated fragment as codewhale.brand_sha256.
With the skin on, lib/client.js also carries an ambient ocean: a
full-viewport <canvas> (position: fixed; inset: 0; z-index: -1; pointer-events: none, painted below #root and above the body background)
with a visible depth gradient, one near and one far whale silhouette (blunt
head, low dorsal hump, long pectoral flipper, horizontal fluke flexing ±10°)
gliding slowly across on a gentle sine, biased to the lower half and the top
edge so they never cross the composer card, an occasional short spout of
bubbles from the head, a small school of Codewhale glyph fish (><> /
><o> in the code font, flocking-lite behind a wandering leader) and faint
rising bubbles. The
palette is the skin's own (surface_bg, accent_primary, text_body,
text_dim for light and dark); the scene follows DSH's theme/change event
so it flips with the app.
To let the canvas show through, the client re-issues two background tokens
as translucent rgba over the opaque table while the scene is on:
--dsw-alias-bg-base (α 0.42; the frame and the centre column both paint
it) and --dsw-specific-sidebar-fill (α 0.78, keeping navigation distinct).
Panels,
composer, code blocks and every other layer stay opaque. Verified live on
dsh 0.1.0-rc.6 in both schemes: no console errors, frames differ, and text
stays legible (see docs/design/assets/dsh-ocean-{light,dark}.png).
Budget: requestAnimationFrame capped at ~30 fps, paused while
document.hidden, one static frame under prefers-reduced-motion: reduce,
device-pixel-ratio aware, no per-frame allocations (typed arrays reused).
The scene ships inside client.js because dsh-client-modules serves exactly
one file per client plugin (/plugins/<id>/client.js); there is no
lib/scene.js. package.json records codewhale.ocean and
codewhale.ocean_scene_sha256; the receipt records ocean: true|false.
Off switches, smallest first: in the browser localStorage["codewhale.ocean"] = "off" (or body class codewhale-ocean-off) skips both the canvas and the
translucent tokens on that machine; window.__codewhaleOcean.stop() /
.start() / .setIntensity(0..1) are exposed for the console; and
codewhale integrations dsh update --ocean false regenerates client.js
without the scene (default on; a bare update keeps the previous choice;
--skin false implies no scene).
Escape hatch: codewhale integrations dsh update --skin false regenerates
the bundle without the client half and without the insert row (no pnpm run;
the link: dependency picks the files up in place); update --skin true
turns it back on, and a bare update keeps the previous choice.
install-bundle itself takes no --skin flag. connect --skin / plan --skin record the same decision ahead of a later bundle install and write
no extra files. remove-bundle deletes the client half with the rest of the
Codewhale-owned bundle files, and the overrideTokens layer is disposed
with the plugin, so stock DSH theming returns.
The 0.9.8 --skin CSS/preview export (codewhale-dsh-skin.css,
codewhale-dsh-skin-preview.html) is gone: dsh-client-ui-layout writes
the alias tokens as inline body.style properties, so any stylesheet rule
lost to them by construction. connect/update delete those leftover files
if present.
remove deletes only the overlay (and any 0.9.8 skin/preview leftovers)
under $CODEWHALE_HOME/integrations/dsh/, appends a remove receipt, and
never touches $DSH_HOME or the installed package. DSH keeps working exactly as
before the connection.
DeepSeek Harness is © 2026 DeepSeek, MIT licensed; the integration invokes the installed launcher and does not redistribute it. This is not native Codewhale functionality: every surface labels it "DeepSeek Harness connected through Codewhale".