Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ Supported provider names today:
- `acpx`: any ACP-compatible coding agent (Codex / Claude / Pi / Gemini / ...) via openclaw/acpx
- `claude`: local Claude Code CLI in print mode
- `cursor`: local Cursor Agent CLI (experimental; `doctor` is enabled by default)
- `deepseek`: DeepSeek OpenAI-compatible HTTP API; supports `map`, `review`, and `revalidate`, but not `fix`
- `grok`: local Grok Build CLI
- `minimax`: MiniMax OpenAI-compatible HTTP API; supports `map`, `review`, and `revalidate`, but not `fix`
- `opencode`: local OpenCode CLI
Expand Down
122 changes: 122 additions & 0 deletions docs/deepseek-live-proof.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Live DeepSeek proof for PR #135

Captured 2026-06-14 18:40–18:41 UTC against the real `https://api.deepseek.com/v1`
endpoint using the **built CLI from this branch** (`pnpm build` → `dist/cli.js`),
not a locally-patched install. This is the TypeScript port in `src/provider.ts`
of commit `ae63071`, compiled and executed end-to-end.

## Environment

```text
$ git log -1 --format="%H %s" origin/feat/deepseek-provider
ae63071 docs(providers): add redacted live DeepSeek API proof for PR #135

$ git rev-parse upstream/main
8a939cc6f85e3feb75ce1fb91ce7c391623ab30b (real openclaw/clawpatch main)

$ node -p "require('./package.json').version"
0.6.1 (cloned + reset, no version bump in this PR)

$ pnpm build
> tsc -p tsconfig.build.json
(no errors; dist/cli.js written, 17117 bytes)

$ node -e "import('fs').then(fs => console.log('dist/provider.js deepseek refs:', fs.readFileSync('dist/provider.js','utf8').match(/deepseek/g)?.length))"
dist/provider.js deepseek refs: 76
```

`DEEPSEEK_API_KEY` is intentionally not exported as an env var. The
upstream `src/provider.ts` reads `process.env["DEEPSEEK_API_KEY"]` first, and
the author's patched CLI (not part of this PR) falls back to
`~/.hermes/auth.json` `credential_pool.deepseek[0].access_token`. For these
runs the author sourced the key from `auth.json` and passed it explicitly:

```bash
DEEPSEEK_API_KEY=*** node dist/cli.js doctor --provider deepseek
```

`DEEPSEEK_API_KEY` here is the same key the existing local patch has been
using since 2026-06-07; the value is the same `sk-...` from
`~/.hermes/auth.json` `credential_pool.deepseek[0].access_token` and is
intentionally redacted in this transcript.

## doctor (connectivity check, built CLI)

```text
$ DEEPSEEK_API_KEY=*** node /home/fermin/git/clawpatch/dist/cli.js doctor --provider deepseek
root: /home/fermin/git/clawpatch
state: missing
provider: deepseek
model: deepseek-v4-flash
reasoningEffort: null
providerVersion: provider=deepseek default-model=deepseek-v4-flash base=https://api.deepseek.com/v1
secrets: redacted
```

`state: missing` is correct: there is no `.clawpatch/` inside the clawpatch
repo itself. Doctor still completes the `GET /models` round-trip through
`provider.check()`. The 30-second `DEEPSEEK_CHECK_TIMEOUT_MS` ceiling
applies; this run completed in <1 second.

## review (real review run, built CLI)

```text
$ DEEPSEEK_API_KEY=*** node /home/fermin/git/clawpatch/dist/cli.js review \
--limit 1 --jobs 1 --provider deepseek --model deepseek-v4-flash \
--root /home/fermin/git/budget
clawpatch review start run=20260614T184101-63fc19 features=1 jobs=1
clawpatch review feature-start index=1 total=1 feature=feat_library_4580b8205e title=Python source src/budget/:atm
clawpatch review feature-done index=1 total=1 feature=feat_library_4580b8205e findings=0 elapsed=31s
clawpatch review done run=20260614T184101-63fc19 reviewed=1 findings=0
run: 20260614T184101-63fc19
reviewed: 1
findings: 0
jobs: 1
report: /home/fermin/git/budget/.clawpatch/reports/20260614T184101-63fc19.md
next: clawpatch status
```

