Ctrl K
ComunidadGuías…

src/runner/run-notice.ts

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

Archivo original ↗Preguntar a KiBot
import type { RunFailure, ScmPlatform } from './failures.ts';

/** The platform-neutral durable notice persisted alongside a run result. */
export interface RunNotice {
  run_id: string;
  head: string;
  status: string;
  phase: string;
  reason: string;
  mentions: string[];
  bot_login?: string;
  platform?: ScmPlatform;
  failure?: RunFailure;
}

export const failureTitles: Record<string, string> = {
  provider_upstream_unavailable: '模型服务暂时不可用',
  provider_auth_failed: '模型服务认证失败',
  provider_request_rejected: '模型服务拒绝了请求',
  provider_configuration_error: '模型 Provider 配置不可用',
  provider_protocol_error: '模型服务返回格式异常',
  model_output_truncated: '模型输出被截断,尚未完成',
  github_unavailable: 'GitHub 服务暂时不可用',
  gitlab_auth_failed: 'GitLab 认证失败',
  gitlab_configuration_error: 'GitLab 连接配置错误',
  gitlab_unavailable: 'GitLab 服务暂时不可用',
  gitlab_request_rejected: 'GitLab 拒绝了请求',
  gitlab_protocol_error: 'GitLab 服务返回格式异常',
  internal_error: 'PatchPaw 内部错误',
};

function targetFor(notice: RunNotice) {
  const platform = notice.platform ?? notice.failure?.scm_platform ?? 'github';
  return platform === 'gitlab' ? 'MR' : 'PR';
}

function failureDetails(failure: RunFailure) {
  const scm = failure.category === 'scm';
  const platform = failure.scm_platform === 'gitlab' ? 'GitLab' : failure.scm_platform === 'github' ? 'GitHub' : 'SCM';
  const nextAction = failure.user_action === 'retry' ? '稍后重新发送原命令'
    : failure.user_action === 'check_configuration'
      ? failure.scm_platform === 'gitlab' ? '检查 GitLab token、连接配置、项目绑定和 Bot identity 配置' : failure.scm_platform === 'github' ? '检查 GitHub App 配置' : '检查 Provider 凭据和配置'
      : failure.user_action === 'human_review' ? '请人工检查当前状态' : '查看 Run trace 后再决定下一步';
  return [
    `失败代码:\`${failure.code}\``,
    failure.category === 'provider' ? '这是模型服务或 Provider 基础设施故障,不等同于代码测试失败。'
      : scm ? `这是 ${platform} 通信故障,不等同于代码测试失败。` : undefined,
    `原因:${failure.message}`,
    failure.upstream_status === undefined ? undefined : `上游状态:HTTP ${failure.upstream_status}`,
    failure.upstream_code === undefined ? undefined : `上游错误码:\`${failure.upstream_code}\``,
    failure.attempts === undefined ? undefined : `Provider 请求尝试:${failure.attempts} 次`,
    `是否可重试:${failure.retryable ? '可以' : '不建议自动重试'}`,
    `下一步:${nextAction}`,
  ].filter((line): line is string => !!line).join('\n');
}

export function runNoticeBody(notice: RunNotice, includeCloseoutMarker = true) {
  const target = targetFor(notice);
  const title = notice.failure ? failureTitles[notice.failure.code] ?? '任务执行失败'
    : notice.status === 'stopped' ? '已按 /stop 暂停,等待沟通' : notice.status === 'needs_human' ? '需要人工答复' : notice.status === 'budget_exhausted' ? '本轮执行预算已用完,候选修复尚未完成' : notice.status === 'model_output_truncated' ? '模型输出被截断,尚未完成' : '任务停止,尚未完成';
  const instruction = notice.failure?.user_action === 'retry' || notice.status === 'model_output_truncated'
    ? `请在此 ${target} 新增评论,@ 本条评论的机器人,并使用原请求的 \`/readme\` 重新执行;无需改用 \`/review\`。同一 ${target} 保留对话记忆。`
    : `请在此 ${target} 新增评论,@ 本条评论的机器人,并写下你的答复或处理要求。`
      + `可以直接提问或讨论;继续执行任务请在 @ 后使用 /conflict、/review 、/CI 或 /stop,每条评论一个命令。同一 ${target} 保留对话记忆;/conflict 在 Git 状态兼容且工作区可安全接管时继续因预算或人工决策暂停的候选。`;
  return `## PatchPaw:${title}\n\n`
    + `Run: \`${notice.run_id}\` · Head: \`${notice.head || '未获取'}\`\n\n`
    + `阶段:\`${notice.phase}\` · 状态:\`${notice.status}\`\n\n${notice.failure ? `${failureDetails(notice.failure)}\n\n` : ''}${notice.reason}\n\n`
    + instruction
    + '\n\n不要在评论中粘贴密钥。'
    + (includeCloseoutMarker && ['budget_exhausted', 'stopped'].includes(notice.status) ? `\n\n<!-- patchpaw-budget-closeout:${notice.run_id} -->` : '');
}