mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-06 15:39:41 +02:00
feat(deepseek): add DeepSeek Harness (dsh) as a ninth CLI run mode
Adds `mode: 'deepseek'` alongside claude/shell/opencode/codex/gemini/ antigravity/pi/grok, plus a shortcut that opens the harness's own browser UI as a Codeman web tab. DeepSeek is wired unlike its siblings in three ways, each of which is the reason for a design decision rather than an accident: 1. The agent is a PROFILE, not the binary. `dsh` is a launcher over $DSH_HOME/profiles/<name>, and DeepSeek ships only `web`, `headless` and `base` -- the interactive terminal front door is always a third-party plugin. So availability is two questions: `isDeepSeekAvailable()` (binary) and `isDeepSeekRunnable()` (binary AND a pane-capable profile). The Run button gates on the latter, because reporting only the binary would spawn a pane that dies on arrival. When the binary is present but no profile is, the run menu offers to install one (POST /api/deepseek/install-profile). 2. The permission switch is an env var, not a flag. The harness has no command-line permission option; its sandbox/approval rows read DSH_PERMISSION_MODE (read-only / workspace-write / danger-full-access). Exported via `tmux setenv`, never on the spawn line. Absent = the harness's own workspace-write, which still asks, so the multi-user clamp is the only-if-sent branch and clamps to workspace-write, never read-only. 3. It is the only non-claude mode that passes hooksAvailableForMode(), and it earned that. The terminal front door reports idle/working/blocked to a supervising process over a generic env-gated contract; a generated shim (deepseek-status-shim.ts) makes Codeman that supervisor and forwards each report to /api/hook-event as stop / agent_working / permission_prompt. So a dsh session gets definitive respawn triggers, real wait-endpoint signals and real Approvals Inbox items instead of output-stabilization guesswork. `agent_working` is new (157th SSE constant) and joins APPROVAL_RESOLVING_EVENTS so a dialog answered in the terminal clears its alert at once. The resolver needs the strictest identity probe of the family: `dsh` is not merely a squattable npm name, Debian ships an unrelated `dsh` (dancer's shell), so `dsh --help` must print the harness's own banner before a candidate is handed a spawn line. Model is deliberately not a session field -- it is a composition entry in the profile's config tree. Env allowlist gains DSH_* and DEEPSEEK_* only; provider keys named by a settings-file `apiKeyEnv` stay out, which is pi's 34-provider-key problem in a new shape. Verified live against dsh 0.1.1-rc.2 and @deepseek-harness-tui/dsh-tui: the status endpoint's two-part answer, the no-profile refusal, the profile bootstrap, a real session whose pane runs `dsh --profile dsh-tui` with the permission mode injected via setenv, and the full status bridge -- a send-and-wait returned signal "stop" from a real turn, and blocked/working created and cleared an Approvals Inbox item. Docs: docs/deepseek-integration.md (guide), docs/deepseek-integration-plan.md (decisions + honest gaps). Tests: test/deepseek-mode.test.ts, test/deepseek-cli-resolver.test.ts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2501,6 +2501,16 @@ body.solo-mode .btn-lifecycle-log {
|
||||
color: #d4d4d8;
|
||||
}
|
||||
|
||||
/* DeepSeek: the vendor's own brand blue. Deliberately NOT added to the
|
||||
light-skin `--accent-d` override list above (which rescues gemini/antigravity/
|
||||
pi/grok, whose pastels wash out on paper backgrounds) — this indigo already
|
||||
carries enough contrast on the light skins, and overriding it would throw away
|
||||
the one cue that separates a dsh tab from its neighbours. */
|
||||
.session-tab .tab-mode.deepseek {
|
||||
background: rgba(77, 107, 254, 0.18);
|
||||
color: #7c93ff;
|
||||
}
|
||||
|
||||
/* Timer Banner - Compact */
|
||||
.timer-banner {
|
||||
display: flex;
|
||||
@@ -4975,6 +4985,25 @@ body.touch-device .terminal-container .xterm .xterm-helper-textarea {
|
||||
color: #fafafa;
|
||||
}
|
||||
|
||||
/* DeepSeek mode colors. Same cascade note as pi/grok above: this base-sheet pair
|
||||
only renders on the `og` skin — the nested `html:not([data-skin="og"])` block
|
||||
re-declares `.btn-toolbar.btn-run` at a HIGHER specificity, so deepseek also
|
||||
carries a rule inside that block (search `.btn-toolbar.btn-run.mode-deepseek`). */
|
||||
.btn-toolbar.btn-run.mode-deepseek,
|
||||
.btn-toolbar.btn-run-gear.mode-deepseek {
|
||||
background: linear-gradient(135deg, #101a4d 0%, #2740c4 55%, #4d6bfe 100%);
|
||||
border-color: rgba(124, 147, 255, 0.55);
|
||||
color: #eef2ff;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
.btn-toolbar.btn-run.mode-deepseek:hover,
|
||||
.btn-toolbar.btn-run-gear.mode-deepseek:hover {
|
||||
background: linear-gradient(135deg, #16225f 0%, #3350e6 55%, #6b83ff 100%);
|
||||
box-shadow: 0 0 12px rgba(77, 107, 254, 0.35), 0 2px 8px rgba(39, 64, 196, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||
border-color: rgba(150, 170, 255, 0.65);
|
||||
color: #f8faff;
|
||||
}
|
||||
|
||||
/* Dropdown menu */
|
||||
.run-mode-menu {
|
||||
display: none;
|
||||
@@ -5059,6 +5088,7 @@ body.touch-device .terminal-container .xterm .xterm-helper-textarea {
|
||||
.run-mode-dot.antigravity { background: #22d3ee; }
|
||||
.run-mode-dot.pi { background: #f472b6; }
|
||||
.run-mode-dot.grok { background: #a1a1aa; }
|
||||
.run-mode-dot.deepseek { background: #4d6bfe; }
|
||||
.run-mode-dot.shell { background: #94a3b8; }
|
||||
|
||||
/* Phone-only Enter button (see index.html). Hidden by default at every width;
|
||||
@@ -14403,6 +14433,16 @@ html:not([data-skin="og"]) {
|
||||
color: #fafafa;
|
||||
}
|
||||
.btn-toolbar.btn-run.mode-grok:hover { box-shadow: 0 0 14px -2px rgba(161, 161, 170, 0.5); }
|
||||
/* DeepSeek keeps its indigo on the non-og skins — same specificity trap as pi
|
||||
and grok above: without this rule the generic `.btn-toolbar.btn-run` in this
|
||||
nested block wins and deepseek renders as generic claude blue, which is the
|
||||
one colour it must not be mistaken for. */
|
||||
.btn-toolbar.btn-run.mode-deepseek {
|
||||
background: linear-gradient(135deg, #2740c4, #4d6bfe);
|
||||
border-color: #1b2a8f;
|
||||
color: #f8faff;
|
||||
}
|
||||
.btn-toolbar.btn-run.mode-deepseek:hover { box-shadow: 0 0 14px -2px rgba(77, 107, 254, 0.55); }
|
||||
.btn-toolbar.btn-run-gear {
|
||||
background: var(--accent-d);
|
||||
border-color: var(--accent);
|
||||
|
||||
Reference in New Issue
Block a user