diff --git a/CHANGELOG.md b/CHANGELOG.md index d2bbfa575c3..91045a14ddc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,7 @@ Docs: https://docs.openclaw.ai - ACP/stream relay: pass parent delivery context to ACP stream relay system events so `streamTo="parent"` updates route to the correct thread or topic instead of falling back to the main DM. (#57056) Thanks @pingren. - Agents/sessions: preserve announce `threadId` when `sessions.list` fallback rehydrates agent-to-agent announce targets so final announce messages stay in the originating thread/topic. (#63506) Thanks @SnowSky1. - iMessage/self-chat: remember ambiguous `sender === chat_identifier` outbound rows with missing `destination_caller_id` in self-chat dedupe state so the later reflected inbound copy still drops instead of re-entering inbound handling when the echo cache misses. Thanks @neeravmakwana. +- Claude CLI: stop marking spawned Claude Code runs as host-managed so they keep using normal CLI subscription behavior. (#64023) Thanks @Alex-Alaniz. ## 2026.4.9 diff --git a/extensions/anthropic/cli-backend.ts b/extensions/anthropic/cli-backend.ts index 6e66fccb66f..c7aeb31809c 100644 --- a/extensions/anthropic/cli-backend.ts +++ b/extensions/anthropic/cli-backend.ts @@ -7,7 +7,6 @@ import { CLAUDE_CLI_BACKEND_ID, CLAUDE_CLI_DEFAULT_MODEL_REF, CLAUDE_CLI_CLEAR_ENV, - CLAUDE_CLI_HOST_MANAGED_ENV, CLAUDE_CLI_MODEL_ALIASES, CLAUDE_CLI_SESSION_ID_FIELDS, normalizeClaudeBackendConfig, @@ -63,7 +62,6 @@ export function buildAnthropicCliBackend(): CliBackendPlugin { systemPromptArg: "--append-system-prompt", systemPromptMode: "append", systemPromptWhen: "first", - env: { ...CLAUDE_CLI_HOST_MANAGED_ENV }, clearEnv: [...CLAUDE_CLI_CLEAR_ENV], reliability: { watchdog: { diff --git a/extensions/anthropic/cli-shared.test.ts b/extensions/anthropic/cli-shared.test.ts index 6a81705b48c..0542a810077 100644 --- a/extensions/anthropic/cli-shared.test.ts +++ b/extensions/anthropic/cli-shared.test.ts @@ -2,7 +2,6 @@ import { describe, expect, it } from "vitest"; import { buildAnthropicCliBackend } from "./cli-backend.js"; import { CLAUDE_CLI_CLEAR_ENV, - CLAUDE_CLI_HOST_MANAGED_ENV, normalizeClaudeBackendConfig, normalizeClaudePermissionArgs, normalizeClaudeSettingSourcesArgs, @@ -132,10 +131,10 @@ describe("normalizeClaudeBackendConfig", () => { expect(normalized?.resumeArgs).toContain("user"); }); - it("marks claude cli as host-managed, restricts setting sources, and clears inherited env overrides", () => { + it("leaves claude cli subscription-managed, restricts setting sources, and clears inherited env overrides", () => { const backend = buildAnthropicCliBackend(); - expect(backend.config.env).toEqual(CLAUDE_CLI_HOST_MANAGED_ENV); + expect(backend.config.env).toBeUndefined(); expect(backend.config.args).toContain("--setting-sources"); expect(backend.config.args).toContain("user"); expect(backend.config.resumeArgs).toContain("--setting-sources"); diff --git a/extensions/anthropic/cli-shared.ts b/extensions/anthropic/cli-shared.ts index e3543cc4a92..3d1e51f5e96 100644 --- a/extensions/anthropic/cli-shared.ts +++ b/extensions/anthropic/cli-shared.ts @@ -40,10 +40,6 @@ export const CLAUDE_CLI_SESSION_ID_FIELDS = [ "conversationId", ] as const; -export const CLAUDE_CLI_HOST_MANAGED_ENV = { - CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST: "1", -} as const; - // Claude Code honors provider-routing, auth, and config-root env before // consulting its local login state, so inherited shell overrides must not // steer OpenClaw-managed Claude CLI runs toward a different provider, diff --git a/extensions/zalo/src/setup-allow-from.ts b/extensions/zalo/src/setup-allow-from.ts index 9a3284083a1..19b98543b93 100644 --- a/extensions/zalo/src/setup-allow-from.ts +++ b/extensions/zalo/src/setup-allow-from.ts @@ -30,9 +30,9 @@ export async function noteZaloTokenHelp( export async function promptZaloAllowFrom(params: { cfg: OpenClawConfig; prompter: Parameters>[0]["prompter"]; - accountId: string; + accountId?: string; }): Promise { - const { cfg, prompter, accountId } = params; + const { cfg, prompter, accountId = DEFAULT_ACCOUNT_ID } = params; const resolved = resolveZaloAccount({ cfg, accountId }); const existingAllowFrom = resolved.config.allowFrom ?? []; const entry = await prompter.text({ diff --git a/extensions/zalo/src/setup-surface.ts b/extensions/zalo/src/setup-surface.ts index 0931aa1c7d5..a1b5fd320cc 100644 --- a/extensions/zalo/src/setup-surface.ts +++ b/extensions/zalo/src/setup-surface.ts @@ -10,7 +10,7 @@ import { type SecretInput, } from "openclaw/plugin-sdk/setup"; import { resolveZaloAccount } from "./accounts.js"; -import { noteZaloTokenHelp } from "./setup-allow-from.js"; +import { noteZaloTokenHelp, promptZaloAllowFrom } from "./setup-allow-from.js"; import { zaloDmPolicy } from "./setup-core.js"; const channel = "zalo" as const; diff --git a/src/agents/cli-backends.test.ts b/src/agents/cli-backends.test.ts index 1e5cb563f60..188176a72f3 100644 --- a/src/agents/cli-backends.test.ts +++ b/src/agents/cli-backends.test.ts @@ -139,9 +139,6 @@ beforeEach(() => { ], output: "jsonl", input: "stdin", - env: { - CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST: "1", - }, clearEnv: [ "ANTHROPIC_API_KEY", "ANTHROPIC_API_KEY_OLD", @@ -364,7 +361,7 @@ describe("resolveCliBackendConfig claude-cli defaults", () => { expect(resolved?.config.resumeArgs).toContain("user"); expect(resolved?.config.resumeArgs).toContain("--permission-mode"); expect(resolved?.config.resumeArgs).toContain("bypassPermissions"); - expect(resolved?.config.env).toEqual({ CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST: "1" }); + expect(resolved?.config.env).not.toHaveProperty("CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST"); expect(resolved?.config.clearEnv).toContain("ANTHROPIC_API_TOKEN"); expect(resolved?.config.clearEnv).toContain("ANTHROPIC_BASE_URL"); expect(resolved?.config.clearEnv).toContain("ANTHROPIC_CUSTOM_HEADERS"); @@ -580,7 +577,6 @@ describe("resolveCliBackendConfig claude-cli defaults", () => { expect(resolved).not.toBeNull(); expect(resolved?.config.env).toEqual({ - CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST: "1", SAFE_CUSTOM: "ok", ANTHROPIC_BASE_URL: "https://evil.example.com/v1", }); diff --git a/src/agents/cli-runner.spawn.test.ts b/src/agents/cli-runner.spawn.test.ts index f703dd43a38..af07ce4cfdc 100644 --- a/src/agents/cli-runner.spawn.test.ts +++ b/src/agents/cli-runner.spawn.test.ts @@ -558,7 +558,7 @@ describe("runCliAgent spawn path", () => { expect(input.env?.SAFE_OVERRIDE).toBe("from-override"); }); - it("clears claude-cli provider-routing, auth, and telemetry env while keeping host-managed hardening", async () => { + it("clears claude-cli provider-routing, auth, telemetry, and host-managed env", async () => { vi.stubEnv("ANTHROPIC_BASE_URL", "https://proxy.example.com/v1"); vi.stubEnv("ANTHROPIC_API_TOKEN", "env-api-token"); vi.stubEnv("ANTHROPIC_CUSTOM_HEADERS", "x-test-header: env"); @@ -573,6 +573,7 @@ describe("runCliAgent spawn path", () => { vi.stubEnv("OTEL_TRACES_EXPORTER", "none"); vi.stubEnv("OTEL_EXPORTER_OTLP_PROTOCOL", "none"); vi.stubEnv("OTEL_SDK_DISABLED", "true"); + vi.stubEnv("CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST", "1"); mockSuccessfulCliRun(); await executePreparedCliRun( @@ -611,7 +612,7 @@ describe("runCliAgent spawn path", () => { env?: Record; }; expect(input.env?.SAFE_KEEP).toBe("ok"); - expect(input.env?.CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST).toBe("1"); + expect(input.env?.CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST).toBeUndefined(); expect(input.env?.ANTHROPIC_BASE_URL).toBe("https://override.example.com/v1"); expect(input.env?.ANTHROPIC_API_TOKEN).toBeUndefined(); expect(input.env?.ANTHROPIC_CUSTOM_HEADERS).toBeUndefined(); diff --git a/src/agents/cli-runner.test-support.ts b/src/agents/cli-runner.test-support.ts index 31ce17b69bf..b3c704f586c 100644 --- a/src/agents/cli-runner.test-support.ts +++ b/src/agents/cli-runner.test-support.ts @@ -240,9 +240,6 @@ function buildAnthropicCliBackendFixture(): CliBackendPlugin { systemPromptArg: "--append-system-prompt", systemPromptMode: "append", systemPromptWhen: "first", - env: { - CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST: "1", - }, clearEnv: [...clearEnv], reliability: { watchdog: { diff --git a/src/agents/cli-runner/execute.ts b/src/agents/cli-runner/execute.ts index 3a6ba5c9eb1..2f7b724dc64 100644 --- a/src/agents/cli-runner/execute.ts +++ b/src/agents/cli-runner/execute.ts @@ -234,6 +234,12 @@ export async function executePreparedCliRun( ); } Object.assign(next, context.preparedBackend.env); + + // Never mark Claude CLI as host-managed. That marker routes runs into + // Anthropic's separate host-managed usage tier instead of normal CLI + // subscription behavior. + delete next["CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST"]; + return next; })(); if (logOutputText) {