Ctrl K
ComunidadGuías…

src/runner/failures.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { AgentFinalResponseMissing, ExecutionBudgetExhausted, ModelOutputTruncated } from '../harness/runtime.ts';
import { ProviderResponseError, ProviderUnavailable, providerError, type ProviderFailureCode } from '../harness/retry.ts';
import { ModelAdapterError } from '../models/types.ts';
import { ControlPlaneError } from '../control-plane/errors.ts';
import { GitLabHttpError } from '../scm/gitlab/client.ts';

export type FailureCategory = 'provider' | 'model' | 'scm' | 'workspace' | 'internal';
export type FailureAction = 'retry' | 'check_configuration' | 'human_review' | 'inspect_logs';
export type ScmPlatform = 'github' | 'gitlab';

export interface RunFailure {
  code: string;
  category: FailureCategory;
  retryable: boolean;
  user_action: FailureAction;
  message: string;
  upstream_status?: number;
  upstream_code?: string | number;
  attempts?: number;
  scm_platform?: ScmPlatform;
}

function sanitize(value: string) {
  return value
    .replace(/glpat-[A-Za-z0-9_-]+/g, '[REDACTED]')
    .replace(/(?:PRIVATE-TOKEN|GITLAB_TOKEN|Authorization)\s*[:=]\s*[^\s,;]+/gi, '$1: [REDACTED]');
}

function clip(value: string, max = 1000) {
  const safe = sanitize(value);
  return safe.length > max ? `${safe.slice(0, max)}…` : safe;
}

function providerFailure(code: ProviderFailureCode | 'provider_unavailable', details: {
  message: string; retryable?: boolean; status?: number; upstreamCode?: string | number; attempts?: number;
}): RunFailure {
  const retryable = details.retryable ?? code === 'provider_upstream_unavailable';
  return { code, category: 'provider', retryable,
    user_action: retryable ? 'retry' : code === 'provider_auth_failed' || code === 'provider_configuration_error' ? 'check_configuration' : 'inspect_logs',
    message: clip(details.message), ...(details.status === undefined ? {} : { upstream_status: details.status }),
    ...(details.upstreamCode === undefined ? {} : { upstream_code: details.upstreamCode }),
    ...(details.attempts === undefined ? {} : { attempts: details.attempts }) };
}

