docs: user documentation and headless sample host

This commit is contained in:
VolcanicAgenticCore
2026-09-25 12:36:24 +00:00
parent c220428cb7
commit 018c92af49
7 changed files with 879 additions and 1 deletions

View 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).