Elapsed: 31s for one bounded feature — well within the PR's
`DEEPSEEK_DEFAULT_TIMEOUT_MS = 1_800_000` (30 min) ceiling. Zero findings
on a clean feature (`feat_library_4580b8205e`, Python source
`src/budget/:atm`). The full request → response → JSON parse → Zod
validate → review-output assembly path works on the live API.

## revalidate (real revalidate run, built CLI)

```text
$ DEEPSEEK_API_KEY=*** node /home/fermin/git/clawpatch/dist/cli.js revalidate \
--finding fnd_sig-feat-library-41a3b4ec72-a1bc_ddf7653b5e \
--provider deepseek --model deepseek-v4-flash \
--root /home/fermin/git/budget
clawpatch revalidate start run=20260614T184132-61f0fe findings=1
clawpatch revalidate finding-start index=1 total=1 finding=fnd_sig-feat-library-41a3b4ec72-a1bc_ddf7653b5e title=_latest_active_month crashes on empty month_summaries
clawpatch revalidate finding-done index=1 total=1 finding=fnd_sig-feat-library-41a3b4ec72-a1bc_ddf7653b5e outcome=open elapsed=4s
clawpatch revalidate done run=20260614T184132-61f0fe revalidated=1 fixed=0 open=1 uncertain=0 falsePositive=0
finding: fnd_sig-feat-library-41a3b4ec72-a1bc_ddf7653b5e
outcome: open
reasoning: The current code at the exact location still has the same vulnerability: `report.month_summaries[-1].month_number` raises IndexError when `month_summaries` is empty. No patch has been applied.
```

4s for one revalidate. Outcome `open` is the correct determination for this
finding: the model examined the code at the cited lines and confirmed the
bug is still present (no patch was applied between runs). The reasoning
text is content the model generated, not a synthesized string — the
`revalidate` schema's reasoning field is populated from the provider's
response content, validating the full content-extraction path.

## what the proof shows

| Operation | Status | Time | Evidence |
| ---------------- | ------ | ---- | -------------------------------------------------------------------------------------------------------- |
| `check` (doctor) | ✅ | <1s | `providerVersion: provider=deepseek default-model=deepseek-v4-flash base=https://api.deepseek.com/v1` |
| `review` | ✅ | 31s | 1 feature, 0 findings (clean code path), runtime `lastRun: 20260614T184101-63fc19` |
| `revalidate` | ✅ | 4s | 1 finding re-checked, outcome `open` with non-empty provider-generated reasoning |
| `fix` | n/a | — | Not supported (chat completions API has no FS access). Mirrors the PR's `unsupported-provider` contract. |

All three supported operations work end-to-end against the live API,
through the **TypeScript code in this PR**, built with `tsc`, not through
the previously-patched JS install. The PR's port is a clean
re-implementation of this proven shape with added TypeScript types,
`extractJson` instead of `JSON.parse(content)`, and byte-bounded response
reads. The behavior is functionally identical.
60 changes: 59 additions & 1 deletion docs/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Provider names today:
- `claude`: shells out to Claude Code in print mode (`claude -p`)
- `grok`: shells out to the xAI Grok Build CLI in headless mode (`grok --prompt-file`)
- `minimax`: calls the MiniMax OpenAI-compatible HTTP API directly
- `deepseek`: calls the DeepSeek OpenAI-compatible HTTP API directly
- `opencode`: shells out to `opencode run --format json`
- `pi`: shells out to `pi -p` (non-interactive print mode)
- `cursor`: shells out to `cursor-agent -p --output-format json`
Expand Down Expand Up @@ -401,4 +402,61 @@ checkout and avoid sending secret-bearing files.
Direct local-model and multi-model panel providers are not implemented yet. The
`acpx` provider is the generic route for ACP-compatible agents; the `grok`,
`opencode`, `pi`, and `cursor` providers are direct integrations for local CLIs;
the `minimax` provider is a direct integration for the MiniMax HTTP API.
the `minimax` and `deepseek` providers are direct integrations for pay-per-use
HTTP APIs.

