@evermore.work/server 2026.520.0-canary.0 → 2026.522.0-canary.1

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 (113) hide show
  1. package/dist/app.d.ts +1 -0
  2. package/dist/app.d.ts.map +1 -1
  3. package/dist/app.js +53 -16
  4. package/dist/app.js.map +1 -1
  5. package/dist/http/body-limits.d.ts +4 -0
  6. package/dist/http/body-limits.d.ts.map +1 -0
  7. package/dist/http/body-limits.js +4 -0
  8. package/dist/http/body-limits.js.map +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +23 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/middleware/error-handler.d.ts.map +1 -1
  13. package/dist/middleware/error-handler.js +10 -1
  14. package/dist/middleware/error-handler.js.map +1 -1
  15. package/dist/routes/companies.d.ts.map +1 -1
  16. package/dist/routes/companies.js +2 -1
  17. package/dist/routes/companies.js.map +1 -1
  18. package/dist/routes/company-import-paths.d.ts +3 -0
  19. package/dist/routes/company-import-paths.d.ts.map +1 -0
  20. package/dist/routes/company-import-paths.js +3 -0
  21. package/dist/routes/company-import-paths.js.map +1 -0
  22. package/dist/routes/issues.d.ts.map +1 -1
  23. package/dist/routes/issues.js +142 -10
  24. package/dist/routes/issues.js.map +1 -1
  25. package/dist/services/issue-thread-interactions.d.ts +5 -0
  26. package/dist/services/issue-thread-interactions.d.ts.map +1 -1
  27. package/dist/services/issue-thread-interactions.js +78 -2
  28. package/dist/services/issue-thread-interactions.js.map +1 -1
  29. package/dist/services/issues.d.ts +2 -0
  30. package/dist/services/issues.d.ts.map +1 -1
  31. package/dist/services/issues.js +27 -2
  32. package/dist/services/issues.js.map +1 -1
  33. package/dist/services/plugin-lifecycle.d.ts.map +1 -1
  34. package/dist/services/plugin-lifecycle.js +30 -5
  35. package/dist/services/plugin-lifecycle.js.map +1 -1
  36. package/dist/services/plugin-worker-manager.d.ts.map +1 -1
  37. package/dist/services/plugin-worker-manager.js +3 -1
  38. package/dist/services/plugin-worker-manager.js.map +1 -1
  39. package/package.json +16 -16
  40. package/ui-dist/assets/{_basePickBy-ur36FGgu.js → _basePickBy-BAfyEDHh.js} +1 -1
  41. package/ui-dist/assets/{_baseUniq-DOtXUkii.js → _baseUniq-BVtZ4ePq.js} +1 -1
  42. package/ui-dist/assets/{arc-CgvHLduO.js → arc-NM4T7ncJ.js} +1 -1
  43. package/ui-dist/assets/{architectureDiagram-VXUJARFQ-C9P1idjU.js → architectureDiagram-VXUJARFQ-CcghPpe4.js} +1 -1
  44. package/ui-dist/assets/{blockDiagram-VD42YOAC-CAMu_DcH.js → blockDiagram-VD42YOAC-CoPJnEvc.js} +1 -1
  45. package/ui-dist/assets/{c4Diagram-YG6GDRKO-BbUtuYGo.js → c4Diagram-YG6GDRKO-DC8HKuV5.js} +1 -1
  46. package/ui-dist/assets/channel-oeICJafe.js +1 -0
  47. package/ui-dist/assets/{chunk-4BX2VUAB-CTrEBNY1.js → chunk-4BX2VUAB-BFyK9Z-9.js} +1 -1
  48. package/ui-dist/assets/{chunk-55IACEB6-CIe1wGln.js → chunk-55IACEB6-CUhPuWM1.js} +1 -1
  49. package/ui-dist/assets/{chunk-B4BG7PRW-B8p6GPTa.js → chunk-B4BG7PRW-D3p8bK_5.js} +1 -1
  50. package/ui-dist/assets/{chunk-DI55MBZ5-D_q_KdSN.js → chunk-DI55MBZ5-CCD5K6PL.js} +1 -1
  51. package/ui-dist/assets/{chunk-FMBD7UC4-pn3dIZ5Z.js → chunk-FMBD7UC4-CA4Hn0su.js} +1 -1
  52. package/ui-dist/assets/{chunk-QN33PNHL-DjVHgBi3.js → chunk-QN33PNHL-6LyF9JFT.js} +1 -1
  53. package/ui-dist/assets/{chunk-QZHKN3VN-BmX_FU-9.js → chunk-QZHKN3VN-D5iUp0wq.js} +1 -1
  54. package/ui-dist/assets/{chunk-TZMSLE5B-BsS-f8EW.js → chunk-TZMSLE5B-DwQdEWk6.js} +1 -1
  55. package/ui-dist/assets/classDiagram-2ON5EDUG-BLDIxz8H.js +1 -0
  56. package/ui-dist/assets/classDiagram-v2-WZHVMYZB-BLDIxz8H.js +1 -0
  57. package/ui-dist/assets/clone-CXxoKrQC.js +1 -0
  58. package/ui-dist/assets/{cose-bilkent-S5V4N54A-Rrh2YWZD.js → cose-bilkent-S5V4N54A-CJXAQtlZ.js} +1 -1
  59. package/ui-dist/assets/{dagre-6UL2VRFP-BYlLrV1l.js → dagre-6UL2VRFP-DGu0oMQF.js} +1 -1
  60. package/ui-dist/assets/{diagram-PSM6KHXK-CGmUzDUl.js → diagram-PSM6KHXK-Cdabn9s9.js} +1 -1
  61. package/ui-dist/assets/{diagram-QEK2KX5R-WaFjpU2E.js → diagram-QEK2KX5R-CVNLM2e3.js} +1 -1
  62. package/ui-dist/assets/{diagram-S2PKOQOG-CrTMNqtq.js → diagram-S2PKOQOG-17idWIQN.js} +1 -1
  63. package/ui-dist/assets/{erDiagram-Q2GNP2WA-Dm3faCdS.js → erDiagram-Q2GNP2WA-DWvqhZQi.js} +1 -1
  64. package/ui-dist/assets/{flowDiagram-NV44I4VS-B4Q5YtnD.js → flowDiagram-NV44I4VS-DvtW8STD.js} +1 -1
  65. package/ui-dist/assets/{ganttDiagram-JELNMOA3-DwbA62qK.js → ganttDiagram-JELNMOA3-DXicwquE.js} +1 -1
  66. package/ui-dist/assets/{gitGraphDiagram-V2S2FVAM-D9qrL3NH.js → gitGraphDiagram-V2S2FVAM-C25-WuW6.js} +1 -1
  67. package/ui-dist/assets/{graph-DFXafxQf.js → graph-CFYRqodg.js} +1 -1
  68. package/ui-dist/assets/{index-SDl3zIIz.js → index-BHGC2J3x.js} +1 -1
  69. package/ui-dist/assets/{index-DKDHGw8g.js → index-BVHQhywh.js} +1 -1
  70. package/ui-dist/assets/{index-Dh6pW3kR.js → index-BXkIyLU3.js} +1 -1
  71. package/ui-dist/assets/{index-DbI_ounN.js → index-Bat3FSRG.js} +1 -1
  72. package/ui-dist/assets/{index-DqUoy5Xt.js → index-BjTEWd_N.js} +1 -1
  73. package/ui-dist/assets/{index-BLMvao6K.js → index-BwH24qCy.js} +1 -1
  74. package/ui-dist/assets/{index-2l4kaSnK.js → index-CAF13J8j.js} +1 -1
  75. package/ui-dist/assets/{index-B9TjE7C1.js → index-CCXEIc5t.js} +1 -1
  76. package/ui-dist/assets/{index-Wd0d_e7x.js → index-CQCOD315.js} +1 -1
  77. package/ui-dist/assets/{index-Bu_Yw-rq.js → index-CT8XPEg8.js} +1 -1
  78. package/ui-dist/assets/{index-DrFHxroW.js → index-CUNx0bRn.js} +126 -126
  79. package/ui-dist/assets/{index-CGQCTx4q.js → index-Ca3xBUXp.js} +1 -1
  80. package/ui-dist/assets/{index-Bv-NIGkH.js → index-Cw3iF_k6.js} +1 -1
  81. package/ui-dist/assets/{index-D0AW6Ex4.js → index-CxZQpQTP.js} +1 -1
  82. package/ui-dist/assets/{index-KfGo4tR4.js → index-D6uEP1N1.js} +1 -1
  83. package/ui-dist/assets/{index-DONP_c_6.js → index-D85jUak7.js} +1 -1
  84. package/ui-dist/assets/{index-C6epqsMd.js → index-DMnKD2AG.js} +1 -1
  85. package/ui-dist/assets/{index-D_YO6Ibe.js → index-Ds2tRcF9.js} +1 -1
  86. package/ui-dist/assets/{index-CoHgnjUU.js → index-IDPqK6dk.js} +1 -1
  87. package/ui-dist/assets/{index-AdFQqp3g.js → index-NsXec_rb.js} +1 -1
  88. package/ui-dist/assets/{index-B7P1n35S.js → index-SHt_ZvlJ.js} +1 -1
  89. package/ui-dist/assets/{index-36xStgzi.js → index-VNR5mBPK.js} +1 -1
  90. package/ui-dist/assets/{index-BTcY-hP2.js → index-ZglV-fz_.js} +1 -1
  91. package/ui-dist/assets/{infoDiagram-HS3SLOUP-BpSoPdCX.js → infoDiagram-HS3SLOUP-DK9j0BGY.js} +1 -1
  92. package/ui-dist/assets/{journeyDiagram-XKPGCS4Q-CteiGqXX.js → journeyDiagram-XKPGCS4Q-BQ1wv45a.js} +1 -1
  93. package/ui-dist/assets/{kanban-definition-3W4ZIXB7-bxXoEdtx.js → kanban-definition-3W4ZIXB7-CWNvcmce.js} +1 -1
  94. package/ui-dist/assets/{layout-C73CcCuC.js → layout-yOBskK5Z.js} +1 -1
  95. package/ui-dist/assets/{linear-CxlfBvt7.js → linear-CJ5iTPUv.js} +1 -1
  96. package/ui-dist/assets/{mermaid.core-Fg3ZO6QO.js → mermaid.core-DlHhoBx2.js} +4 -4
  97. package/ui-dist/assets/{mindmap-definition-VGOIOE7T-nu_pvWep.js → mindmap-definition-VGOIOE7T-BJ2DXzjK.js} +1 -1
  98. package/ui-dist/assets/{pieDiagram-ADFJNKIX-BOA6WgQP.js → pieDiagram-ADFJNKIX-CDCDTKT8.js} +1 -1
  99. package/ui-dist/assets/{quadrantDiagram-AYHSOK5B-Dxy0YFqG.js → quadrantDiagram-AYHSOK5B-VQAT-8Am.js} +1 -1
  100. package/ui-dist/assets/{requirementDiagram-UZGBJVZJ-Di4nW5Aa.js → requirementDiagram-UZGBJVZJ-D0thUX7y.js} +1 -1
  101. package/ui-dist/assets/{sankeyDiagram-TZEHDZUN-DZQ4EGbS.js → sankeyDiagram-TZEHDZUN-DxPP5JxP.js} +1 -1
  102. package/ui-dist/assets/{sequenceDiagram-WL72ISMW-Bi6X93SR.js → sequenceDiagram-WL72ISMW-DrjVPEcr.js} +1 -1
  103. package/ui-dist/assets/{stateDiagram-FKZM4ZOC-7FE9LR7S.js → stateDiagram-FKZM4ZOC-BFvVUDkq.js} +1 -1
  104. package/ui-dist/assets/stateDiagram-v2-4FDKWEC3-pah5kxfq.js +1 -0
  105. package/ui-dist/assets/{timeline-definition-IT6M3QCI-BHt5ey38.js → timeline-definition-IT6M3QCI-DqFsh20q.js} +1 -1
  106. package/ui-dist/assets/{treemap-GDKQZRPO-BgLxlWZU.js → treemap-GDKQZRPO-BdHExSsY.js} +1 -1
  107. package/ui-dist/assets/{xychartDiagram-PRI3JC2R-yLw6fryP.js → xychartDiagram-PRI3JC2R-BuufhaB_.js} +1 -1
  108. package/ui-dist/index.html +1 -1
  109. package/ui-dist/assets/channel-DKyh0X3p.js +0 -1
  110. package/ui-dist/assets/classDiagram-2ON5EDUG-Dn8GiEC1.js +0 -1
  111. package/ui-dist/assets/classDiagram-v2-WZHVMYZB-Dn8GiEC1.js +0 -1
  112. package/ui-dist/assets/clone-CgBtIqNi.js +0 -1
  113. package/ui-dist/assets/stateDiagram-v2-4FDKWEC3-DNlLaaUR.js +0 -1
