diff --git a/cli/README.md b/cli/README.md index d62c8475..fc1e4873 100644 --- a/cli/README.md +++ b/cli/README.md @@ -163,8 +163,9 @@ environment automatically. See `src/configuration.ts` for all options. -DeepSeek Harness ACP uses `dsh-acp-demo` by default. Override the executable or -its arguments without shell parsing: +DeepSeek Harness ACP uses the official `dsh` CLI with `--profile acp` by default +(the ACP profile ships with `@deepseek-ai/dsh` since 0.2.0). Override the +executable or its arguments without shell parsing: ```bash export HAPI_DSH_ACP_COMMAND=dsh-acp-demo @@ -172,6 +173,11 @@ export HAPI_DSH_ACP_CONFIG=/path/to/deepseek-harness/examples/acp-agent/cordis.y hapi dsh ``` +`HAPI_DSH_ACP_CONFIG` maps to `--config` for the configured server. Without +`HAPI_DSH_ACP_COMMAND` it instead applies to the official CLI, which has no +`--config` flag — the path is passed as a `--patch` overlay on top of +`--profile acp`. + For a source checkout, use JSON arguments: ```bash @@ -196,8 +202,8 @@ controls for DSH. - `HAPI_EXPERIMENTAL` - Enable experimental features (true/1/yes). - `HAPI_EXTRA_HEADERS_JSON` - JSON object of extra headers to send on CLI → hub requests, e.g. `{"Cookie":"CF_Authorization=..."}`. Can also be set as the `extraHeaders` object in `~/.hapi/settings.json` (environment variable wins). - `HAPI_CLAUDE_PATH` - Path to a specific `claude` executable. -- `HAPI_DSH_ACP_COMMAND` - ACP server executable for `hapi dsh` (default: `dsh-acp-demo`). -- `HAPI_DSH_ACP_CONFIG` - Optional `dsh-acp-demo --config` path. +- `HAPI_DSH_ACP_COMMAND` - ACP server executable for `hapi dsh` (default: `dsh` with `--profile acp`). +- `HAPI_DSH_ACP_CONFIG` - Optional config path for the configured DSH ACP server (`--config` with `HAPI_DSH_ACP_COMMAND`, `--patch` overlay on the default official CLI). - `HAPI_DSH_ACP_ARGS_JSON` - Optional JSON array of ACP server arguments. - `HAPI_HTTP_MCP_URL` - Default MCP target for `hapi mcp`. diff --git a/cli/src/agent/agentAvailability.test.ts b/cli/src/agent/agentAvailability.test.ts index 8d3e1ac0..dd28b75f 100644 --- a/cli/src/agent/agentAvailability.test.ts +++ b/cli/src/agent/agentAvailability.test.ts @@ -91,6 +91,7 @@ describe('agent executable resolution', () => { it('rejects malformed or missing DSH static configuration', async () => { const directory = await mkdtemp(join(tmpdir(), 'hapi-agent-path-')) + await makeExecutable(directory, 'dsh') await makeExecutable(directory, 'dsh-acp-demo') expect(getAgentAvailability('dsh', { PATH: directory, @@ -107,6 +108,11 @@ describe('agent executable resolution', () => { PATH: directory, HAPI_DSH_ACP_CONFIG: config, })).toEqual({ agent: 'dsh', available: true }) + expect(getAgentAvailability('dsh', { + PATH: directory, + HAPI_DSH_ACP_COMMAND: 'dsh-acp-demo', + HAPI_DSH_ACP_CONFIG: config, + })).toEqual({ agent: 'dsh', available: true }) }) it('accepts a macOS Codex app executable when the CLI is absent', async () => { diff --git a/cli/src/agent/agentLaunchCommand.ts b/cli/src/agent/agentLaunchCommand.ts index 5a9607f7..cb63681d 100644 --- a/cli/src/agent/agentLaunchCommand.ts +++ b/cli/src/agent/agentLaunchCommand.ts @@ -11,7 +11,7 @@ const DEFAULT_COMMANDS: Record = { codex: 'codex', copilot: 'copilot', cursor: 'agent', - dsh: 'dsh-acp-demo', + dsh: 'dsh', gemini: 'gemini', grok: 'grok', kimi: 'kimi', diff --git a/cli/src/dsh/utils/dshBackend.test.ts b/cli/src/dsh/utils/dshBackend.test.ts index 5a378f43..5a71a31c 100644 --- a/cli/src/dsh/utils/dshBackend.test.ts +++ b/cli/src/dsh/utils/dshBackend.test.ts @@ -2,20 +2,45 @@ import { describe, expect, it } from 'vitest' import { resolveDshAcpCommand } from './dshBackend' describe('resolveDshAcpCommand', () => { - it('uses the official ACP demo executable by default', () => { + it('defaults to the official dsh CLI with the ACP profile', () => { expect(resolveDshAcpCommand({})).toEqual({ - command: 'dsh-acp-demo', - args: [] + command: 'dsh', + args: ['--profile', 'acp'] }) }) - it('supports a config path for dsh-acp-demo', () => { - expect(resolveDshAcpCommand({ HAPI_DSH_ACP_CONFIG: '/tmp/cordis.yml' })).toEqual({ + it('supports a config path override', () => { + expect(resolveDshAcpCommand({ + HAPI_DSH_ACP_COMMAND: 'dsh-acp-demo', + HAPI_DSH_ACP_CONFIG: '/tmp/cordis.yml' + })).toEqual({ command: 'dsh-acp-demo', args: ['--config', '/tmp/cordis.yml'] }) }) + it('maps a config-only override onto the official CLI profile and patch overlay', () => { + // The official CLI has no `--config` flag: config overlays go through + // `--patch` on top of a profile, and the ACP server still needs + // `--profile acp` to boot. `--config` stays a legacy-server escape + // hatch behind HAPI_DSH_ACP_COMMAND. + expect(resolveDshAcpCommand({ + HAPI_DSH_ACP_CONFIG: '/tmp/cordis.yml' + })).toEqual({ + command: 'dsh', + args: ['--profile', 'acp', '--patch', '/tmp/cordis.yml'] + }) + }) + + it('keeps bare arguments for an explicitly configured server', () => { + // Custom wrappers must not silently receive the official CLI's + // `--profile acp` flags; they opt into args via ARGS_JSON or CONFIG. + expect(resolveDshAcpCommand({ HAPI_DSH_ACP_COMMAND: 'dsh-acp-demo' })).toEqual({ + command: 'dsh-acp-demo', + args: [] + }) + }) + it('supports source checkouts and community servers through JSON args', () => { expect(resolveDshAcpCommand({ HAPI_DSH_ACP_COMMAND: 'pnpm', diff --git a/cli/src/dsh/utils/dshBackend.ts b/cli/src/dsh/utils/dshBackend.ts index 3cbcf589..95da7206 100644 --- a/cli/src/dsh/utils/dshBackend.ts +++ b/cli/src/dsh/utils/dshBackend.ts @@ -21,16 +21,32 @@ function parseArgsJson(raw: string): string[] { return value } +/** Default ACP entrypoint: the official `dsh` CLI ships the ACP profile since 0.2.x. */ +export const DSH_DEFAULT_ACP_COMMAND = 'dsh' +export const DSH_DEFAULT_ACP_ARGS = ['--profile', 'acp'] + export function resolveDshAcpCommand(env: DshAcpEnvironment = process.env): { command: string args: string[] } { - const command = env.HAPI_DSH_ACP_COMMAND?.trim() || 'dsh-acp-demo' + const override = env.HAPI_DSH_ACP_COMMAND?.trim() + const command = override || DSH_DEFAULT_ACP_COMMAND if (env.HAPI_DSH_ACP_ARGS_JSON?.trim()) { return { command, args: parseArgsJson(env.HAPI_DSH_ACP_ARGS_JSON.trim()) } } const config = env.HAPI_DSH_ACP_CONFIG?.trim() - return { command, args: config ? ['--config', config] : [] } + if (config) { + // The official CLI has no `--config` flag: it composes config overlays + // via `--patch` on top of a profile, so a config-only override still + // needs `--profile acp`. Legacy community servers that speak + // `--config` must be pinned with HAPI_DSH_ACP_COMMAND. + if (override) return { command, args: ['--config', config] } + return { command, args: [...DSH_DEFAULT_ACP_ARGS, '--patch', config] } + } + // An explicitly configured server keeps bare `[]` args: custom wrappers + // must not silently receive the official CLI's `--profile acp` flags. + if (override) return { command, args: [] } + return { command, args: DSH_DEFAULT_ACP_ARGS } } export function createDshBackend(env: NodeJS.ProcessEnv = process.env): AcpSdkBackend { diff --git a/docs/guide/agents.md b/docs/guide/agents.md index 5500d269..26c00c7d 100644 --- a/docs/guide/agents.md +++ b/docs/guide/agents.md @@ -19,7 +19,7 @@ agent's integration; their supported syntax varies by agent. | GitHub Copilot | `hapi copilot` | ACP (`copilot --acp --stdio`) | ✓ | ✓ | `default` `read-only` `safe-yolo` `yolo` | ✓ | | Kimi | `hapi kimi` | ACP (`kimi acp`) | ✓ | ✓ | `default` `read-only` `safe-yolo` `yolo` | ✓ | | OpenCode | `hapi opencode` | ACP (`opencode acp`) | ✓ | ✓ | `default` `plan` `yolo` | ✓ | -| DeepSeek Harness | `hapi dsh` | ACP (`dsh-acp-demo` or configured server) | — | ✓ | Managed by DSH ACP composition | — | +| DeepSeek Harness | `hapi dsh` | ACP (`dsh --profile acp` or configured server) | — | ✓ | Managed by DSH ACP composition | — | | Antigravity (agy) | `hapi agy` | Headless print mode (per-turn `agy -p` + NDJSON) | — | ✓ | `request-review` `always-proceed` | ✓ | | Pi | `hapi pi` | `pi --mode rpc` (JSON-line RPC over stdio) | — | ✓ | none (always auto-approve) | ✓ | | Gemini CLI | — | **Removed** — Google sunset the consumer Gemini CLI (2026-06-18) | — | — | — | — | @@ -256,12 +256,22 @@ If a remote session reports authentication failure, run `grok login --device-aut ## DeepSeek Harness `hapi dsh` uses the shared ACP transport and keeps DSH's runtime outside HAPI. The -default executable is `dsh-acp-demo`; configure a different ACP server or a +default entrypoint is the official `dsh` CLI with `--profile acp` (the ACP profile +ships with `@deepseek-ai/dsh` since 0.2.0). Configure a different ACP server or a source checkout with `HAPI_DSH_ACP_COMMAND`, `HAPI_DSH_ACP_CONFIG`, or the JSON -argument array `HAPI_DSH_ACP_ARGS_JSON`. +argument array `HAPI_DSH_ACP_ARGS_JSON`. `HAPI_DSH_ACP_CONFIG` passes a `--config` +path to the server named by `HAPI_DSH_ACP_COMMAND`; on the default official CLI +(which has no `--config` flag) it is applied as a `--patch` overlay on top of +`--profile acp`. -The official demo is published as `@deepseek-ai/dsh-acp-demo`; use an exact -version such as `0.1.0-rc.7` rather than npm's stale `latest` tag: +```bash +npm install -g @deepseek-ai/dsh@0.2.0-rc.2 +hapi dsh +``` + +The standalone legacy demo is still published as `@deepseek-ai/dsh-acp-demo`; use +it through an explicit override and an exact version such as `0.1.0-rc.7` rather +than npm's stale `latest` tag: ```bash npm install -g @deepseek-ai/dsh-acp-demo@0.1.0-rc.7