Governed substrate for autonomous agents: scoped identity (passports), audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
52 lines
2 KiB
JavaScript
52 lines
2 KiB
JavaScript
/**
|
|
* ADR-0057 WP-B — Auth-error matcher for Claude CLI output.
|
|
*
|
|
* Claude Code CLI surfaces Anthropic API 401s (OAuth-token rotation race during
|
|
* parallel CLI processes) as plain assistant text. Before WP-B those strings
|
|
* were persisted via saveStepMessage as content_type='text' and rendered like
|
|
* the agent's actual response. See conversation 3178 msg #558510 incident.
|
|
*
|
|
* This module exposes a single matcher used by agent-job/create.js to detect
|
|
* the auth-error final response and route it to an agent_status row instead.
|
|
*/
|
|
|
|
const AUTH_ERROR_PATTERNS = [
|
|
/Failed to authenticate\. API Error: 401/i,
|
|
/Invalid authentication credentials/i,
|
|
/authentication_error/i,
|
|
/invalid_api_key/i,
|
|
/OAuth token expired/i,
|
|
];
|
|
|
|
export function isAuthError(text) {
|
|
if (typeof text !== 'string' || !text) return false;
|
|
return AUTH_ERROR_PATTERNS.some((re) => re.test(text));
|
|
}
|
|
|
|
/**
|
|
* ADR-0057 WP-B (step-level extension): decide whether a streamed assistant
|
|
* content block should be suppressed from chat because it's a leaked CLI
|
|
* auth-error blob.
|
|
*
|
|
* The original WP-B only guarded the *final* result content. But the Claude CLI
|
|
* also surfaces 401s mid-run as a `thinking` or `text` block during OAuth-token
|
|
* rotation (see conv 3454 msgs 678432/678434, conv 2068 680051/680053). Those
|
|
* leak into chat as raw "Invalid authentication credentials" bubbles AND count
|
|
* as step output — which suppresses the empty-result safety net, so the turn
|
|
* ends with no final text and only a Continue button.
|
|
*/
|
|
export function isAuthErrorBlock(block) {
|
|
if (!block || typeof block !== 'object') return false;
|
|
const text =
|
|
block.type === 'thinking' ? block.thinking :
|
|
block.type === 'text' ? block.text :
|
|
null;
|
|
return isAuthError(text);
|
|
}
|
|
|
|
/** Pull the Anthropic request_id out of the error blob, if present. */
|
|
export function extractRequestId(text) {
|
|
if (typeof text !== 'string') return null;
|
|
const m = text.match(/request_id["':\s]+(req_[A-Za-z0-9]+)/);
|
|
return m ? m[1] : null;
|
|
}
|