fix: make connection checks and task handoffs reliable (#13404)

Preserve connection-probe outcomes through cleanup, reduce unrelated startup work, and report selected Claude authentication accurately. Make artifact download actions match their labels.

Route delegated feedback through its active child, retain accepted messages across completion, and avoid redundant worker runs for proven closing notes. Preserve explicit follow-ups, human input, company boundaries, source provenance, and mixed issue references.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip authored and GitHub committed 2026-09-14 10:06:06 -05:00
1 parent 2d47a8058d
commit f1d57863d2
20 files changed
+740 -65

No files matched your search

@@ -4,6 +4,7 @@ import { currentConversationCommentCondition } from "../../../services/agent-con
import { getExecutionBlocker } from "../../../services/execution-blocker.js";
import { and, asc, eq, inArray, isNull, notInArray, or, sql } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import { extractIssueReferenceIdentifiers } from "@paperclipai/shared";
import {
agentWakeupRequests,
agents,
@@ -373,6 +374,44 @@ function buildTransaction(tx: Db, deps: WakeQueuePostgresAdapterDeps, db: Db, ru
return { allSelfAuthored: rows.length > 0 && rows.every((row) => row.createdByRunId === finishingRunId) };
},
async isCompletedDelegationMention({ companyId, issueId, finishingRunId, wakeAgentId, commentIds }) {
if (
run.companyId !== companyId || run.id !== finishingRunId ||
readNonEmptyString(parseObject(run.contextSnapshot).issueId) !== issueId
) return false;
const uniqueCommentIds = [...new Set(commentIds)];
if (uniqueCommentIds.length === 0) return false;
const parent = await tx.select({ status: issues.status, assigneeAgentId: issues.assigneeAgentId, identifier: issues.identifier })
.from(issues).where(and(eq(issues.companyId, companyId), eq(issues.id, issueId)))
.then((rows) => rows[0]);
if (parent?.status !== "done" || parent.assigneeAgentId !== run.agentId) return false;
const comments = await tx.select({ body: issueComments.body, createdByRunId: issueComments.createdByRunId,
authorAgentId: issueComments.authorAgentId })
.from(issueComments).where(and(
eq(issueComments.companyId, companyId), eq(issueComments.issueId, issueId),
inArray(issueComments.id, uniqueCommentIds), isNull(issueComments.deletedAt), currentConversationCommentCondition(),
));
if (comments.length !== uniqueCommentIds.length || comments.some((comment) =>
comment.createdByRunId !== run.id || (comment.authorAgentId !== null && comment.authorAgentId !== run.agentId)
)) return false;
const referencesByComment = comments.map((comment) => extractIssueReferenceIdentifiers(comment.body)
.filter((identifier) => identifier !== parent.identifier));
// A parent link is context; every other reference must identify the
// single completed child. Never discard unrelated follow-up work.
if (referencesByComment.some((references) => references.length !== 1)) return false;
const identifiers = [...new Set(referencesByComment.flat())];
const children = await tx.select({ identifier: issues.identifier, status: issues.status })
.from(issues).where(and(
eq(issues.companyId, companyId), eq(issues.parentId, issueId),
eq(issues.assigneeAgentId, wakeAgentId), inArray(issues.identifier, identifiers),
));
return referencesByComment.every((references) => {
const referencedChildren = children.filter((child) => child.identifier !== null && references.includes(child.identifier));
return referencedChildren.length === 1 && referencedChildren[0].status === "done";
});
},
async reopenIssue({ companyId, issueId }) {
const updated = await issuesSvc.updateForCompany(issueId, companyId, { status: "todo", executionState: null }, tx);
return updated ? toIssueSnapshot(updated as unknown as IssueRow) : null;