Files
PaperClipAI/server/src/services/native-runtime/native-review-dispatch.ts
T
DottaandPaperclip e6dcc6b095 Connect personal Muse through secure native execution and Vault OAuth (#16448)
Add company-scoped Muse native control, OAuth, wake receiver and recovery.

Recheck current authority after waits and before effects or receipt replay. Preserve task-read scope, assignment expiry, independent holds, nonblocking reassignment and truthful cleanup provenance.

Retain source trust on progress comments and verify current Muse V8 authority through real signed-JWT HTTP requests. Preserve the upstream trimmed ordinary authentication lookup.

Refs #16448.

Preserve captured canonical queue recovery and concurrent environment preparation while retaining the scoped Muse V8 exclusion. Normal migration 0354 and existing Muse SQL remain unchanged. Fresh current-head Greptile review is 5/5 with the project-write finding addressed. Required CI and independent technical review gate the merge; maintainer override is reserved for formal approval rules.

Recheck current Muse project-write authority after all receipt/task/run/admission waits, before creation or replay. Real PostgreSQL regressions reproduce four late effects/replays on the old route; the repaired three-file treatment passes 354 cases with no skips, and the scoped server typecheck passes. Ordinary/Dot behavior is preserved.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-17 22:44:36 -05:00

53 lines
3.0 KiB
TypeScript

import { withExternalAdmissionGuard, assertNoExternalOverlap } from "../../modules/external-agents/index.js";
import { agentWakeupRequests, heartbeatRuns, issues, type Db } from "@paperclipai/db";
import { and, eq } from "drizzle-orm";
import type { PgUpdateSetSource } from "drizzle-orm/pg-core";
import { claimNativeReviewExecutionLock, readNativeReviewAssignmentContext } from "./native-review-participant.js";
/** The run, wake, and issue lock form one reviewer admission claim. */
export async function claimQueuedNativeReviewRun(db: Db, input: {
run: typeof heartbeatRuns.$inferSelect;
agentNameKey: string | null;
claimedAt: Date;
claimValues: PgUpdateSetSource<typeof heartbeatRuns>;
}): Promise<typeof heartbeatRuns.$inferSelect | null> {
const context = input.run.contextSnapshot as Record<string, unknown> | null;
const issueId = context?.issueId;
if (typeof issueId !== "string" || !readNativeReviewAssignmentContext(context)) return null;
return db.transaction(async (tx) => {
// Match the queue editor's lock order: issue, wake, then run.
const [issue] = await tx.select({ id: issues.id }).from(issues).where(and(
eq(issues.id, issueId), eq(issues.companyId, input.run.companyId),
)).for("update");
if (!issue) return null;
if (input.run.wakeupRequestId) {
const [wake] = await tx.select().from(agentWakeupRequests).where(and(
eq(agentWakeupRequests.id, input.run.wakeupRequestId),
eq(agentWakeupRequests.companyId, input.run.companyId),
eq(agentWakeupRequests.agentId, input.run.agentId),
)).for("update");
if (!wake || wake.status !== "queued" || wake.runId !== input.run.id) return null;
}
const [run] = await tx.select().from(heartbeatRuns).where(and(
eq(heartbeatRuns.id, input.run.id), eq(heartbeatRuns.companyId, input.run.companyId),
eq(heartbeatRuns.agentId, input.run.agentId),
)).for("update");
if (!run || run.status !== "queued" || run.wakeupRequestId !== input.run.wakeupRequestId) return null;
if ((run.contextSnapshot as Record<string, unknown> | null)?.issueId !== issueId) return null;
await withExternalAdmissionGuard(tx, run.companyId, run.agentId, () => assertNoExternalOverlap(tx, run.companyId, run.agentId));
const locked = await claimNativeReviewExecutionLock(tx as unknown as Db, {
companyId: run.companyId, issueId, agentId: run.agentId, runId: run.id,
contextSnapshot: context, agentNameKey: input.agentNameKey, claimedAt: input.claimedAt,
});
if (!locked) return null;
const [claimed] = await tx.update(heartbeatRuns).set({
...input.claimValues, status: "running", startedAt: run.startedAt ?? input.claimedAt,
updatedAt: input.claimedAt,
}).where(eq(heartbeatRuns.id, run.id)).returning();
if (run.wakeupRequestId) await tx.update(agentWakeupRequests).set({
status: "claimed", claimedAt: input.claimedAt, updatedAt: input.claimedAt,
}).where(eq(agentWakeupRequests.id, run.wakeupRequestId));
return claimed ?? null;
});
}