export function classifyRunFailure(error: unknown): RunFailure {
  if (error instanceof ExecutionBudgetExhausted) {
    return { code: 'budget_exhausted', category: 'model', retryable: true, user_action: 'retry',
      message: 'Agent 执行预算已耗尽,尚未生成可交付的最终答复。' };
  }
  if (error instanceof AgentFinalResponseMissing) {
    return { code: 'agent_final_response_missing', category: 'model', retryable: false, user_action: 'inspect_logs',
      message: 'Agent 未生成可交付的最终自然语言答复。' };
  }
  if (error instanceof ModelOutputTruncated) {
    return { code: 'model_output_truncated', category: 'model', retryable: true, user_action: 'retry',
      message: '模型输出达到上限,未生成完整结果。' };
  }
  if (error instanceof ProviderResponseError) {
    return providerFailure(error.failureCode, { message: error.upstreamMessage ?? error.message, retryable: error.retryable,
      status: error.status, upstreamCode: error.upstreamCode });
  }
  if (error instanceof ProviderUnavailable) {
    const details = error.details;
    return providerFailure(details.failureCode ?? 'provider_unavailable', { message: details.upstreamMessage ?? error.message,
      retryable: details.retryable, status: details.status, upstreamCode: details.upstreamCode, attempts: details.attempts });
  }
  if (error instanceof ModelAdapterError) {
    return providerFailure(error.code === 'provider_unavailable' ? 'provider_configuration_error' : 'provider_protocol_error', {
      message: error.message, retryable: false,
    });
  }

  const facts = providerError(error);
  const providerCode = facts.failure_code;
  if (providerCode && ['provider_upstream_unavailable', 'provider_auth_failed', 'provider_request_rejected',
    'provider_configuration_error', 'provider_protocol_error'].includes(providerCode)) {
    return providerFailure(providerCode as ProviderFailureCode, { message: facts.upstream_message ?? (error as Error)?.message ?? String(error),
      retryable: facts.retryable, status: facts.upstream_status, upstreamCode: facts.upstream_code, attempts: facts.attempts });
  }
  if (error instanceof ControlPlaneError && (error.code === 'provider_unavailable'
      || error.code === 'invalid_configuration' && (/provider|model|credential|request option/i.test(error.message)))) {
    return providerFailure('provider_configuration_error', { message: error.message, retryable: false });
  }
  const errorStatus = facts.status;
  const errorMessage = error instanceof Error ? error.message : String(error);
  if (error instanceof GitLabHttpError || error instanceof Error && error.name === 'GitLabHttpError') {
    const status = error instanceof GitLabHttpError ? error.status : errorStatus;
    const message = clip(errorMessage);
    if (/invalid json|protocol|returned invalid/i.test(errorMessage)) {
      return { code: 'gitlab_protocol_error', category: 'scm', scm_platform: 'gitlab', retryable: false,
        user_action: 'inspect_logs', message, ...(status === undefined ? {} : { upstream_status: status }),
        ...(facts.upstream_code === undefined ? {} : { upstream_code: facts.upstream_code }) };
    }
    if (status === 401 || status === 403) {
      return { code: 'gitlab_auth_failed', category: 'scm', scm_platform: 'gitlab', retryable: false,
        user_action: 'check_configuration', message, upstream_status: status,
        ...(facts.upstream_code === undefined ? {} : { upstream_code: facts.upstream_code }) };
    }
    if (status === undefined || status === 408 || status === 429 || status >= 500) {
      return { code: 'gitlab_unavailable', category: 'scm', scm_platform: 'gitlab', retryable: true,
        user_action: 'retry', message, ...(status === undefined ? {} : { upstream_status: status }),
        ...(facts.upstream_code === undefined ? {} : { upstream_code: facts.upstream_code }) };
    }
    return { code: 'gitlab_request_rejected', category: 'scm', scm_platform: 'gitlab', retryable: false,
      user_action: 'check_configuration', message, upstream_status: status,
      ...(facts.upstream_code === undefined ? {} : { upstream_code: facts.upstream_code }) };
  }
  if (facts.code === 'GITLAB_CONFIGURATION_ERROR') {
    return { code: 'gitlab_configuration_error', category: 'scm', scm_platform: 'gitlab', retryable: false,
      user_action: 'check_configuration', message: clip(errorMessage) };
  }
  if (facts.code === 'GITLAB_NETWORK_ERROR' || /GitLab request failed/i.test(errorMessage) && facts.code) {
    return { code: 'gitlab_unavailable', category: 'scm', scm_platform: 'gitlab', retryable: true,
      user_action: 'retry', message: clip(errorMessage), ...(facts.upstream_status === undefined ? {} : { upstream_status: facts.upstream_status }),
      ...(facts.upstream_code === undefined ? {} : { upstream_code: facts.upstream_code }) };
  }
  if (error instanceof Error && (error.name === 'HttpError' || error.name === 'RequestError')) {
    const retryable = facts.status === undefined || facts.status === 408 || facts.status === 429 || facts.status >= 500;
    return { code: 'github_unavailable', category: 'scm', scm_platform: 'github', retryable, user_action: retryable ? 'retry' : 'human_review',
      message: clip(error.message), ...(facts.status === undefined ? {} : { upstream_status: facts.status }) };
  }
  return { code: 'internal_error', category: 'internal', retryable: false, user_action: 'inspect_logs',
    message: clip(errorMessage) };
}

export function terminalStatusForFailure(failure: RunFailure) {
  if (failure.code === 'model_output_truncated') return 'model_output_truncated' as const;
  if (failure.code === 'budget_exhausted') return 'budget_exhausted' as const;
  if (failure.category === 'provider') return 'provider_unavailable' as const;
  return 'harness_failed' as const;
}