## DeepSeek

The `deepseek` provider calls the DeepSeek OpenAI-compatible HTTP API directly.
It requires a pay-per-use API key in `DEEPSEEK_API_KEY` and defaults to
`https://api.deepseek.com/v1`.

```bash
export DEEPSEEK_API_KEY=sk-...
clawpatch doctor --provider deepseek
clawpatch review --provider deepseek
clawpatch review --provider deepseek --model deepseek-v4-pro
```

How the DeepSeek provider works:

- Endpoint: `POST ${DEEPSEEK_BASE_URL:-https://api.deepseek.com/v1}/chat/completions`
with `Authorization: Bearer ${DEEPSEEK_API_KEY}`. `DEEPSEEK_BASE_URL` is
trimmed, normalized, and must use `https` unless it targets loopback HTTP for
local development; the bearer token is sent to that configured endpoint.
- Operations: `map`, `review`, and `revalidate` are supported. `fix` is not
supported by this provider in this PR: clawpatch's `fix` is an agentic
tool-loop (model emits tool calls → host executes them in the worktree →
model sees results → …) and this provider exposes DeepSeek's
chat-completions API as a single text/JSON responder, not a tool-call
router. DeepSeek's API does support a `tools` parameter and the v4 model
can call tools, but wiring that up to clawpatch's tool-loop is out of
scope for this PR and matches the contract the `minimax` provider on
`main` already publishes. `fix` fails before checking credentials or
making network calls with `unsupported-provider` and exit code 2.
- Structured output: DeepSeek's chat completions API supports
`response_format: {type: "json_object"}` but **rejects
`response_format: {type: "json_schema", ...}` with HTTP 400**. Clawpatch
therefore sends `{type: "json_object"}`, embeds the provider schema in the
prompt, asks the model to return one JSON object, and validates it locally
with the same Zod schemas used by other providers. See `provider.ts` for the
load-bearing comment near `deepseekRequestBody`.
- Model selection: `--model <name>` sets the request `model`; otherwise
`DEEPSEEK_MODEL` is used, then `deepseek-v4-flash`.
- HTTP failures: `401` and `403` map to exit code 4; `402` (insufficient
pay-per-use balance) and `429` map to exit code 5; other non-2xx statuses map
to exit code 1. Error bodies are reduced to safe `error.type` / `error.code` /
`error.param` signals and byte counts; the raw `error.message` is never
logged.
- Timeout: 30 minutes by default for provider calls, override with
`CLAWPATCH_DEEPSEEK_TIMEOUT_MS` or `CLAWPATCH_PROVIDER_TIMEOUT_MS`. The
`/models` doctor probe uses a 30-second timeout. Clawpatch uses a custom
undici dispatcher and an operation abort signal so socket headers/body
timeouts and full response-body reads track the configured timeout.
- Bounds: request bodies over 64 MiB and responses over 10 MiB fail before
parsing; error bodies are capped separately.

Permission caveat: DeepSeek is a remote API provider. Review inputs are sent to
the configured DeepSeek endpoint, and read-only behavior depends on the remote
model following the prompt. For untrusted code, run clawpatch in an isolated
checkout and avoid sending secret-bearing files.
1 change: 1 addition & 0 deletions docs/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,7 @@ Implemented providers:
- `cursor`: experimental Cursor Agent CLI integration.
- `grok`: Grok Build CLI.
- `minimax`: MiniMax OpenAI-compatible HTTP API for map, review, and revalidate.
- `deepseek`: DeepSeek OpenAI-compatible HTTP API for map, review, and revalidate.
- `opencode`: OpenCode CLI.
- `pi`: pi coding agent.
- `mock` / `mock-fail`: deterministic test providers.
Expand Down
Loading