chore(審查記錄): AI 失敗時輸出 exit code 與 stderr/stdout 供診斷
This commit is contained in:
+36
-4
@@ -13,6 +13,38 @@ const templates = require('./templates');
|
|||||||
const PER_FILE_DIFF_LIMIT = 16_000;
|
const PER_FILE_DIFF_LIMIT = 16_000;
|
||||||
const TOTAL_DIFF_LIMIT = 160_000;
|
const TOTAL_DIFF_LIMIT = 160_000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 從 `runAgent` 的失敗結果組出可診斷的一行摘要:退出碼/訊號/stderr/stdout 片段。
|
||||||
|
*
|
||||||
|
* AI CLI 失敗時常見 stderr 為空、真正原因印在 stdout(例如 CLI 用法錯誤、未認證或額度提示),
|
||||||
|
* 若只記 `error.message` 會看不出原因。本函式把 exit code、stderr、stdout 各截前 500 字併成一行,
|
||||||
|
* 供各失敗點的 WRN log 使用。純函式、不拋例外。
|
||||||
|
*
|
||||||
|
* @param {{error: (Error & {code?: number|string, signal?: string, killed?: boolean})|null, stderr?: string, output?: string}} res
|
||||||
|
* `runAgent` 的回傳物件。
|
||||||
|
* @returns {string} 單行診斷摘要(各段以「|」分隔);無任何資訊時回傳 error.message 或「未知錯誤」。
|
||||||
|
* @remarks
|
||||||
|
* 使用情境:{@link runAttackers}/{@link runDefenders}/{@link fillPurposes}/{@link selectLabels}
|
||||||
|
* 判定 `!res.ok` 時,以本函式把失敗細節寫進 WRN log,讓 CI 記錄能看出 AI CLI 為何失敗。
|
||||||
|
* 本函式未匯出,僅供模組內部使用。
|
||||||
|
*/
|
||||||
|
function agentFailureDetail(res) {
|
||||||
|
const parts = [];
|
||||||
|
const err = res && res.error;
|
||||||
|
if (err) {
|
||||||
|
if (err.killed) parts.push('已逾時終止');
|
||||||
|
if (typeof err.code === 'number') parts.push(`exit ${err.code}`);
|
||||||
|
else if (err.code) parts.push(`code ${err.code}`);
|
||||||
|
else if (err.signal) parts.push(`signal ${err.signal}`);
|
||||||
|
}
|
||||||
|
const stderr = String((res && res.stderr) || '').trim();
|
||||||
|
if (stderr) parts.push(`stderr:${stderr.slice(0, 500)}`);
|
||||||
|
const stdout = String((res && res.output) || '').trim();
|
||||||
|
if (stdout) parts.push(`stdout:${stdout.slice(0, 500)}`);
|
||||||
|
if (parts.length === 0) parts.push((err && err.message) || '未知錯誤');
|
||||||
|
return parts.join('|');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 讀取工作目錄下的 `.reviewignore`,解析為忽略路徑前綴清單。
|
* 讀取工作目錄下的 `.reviewignore`,解析為忽略路徑前綴清單。
|
||||||
*
|
*
|
||||||
@@ -140,7 +172,7 @@ ${sections}
|
|||||||
- 不得輸出個資(PII)。`;
|
- 不得輸出個資(PII)。`;
|
||||||
const res = await runAgent(tool, { model, prompt, cwd, timeoutMs: 300_000 });
|
const res = await runAgent(tool, { model, prompt, cwd, timeoutMs: 300_000 });
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
log('步驟4', 'WRN', '檔案用途摘要產生失敗,以「—」代替。');
|
log('步驟4', 'WRN', `檔案用途摘要產生失敗,以「—」代替:${agentFailureDetail(res)}。`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const parsed = extractJson(res.output);
|
const parsed = extractJson(res.output);
|
||||||
@@ -279,7 +311,7 @@ async function runAttackers({ tool, model, cwd, attackers, diffRows }) {
|
|||||||
log('步驟6', 'INF', `攻擊方 ${role.meta.name} 開始分析。`);
|
log('步驟6', 'INF', `攻擊方 ${role.meta.name} 開始分析。`);
|
||||||
const res = await runAgent(tool, { model, prompt: buildAttackPrompt(role, diffRows), cwd });
|
const res = await runAgent(tool, { model, prompt: buildAttackPrompt(role, diffRows), cwd });
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
log('步驟6', 'WRN', `攻擊方 ${role.meta.name} 執行失敗:${(res.error && res.error.message) || '未知錯誤'}。`);
|
log('步驟6', 'WRN', `攻擊方 ${role.meta.name} 執行失敗:${agentFailureDetail(res)}。`);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const parsed = extractJson(res.output);
|
const parsed = extractJson(res.output);
|
||||||
@@ -459,7 +491,7 @@ async function runDefenders({ tool, model, cwd, defenders, findings }) {
|
|||||||
});
|
});
|
||||||
const verdicts = new Map();
|
const verdicts = new Map();
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
log('步驟8', 'WRN', `防守方 ${role.meta.name} 執行失敗,該角色視為全部保留。`);
|
log('步驟8', 'WRN', `防守方 ${role.meta.name} 執行失敗,該角色視為全部保留:${agentFailureDetail(res)}。`);
|
||||||
return { role: role.meta.name, verdicts };
|
return { role: role.meta.name, verdicts };
|
||||||
}
|
}
|
||||||
const parsed = extractJson(res.output);
|
const parsed = extractJson(res.output);
|
||||||
@@ -633,7 +665,7 @@ ${JSON.stringify(brief)}
|
|||||||
- 沒有適合的標籤時輸出 []。`;
|
- 沒有適合的標籤時輸出 []。`;
|
||||||
const res = await runAgent(tool, { model, prompt, cwd, timeoutMs: 300_000 });
|
const res = await runAgent(tool, { model, prompt, cwd, timeoutMs: 300_000 });
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
log('建問題', 'WRN', '標籤挑選失敗,issue 不掛標籤。');
|
log('建問題', 'WRN', `標籤挑選失敗,issue 不掛標籤:${agentFailureDetail(res)}。`);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const parsed = extractJson(res.output);
|
const parsed = extractJson(res.output);
|
||||||
|
|||||||
Reference in New Issue
Block a user