From c4080e63292da231d462e636187aae86bde804e0 Mon Sep 17 00:00:00 2001 From: vvillait88 Date: Sat, 3 Oct 2026 15:53:14 -0700 Subject: [PATCH] fix: quota denial message no longer tells the agent to retry --- src/_response.ts | 4 ++++ src/core.ts | 6 +++--- tests/edge-cases.test.ts | 4 +++- tests/hono.test.ts | 3 ++- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/_response.ts b/src/_response.ts index cc76676..1ebebee 100644 --- a/src/_response.ts +++ b/src/_response.ts @@ -73,6 +73,10 @@ const API_ERROR_INSTRUCTIONS = JSON.stringify({ 'Verification is temporarily unavailable. Please try again in a moment: this is a transient issue, not a problem with your account.', }); +// The message paired with QUOTA_EXCEEDED_INSTRUCTIONS. The api_error default says the failure is +// transient and worth retrying, which contradicts the contact_merchant instructions on this path. +export const QUOTA_EXCEEDED_MESSAGE = 'AgentScore identity verification is unavailable for this merchant. Retrying will not help.'; + export const QUOTA_EXCEEDED_INSTRUCTIONS = JSON.stringify({ action: 'contact_merchant', steps: [ diff --git a/src/core.ts b/src/core.ts index 7f383d5..4177939 100644 --- a/src/core.ts +++ b/src/core.ts @@ -9,7 +9,7 @@ import { TokenExpiredError, } from '@agent-score/sdk'; import { isFixableDenial } from './_denial'; -import { QUOTA_EXCEEDED_INSTRUCTIONS } from './_response'; +import { QUOTA_EXCEEDED_INSTRUCTIONS, QUOTA_EXCEEDED_MESSAGE } from './_response'; import { normalizeAddress } from './address'; import { TTLCache } from './cache'; import type { PaymentSigner } from './signer'; @@ -864,7 +864,7 @@ export function createAgentScoreCore(options: AgentScoreCoreOptions): AgentScore if (failOpen) return { kind: 'allow', degraded: true, infraReason: 'quota_exceeded' }; return { kind: 'deny', - reason: { code: 'api_error', agent_instructions: QUOTA_EXCEEDED_INSTRUCTIONS }, + reason: { code: 'api_error', agent_instructions: QUOTA_EXCEEDED_INSTRUCTIONS, message: QUOTA_EXCEEDED_MESSAGE }, }; } if (err instanceof SdkTimeoutError) { @@ -883,7 +883,7 @@ export function createAgentScoreCore(options: AgentScoreCoreOptions): AgentScore if (failOpen) return { kind: 'allow', degraded: true, infraReason: 'quota_exceeded' }; return { kind: 'deny', - reason: { code: 'api_error', agent_instructions: QUOTA_EXCEEDED_INSTRUCTIONS }, + reason: { code: 'api_error', agent_instructions: QUOTA_EXCEEDED_INSTRUCTIONS, message: QUOTA_EXCEEDED_MESSAGE }, }; } if (errName === 'TimeoutError' || errName === 'AbortError') { diff --git a/tests/edge-cases.test.ts b/tests/edge-cases.test.ts index 0a6728c..661bcc3 100644 --- a/tests/edge-cases.test.ts +++ b/tests/edge-cases.test.ts @@ -223,7 +223,9 @@ describe('error response edge cases', () => { expect(next).not.toHaveBeenCalled(); expect(status).toHaveBeenCalledWith(503); - expect(json).toHaveBeenCalledWith(expect.objectContaining({ error: expect.objectContaining({ code: 'api_error' }) })); + expect(json).toHaveBeenCalledWith(expect.objectContaining({ + error: expect.objectContaining({ code: 'api_error', message: expect.stringContaining('Retrying will not help') }), + })); }); it('typed QuotaExceededError fails open (degraded allow) when failOpen is true', async () => { diff --git a/tests/hono.test.ts b/tests/hono.test.ts index d3dc1b6..5c17b24 100644 --- a/tests/hono.test.ts +++ b/tests/hono.test.ts @@ -625,8 +625,9 @@ describe('Hono adapter: error paths', () => { const res = await app.request('/test', { headers: { 'x-wallet-address': WALLET } }); expect(res.status).toBe(503); - const body = await res.json() as { error: { code: string }; agent_instructions: string }; + const body = await res.json() as { error: { code: string; message: string }; agent_instructions: string }; expect(body.error.code).toBe('api_error'); + expect(body.error.message).toContain('Retrying will not help'); const instructions = JSON.parse(body.agent_instructions); expect(instructions.action).toBe('contact_merchant'); expect(instructions.steps[0]).toContain('merchant-side issue');