feat: return completed handoffs to Agent Chat (#14408)

Return completed Agent Chat handoffs through a durable outbox and scope each generated update to its supplied tasks. Add recovery, browser delivery, result access, and calibrated quality coverage.

Validated with two consecutive ten-case Claude/Codex campaigns, all CI checks, and a 5/5 review.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip authored and GitHub committed 2026-09-29 10:25:52 -05:00
1 parent 3b4b270650
commit 83076d7e7c
59 files changed
+2696 -83

No files matched your search

@@ -18,6 +18,7 @@ import { ISSUE_DISPOSITION_REPAIR_RETRY_REASON } from "@paperclipai/shared";
import { parseObject } from "../../../adapters/utils.js";
import { evaluateAgentInvokabilityFromDb } from "../../../services/agent-invokability.js";
import { budgetService } from "../../../services/budgets.js";
import { isCompletedOnboardingHandoffWake } from "../../../services/chat-completion-delivery.js";
import { isHeartbeatWakeOnDemandEnabled } from "../../../services/heartbeat-policy.js";
import { collectDispositionRepairSourceState } from "../../../services/recovery/disposition-repair.js";
import { legacyDispositionEpisode, legacyDispositionFingerprint } from "../../../services/recovery/legacy-continuation.js";
@@ -608,6 +609,10 @@ export function createPostgresRunDispatchAdapter(
isNonAssigneeWorkspaceBusyRetry: isNonAssigneeWorkspaceBusyRetry(retryReason, context),
resumeIntent,
wakeCommentIdPresent: Boolean(wakeCommentId),
isCompletedOnboardingHandoffWake: await isCompletedOnboardingHandoffWake(dbOrTx, {
companyId: input.companyId, issueId, agentId: input.agentId,
reason: wakeReason, contextSnapshot: context,
}),
continuationParkApplies,
continuationParksExecutor,
continuationSummaryBody,
@@ -436,6 +436,28 @@ describe("decideQueuedRunStaleness", () => {
expect(decideQueuedRunStaleness(facts, NOW)).toEqual({ stale: false });
});
describe("completed onboarding handoff report", () => {
const reportingFacts = (): QueuedRunFacts => ({
...baseStalenessFacts(), issueStatus: "done", wakeReason: "issue_children_completed",
isCompletedOnboardingHandoffWake: true,
});
it("allows a verified child-completion report without reopening the parent", () => {
expect(decideQueuedRunStaleness(reportingFacts(), NOW)).toEqual({ stale: false });
});
it.each([
{ overrides: { isCompletedOnboardingHandoffWake: false }, errorCode: "issue_terminal_status" },
{ overrides: { wakeReason: "issue_assigned" }, errorCode: "issue_terminal_status" },
{ overrides: { issueStatus: "cancelled" }, errorCode: "issue_terminal_status" },
{ overrides: { issueAssigneeAgentId: "agent-2" }, errorCode: "issue_assignee_changed" },
{ overrides: { retryReasonKind: "max_turn_continuation" as const }, errorCode: "issue_not_in_progress" },
])("preserves $errorCode guard with $overrides", ({ overrides, errorCode }) => {
expect(decideQueuedRunStaleness({ ...reportingFacts(), ...overrides }, NOW))
.toMatchObject({ stale: true, errorCode });
});
});
it("allows a non-assignee workspace-busy retry to bypass the ownership check", () => {
const facts: QueuedRunFacts = {
...baseStalenessFacts(),
@@ -151,6 +151,8 @@ export type QueuedRunFacts = {
resumeIntent: boolean;
wakeCommentIdPresent: boolean;
/** Verified from current company-scoped parent/child state immediately before dispatch. */
isCompletedOnboardingHandoffWake?: boolean;
/** True when the run's wake or retry reason asks for a continuation the parked-summary check must inspect. */
continuationParkApplies: boolean;
@@ -609,7 +611,10 @@ export function decideQueuedRunStaleness(
const statusOutcome = decideIssueStatus({
status: facts.issueStatus,
requiresInProgress,
terminalBypass: facts.resumeIntent || facts.wakeCommentIdPresent,
terminalBypass: facts.resumeIntent || facts.wakeCommentIdPresent || (
facts.isCompletedOnboardingHandoffWake === true &&
facts.wakeReason === "issue_children_completed" && facts.issueStatus === "done"
),
});
if (statusOutcome === "terminal") {
return {