/**
 * Classify the free-text return body from a dispatched executor subagent into
 * an actionable failure class for the orchestrator's recovery router.
 *
 * Context (#3095): the orchestrator currently treats every non-success agent
 * return as a generic "real failure" except for the Claude Code
 * `classifyHandoffIfNeeded is not defined` runtime bug. Quota / rate-limit
 * terminations look identical to a crashed agent — but the right user
 * response is "wait for reset and resume", not "retry now or abort".
 *
 * Sentinel coverage spans the runtimes GSD supports as executor targets:
 *   - Anthropic / Claude Code  — "usage limit", "rate limit", "quota", "429", "retry-after"
 *   - GitHub Copilot CLI       — "rate limit", "rate_limited", "user_weekly_rate_limited"
 *   - OpenAI Codex CLI         — "429", "usage_limit_reached", "too many requests"
 *   - Google Gemini CLI        — "RESOURCE_EXHAUSTED", "exceeded your", "quota"
 *
 * See docs/research/provider-rate-limit-signals.md for the proactive (header
 * / SDK event) signals the orchestrator could use once the host runtime
 * (Claude Code, Copilot, Codex) exposes them to hooks.
 */
// Order matters: the first match wins, so list the most specific / most
// actionable sentinels first. "429" beats "too many requests"; "quota" beats
// "RESOURCE_EXHAUSTED" because "quota" is the universal token. All matches
// are case-insensitive (sentinel value below is the lower-cased form).
const QUOTA_SENTINELS = [
    '429',
    'usage_limit_reached',
    'usage limit',
    'rate limit',
    'rate-limited',
    // `rate_limit` (stem) covers `rate_limited`, `rate_limit_error`,
    // `rate_limit_exceeded`, and Copilot's `user_weekly_rate_limited`.
    'rate_limit',
    'resource_exhausted',
    'quota',
    'too many requests',
    'exceeded your',
];
const CLASSIFY_HANDOFF_SENTINEL = 'classifyhandoffifneeded is not defined';
function parseRetryAfter(body) {
    // Match "retry-after: N" or "Retry-After: N" with optional surrounding
    // punctuation. Captures integer seconds only — HTTP-date form is rare in
    // agent return bodies and not worth the surface area.
    const match = body.match(/\bretry[-_ ]after[:\s]+(\d+)\b/i);
    if (!match)
        return undefined;
    const seconds = Number.parseInt(match[1], 10);
    return Number.isFinite(seconds) ? seconds : undefined;
}
/**
 * Query-handler wrapper for `agent.classify-failure`. Reads the body to
 * classify from the joined positional args (typed via `--`) so workflow
 * shell snippets can pass it as `gsd-sdk query agent.classify-failure -- "$BODY"`.
 */
export async function agentClassifyFailure(args) {
    const body = args.join(' ');
    return { data: classifyAgentFailure(body) };
}
export function classifyAgentFailure(body) {
    const normalized = body.toLowerCase();
    if (normalized.trim() === '') {
        return { class: 'unknown-failure' };
    }
    // Quota sentinels take precedence over the classifyHandoff runtime bug:
    // a quota-kill that *also* crashed the completion handler is still a quota
    // event, and the recovery path differs (wait vs. spot-check-and-treat-as-OK).
    for (const sentinel of QUOTA_SENTINELS) {
        if (normalized.includes(sentinel)) {
            const retryAfterSeconds = parseRetryAfter(body);
            return retryAfterSeconds === undefined
                ? { class: 'quota-exceeded', sentinel }
                : { class: 'quota-exceeded', sentinel, retryAfterSeconds };
        }
    }
    if (normalized.includes(CLASSIFY_HANDOFF_SENTINEL)) {
        return {
            class: 'classify-handoff-bug',
            sentinel: CLASSIFY_HANDOFF_SENTINEL,
        };
    }
    return { class: 'unknown-failure' };
}
//# sourceMappingURL=agent-failure-classifier.js.map