fix(credentials): address review feedback
- wakeContainer now never throws — returns Promise<boolean>, catches internally. Closes the regression risk for the 5 awaited callers in agent-to-agent, interactive, and approvals/response-handler that the previous version left unwrapped. Router uses the boolean to stop the typing indicator on transient failure; host-sweep just awaits. - Tighten AUTH_REQUIRED_RE: anchor to start-of-string with the specific `·` (U+00B7) separator the CLI uses, so an agent that quotes the banner mid-sentence in a normal reply doesn't trip the classifier. - Log a one-line note from writeAuthRequiredMessage so substitutions are visible when debugging "user got the credentials message but I don't see why." - Add unit tests for ClaudeProvider.isAuthRequired covering both banner variants, trailing content, mid-sentence quoting, leading-prose quoting, alternate separators, and unrelated text. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,7 @@ const AUTH_REQUIRED_USER_TEXT =
|
||||
"I can't reach my Anthropic credentials right now. The operator running NanoClaw needs to re-run setup, or run `claude` in the project directory on the machine I'm running on.";
|
||||
|
||||
function writeAuthRequiredMessage(routing: RoutingContext): void {
|
||||
log('Auth-required detected — substituting host-aware message for the user');
|
||||
writeMessageOut({
|
||||
id: generateId(),
|
||||
kind: 'chat',
|
||||
|
||||
37
container/agent-runner/src/providers/claude.test.ts
Normal file
37
container/agent-runner/src/providers/claude.test.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { describe, it, expect } from 'bun:test';
|
||||
|
||||
import { ClaudeProvider } from './claude.js';
|
||||
|
||||
describe('ClaudeProvider.isAuthRequired', () => {
|
||||
const provider = new ClaudeProvider();
|
||||
|
||||
it('matches the "Not logged in" banner', () => {
|
||||
expect(provider.isAuthRequired('Not logged in · Please run /login')).toBe(true);
|
||||
});
|
||||
|
||||
it('matches the "Invalid API key" banner', () => {
|
||||
expect(provider.isAuthRequired('Invalid API key · Please run /login')).toBe(true);
|
||||
});
|
||||
|
||||
it('matches with trailing content after the banner', () => {
|
||||
expect(provider.isAuthRequired('Not logged in · Please run /login\n\nstack trace …')).toBe(true);
|
||||
});
|
||||
|
||||
it('does not match when the agent quotes the phrase mid-sentence', () => {
|
||||
const quoted = "The error 'Invalid API key · Please run /login' means your auth has expired.";
|
||||
expect(provider.isAuthRequired(quoted)).toBe(false);
|
||||
});
|
||||
|
||||
it('does not match when the agent leads its reply with the phrase in prose', () => {
|
||||
const prose = '"Not logged in · Please run /login" is a Claude Code error.';
|
||||
expect(provider.isAuthRequired(prose)).toBe(false);
|
||||
});
|
||||
|
||||
it('does not match a different separator (defensive against typos in agent output)', () => {
|
||||
expect(provider.isAuthRequired('Not logged in - Please run /login')).toBe(false);
|
||||
});
|
||||
|
||||
it('does not match unrelated text', () => {
|
||||
expect(provider.isAuthRequired('Tool execution failed: timeout')).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -237,12 +237,16 @@ const CLAUDE_CODE_AUTO_COMPACT_WINDOW = '165000';
|
||||
const STALE_SESSION_RE = /no conversation found|ENOENT.*\.jsonl|session.*not found/i;
|
||||
|
||||
/**
|
||||
* Auth-required detection. Matches Claude Code's output when no usable
|
||||
* Auth-required detection. Matches Claude Code's banner when no usable
|
||||
* credential is available — "Not logged in · Please run /login" or
|
||||
* "Invalid API key · Please run /login". The user can't run /login from
|
||||
* chat, so the poll-loop substitutes a host-aware message.
|
||||
*
|
||||
* Anchored to start-of-string with the specific `·` separator (U+00B7)
|
||||
* the CLI uses, so an agent that quotes the phrase verbatim mid-sentence
|
||||
* in a normal reply doesn't trip the classifier.
|
||||
*/
|
||||
const AUTH_REQUIRED_RE = /(Not logged in|Invalid API key)[\s\S]*?Please run \/login/i;
|
||||
const AUTH_REQUIRED_RE = /^(Not logged in|Invalid API key)\s*·\s*Please run \/login/;
|
||||
|
||||
export class ClaudeProvider implements AgentProvider {
|
||||
readonly supportsNativeSlashCommands = true;
|
||||
|
||||
Reference in New Issue
Block a user