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:
parent
e52ba2921c
commit
ab9c60b67c
3 changed files with 37 additions and 0 deletions
15
package-lock.json
generated
Normal file
15
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"name": "token-monitor",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "token-monitor",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
return parseTeamsHeaders(response.headers, response.status, providerName);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
export function getSeverity(provider) {
|
export function getSeverity(provider) {
|
||||||
if (provider.type === 'teams-direct') {
|
if (provider.type === 'teams-direct') {
|
||||||
if (provider.status === 'rejected') return 'critical';
|
if (provider.status === 'rejected') return 'critical';
|
||||||
|
if (provider.status === 'policy_rejected') return 'critical';
|
||||||
if (provider.utilization_7d > 0.85) return 'warning';
|
if (provider.utilization_7d > 0.85) return 'warning';
|
||||||
if (provider.utilization_5h > 0.7) return 'warning';
|
if (provider.utilization_5h > 0.7) return 'warning';
|
||||||
return 'ok';
|
return 'ok';
|
||||||
|
|
@ -99,6 +100,8 @@ export function generateReport(result) {
|
||||||
if (p.type === 'teams-direct') {
|
if (p.type === 'teams-direct') {
|
||||||
if (p.status === 'invalid_key') {
|
if (p.status === 'invalid_key') {
|
||||||
detail = 'Invalid API key (401)';
|
detail = 'Invalid API key (401)';
|
||||||
|
} else if (p.status === 'policy_rejected') {
|
||||||
|
detail = `POLICY BLOCKED — 7d: ${pct(p.utilization_7d)} | extra-usage billing required`;
|
||||||
} else if (p.status === 'rejected') {
|
} else if (p.status === 'rejected') {
|
||||||
const resetIn = formatDuration(p.reset_in_seconds);
|
const resetIn = formatDuration(p.reset_in_seconds);
|
||||||
detail = `MAXED — 7d: ${pct(p.utilization_7d)} | resets in ${resetIn}`;
|
detail = `MAXED — 7d: ${pct(p.utilization_7d)} | resets in ${resetIn}`;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue