From 9b9d0d0466dccf97493067c452344e12132cdeb6 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Sun, 4 Oct 2026 00:05:34 -0700 Subject: [PATCH 1/2] fix(claude): open native chat for API-key users instead of saying "not signed in" Claude reports tokenSource "none" beside apiKeySource "ANTHROPIC_API_KEY" when it runs on an API key (environment or settings env). The startup check read only tokenSource, so it refused those chats as signed out. Refuse only when Claude reports no token source and no API-key source. Co-Authored-By: Claude --- .../claude/claude-structured-init-proof.ts | 7 +++-- .../claude-structured-session-startup.test.ts | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/main/claude/claude-structured-init-proof.ts b/src/main/claude/claude-structured-init-proof.ts index ae2ee5896a8b..1deb789830df 100644 --- a/src/main/claude/claude-structured-init-proof.ts +++ b/src/main/claude/claude-structured-init-proof.ts @@ -71,8 +71,11 @@ export function claudeInitializationAuthError( initialization: unknown ): AgentSessionAcquisitionRefusal | null { const account = - isRecord(initialization) && isRecord(initialization.account) ? initialization.account : null - return readClaudeFrameString(account ?? {}, 'tokenSource') === 'none' + isRecord(initialization) && isRecord(initialization.account) ? initialization.account : {} + // An ANTHROPIC_API_KEY (env or settings) reports tokenSource "none" beside its apiKeySource. + const apiKeySource = readClaudeFrameString(account, 'apiKeySource') + return readClaudeFrameString(account, 'tokenSource') === 'none' && + (apiKeySource === null || apiKeySource === 'none') ? new AgentSessionAcquisitionRefusal( 'Claude is not signed in for the selected account. Sign in with the Claude CLI for this CLAUDE_CONFIG_DIR, then retry.', 'notSignedIn' diff --git a/src/main/claude/claude-structured-session-startup.test.ts b/src/main/claude/claude-structured-session-startup.test.ts index ceb9e9cdf6d6..c0390ea927b5 100644 --- a/src/main/claude/claude-structured-session-startup.test.ts +++ b/src/main/claude/claude-structured-session-startup.test.ts @@ -207,6 +207,37 @@ describe('Claude structured session publishes before the CLI answers initialize' }) }) + // Accounts as Claude 2.1.280 reports them at initialize, with no subscription login. + it.each([ + ['an ANTHROPIC_API_KEY', { tokenSource: 'none', apiKeySource: 'ANTHROPIC_API_KEY' }], + ['an apiKeyHelper', { tokenSource: 'apiKeyHelper', apiKeySource: 'apiKeyHelper' }], + ['an ANTHROPIC_AUTH_TOKEN', { tokenSource: 'ANTHROPIC_AUTH_TOKEN' }], + ['a third-party provider', { apiProvider: 'bedrock' }] + ])('starts a session Claude authenticates with %s', async (_label, account) => { + const claude = fakeClaude({ initAccount: { apiProvider: 'firstParty', ...account } }) + const { adapter, events } = startingAdapter(claude) + await adapter.acquire(ACQUIRE) + await adapter.awaitStarted('session-1') + + expect(events.some((event) => event.type === 'started')).toBe(true) + expect(events.some((event) => event.type === 'ended')).toBe(false) + await adapter.closeAll() + }) + + it('still refuses a start whose API key source is reported as none', async () => { + const claude = fakeClaude({ + initAccount: { apiProvider: 'firstParty', tokenSource: 'none', apiKeySource: 'none' } + }) + const { adapter, events } = startingAdapter(claude) + await adapter.acquire(ACQUIRE) + await adapter.awaitStarted('session-1') + await adapter.drainObservedExits() + + expect(events.find((event) => event.type === 'ended')).toMatchObject({ + reason: expect.stringMatching(/not signed in/) + }) + }) + // A Stop that closes a child still starting must end the wait the host's delivery loop is in, // though initialize never answers; otherwise every later send joins a loop that never moves. it('ends the wait on a start closed before init, without faulting it', async () => { From dc89aabacb105b589390067a7ae7eeadfdcc5086 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Mon, 5 Oct 2026 11:50:09 -0700 Subject: [PATCH 2/2] test(claude): cover a Console /login key at chat start --- src/main/claude/claude-structured-init-proof.ts | 2 +- src/main/claude/claude-structured-session-startup.test.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/claude/claude-structured-init-proof.ts b/src/main/claude/claude-structured-init-proof.ts index 1deb789830df..e138abd3641e 100644 --- a/src/main/claude/claude-structured-init-proof.ts +++ b/src/main/claude/claude-structured-init-proof.ts @@ -72,7 +72,7 @@ export function claudeInitializationAuthError( ): AgentSessionAcquisitionRefusal | null { const account = isRecord(initialization) && isRecord(initialization.account) ? initialization.account : {} - // An ANTHROPIC_API_KEY (env or settings) reports tokenSource "none" beside its apiKeySource. + // An API key (ANTHROPIC_API_KEY or a Console /login key) reports tokenSource "none". const apiKeySource = readClaudeFrameString(account, 'apiKeySource') return readClaudeFrameString(account, 'tokenSource') === 'none' && (apiKeySource === null || apiKeySource === 'none') diff --git a/src/main/claude/claude-structured-session-startup.test.ts b/src/main/claude/claude-structured-session-startup.test.ts index c0390ea927b5..a49120f53595 100644 --- a/src/main/claude/claude-structured-session-startup.test.ts +++ b/src/main/claude/claude-structured-session-startup.test.ts @@ -207,9 +207,10 @@ describe('Claude structured session publishes before the CLI answers initialize' }) }) - // Accounts as Claude 2.1.280 reports them at initialize, with no subscription login. + // Accounts as Claude 2.1.280 reports them at initialize; the /login key row is from its source. it.each([ ['an ANTHROPIC_API_KEY', { tokenSource: 'none', apiKeySource: 'ANTHROPIC_API_KEY' }], + ['a Console /login key', { tokenSource: 'none', apiKeySource: '/login managed key' }], ['an apiKeyHelper', { tokenSource: 'apiKeyHelper', apiKeySource: 'apiKeyHelper' }], ['an ANTHROPIC_AUTH_TOKEN', { tokenSource: 'ANTHROPIC_AUTH_TOKEN' }], ['a third-party provider', { apiProvider: 'bedrock' }]