@xdevops/issue-auto-finish 1.0.73 → 1.0.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/ai-runner/ModelMapping.d.ts +3 -2
  2. package/dist/ai-runner/ModelMapping.d.ts.map +1 -1
  3. package/dist/{ai-runner-U4WLO5GZ.js → ai-runner-RUE5D72W.js} +2 -2
  4. package/dist/{analyze-67N2EEJY.js → analyze-IZFTWCNR.js} +3 -3
  5. package/dist/{braindump-6NNPGVFV.js → braindump-SXLAURR4.js} +4 -4
  6. package/dist/{chunk-XAMJNIXE.js → chunk-66OK4LG3.js} +40 -28
  7. package/dist/chunk-66OK4LG3.js.map +1 -0
  8. package/dist/{chunk-ZPHUJTZP.js → chunk-JL6ALTPS.js} +6 -6
  9. package/dist/{chunk-BCOL4JRM.js → chunk-KWOM6URK.js} +16 -12
  10. package/dist/chunk-KWOM6URK.js.map +1 -0
  11. package/dist/{chunk-RFMARJ5D.js → chunk-MC7YPVJG.js} +51 -14
  12. package/dist/chunk-MC7YPVJG.js.map +1 -0
  13. package/dist/{chunk-VM5NQHEO.js → chunk-NBX5LNCU.js} +3 -3
  14. package/dist/{chunk-SKA53TLK.js → chunk-OOJNTGB5.js} +5 -3
  15. package/dist/chunk-OOJNTGB5.js.map +1 -0
  16. package/dist/{chunk-WR3O4ATI.js → chunk-URE5IBQE.js} +2 -2
  17. package/dist/{chunk-C4NES56F.js → chunk-VYNKAT4P.js} +3 -2
  18. package/dist/{chunk-C4NES56F.js.map → chunk-VYNKAT4P.js.map} +1 -1
  19. package/dist/cli/setup/env-metadata.d.ts.map +1 -1
  20. package/dist/cli.js +7 -7
  21. package/dist/{config-C6QBYWFQ.js → config-LLOHFS6J.js} +3 -3
  22. package/dist/config-schema.d.ts +2 -0
  23. package/dist/config-schema.d.ts.map +1 -1
  24. package/dist/config.d.ts +2 -0
  25. package/dist/config.d.ts.map +1 -1
  26. package/dist/errors/AIExecutionError.d.ts +2 -0
  27. package/dist/errors/AIExecutionError.d.ts.map +1 -1
  28. package/dist/index.js +7 -7
  29. package/dist/{init-J26XGSST.js → init-EQWSE5FX.js} +5 -5
  30. package/dist/lib.js +3 -3
  31. package/dist/lifecycle/ActionLifecycleManager.d.ts +2 -2
  32. package/dist/lifecycle/ActionLifecycleManager.d.ts.map +1 -1
  33. package/dist/orchestrator/PipelineOrchestrator.d.ts.map +1 -1
  34. package/dist/orchestrator/steps/FailureHandler.d.ts.map +1 -1
  35. package/dist/phases/BasePhase.d.ts +5 -0
  36. package/dist/phases/BasePhase.d.ts.map +1 -1
  37. package/dist/{restart-PD3WUJGI.js → restart-24AKNL74.js} +5 -5
  38. package/dist/run.js +7 -7
  39. package/dist/{start-2NRFI3LI.js → start-YUT6GSVS.js} +5 -5
  40. package/dist/tracker/IssueState.d.ts +2 -0
  41. package/dist/tracker/IssueState.d.ts.map +1 -1
  42. package/dist/tracker/IssueTracker.d.ts +1 -1
  43. package/dist/tracker/IssueTracker.d.ts.map +1 -1
  44. package/package.json +1 -1
  45. package/src/web/frontend/dist/assets/{index-CtY1m1Qk.js → index-C82qOYx3.js} +40 -40
  46. package/src/web/frontend/dist/index.html +1 -1
  47. package/dist/chunk-BCOL4JRM.js.map +0 -1
  48. package/dist/chunk-RFMARJ5D.js.map +0 -1
  49. package/dist/chunk-SKA53TLK.js.map +0 -1
  50. package/dist/chunk-XAMJNIXE.js.map +0 -1
  51. /package/dist/{ai-runner-U4WLO5GZ.js.map → ai-runner-RUE5D72W.js.map} +0 -0
  52. /package/dist/{analyze-67N2EEJY.js.map → analyze-IZFTWCNR.js.map} +0 -0
  53. /package/dist/{braindump-6NNPGVFV.js.map → braindump-SXLAURR4.js.map} +0 -0
  54. /package/dist/{chunk-ZPHUJTZP.js.map → chunk-JL6ALTPS.js.map} +0 -0
  55. /package/dist/{chunk-VM5NQHEO.js.map → chunk-NBX5LNCU.js.map} +0 -0
  56. /package/dist/{chunk-WR3O4ATI.js.map → chunk-URE5IBQE.js.map} +0 -0
  57. /package/dist/{config-C6QBYWFQ.js.map → config-LLOHFS6J.js.map} +0 -0
  58. /package/dist/{init-J26XGSST.js.map → init-EQWSE5FX.js.map} +0 -0
  59. /package/dist/{restart-PD3WUJGI.js.map → restart-24AKNL74.js.map} +0 -0
  60. /package/dist/{start-2NRFI3LI.js.map → start-YUT6GSVS.js.map} +0 -0
