Claude Agent SDK, Wizard polish#6
Merged
Merged
Conversation
The wizard now asks which auth path the user wants and prompts only for the chosen credential: - Subscription (Pro/Max/Team): CLAUDE_CODE_OAUTH_TOKEN via `claude setup-token`. No per-message dollar figure is logged — usage is billed against the plan. - API key (pay-as-you-go): ANTHROPIC_API_KEY. Cost is logged per turn. config.auth_mode resolves to "subscription" / "api_key" / "" based on which credential is configured. _build_env scrubs every Claude/Anthropic env var from the subprocess env before injecting exactly the one matching the chosen mode, so a leftover token from a prior shell can't override the selection. When the wizard switches modes it also clears the dropped credential from the running process's os.environ, otherwise MemclawConfig would re-read the stale value through python-dotenv on the same run. ALLOWED_USER_IDS is now required during `memclaw telegram` setup — without it the bot rejects every message, so calling it optional was misleading. Prompt order is now auth-mode panel → chosen Claude credential → OpenAI (labelled with what it powers) → channel-specific keys. CLI auth gate is factored into a single _require_claude_auth() helper. The dead _session_id field on MemclawAgent is removed.
Served its purpose diagnosing the dotenv-reload bug fixed in the previous commit. Kept on branch feat/claude-agent-sdk-debug-env in case it's needed again.
- Rewrite agent.py module docstring to match the two-mode auth behavior (the WIP version claimed it always strips ANTHROPIC_API_KEY). - Add SAFETY comment on permission_mode="bypassPermissions" naming the guardrails (allowed_tools + _BUILTIN_TOOLS_DISALLOW) it depends on. - Migrate test_agent.py from mocking the removed anthropic._client to mocking ClaudeSDKClient via a helper that yields real AssistantMessage and ResultMessage so isinstance checks still hold. - Cap claude-agent-sdk to <0.2 — 0.1.x ships breaking changes between minor versions.
…ocol
MemclawAgent now owns memory, search, history, consolidation, and the
system-prompt shape, but delegates every LLM call to an AgentBackend
(memclaw/backends/base.py). The Claude Agent SDK becomes one of N
possible backends instead of being hardwired into the agent.
Selection happens via the AGENT_BACKEND env var (config.agent_backend),
defaulting to "claude". The wizard auto-shows a selector panel as soon
as a second backend is registered; with only Claude available today it
stays silent.
Layout:
- memclaw/backends/base.py AgentBackend protocol + TurnResult
- memclaw/backends/claude.py Claude SDK guts, MCP wrapping, env scrubbing,
stream-json image protocol, auth-mode UX
- memclaw/backends/__init__.py REGISTRY + build_backend()
Each backend owns: credential prompts (wizard_setup), readiness check
(is_configured), env-var construction for its subprocess, native tool
plumbing, response parsing, usage/cost normalization. The protocol is
intentionally narrow — no SDK types leak into MemclawAgent.
Adding a backend (e.g. Cursor SDK) is now: implement the protocol, add
the class to REGISTRY, set AGENT_BACKEND=<name>. No changes needed in
agent.py, tools.py, store/index/search/reminders, or the bot handlers.
Tests are split: tests/test_agent.py exercises orchestration with a
FakeBackend; tests/backends/test_claude.py covers Claude-specific
behavior (auth mode, env scrubbing, run_one_shot, run_turn) using the
existing SDK mock helper.
Drive-by: cap claude-agent-sdk dependency takes effect in uv.lock
(0.2.x → 0.1.x).
- Badge: Anthropic API → Claude Agent SDK. - Rewrite the Architecture intro and Agent Layer paragraph to describe claude-agent-sdk (which drives the claude CLI subprocess) instead of the raw Anthropic API + hand-rolled loop. - Add a Configuration subsection that explains the two auth modes: Claude subscription (CLAUDE_CODE_OAUTH_TOKEN, no per-message cost) and Anthropic API key (ANTHROPIC_API_KEY, pay-as-you-go). - Restructure the variables table: CLAUDE_CODE_OAUTH_TOKEN and ANTHROPIC_API_KEY are now "One of these two"; add AGENT_BACKEND (optional). Rename the section to "Environment variables" since the table also lists allowlists and selectors that aren't API keys. - Filesystem-guardrail bullet: drop the stale "SDK-level callback" framing — the path check now lives in the tool executor. - Acknowledgments: point at claude-agent-sdk for the runtime, Anthropic for Claude itself. - Tighten pitch copy: "two API keys" / "your API keys" → "two API tokens" / "your API tokens" so the wording covers both an OpenAI API key and a Claude OAuth token.
Splits the figlet output into "mem" (white) and "claw" (cyan) halves and assembles them with rich.Text so the color split mirrors the logo. The two halves are pre-generated and pasted in as string constants — pyfiglet is only needed to regenerate, not at runtime. Banner sits between matching blank lines so it visually separates from the prompt above and the welcome panel below.
Register an error handler that logs NetworkError/TimedOut as a single warning line so PTB's polling loop can retry quietly instead of dumping a full traceback when the socket dies during sleep.
Replace Prompt.ask with a custom cbreak-mode reader for the setup wizard's secret fields so pasted API keys and tokens echo only their first 4 characters in clear; everything after shows as '*'. Applies to both the generic KEYS loop and the Claude backend's credential prompt.
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.
Summary
Migrates the agent from raw Anthropic API calls to
claude-agent-sdk,adds a pluggable
AgentBackendprotocol so future SDKs (Cursor, OpenAIAgents) can drop in without touching the agent core, and gives users two
ways to authenticate Claude: a subscription OAuth token or an Anthropic
API key.
This is Phase 1 of the harness-agnostic agent layer — only the Claude
backend ships; the protocol is in place for a separate Cursor-SDK PR.
Highlights
Agent backend abstraction
memclaw/backends/package:base.pydefinesAgentBackend+TurnResult,claude.pyis the first implementation,__init__.pyholds the registry.
MemclawAgentowns memory / search / history / consolidation / systemprompt; every LLM call goes through
backend.run_turn(with tools) orbackend.run_one_shot(consolidation). No SDK imports remain inagent.py.AGENT_BACKENDenv var, defaulting toclaude. The wizard auto-shows a selector panel once a second backendis registered.
Two-mode Claude auth
CLAUDE_CODE_OAUTH_TOKENfromclaude setup-token.Billed against your Claude plan; the per-message cost line is omitted
from logs.
ANTHROPIC_API_KEY(sk-ant-…). Pay-as-you-go; cost islogged per turn.
_build_envstrips every Claude/Anthropic credential env var beforeinjecting exactly one based on the chosen mode — a leftover token in
the parent shell can't shadow the selection.
Wizard polish
(
wizard_setup()).characters verbatim and
*after — pasted tokens don't sit in thescrollback in clear.
ALLOWED_USER_IDSis now required duringmemclaw telegramsetup(without it the bot rejected every message — calling it optional was
misleading).
the saved
.envand the liveos.environ, soMemclawConfigcan'tre-read a stale value mid-run.
Telegram resilience
NetworkError/TimedOutfrom thepolling socket (laptop sleep/wake, transient connectivity drops) logs
a single warning line and lets PTB retry quietly, instead of dumping
a full traceback every time.
Tests
tests/test_agent.pyrewritten to exercise orchestration against aFakeBackend— no SDK dependency in agent-level tests.tests/backends/test_claude.pycovers Claude-specific behavior(auth-mode detection, env scrubbing,
run_one_shot,run_turn) via amock
ClaudeSDKClient.Docs
subsection; variables table renamed to "Environment variables" and
restructured around the OR between OAuth/API key;
AGENT_BACKENDrowadded; pitch and Quick Start copy switched from "API keys" → "API
tokens" to cover both OpenAI API keys and Claude OAuth tokens;
filesystem-guardrail bullet rewritten (the path check now lives in the
tool executor, not the SDK).
Migration / breaking notes
Existing users will need to re-run
memclaw configureonce. The wizardnow asks which auth mode they want and writes either
CLAUDE_CODE_OAUTH_TOKENorANTHROPIC_API_KEY— whichever wasn'tchosen is removed from
.env.AGENT_BACKEND=claudeis also writtenexplicitly (defaults preserve old behavior, but writing it makes the
choice visible).
The
claude-agent-sdkdependency is capped to<0.2— 0.1.x shipsbreaking changes between minor versions.