@@ -375,6 +375,15 @@ function shouldImplicitlyMoveCommentedIssueToTodo(input) {
375
375
  return false;
376
376
  return true;
377
377
  }
378
+ function shouldHumanCommentResumeInProgressScheduledRetry(input) {
379
+ if (!input.hasComment)
380
+ return false;
381
+ if (input.actorType !== "user")
382
+ return false;
383
+ if (input.issueStatus !== "in_progress")
384
+ return false;
385
+ return typeof input.assigneeAgentId === "string" && input.assigneeAgentId.length > 0;
386
+ }
378
387
  function isExplicitResumeCapableStatus(status) {
379
388
  return status === "done" || status === "blocked" || status === "todo" || status === "in_progress";
380
389
  }
@@ -561,6 +570,34 @@ export function issueRoutes(db, storage, opts = {}) {
561
570
  };
562
571
  const feedbackExportService = opts?.feedbackExportService;
563
572
  const environmentsSvc = environmentService(db);
573
+ async function cancelScheduledRetrySupersededByComment(input) {
574
+ const scheduledRetryRunId = readNonEmptyString(input.scheduledRetryRunId);
575
+ if (!scheduledRetryRunId)
576
+ return null;
577
+ try {
578
+ const cancelled = await heartbeat.cancelRun(scheduledRetryRunId);
579
+ const cancelledRunId = cancelled?.id ?? scheduledRetryRunId;
580
+ await logActivity(db, {
581
+ companyId: input.issue.companyId,
582
+ actorType: input.actor.actorType,
583
+ actorId: input.actor.actorId,
584
+ agentId: input.actor.agentId,
585
+ runId: input.actor.runId,
586
+ action: "heartbeat.cancelled",
587
+ entityType: "heartbeat_run",
588
+ entityId: cancelledRunId,
589
+ details: {
590
+ source: "issue_comment_scheduled_retry_superseded",
591
+ issueId: input.issue.id,
592
+ },
593
+ });
594
+ return cancelledRunId;
595
+ }
596
+ catch (err) {
597
+ logger.error({ err, issueId: input.issue.id, runId: scheduledRetryRunId }, "failed to cancel scheduled retry superseded by issue comment");
598
+ throw err;
599
+ }
600
+ }
564
601
  async function classifySourceRecoveryRevalidation(input) {
565
602
  const { issue } = input;
566
603
  if (issue.status === "done" || issue.status === "cancelled") {
@@ -1298,6 +1335,8 @@ export function issueRoutes(db, storage, opts = {}) {
1298
1335
  ? Number.parseInt(rawOffset, 10)
1299
1336
  : null;
1300
1337
  const attention = req.query.attention;
1338
+ const sortField = req.query.sortField;
1339
+ const sortDir = req.query.sortDir;
1301
1340
  if (assigneeUserFilterRaw === "me" && (!assigneeUserId || req.actor.type !== "board")) {
1302
1341
  res.status(403).json({ error: "assigneeUserId=me requires board authentication" });
1303
1342
  return;
@@ -1326,6 +1365,14 @@ export function issueRoutes(db, storage, opts = {}) {
1326
1365
  res.status(400).json({ error: "offset must be a non-negative integer" });
1327
1366
  return;
1328
1367
  }
1368
+ if (sortField !== undefined && sortField !== "updated") {
1369
+ res.status(400).json({ error: "sortField must be 'updated' when provided" });
1370
+ return;
1371
+ }
1372
+ if (sortDir !== undefined && sortDir !== "asc" && sortDir !== "desc") {
1373
+ res.status(400).json({ error: "sortDir must be 'asc' or 'desc' when provided" });
1374
+ return;
1375
+ }
1329
1376
  const offset = parsedOffset ?? 0;
1330
1377
  const result = await svc.list(companyId, {
1331
1378
  attention: attention === "blocked" ? "blocked" : undefined,
@@ -1353,6 +1400,8 @@ export function issueRoutes(db, storage, opts = {}) {
1353
1400
  q: req.query.q,
1354
1401
  limit,
1355
1402
  offset,
1403
+ sortField: sortField === "updated" ? "updated" : undefined,
1404
+ sortDir: sortDir === "asc" || sortDir === "desc" ? sortDir : undefined,
1356
1405
  });
1357
1406
  const issueIds = result.map((issue) => issue.id);
1358
1407
  const [handoffStates, recoveryActionByIssue] = await Promise.all([
@@ -2736,6 +2785,16 @@ export function issueRoutes(db, storage, opts = {}) {
2736
2785
  !(await assertRecoveryActionAuthority(req, res, existing, activeRecoveryActionBeforeUpdate, { source: "issue_update" }))) {
2737
2786
  return;
2738
2787
  }
2788
+ const scheduledRetryForHumanComment = shouldHumanCommentResumeInProgressScheduledRetry({
2789
+ hasComment: !!commentBody,
2790
+ issueStatus: existing.status,
2791
+ assigneeAgentId: requestedAssigneeAgentId,
2792
+ actorType: actor.actorType,
2793
+ })
2794
+ ? await svc.getCurrentScheduledRetry(existing.id)
2795
+ : null;
2796
+ const shouldResumeInProgressScheduledRetry = !!scheduledRetryForHumanComment &&
2797
+ scheduledRetryForHumanComment.agentId === requestedAssigneeAgentId;
2739
2798
  const effectiveMoveToTodoRequested = explicitMoveToTodoRequested ||
2740
2799
  (!!commentBody &&
2741
2800
  shouldImplicitlyMoveCommentedIssueToTodo({
@@ -2743,7 +2802,8 @@ export function issueRoutes(db, storage, opts = {}) {
2743
2802
  assigneeAgentId: requestedAssigneeAgentId,
2744
2803
  actorType: actor.actorType,
2745
2804
  actorId: actor.actorId,
2746
- }));
2805
+ })) ||
2806
+ shouldResumeInProgressScheduledRetry;
2747
2807
  const updateReferenceSummaryBefore = titleOrDescriptionChanged
2748
2808
  ? await issueReferencesSvc.listIssueReferenceSummary(existing.id)
2749
2809
  : null;
@@ -2797,10 +2857,20 @@ export function issueRoutes(db, storage, opts = {}) {
2797
2857
  }
2798
2858
  if (commentBody &&
2799
2859
  effectiveMoveToTodoRequested &&
2800
- (isClosed || (isBlocked && !hasUnresolvedFirstClassBlockers)) &&
2860
+ (isClosed || (isBlocked && !hasUnresolvedFirstClassBlockers) || shouldResumeInProgressScheduledRetry) &&
2801
2861
  updateFields.status === undefined) {
2802
2862
  updateFields.status = "todo";
2803
2863
  }
2864
+ let cancelledScheduledRetryRunId = null;
2865
+ if (commentBody &&
2866
+ shouldResumeInProgressScheduledRetry &&
2867
+ updateFields.status === "todo") {
2868
+ cancelledScheduledRetryRunId = await cancelScheduledRetrySupersededByComment({
2869
+ scheduledRetryRunId: scheduledRetryForHumanComment?.runId,
2870
+ issue: existing,
2871
+ actor,
2872
+ });
2873
+ }
2804
2874
  if (req.body.executionPolicy !== undefined) {
2805
2875
  updateFields.executionPolicy = applyActorMonitorScheduledBy(normalizeIssueExecutionPolicy(req.body.executionPolicy), actor.actorType);
2806
2876
  }
@@ -3023,6 +3093,10 @@ export function issueRoutes(db, storage, opts = {}) {
3023
3093
  previous.status !== undefined &&
3024
3094
  issue.status === "todo";
3025
3095
  const reopenFromStatus = reopened ? existing.status : null;
3096
+ const scheduledRetrySupersededByComment = shouldResumeInProgressScheduledRetry &&
3097
+ previous.status !== undefined &&
3098
+ existing.status === "in_progress" &&
3099
+ issue.status === "todo";
3026
3100
  const statusChangedFromBlockedToTodo = existing.status === "blocked" &&
3027
3101
  issue.status === "todo" &&
3028
3102
  (req.body.status !== undefined || reopened);
@@ -3062,6 +3136,13 @@ export function issueRoutes(db, storage, opts = {}) {
3062
3136
  ...(commentBody ? { source: "comment" } : {}),
3063
3137
  ...(resumeRequested === true ? { resumeIntent: true, followUpRequested: true } : {}),
3064
3138
  ...(reopened ? { reopened: true, reopenedFrom: reopenFromStatus } : {}),
3139
+ ...(scheduledRetrySupersededByComment
3140
+ ? {
3141
+ scheduledRetrySupersededByComment: true,
3142
+ scheduledRetryRunId: scheduledRetryForHumanComment?.runId ?? null,
3143
+ ...(cancelledScheduledRetryRunId ? { cancelledScheduledRetryRunId } : {}),
3144
+ }
3145
+ : {}),
3065
3146
  ...(interruptedRunId ? { interruptedRunId } : {}),
3066
3147
  ...(cancelledStatusRunId ? { cancelledStatusRunId } : {}),
3067
3148
  ...(workspaceChange ? { workspaceChange } : {}),
@@ -3266,6 +3347,13 @@ export function issueRoutes(db, storage, opts = {}) {
3266
3347
  issueTitle: issue.title,
3267
3348
  ...(resumeRequested === true ? { resumeIntent: true, followUpRequested: true } : {}),
3268
3349
  ...(reopened ? { reopened: true, reopenedFrom: reopenFromStatus, source: "comment" } : {}),
3350
+ ...(scheduledRetrySupersededByComment
3351
+ ? {
3352
+ scheduledRetrySupersededByComment: true,
3353
+ scheduledRetryRunId: scheduledRetryForHumanComment?.runId ?? null,
3354
+ ...(cancelledScheduledRetryRunId ? { cancelledScheduledRetryRunId } : {}),
3355
+ }
3356
+ : {}),
3269
3357
  ...(interruptedRunId ? { interruptedRunId } : {}),
3270
3358
  ...(hasFieldChanges ? { updated: true } : {}),
3271
3359
  ...summarizeIssueReferenceActivityDetails({
@@ -3709,7 +3797,16 @@ export function issueRoutes(db, storage, opts = {}) {
3709
3797
  return;
3710
3798
  }
3711
3799
  assertCompanyAccess(req, issue.companyId);
3712
- const interactions = await issueThreadInteractionService(db).listForIssue(id);
3800
+ const actor = getActorInfo(req);
3801
+ const interactionSvc = issueThreadInteractionService(db);
3802
+ const expiredInteractions = await interactionSvc.expireRequestConfirmationsSupersededByHistoricalComments(issue);
3803
+ await logExpiredRequestConfirmations({
3804
+ issue,
3805
+ interactions: expiredInteractions,
3806
+ actor,
3807
+ source: "issue.interactions.catchup_superseded_by_comment",
3808
+ });
3809
+ const interactions = await interactionSvc.listForIssue(id);
3713
3810
  res.json(interactions);
3714
3811
  });
3715
3812
  router.post("/issues/:id/interactions", validate(createIssueThreadInteractionSchema), async (req, res) => {
@@ -4155,13 +4252,24 @@ export function issueRoutes(db, storage, opts = {}) {
4155
4252
  const isClosed = isClosedIssueStatus(issue.status);
4156
4253
  const isBlocked = issue.status === "blocked";
4157
4254
  const explicitMoveToTodoRequested = reopenRequested || resumeRequested === true;
4255
+ const scheduledRetryForHumanComment = shouldHumanCommentResumeInProgressScheduledRetry({
4256
+ hasComment: true,
4257
+ issueStatus: issue.status,
4258
+ assigneeAgentId: issue.assigneeAgentId,
4259
+ actorType: actor.actorType,
4260
+ })
4261
+ ? await svc.getCurrentScheduledRetry(issue.id)
4262
+ : null;
4263
+ const shouldResumeInProgressScheduledRetry = !!scheduledRetryForHumanComment &&
4264
+ scheduledRetryForHumanComment.agentId === issue.assigneeAgentId;
4158
4265
  const effectiveMoveToTodoRequested = explicitMoveToTodoRequested ||
4159
4266
  shouldImplicitlyMoveCommentedIssueToTodo({
4160
4267
  issueStatus: issue.status,
4161
4268
  assigneeAgentId: issue.assigneeAgentId,
4162
4269
  actorType: actor.actorType,
4163
4270
  actorId: actor.actorId,
4164
- });
4271
+ }) ||
4272
+ shouldResumeInProgressScheduledRetry;
4165
4273
  const hasUnresolvedFirstClassBlockers = isBlocked && effectiveMoveToTodoRequested
4166
4274
  ? (await svc.getDependencyReadiness(issue.id)).unresolvedBlockerCount > 0
4167
4275
  : false;
@@ -4174,14 +4282,25 @@ export function issueRoutes(db, storage, opts = {}) {
4174
4282
  let interruptedRunId = null;
4175
4283
  let currentIssue = issue;
4176
4284
  const commentReferenceSummaryBefore = await issueReferencesSvc.listIssueReferenceSummary(issue.id);
4177
- if (effectiveMoveToTodoRequested && (isClosed || (isBlocked && !hasUnresolvedFirstClassBlockers))) {
4285
+ let scheduledRetrySupersededByComment = false;
4286
+ let cancelledScheduledRetryRunId = null;
4287
+ if (effectiveMoveToTodoRequested &&
4288
+ (isClosed || (isBlocked && !hasUnresolvedFirstClassBlockers) || shouldResumeInProgressScheduledRetry)) {
4289
+ scheduledRetrySupersededByComment = shouldResumeInProgressScheduledRetry && issue.status === "in_progress";
4290
+ cancelledScheduledRetryRunId = scheduledRetrySupersededByComment
4291
+ ? await cancelScheduledRetrySupersededByComment({
4292
+ scheduledRetryRunId: scheduledRetryForHumanComment?.runId,
4293
+ issue,
4294
+ actor,
4295
+ })
4296
+ : null;
4178
4297
  const reopenedIssue = await svc.update(id, { status: "todo" });
4179
4298
  if (!reopenedIssue) {
4180
4299
  res.status(404).json({ error: "Issue not found" });
4181
4300
  return;
4182
4301
  }
4183
- reopened = true;
4184
- reopenFromStatus = issue.status;
4302
+ reopened = isClosed || (isBlocked && !hasUnresolvedFirstClassBlockers);
4303
+ reopenFromStatus = reopened ? issue.status : null;
4185
4304
  currentIssue = reopenedIssue;
4186
4305
  await logActivity(db, {
4187
4306
  companyId: currentIssue.companyId,
@@ -4194,8 +4313,14 @@ export function issueRoutes(db, storage, opts = {}) {
4194
4313
  entityId: currentIssue.id,
4195
4314
  details: {
4196
4315
  status: "todo",
4197
- reopened: true,
4198
- reopenedFrom: reopenFromStatus,
4316
+ ...(reopened ? { reopened: true, reopenedFrom: reopenFromStatus } : {}),
4317
+ ...(scheduledRetrySupersededByComment
4318
+ ? {
4319
+ scheduledRetrySupersededByComment: true,
4320
+ scheduledRetryRunId: scheduledRetryForHumanComment?.runId ?? null,
4321
+ ...(cancelledScheduledRetryRunId ? { cancelledScheduledRetryRunId } : {}),
4322
+ }
4323
+ : {}),
4199
4324
  source: "comment",
4200
4325
  ...(resumeRequested === true ? { resumeIntent: true, followUpRequested: true } : {}),
4201
4326
  identifier: currentIssue.identifier,
@@ -4257,6 +4382,13 @@ export function issueRoutes(db, storage, opts = {}) {
4257
4382
  issueTitle: currentIssue.title,
4258
4383
  ...(resumeRequested === true ? { resumeIntent: true, followUpRequested: true } : {}),
4259
4384
  ...(reopened ? { reopened: true, reopenedFrom: reopenFromStatus, source: "comment" } : {}),
4385
+ ...(scheduledRetrySupersededByComment
4386
+ ? {
4387
+ scheduledRetrySupersededByComment: true,
4388
+ scheduledRetryRunId: scheduledRetryForHumanComment?.runId ?? null,
4389
+ ...(cancelledScheduledRetryRunId ? { cancelledScheduledRetryRunId } : {}),
4390
+ }
4391
+ : {}),
4260
4392
  ...(interruptedRunId ? { interruptedRunId } : {}),
4261
4393
  ...summarizeIssueReferenceActivityDetails({
4262
4394
  addedReferencedIssues: commentReferenceDiff.addedReferencedIssues.map(summarizeIssueRelationForActivity),
@@ -4279,7 +4411,7 @@ export function issueRoutes(db, storage, opts = {}) {
4279
4411
  issue: currentIssue,
4280
4412
  trigger: "comment",
4281
4413
  actor,
4282
- statusChanged: reopened,
4414
+ statusChanged: reopened || scheduledRetrySupersededByComment,
4283
4415
  resumeRequested: resumeRequested === true,
4284
4416
  reopened,
4285
4417
  blockedToTodoRecovery: reopened && reopenFromStatus === "blocked" && currentIssue.status === "todo",