Bundle
dsh-subagent-claude-live
Claude Code as a real, streaming dsh subagent: session-backed child, free model/effort choice per call, live model list from the CLI
- Source
- Plaaasma
- License
- MIT
- Updated
- Updated 2 days ago
Readme
# dsh-subagent-claude-live
Claude Code as a **real, streaming subagent** inside [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`).
The harness ships a one-shot Claude Code provider (`@deepseek-ai/dsh-subagent-claude-code`). It works, but every provider row is pinned to one model, the model list is whatever you wrote into config, and the child is invisible while it runs — the parent only ever sees the final answer. This plugin does the three things that one cannot:
| | `@deepseek-ai/dsh-subagent-claude-code` | this plugin |
|---|---|---|
| Model choice | one provider row + one tool **per model**, fixed at config time | a `model` argument on every call: preset name, CLI alias, or any full id the CLI offers |
| Model discovery | none | `claude_models` asks the installed CLI what it offers right now, so a model released tomorrow is usable tomorrow |
| Effort / permission mode | fixed per row | per call (`effort`, `permission_mode`) |
| Visibility | final answer only; "Failed to read output" in sidebar job panes | the child is a **session-backed subagent**: it appears in the subagent catalog and its trajectory — thinking, text, tool calls, tool results — streams live, exactly like a spawned in-process child |
| Claude Code binary | the SDK's bundled payload (254 MB per profile, pinned to the plugin's SDK version) | your own `claude` on PATH by default, so CLI auto-updates flow through |
## Install
```sh
dsh plugin --profile web add dsh-subagent-claude-live
dsh plugin --profile headless add dsh-subagent-claude-live # optional
```
Restart the harness afterwards. Requirements:
- A working, authenticated Claude Code CLI on `PATH` (`claude --version`). Native settings, hooks, and login state are read from your normal `~/.claude`, exactly as the shipped provider does. Without one, set `executable: bundled` to run the Claude Code payload that the Agent SDK dependency installs alongside this plugin (about 250 MB per profile).
- dsh `>= 0.1.1-rc.1`.
## What the model sees
Two tools, registered on the host plane so every agent composition sees them:
**`claude_code`** — delegate one self-contained task.
| argument | meaning |
|---|---|
| `description` | short label, shown in the subagent list |
| `task` | the complete task text; Claude Code does not see the parent conversation |
| `model` | preset name, CLI alias (`opus`, `sonnet`, `haiku`, `fable`), or full id (`claude-fable-5-1`). Omit for the configured default |
| `effort` | `low` · `medium` · `high` · `xhigh` · `max`, on models that support it |
| `permission_mode` | `bypassPermissions` · `acceptEdits` · `auto` · `dontAsk` · `plan` — overrides the configured mode (can be disabled) |
| `run_in_background` | `true` returns a job id immediately; the child keeps streaming in the sidebar, the transcript is readable through `job_output`, `job_kill` cancels |
**`claude_models`** — the live model list (`supportedModels()` from the Claude Agent SDK, cached ten minutes; `refresh=true` bypasses the cache) plus the configured presets and default.
## How the streaming works
`start()` creates a real dsh child session (`origin: subagent`, `parentSession`, `delegationDepth`, one `subagent/descriptor`) and drives the official Claude Agent SDK with `includePartialMessages`. Every top-level SDK message is written into that session as the agent loop would have written it: `turn/start` → one `step` per assistant message (its `tool/call`s and their `tool/result`s stay inside that step, which is what the session log invariant requires) → `assistant/chunk` token deltas → `assistant/message` → `turn/end`. The web UI renders it live; on completion the session is flushed to persistence and leaves the live store, so the catalog then serves it cold — the same lifecycle as an in-process one-shot child.
Claude's own internal subagents (messages with a `parent_tool_use_id`) are not mirrored; their outcome arrives through the parent-level Task tool result.
The CLI process itself is spawned through `dsh-subprocess` via the SDK's custom-spawn hook, so it inherits the harness's credential-scrubbed environment and is torn down to whole-tree quiescence with the run.
## Configuration
Defaults from the bundle layer (`cordis.patch.yml`). Override by targeting `subagent-claude-live` in your profile's `cordis.patch.yml` — a config override replaces the whole object, so restate the keys you keep.
```yaml
- id: subagent-claude-live
config:
providerName: claude # name on ctx.subagents
toolName: claude_code
modelsToolName: claude_models
defaultModel: fable # preset, alias, full id, or '' for the CLI's own default
presets: # friendly name -> what the CLI accepts
fable-5.1: claude-fable-5-1
fable: fable
opus: opus
sonnet: sonnet
haiku: haiku
permissionMode: bypassPermissions
allowPermissionModeOverride: true
executable: '' # '' = `claude` on PATH / ~/.local/bin; 'bundled' = SDK payload; or a path
env: {} # layered over the scrubbed parent environment (e.g. ANTHROPIC_BASE_URL)
disposeGraceMs: 3000
modelsCacheMs: 600000
maxTurns: 0 # 0 = CLI default
aliases: {} # extra tool names with the model pinned, see below
```
### Aliases
`aliases` registers additional delegation tools with the model (and optionally the effort) pinned and no `model` parameter:
```yaml
aliases:
subagent_claude_fable: { model: fable }
review_with_opus: { model: opus, effort: high, description: 'Code review by Opus.' }
```
This exists for one reason: a session whose history contains a call to a tool name that no longer exists will happily issue that call again, and an OpenAI-compatible serving stack drops a call to an undeclared function before the harness ever sees it — the turn ends with nothing said and no error reaches the model. If you are replacing the shipped per-model `subagent_claude_*` tools, alias them for the sessions that remember them.
`bypassPermissions` is the default because a delegated coding task that stops at the first permission prompt is a failed task — there is no human on the child's side to answer it. It means the child edits and executes in the parent session's working directory with no checks. Set `permissionMode: dontAsk` (deny anything not pre-authorized in your Claude settings) or `plan` (read-only, returns a plan) if that is not what you want, and `allowPermissionModeOverride: false` to stop the model raising it per call.
## Limitations
- One fresh CLI process per run; no resume of a child conversation.
- No `outputSchema`, tool filter, persona, or depth enforcement — like every out-of-process provider, this one advertises no start-time capabilities.
- Credential-shaped ambient variables are scrubbed before spawn; an API key meant for the child goes in `env`.
## License
MIT
Install
dsh plugin --profile web add github:Plaaasma/dsh-subagent-claude-live
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-subagent-claude-live from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.