@@ -48,7 +48,7 @@ import {
48
48
  createAIRunner,
49
49
  getRunnerCapabilities,
50
50
  isShuttingDown
51
- } from "./chunk-XAMJNIXE.js";
51
+ } from "./chunk-66OK4LG3.js";
52
52
  import {
53
53
  logger,
54
54
  runWithIssueContext
@@ -656,16 +656,17 @@ var IssueTracker = class extends BaseTracker {
656
656
  logger5.info("Issue state updated", { issueIid, state });
657
657
  eventBus.emitTyped("issue:stateChanged", { issueIid, state, record });
658
658
  }
659
- markFailed(issueIid, error, failedAtState) {
659
+ markFailed(issueIid, error, failedAtState, isRetryable) {
660
660
  const record = this.collection[this.key(issueIid)];
661
661
  if (!record) return;
662
662
  record.state = "failed" /* Failed */;
663
663
  record.lastError = error;
664
664
  record.failedAtState = failedAtState;
665
+ record.lastErrorRetryable = isRetryable;
665
666
  record.attempts += 1;
666
667
  record.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
667
668
  this.save();
668
- logger5.warn("Issue marked as failed", { issueIid, error, failedAtState, attempts: record.attempts });
669
+ logger5.warn("Issue marked as failed", { issueIid, error, failedAtState, attempts: record.attempts, isRetryable });
669
670
  eventBus.emitTyped("issue:failed", { issueIid, error, failedAtState, record });
670
671
  }
671
672
  isProcessing(issueIid) {
@@ -697,7 +698,7 @@ var IssueTracker = class extends BaseTracker {
697
698
  getDrivableIssues(maxRetries, stalledThresholdMs) {
698
699
  return this.getAllRecords().filter((record) => {
699
700
  const lm = this.lifecycleFor(record);
700
- if (lm.isDrivable(record.state, record.attempts, maxRetries)) return true;
701
+ if (lm.isDrivable(record.state, record.attempts, maxRetries, record.lastErrorRetryable)) return true;
701
702
  if (this.isStalled(getIid(record), stalledThresholdMs)) return true;
702
703
  return false;
703
704
  });
@@ -1249,7 +1250,8 @@ ${t("basePhase.rulesSection", { rules: matchedRulesText })}` : basePrompt;
1249
1250
  );
1250
1251
  throw new AIExecutionError(this.phaseName, `Phase ${this.phaseName} failed: ${shortReason}`, {
1251
1252
  output: result.output,
1252
- exitCode: result.exitCode
1253
+ exitCode: result.exitCode,
1254
+ isRetryable: this.classifyRetryable(result)
1253
1255
  });
1254
1256
  }
1255
1257
  this.persistSessionId(result.sessionId);
@@ -1341,6 +1343,22 @@ ${t("basePhase.rulesSection", { rules: matchedRulesText })}` : basePrompt;
1341
1343
  }
1342
1344
  return result;
1343
1345
  }
1346
+ /**
1347
+ * 判断 AI 执行失败是否为永久性错误(不可重试)。
1348
+ * 模型不存在、API key 无效等配置问题重试不会成功。
1349
+ */
1350
+ classifyRetryable(result) {
1351
+ const msg = (result.errorMessage ?? result.output ?? "").toLowerCase();
1352
+ const permanentPatterns = [
1353
+ /model\b.*\b(?:not found|not supported|unavailable|service info not found)/,
1354
+ /invalid.?api.?key/,
1355
+ /authentication.*(?:failed|denied|error)/,
1356
+ /permission.?denied/,
1357
+ /billing/,
1358
+ /quota.*exceeded/
1359
+ ];
1360
+ return !permanentPatterns.some((p) => p.test(msg));
1361
+ }
1344
1362
  /**
1345
1363
  * Heuristic: a resume failure is typically an immediate process exit
1346
1364
  * (exit code != 0, empty output) caused by an invalid/expired session ID.
@@ -1348,7 +1366,14 @@ ${t("basePhase.rulesSection", { rules: matchedRulesText })}` : basePrompt;
1348
1366
  isResumeFailure(result) {
1349
1367
  if (result.success) return false;
1350
1368
  const msg = (result.errorMessage ?? "").toLowerCase();
1351
- return msg.includes("session") || msg.includes("resume") || msg.includes("not found") || result.output.length === 0 && result.exitCode !== null && result.exitCode !== 0;
1369
+ if (msg.includes("session") || msg.includes("resume") || msg.includes("session_id")) {
1370
+ return true;
1371
+ }
1372
+ if (result.output.length === 0 && result.exitCode !== null && result.exitCode !== 0) {
1373
+ const isConfigError = msg.includes("model") || msg.includes("api key") || msg.includes("authentication");
1374
+ return !isConfigError;
1375
+ }
1376
+ return false;
1352
1377
  }
1353
1378
  persistSessionId(sessionId) {
1354
1379
  if (sessionId) {
@@ -2250,7 +2275,7 @@ var ActionLifecycleManager = class {
2250
2275
  * - running → 不可驱动(stalled 由外部叠加)
2251
2276
  * - done/skipped → 不可驱动
2252
2277
  */
2253
- isDrivable(state, attempts, maxRetries) {
2278
+ isDrivable(state, attempts, maxRetries, lastErrorRetryable) {
2254
2279
  if (state === "phase_done" /* PhaseDone */) return true;
2255
2280
  if (state === "phase_approved" /* PhaseApproved */) return true;
2256
2281
  if (state === "phase_running" /* PhaseRunning */) return false;
@@ -2261,6 +2286,7 @@ var ActionLifecycleManager = class {
2261
2286
  case "ready":
2262
2287
  return true;
2263
2288
  case "failed":
2289
+ if (lastErrorRetryable === false) return false;
2264
2290
  return attempts < maxRetries;
2265
2291
  case "waiting":
2266
2292
  case "running":
@@ -2384,6 +2410,19 @@ var ActionLifecycleManager = class {
2384
2410
  derivePhaseStatuses(currentState, currentPhase) {
2385
2411
  const result = {};
2386
2412
  let passedCurrent = false;
2413
+ if (currentState === "failed" /* Failed */ && currentPhase) {
2414
+ for (const phase of this.def.phases) {
2415
+ if (passedCurrent) {
2416
+ result[phase.name] = "pending";
2417
+ } else if (phase.name === currentPhase) {
2418
+ result[phase.name] = "failed";
2419
+ passedCurrent = true;
2420
+ } else {
2421
+ result[phase.name] = "completed";
2422
+ }
2423
+ }
2424
+ return result;
2425
+ }
2387
2426
  if ((currentState === "phase_running" /* PhaseRunning */ || currentState === "phase_done" /* PhaseDone */) && currentPhase) {
2388
2427
  for (const phase of this.def.phases) {
2389
2428
  if (passedCurrent) {
@@ -3990,13 +4029,14 @@ async function executeCompletion(ctx, deps, phaseResult, _wtGitMap) {
3990
4029
  var logger18 = logger.child("FailureHandler");
3991
4030
  async function handleFailure(err, issue, wtCtx, deps) {
3992
4031
  const errorMsg = err.message;
3993
- logger18.error("Issue processing failed", { iid: issue.iid, error: errorMsg });
4032
+ const isRetryable = err instanceof AIExecutionError ? err.isRetryable : true;
4033
+ logger18.error("Issue processing failed", { iid: issue.iid, error: errorMsg, isRetryable });
3994
4034
  metrics.incCounter("iaf_issues_failed_total");
3995
4035
  const currentRecord = deps.tracker.get(issue.iid);
3996
4036
  const failedAtState = currentRecord?.state || "pending" /* Pending */;
3997
4037
  const wasReset = failedAtState === "pending" /* Pending */ && currentRecord?.attempts === 0;
3998
4038
  if (failedAtState !== "failed" /* Failed */ && !wasReset) {
3999
- deps.tracker.markFailed(issue.iid, errorMsg.slice(0, 500), failedAtState);
4039
+ deps.tracker.markFailed(issue.iid, errorMsg.slice(0, 500), failedAtState, isRetryable);
4000
4040
  }
4001
4041
  if (wasReset) {
4002
4042
  logger18.info("Issue was reset during processing, skipping failure marking", { iid: issue.iid });
@@ -4347,7 +4387,7 @@ var PipelineOrchestrator = class {
4347
4387
  } catch {
4348
4388
  }
4349
4389
  });
4350
- this.tracker.delete(issueIid);
4390
+ this.tracker.updateState(issueIid, "skipped" /* Skipped */);
4351
4391
  logger19.info("Issue cancelled", { issueIid });
4352
4392
  }
4353
4393
  retryFromPhase(issueIid, phase) {
@@ -4661,9 +4701,6 @@ E2E \u6D4B\u8BD5\u5C06\u5C1D\u8BD5\u4F7F\u7528 config.json \u4E2D\u7684\u9ED8\u8
4661
4701
  async restartPreview(issueIid) {
4662
4702
  const record = this.tracker.get(issueIid);
4663
4703
  if (!record) throw new IssueNotFoundError(issueIid);
4664
- if (record.state !== "completed" /* Completed */) {
4665
- throw new InvalidStateError(record.state, "Issue not in completed state");
4666
- }
4667
4704
  const wtCtx = this.computeWorktreeContext(issueIid, record.branchName);
4668
4705
  if (!fsSync.existsSync(wtCtx.workDir)) {
4669
4706
  throw new InvalidStateError(record.state, "Worktree no longer exists");
@@ -5082,4 +5119,4 @@ export {
5082
5119
  PipelineOrchestrator,
5083
5120
  BrainstormService
5084
5121
  };
5085
- //# sourceMappingURL=chunk-RFMARJ5D.js.map
5122
+ //# sourceMappingURL=chunk-MC7YPVJG.js.map