dscode
DeepSeek Harness coding TUI, distributed as a dsh plugin: this package
carries the grok-leader bridge (the dsh-side server the TUI drives) plus the
dscode launcher, which materializes the release-pinned TUI binary from
GitHub Releases on first run (SHA-256 verified, cached at
~/.dsh/profiles/dscode/bin/).
npx @hqzhao95/dscode
First run reuses an exactly tested dsh on PATH or installs the pin under
~/.dsh/profiles/dscode/runtime, downloads the TUI binary, and links
dscode. It never mutates the global npm prefix.
dscode update reconciles the profile package from the matching npm ref
(latest, beta, or an explicit version) before updating the TUI.
dscode uninstall removes only the owned profile and launcher; shared dsh
sessions and storages remain.
Personal project — not affiliated with, endorsed by, or sponsored by
DeepSeek or xAI; their names appear only to describe the open-source
components this package builds on.
Source, issues, full docs: https://github.com/HQ1995/deepseek-code
The bridge (grok-leader)
Grok leader-protocol unix-socket server that drives DeepSeek Harness agents for grok clients (the TUI, headless modes). It speaks the leader IPC envelope — 4-byte big-endian length plus JSON frames with a type discriminator — and carries ACP JSON-RPC 2.0 payloads inside acp frames, mapping them onto the harness services the ACP bridge drives. The wire facts are pinned by the real TUI capture in tests/fixtures/grok-tui-messages.jsonl and the protocol reference in docs/grok-leader-protocol.md.
This package is a transport adapter, not a UI integration. Interactive rendering stays with the grok client; dsh-side presentation features (plans, titles, usage cards) are not projected onto the wire yet.
Plugin
apply(ctx, config) binds a node:net unix socket after agents and sessionPersistence are ready, answers the registration handshake, and drives the remaining dsh services through structural reads. The socket file is unlinked on disposal, but the bridge never unlinks on EADDRINUSE: it fails loud instead of orphaning a live leader. The launcher owns the path and removes stale files before spawning the leader.
The package doubles as the dscode profile bundle: cordis.patch.yml mounts the server over dsh-base, disables HMR and the implicit DeepSeek route, and inserts the agent-presets roster (default: minimal) plus the host code runtime required by the code preset. A fresh profile is provider-neutral; users add any catalog or custom route through /provider. apps/cli profile-boot patches in the shipped preset root (apps/cli/config/agent-presets) for any composition whose rows include agent-presets.
| Config | Default | Meaning |
|---|
| socketPath | /tmp/dsh-grok-leader.sock | Unix socket path the grok clients connect to. |
| provider | — | Initial provider route for every created agent. |
| model | — | Initial model for every created agent. |
| combineQueuedPrompts | false | grok ui.combine_queued_prompts parity; env DSCODE_COMBINE_QUEUED=1 also enables it. |
| followUpBehavior | queue | What a prompt sent while a turn runs does: queue (grok parity) parks it until the turn ends — Enter on the queued row is Send Now, which cancels the running turn; steer folds it into the running turn at the harness's next step boundary without interrupting (Codex-style steering). Explicit config wins over the DSCODE_FOLLOW_UP env override. |
Protocol contract
| Method | Behavior |
|---|
| register / registered | The first envelope frame must be register; the leader answers registered with ready: true and a leader_binary_version at least the client version, or the TUI evicts and respawns its own leader. A non-register first frame gets envelope error 1, a second registration error 2, and a 30 s registration timeout error 3. |
| ping / pong | Keepalive pair exchanged every 30 s. |
| initialize | Advertises protocol version 1 and the xai.api_key auth method (the pager fails closed on an empty list), plus the persisted model catalog in _meta.modelState. Remote catalog discovery runs afterward and broadcasts an update, so endpoint latency never blocks startup. |
| authenticate | No-op because credentials belong to the harness-side providers; the advertised compatibility method only satisfies the pager's auth gate. |
| session/new | Creates a fresh agent with an absolute cwd; mcpServers must be an empty array (any non-array or non-empty value rejects). _meta.sessionId pins the session id. Permission modes default, workspace-write, plan, bypassPermissions, and always-approve map to dsh's permission/plan services; an explicit mode wins over _meta.yoloMode. Modes or CLI metadata the bridge cannot enforce (auto, acceptEdits, dontAsk, confining sandbox/tool/rule overrides, --no-subagents, and similar) reject instead of silently weakening the launch. sandbox=off and its none alias are accepted because they match the external dsh leader's actual unconfined execution. _meta.agentProfile (a string preset id) or the dsh-native _meta.agentPreset resolves through the preset roster and is recorded as meta.agentPreset; absent either, the roster default composes. Inline JSON agent definitions reject. |
| session/prompt | Flattens text and resource-link blocks, permits one in-flight request per session, echoes a user_message_chunk for the accepted prompt, then settles at the correlated turn end with the grok stopReason vocabulary; a turnless admission settles cancelled. |
| session/cancel | Cancels the addressed agent and settles its pending prompt as cancelled; unknown ids are no-ops. |
| session/update | Streams user_message_chunk, agent_message_chunk, agent_thought_chunk, tool_call, and tool_call_update notifications with per-session eventSeq and promptId stamps. |
| session/load | Validates cwd/mcpServers and CLI metadata like session/new, then resumes the durable session. Interactive loads replay the transcript as isReplay updates; rebuilds state without emitting prior text for headless output. Only the owning client may reload a live session. The latest durable preset and model selections win; a preset may change only before model-visible history exists. |
One connection may own several sessions. Each session has an independent prompt slot, workspace, cancellation path, model selection, and disposer; a disconnected client releases exactly its own sessions.
Slash commands and plugins
Preset support is registry-driven, not an allowlist. The four shipped presets are regression baselines (minimal: two native tools; code: run_code; standard: native catalog; cordis: native plus Cordis tools). User presets under dsh's preset root appear in /preset with their own ids and names. Their actual tool schemas drive TUI capability flags, and commands registered in their dsh scope flow through available_commands_update.
The automatic compatibility boundary is the standard dsh seams: presets, tools, commands, providers, models, settings, and session services. A plugin that requires a browser-only UI slot, a custom frontend panel, a private ACP method, or its own durable session-event vocabulary needs an explicit dscode adapter; pinned dsh rc.8 has no public downstream event-type registration seam. The bridge does not guess or silently emulate unsupported surfaces, and mount or protocol failures stay visible.
The bridge owns /dsh and the headless/raw /preset path. /preset changes composition only before model-visible history exists. Model-facing add-ons are not mounted globally over the preset layer: in particular, the shipped minimal preset remains exactly bash plus str_replace_editor. /loop is capability-gated and stays hidden unless a preset-scoped scheduling composition supplies it. Unsupported dsh extension commands (/compact, /delete, /remember, /mcps, and /skills) are hard-hidden in the TUI and explicitly refused by the bridge, so they never fall through as model prompts.
/dsh add [--trust] <spec> first installs into an isolated npm stage with lifecycle scripts disabled, parses and reports every bundle's composition patch, and requires --trust before registering any executable bundle. The real profile install is re-verified before its bundle list is atomically updated. /dsh remove <name> unregisters the bundle before uninstalling the dependency, so an npm failure leaves inert files rather than a broken profile reference. Core profile packages cannot be added or removed through this path.
Lifecycle
Client disconnect and Cordis disposal share the per-client teardown: owned agents cancel, pending prompts settle cancelled, and disposers run in parallel with every failure reported. Other frontends sharing the Context retain their agents.
Running
The dscode binary bootstraps the leader directly: it resolves the tested dsh from the launcher's DSH_BIN or an existing dsh on PATH, spawns dsh --profile dscode bound to the socket, removes any stale socket file first, waits for the socket, and attaches through the normal --leader path (third_party/grok-build/crates/codegen/xai-grok-pager/src/dsh_leader.rs). It never starts an npm install after entering the alternate screen. The same composition underneath is the agent loop, LLM adapters, session persistence, and this plugin in the dscode profile.
After changing this package, run scripts/update-bridge.sh (repo root): building alone does not replace the copy already installed in the profile. The script packs the local bridge, installs it without changing the profile's registry or file: dependency, and verifies the bridge, launcher, and composition files. A leader that is already running keeps its loaded code either way; it exits with its last client, and the next dscode spawn picks up the refreshed profile.
Model Experience
Prompt text
What the model sees
session/prompt text blocks are concatenated verbatim into one user message; a baseline resource link appears in that message as a bracketed resource_link reference the model may open with its own tools. Protocol metadata, client capabilities, permission choices, and session ids never enter the model request.
Token effect
Prompt tokens are data-dependent and remain in that session's history until compaction. Concurrent leader sessions retain independent contexts.
KV Cache effect
Append-only; the new user message follows the reusable request prefix and does not invalidate prior cache entries.
Permission decisions
What the model sees
Nothing directly. The owning tool records its allowed, rejected, cancelled, or unavailable outcome through the normal tool-result path.
Token effect
Only the owning tool result contributes tokens.
KV Cache effect
Append-only through the owning tool result.
Known Limitations and Deferred Work
- Control plane stubbed — control commands answer a ControlResult error; GetLeaderInfo and CpuProfileStatus are unimplemented (protocol.rs ControlCommand).
- Leader-version pin — leader_binary_version is pinned to 1.0.4 to satisfy the probe-verified TUI; it is not derived from the package version.
- Transcript projection incomplete — plans, titles, and usage cards stay off the wire; cancelRewind and sessionRecap mirror the captured stub without verified semantics.
- Provider-scoped model ids adapted — the first bare model id keeps the TUI-friendly spelling; collisions are qualified as
provider:model, and reasoning-effort memory is keyed by the underlying provider/model pair.
- Replay buffering — live notifications racing a session/load are dropped by the high-water mark instead of buffered for a gap-free flush (server.rs MAX_BUFFERED_LIVE_PER_LOAD).
- Unverified surfaces — the x.ai/ask_user_question request and answer shapes, capability injection into session/new, and the lock-file singleton guard carry TODO(verify) markers with grok file:line citations.
Running the tests
The bridge builds and tests against the official npm @deepseek-ai/dsh-*
packages, declared as devDependencies. No deepseek-harness checkout is
required.
pnpm install
pnpm run build
pnpm exec vitest run
License
Apache-2.0. See LICENSE.