Merge commit '19a97bbe57d1ebd3691c70dd822488efe7e5d3e1' into codex/sleep-stack-c1

* commit '19a97bbe57d1ebd3691c70dd822488efe7e5d3e1':
  fix(server): park persisted deferred admission refusals
This commit is contained in:
Dotta committed 2026-10-30 20:50:39 -05:00
commit c0fe0feffb
5 files changed
+204 -25

No files matched your search

@@ -1,3 +1,4 @@
import { PersistedExecutionWaitRefusal } from "../deferred-execution-wait.js";
import { and, asc, eq, gt, inArray, isNull, or, sql } from "drizzle-orm";
import { agentWakeupRequests, type Db } from "@paperclipai/db";
import type { createDeliveryWorkCoordinator } from "../delivery-work-coordinator.js";
@@ -105,7 +106,11 @@ export function registerDeferredReceiptWork(
onError: error => { errors.push(error); },
});
else if (kind === "task_chat") await owner.settleTaskChat(scope);
else await owner.resume({ ...scope, onError: error => { errors.push(error); } });
else {
const failed = (error: unknown) => { if (!(error instanceof PersistedExecutionWaitRefusal)) errors.push(error); };
try { await owner.resume({ ...scope, onError: failed }); }
catch (error) { failed(error); }
}
if (errors.length) throw new AggregateError(errors, `Deferred receipt ${entry.id} could not settle`);
return next === null ? null : { ...entry, dueAt: next };
},
+15 -16
View File
@@ -1,4 +1,4 @@
import { writeDeferredExecutionWait } from "../deferred-execution-wait.js";
import { PersistedExecutionWaitRefusal, writeDeferredExecutionWait } from "../deferred-execution-wait.js";
import { notifyDeferredExecutionWork } from "../deferred-execution-notifications.js";
import type { DeferredReceiptRecoveryOptions } from "../deferred-execution-notifications.js";
import { notifyRunDispatch } from "../run-dispatch-notifications.js";
@@ -2519,17 +2519,17 @@ export function createHeartbeatQueue(db: Db, dependencies: HeartbeatQueueDepende
skipReason: string,
patch: Partial<typeof agentWakeupRequests.$inferInsert> = {},
waitCondition?: Record<string, unknown>,
) => {
): Promise<{ created: boolean; waitSaved?: boolean }> => {
// A temporary refusal must leave the handoff retryable, without burning
// its one durable receipt ID on a skipped wake.
if (caseRequest) return { created: false };
if (executionWaitRequestId) {
await writeDeferredExecutionWait(db, { companyId: agent.companyId, requestId: executionWaitRequestId }, {
const waitSaved = await writeDeferredExecutionWait(db, { companyId: agent.companyId, requestId: executionWaitRequestId }, {
reason: skipReason, message: patch.error ?? (skipReason === "issue_tree_hold_active"
? "This task is paused. Resume it to send your saved message."
: "Waiting for task execution to be enabled. Your message is saved."),
});
return { created: false };
return { created: false, waitSaved };
}
const request = {
...durableReceiptFields,
@@ -2801,31 +2801,30 @@ export function createHeartbeatQueue(db: Db, dependencies: HeartbeatQueueDepende
},
);
if (budgetBlock) {
await writeSkippedRequest("budget.blocked", { error: budgetBlock.reason }, {
const wait = await writeSkippedRequest("budget.blocked", { error: budgetBlock.reason }, {
scopeType: budgetBlock.scopeType, scopeId: budgetBlock.scopeId,
});
if (caseRequest) return null;
throw conflict(budgetBlock.reason, {
scopeType: budgetBlock.scopeType,
scopeId: budgetBlock.scopeId,
});
const details = { scopeType: budgetBlock.scopeType, scopeId: budgetBlock.scopeId };
throw wait.waitSaved ? new PersistedExecutionWaitRefusal(budgetBlock.reason, details)
: conflict(budgetBlock.reason, details);
}
// Setup delays execution, but must not discard work accepted by a caller.
const invokability = await getAgentInvokability(isAgentAwaitingSetup(agent) ? { ...agent, status: "idle" } : agent);
if (!invokability.invokable) {
if (caseRequest) return null;
let waitSaved = false;
if (opts.requestedByActorType !== "user" || executionWaitRequestId) {
await writeSkippedRequest("agent.not_invokable", {
const wait = await writeSkippedRequest("agent.not_invokable", {
error: invokability.message,
}, { status: agent.status, reason: invokability.reason });
waitSaved = wait.waitSaved === true;
}
throw conflict(invokability.message, {
status: agent.status,
reason: invokability.reason,
invalidOrgChain: invokability.invalidOrgChain,
...invokability.details,
});
const details = { status: agent.status, reason: invokability.reason,
invalidOrgChain: invokability.invalidOrgChain, ...invokability.details };
throw waitSaved ? new PersistedExecutionWaitRefusal(invokability.message, details)
: conflict(invokability.message, details);
}
const policy = parseHeartbeatPolicy(agent);