You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Build a RockBot.Llm.ClaudeAgent provider that routes LLM calls through the Claude Agent SDK via a Node sidecar, so RockBot can draw from the MAX subscription's $100/mo Agent SDK credit instead of paying Azure Foundry rates for every call. Mirrors the shape of RockBot.Llm.Copilot and plugs into the existing per-tier configuration.
Why
Anthropic introduced separate "Agent SDK credit" pools for MAX subscriptions (announced 2026-05-14, effective 2026-06-15). MAX 5x includes $100/mo of agentic credit that exists whether used or not and expires monthly. Rockbot's primary LLM spend is ~$170/mo on Azure Foundry (gpt-5.5), so capturing even partial Agent SDK credit is essentially free money once the wrapper exists.
There's no first-party .NET Claude Agent SDK — only TypeScript and Python. A Node sidecar process owns the SDK; .NET talks to it via stdio JSON-lines, the same isolation model rockbot already uses for MCP server subprocesses.
ClaudeAgentSidecarHost is a singleton; spawns node dist/index.js lazily on first use.
Heartbeat ping/pong every 30s. If the sidecar dies, host restarts with exponential backoff (mirrors the MCP reconnect sweep pattern in McpBridgeService).
Local dev: Agent SDK reads Claude Code credentials from ~/.claude/... automatically — works out of the box.
Cluster deployment: mount Claude Code credentials as a Kubernetes Secret into the agent pod at /home/agent/.claude/. Sidecar process inherits. Open question — needs validation that copied credentials work for headless SDK use.
IPC Protocol
Newline-delimited JSON over stdio. Each line is one object. Multiple concurrent queries multiplex over the channel via id.
Tool execution path. The SDK owns the tool-calling loop (same compromise we accepted for CopilotChatClient). Tools are exposed to the SDK via an in-Node MCP server that proxies tool calls back over stdio: SDK requests a tool, sidecar emits tool_invoke, .NET executes via existing IToolRegistry / McpBridgeService, .NET replies with tool_result, sidecar feeds the result back to the SDK. Tool execution stays entirely in .NET territory; only the loop boundary lives in Node.
Phases (one PR each)
Phase 1 — Node sidecar skeleton. Project scaffold, index.ts stdio loop, ping/pong, structured logging. No SDK integration yet — just prove the IPC channel works end-to-end with a Hello World request/response.
Phase 2 — .NET project + sidecar host. RockBot.Llm.ClaudeAgent csproj, ClaudeAgentSidecarHost subprocess lifecycle, JSON-lines protocol reader/writer, ClaudeAgentChatClient : IChatClient calling the sidecar for a no-tools text query. Mirror CopilotChatClient structure.
Phase 3 — Agent SDK wiring in the sidecar. Real query() call, text/usage/done events streaming back. Validate end-to-end against a logged-in claude CLI session locally.
Phase 4 — Tool path. In-Node MCP server bridging tool calls to .NET; tool_invoke/tool_result handling on both sides; wire through existing McpBridgeService for execution.
Phase 5 — Per-tier registration. Add ClaudeAgent as a tier option in the existing tier config (reuse the Copilot integration's pattern). Route one isolable use case through it — candidates: a subagent type, LlmObservationEvaluator, or SessionSummaryService. Validate behavior parity vs gpt-5.5 before expanding.
Phase 6 — Cluster deployment. Node runtime added to Dockerfile.agent, Claude Code credentials mounted as a k8s Secret, sidecar process supervised by the .NET host (no extra pod). Surface Agent SDK credit consumption via the existing usage-tracker pattern.
Risks
Loop ownership. SDK runs the tool-calling loop, not AgentLoopRunner. Same compromise as Copilot — reasoning scaffolding, completion evaluation, and iteration budget weaken when wrapping the SDK. Mitigation: phase 5 picks a narrow target; expand only after validating behavior.
Model swap. gpt-5.5 → Claude Sonnet/Haiku is a real behavior change. Prompts tuned for gpt-5.5 may not transfer cleanly. Mitigation: isolable phase-5 target with low blast radius.
Credit ceiling. $100/mo cap; overflow at API list rates if extra-usage is enabled, otherwise requests block until reset. Mitigation: surface credit consumption telemetry so spillover is observable; configure overflow policy explicitly.
Headless auth. Agent SDK auth flow assumes interactive claude login. Cluster pods cannot do that. Mitigation: validate copied credentials work for headless SDK use before phase 6. If they don't, MAX-on-cluster is blocked pending an Anthropic non-interactive auth path; the work still benefits local dev.
Node runtime in agent image. Adds Node to a previously .NET-only container. Image size grows; supply chain expands. Mitigation: pin Node version, Alpine base for the sidecar layer, audit @anthropic-ai/claude-agent-sdk transitive deps.
Out of scope
Replacing AgentLoopRunner. This work routes around it for SDK-wrapped tiers, same as Copilot.
Native function calling with .NET-side loop ownership. Would require reviving the dormant text-based tool-calling path or shipping a .NET Agent SDK; neither is in scope here.
Build a
RockBot.Llm.ClaudeAgentprovider that routes LLM calls through the Claude Agent SDK via a Node sidecar, so RockBot can draw from the MAX subscription's $100/mo Agent SDK credit instead of paying Azure Foundry rates for every call. Mirrors the shape ofRockBot.Llm.Copilotand plugs into the existing per-tier configuration.Why
Anthropic introduced separate "Agent SDK credit" pools for MAX subscriptions (announced 2026-05-14, effective 2026-06-15). MAX 5x includes $100/mo of agentic credit that exists whether used or not and expires monthly. Rockbot's primary LLM spend is ~$170/mo on Azure Foundry (gpt-5.5), so capturing even partial Agent SDK credit is essentially free money once the wrapper exists.
There's no first-party .NET Claude Agent SDK — only TypeScript and Python. A Node sidecar process owns the SDK; .NET talks to it via stdio JSON-lines, the same isolation model rockbot already uses for MCP server subprocesses.
Architecture
Projects added
src/RockBot.Llm.ClaudeAgent/— .NET adapter, near-twin ofRockBot.Llm.Copilot.src/RockBot.Llm.ClaudeAgent.Sidecar/— Node TypeScript project owning@anthropic-ai/claude-agent-sdk.Lifecycle
ClaudeAgentSidecarHostis a singleton; spawnsnode dist/index.jslazily on first use.ping/pongevery 30s. If the sidecar dies, host restarts with exponential backoff (mirrors the MCP reconnect sweep pattern inMcpBridgeService).Auth
~/.claude/...automatically — works out of the box./home/agent/.claude/. Sidecar process inherits. Open question — needs validation that copied credentials work for headless SDK use.IPC Protocol
Newline-delimited JSON over stdio. Each line is one object. Multiple concurrent queries multiplex over the channel via
id..NET to Node request
{ "id": "01HV0EX3MJ9YT4...", "type": "query", "model": "claude-sonnet-4-6", "systemPrompt": "...", "messages": [ { "role": "user", "content": "..." }, { "role": "assistant", "content": "...", "toolCalls": [] }, { "role": "tool", "toolCallId": "...", "content": "..." } ], "tools": [ { "name": "memory_search", "description": "...", "inputSchema": {} } ], "maxTurns": 50, "permissionMode": "bypassPermissions" }Node to .NET event stream (terminated by
doneorerror){ "id": "01HV...", "type": "text", "delta": "..." } { "id": "01HV...", "type": "tool_use", "toolUseId": "...", "name": "...", "input": {} } { "id": "01HV...", "type": "tool_invoke", "toolUseId": "...", "name": "...", "input": {} } { "id": "01HV...", "type": "tool_result", "toolUseId": "...", "content": "..." } { "id": "01HV...", "type": "usage", "inputTokens": 1234, "outputTokens": 567, "model": "..." } { "id": "01HV...", "type": "done", "stopReason": "end_turn" } { "id": "01HV...", "type": "error", "code": "rate_limit", "message": "..." }Out-of-band control (no
id){ "type": "ping" } { "type": "pong", "version": "..." } { "type": "log", "level": "info", "message": "..." }Tool execution path. The SDK owns the tool-calling loop (same compromise we accepted for
CopilotChatClient). Tools are exposed to the SDK via an in-Node MCP server that proxies tool calls back over stdio: SDK requests a tool, sidecar emitstool_invoke, .NET executes via existingIToolRegistry/McpBridgeService, .NET replies withtool_result, sidecar feeds the result back to the SDK. Tool execution stays entirely in .NET territory; only the loop boundary lives in Node.Phases (one PR each)
index.tsstdio loop,ping/pong, structured logging. No SDK integration yet — just prove the IPC channel works end-to-end with a Hello World request/response.RockBot.Llm.ClaudeAgentcsproj,ClaudeAgentSidecarHostsubprocess lifecycle, JSON-lines protocol reader/writer,ClaudeAgentChatClient : IChatClientcalling the sidecar for a no-tools text query. MirrorCopilotChatClientstructure.query()call,text/usage/doneevents streaming back. Validate end-to-end against a logged-inclaudeCLI session locally.tool_invoke/tool_resulthandling on both sides; wire through existingMcpBridgeServicefor execution.ClaudeAgentas a tier option in the existing tier config (reuse the Copilot integration's pattern). Route one isolable use case through it — candidates: a subagent type,LlmObservationEvaluator, orSessionSummaryService. Validate behavior parity vs gpt-5.5 before expanding.Dockerfile.agent, Claude Code credentials mounted as a k8s Secret, sidecar process supervised by the .NET host (no extra pod). Surface Agent SDK credit consumption via the existing usage-tracker pattern.Risks
AgentLoopRunner. Same compromise as Copilot — reasoning scaffolding, completion evaluation, and iteration budget weaken when wrapping the SDK. Mitigation: phase 5 picks a narrow target; expand only after validating behavior.claude login. Cluster pods cannot do that. Mitigation: validate copied credentials work for headless SDK use before phase 6. If they don't, MAX-on-cluster is blocked pending an Anthropic non-interactive auth path; the work still benefits local dev.@anthropic-ai/claude-agent-sdktransitive deps.Out of scope
AgentLoopRunner. This work routes around it for SDK-wrapped tiers, same as Copilot.