docs: user documentation and headless sample host
This commit is contained in:
83
docs/claude-code-provider.md
Normal file
83
docs/claude-code-provider.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Claude Code provider
|
||||
|
||||
`Volcanic.AgenticCore.Extensions.ClaudeCode` lets the core use a **Claude subscription** through the locally
|
||||
installed `claude` CLI (Claude Code) instead of an Anthropic API key. It is a C# port of
|
||||
[chem/pi-claude-code-provider](https://github.com/chem/pi-claude-code-provider); read that project's
|
||||
`DESIGN.md` for the full reasoning behind each decision.
|
||||
|
||||
## Requirements
|
||||
|
||||
- `claude` (Claude Code) 2.1.x on `PATH` (or `VOLCANIC_CLAUDE_CODE_PATH`), logged in with a claude.ai
|
||||
subscription (`claude auth status` must report `loggedIn: true`, `authMethod: claude.ai`,
|
||||
`apiProvider: firstParty`).
|
||||
- The .NET runtime (`dotnet`) on `PATH` or `Environment.ProcessPath`: `claude` spawns the bridge as
|
||||
`dotnet Volcanic.AgenticCore.Extensions.ClaudeCode.Bridge.dll`.
|
||||
- None of `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_BASE_URL`, `CLAUDE_CODE_OAUTH_TOKEN`,
|
||||
`CLAUDE_CODE_USE_BEDROCK`, `CLAUDE_CODE_USE_VERTEX` in the host environment: the extension refuses to run
|
||||
with them, because it is designed for subscription auth only.
|
||||
|
||||
Register it and pick a model:
|
||||
|
||||
```csharp
|
||||
o.AddExtension<ClaudeCodeProviderExtension>();
|
||||
o.DefaultModel = "claude-code/sonnet"; // or opus, haiku, fable
|
||||
```
|
||||
|
||||
Run `/claude-code-doctor` in a session to check the installation, auth, required CLI flags and the bridge
|
||||
handshake. It costs no quota.
|
||||
|
||||
## How it works
|
||||
|
||||
Every model call spawns a fresh `claude -p` process. Nothing is resumed on Claude's side: the whole
|
||||
conversation is replayed each time.
|
||||
|
||||
1. The active tools are written to a JSON catalogue; each tool becomes an MCP tool `mcp__volcanic__<alias>`.
|
||||
2. `claude` is started with `--tools ""` (all built-in tools off), `--permission-mode dontAsk`,
|
||||
`--strict-mcp-config --mcp-config <bridge>`, `--input-format stream-json --output-format stream-json
|
||||
--include-partial-messages --verbose`, `--system-prompt-file <tmp>`, `--model <id>`, `--effort <level>`,
|
||||
plus settings that disable hooks, auto-memory, plugins, slash commands and the token reminder (which would
|
||||
defeat prompt caching). Only an allow-list of environment variables is passed through, and compaction
|
||||
inside Claude is disabled: the core owns compaction.
|
||||
3. The bridge (a tiny console app) is the MCP server. It answers `initialize` and `tools/list` with the
|
||||
catalogue and touches a "ready" marker file. It **never executes** anything: `tools/call` returns an error
|
||||
and touches a "violation" marker that fails the request.
|
||||
4. The transcript is sent as one stream-json user message: a header record, then one JSON record per
|
||||
message (user, assistant with text/thinking/toolCall, toolResult). `@` characters are escaped because
|
||||
Claude expands `@path` references. A cache breakpoint is placed on the last record so Claude's prompt
|
||||
cache keeps hitting as the conversation grows. Images are written to a per-session content-addressed store
|
||||
and referenced as `@"path"` attachments.
|
||||
5. Claude's stream-json output is mapped to the core's streaming events. When the model asks for a tool
|
||||
(`message_delta.stop_reason == "tool_use"`), Claude would deny it itself (dontAsk mode), so the provider
|
||||
terminates the process immediately and publishes `Done(ToolUse)`. The core executes the tool and the next
|
||||
call replays the transcript including the result.
|
||||
6. The `system/init` record is validated strictly: the exposed tools, `permissionMode`, empty slash commands,
|
||||
`apiKeySource == "none"` (proof of subscription auth) and a connected `volcanic` MCP server. Anything else
|
||||
fails closed.
|
||||
|
||||
## Limitations (inherited from the design)
|
||||
|
||||
- Full-history replay costs more context than the Messages API and cannot replay thinking signatures.
|
||||
- Claude adds its own identity line and an environment block to every request; a Claude Code upgrade
|
||||
invalidates the prompt cache.
|
||||
- Claude still runs `git ls-files` / `rg --files` in the working directory at startup (slow in huge repos) and
|
||||
reads `.claude/settings*.json` there.
|
||||
- Mid-stream API recovery (`api_retry`, message id changes) fails the request as `stream_interrupted`; the
|
||||
core's retry policy retries it.
|
||||
- Cost is always reported as 0. Cancelling may still consume quota.
|
||||
- Windows: process-tree termination is best effort.
|
||||
|
||||
## Configuration
|
||||
|
||||
| Setting | Where | Default |
|
||||
|---|---|---|
|
||||
| CLI path | `VOLCANIC_CLAUDE_CODE_PATH` or `ClaudeCodeProviderOptions.ClaudePath` | `claude` on PATH |
|
||||
| Idle timeout | `VOLCANIC_CLAUDE_CODE_IDLE_TIMEOUT_MS` | 5 min |
|
||||
| Total timeout | `VOLCANIC_CLAUDE_CODE_TOTAL_TIMEOUT_MS` | 30 min |
|
||||
| Thinking | session thinking level → `--effort` (`low`…`max`), not sent for `haiku` | model default |
|
||||
|
||||
## Errors
|
||||
|
||||
Provider failures surface as assistant error messages with a code prefix, for example
|
||||
`[executable_missing] ...`, `[auth] ...`, `[mcp_startup] ...`, `[protocol_init] ...`. Two are special:
|
||||
`stream_interrupted` messages contain `stream ended before message_stop` (retried automatically), and context
|
||||
overflow messages start with `context_length_exceeded:` (the core compacts and retries once).
|
||||
Reference in New Issue
Block a user