Handle policy_rejected status (Anthropic April 4 billing change)

- anthropic-teams.js: detect HTTP 400 extra-usage policy blocks, return
  status='policy_rejected' with quota headers still readable
- report.js: display policy_rejected as CRITICAL with 'POLICY BLOCKED' label
- getSeverity: treat policy_rejected as critical

Currently the direct API (used by monitor) returns 200; pi's OAuth path
returns 400. This fix future-proofs against the block extending to direct
API calls, and correctly classifies the status if it does.

Refs: trentuna/commons#17, trentuna/token-monitor#4
This commit is contained in:
Vigilio Desto 2026-04-08 05:38:46 +00:00
parent e52ba2921c
commit ab9c60b67c
Signed by: vigilio
GPG key ID: 159D6AD58C8E55E9
3 changed files with 37 additions and 0 deletions

View file

@ -116,6 +116,25 @@ export async function probeTeamsProvider(providerName, baseUrl, apiKey) {
}),
});
// HTTP 400 with "extra_usage" policy: Anthropic changed billing April 4 2026.
// Third-party apps (incl. pi) no longer draw from Teams plan limits.
// Rate-limit headers ARE present but the provider is unusable for sessions.
// Detect this before parseTeamsHeaders so we surface a clear status.
if (response.status === 400) {
let bodyText = '';
try { bodyText = await response.text(); } catch (_) {}
if (bodyText.includes('extra usage') || bodyText.includes('invalid_request_error')) {
// Still parse headers for quota visibility, but override status
const base = parseTeamsHeaders(response.headers, response.status, providerName);
return {
...base,
status: 'policy_rejected',
policy_message: 'Third-party apps blocked (Anthropic April 2026 billing change)',
severity: 'critical',
};
}
}
return parseTeamsHeaders(response.headers, response.status, providerName);
} catch (err) {
return {