Improve orphaned local heartbeat recovery

Persist child-process metadata for local adapter runs, keep detached runs alive when their pid still exists, queue a single automatic retry when the pid is confirmed dead, and clear detached warnings when the original run reports activity again.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
dotta
2026-03-19 11:20:36 -05:00
parent 7f3fad64b8
commit c844ca1a40
17 changed files with 10924 additions and 22 deletions

View File

@@ -820,6 +820,7 @@ export function issueRoutes(db: Db, storage: StorageService) {
}
if (!(await assertAgentRunCheckoutOwnership(req, res, existing))) return;
const actor = getActorInfo(req);
const { comment: commentBody, hiddenAt: hiddenAtRaw, ...updateFields } = req.body;
if (hiddenAtRaw !== undefined) {
updateFields.hiddenAt = hiddenAtRaw ? new Date(hiddenAtRaw) : null;
@@ -856,6 +857,11 @@ export function issueRoutes(db: Db, storage: StorageService) {
return;
}
if (actor.runId) {
await heartbeat.reportRunActivity(actor.runId).catch((err) =>
logger.warn({ err, runId: actor.runId }, "failed to clear detached run warning after issue activity"));
}
// Build activity details with previous values for changed fields
const previous: Record<string, unknown> = {};
for (const key of Object.keys(updateFields)) {
@@ -864,7 +870,6 @@ export function issueRoutes(db: Db, storage: StorageService) {
}
}
const actor = getActorInfo(req);
const hasFieldChanges = Object.keys(previous).length > 0;
await logActivity(db, {
companyId: issue.companyId,
@@ -1278,6 +1283,11 @@ export function issueRoutes(db: Db, storage: StorageService) {
userId: actor.actorType === "user" ? actor.actorId : undefined,
});
if (actor.runId) {
await heartbeat.reportRunActivity(actor.runId).catch((err) =>
logger.warn({ err, runId: actor.runId }, "failed to clear detached run warning after issue comment"));
}
await logActivity(db, {
companyId: currentIssue.companyId,
actorType: actor.actorType,