Add claude engine: headless Claude Code workers, incl. free local inference via Anthropic-compatible endpoints - #76
Open
perpetualsec wants to merge 1 commit into
Conversation
New engine wrapper engines/claude-sandboxed.sh runs the `claude` CLI as a headless Ringer worker under the same macOS Seatbelt design as the OpenCode wrapper. Endpoint config is machine-specific, so it lives outside the repo: the wrapper sources ~/.config/ringer/claude-engine.env when present, which lets the engine run against any Anthropic-compatible /v1/messages endpoint (Ollama, LM Studio, LiteLLM) — a worker lane with no API bill and no prompt leaving hardware you control. With no env file, plain `claude` auth applies. The commented [engines.claude] sample block follows the grok/mock convention and records two findings from probe runs against a 27B qwen3.6 over Ollama: headless claude's injected context (MCP schemas, plugin hooks, global CLAUDE.md) derails small local models — `--bare` + `--strict-mcp-config` is the fix and drops harness overhead from 16k-32k input tokens to ~1.2k; and `--bare` honors ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN despite its help text documenting only API-key auth. MODEL-NOTES carries the dated entry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
A fourth worker lane:
engines/claude-sandboxed.sh, which runs theclaudeCLI (Claude Code) headlessly as a Ringer worker, plus a commented[engines.claude]block inconfig.sample.tomland a dated entry indocs/MODEL-NOTES.md.The wrapper is deliberately the same shape as
engines/opencode-sandboxed.sh— taskdir as the first arg,--no-sandboxas the second wired tofull_access_argsso theallow_full_accessgate still governs escapes, paths reaching the Seatbelt profile as-Dparameters rather than string interpolation, per-run scratch dir wired asTMPDIR/XDG_CACHE_HOME. Nothing new is invented;claudesimply has no OS sandbox of its own in headless mode, exactly like OpenCode.No machine-specific values live in repo code. The wrapper sources
~/.config/ringer/claude-engine.envif that file exists, and that is where the operator putsANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN/ANTHROPIC_API_KEY. Both modes are documented in the header:claudeauth applies (normal Anthropic API)./v1/messagesendpoint: Ollama, LM Studio, LiteLLM, a self-hosted gateway.Why
Mode 2 is the point. It gives Ringer its first local-inference lane: a real model doing real work with no API bill and no prompt leaving hardware the operator controls. That is the mock engine's "try Ringer without a bill" purpose, except the worker actually reasons and the executed check is a genuine gate rather than a rehearsal. It also means the
{model}placeholder now reaches models with no hosted route at all.Two empirical findings, and how they were earned
Both are recorded in
docs/MODEL-NOTES.mdand in the sample block's comments, because both are the kind of thing that costs someone else a day.Probe target was a 27B qwen3.6 served by Ollama, driven through this engine with
claude -p --output-format json. It took two failed probe cycles before the cause was isolated, then a pass:--strict-mcp-config): the worker inherited the operator's entire MCP server roster, made an unrelated tool call, hit a schema error, and abandoned the task — its final answer was an essay on an unrelated subject. Neither required file was written.--strict-mcp-configonly): MCP tool calls stopped, but that flag does not stop plugin hooks or the always-loaded global~/.claude/CLAUDE.md. The worker hallucinated a blocker that reads as prior-session context bleeding in, then ran past the 32k output-token ceiling without finishing.--bare+--strict-mcp-config): pass on attempt 2 of the same one-task manifest, with an executed check — run the generated script, compare its output and a SHA-256 — confirming the artifact rather than a worker self-report.So, finding one: headless Claude Code's injected context derails small local models, and
--bareis the actual fix. It skips hooks, plugin sync, auto-memory, and CLAUDE.md auto-discovery;--strict-mcp-configstays alongside it belt-and-suspenders, since worker tasks have no business calling the operator's MCP servers either way. Harness overhead drops from 16k–32k input tokens per turn to ~1.2k, but the token saving is a side benefit — the point is that the spec stops competing with injected background.Finding two:
--barehonorsANTHROPIC_BASE_URLandANTHROPIC_AUTH_TOKENeven though its--helptext describes auth in that mode as strictlyANTHROPIC_API_KEYorapiKeyHelper(verified 2026-07-24,claudeCLI on macOS). That help text reads like a hard blocker for alternative-endpoint setups and is not one. Without this, findings one and two look mutually exclusive and the whole lane looks impossible.Both flags are passed from
args_templaterather than hardcoded in the wrapper, so a fully-configured Claude Code worker is still possible when that is what you want.Verification
python3 -m unittest discover -s tests→ 218 tests, OK, includingtests/test_contributors.py. No failures, nothing skipped around this change.<taskdir> -p --output-format json --dangerously-skip-permissions --bare --strict-mcp-config --model <slug> "reply PONG"): returned"result":"PONG"withinput_tokens: 1117— the ~1.2k figure above, measured, not asserted — and"canonicalModel":"qwen3.6:latest".claudeonPATHthat prints its environment, so the sourcing logic is proven rather than reviewed: env-file mode passesANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN/emptyANTHROPIC_API_KEYthrough; with the file absent the wrapper adds nothing and leaves normalclaudeauth alone. Sandboxed and--no-sandboxpaths both checked, and child exit status propagates (stub exiting 42 → wrapper exits 42).--output-format jsonpayload above, not written from memory.The Seatbelt profile needed no change for the env file: it is sourced by the wrapper before
sandbox-execis entered, so no read rule is involved.Notes for review
[engines.grok]/[engines.mock]convention — machine-specificbinpath, placeholder endpoint host, the caveats in comments next to the setting they explain.tests/test_contributors.pyrequires.main(4ac3791), single commit.🤖 Generated with Claude Code