mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-28 04:02:18 +01:00
perf(server): bound run reads in authentication and conversation recovery (#17663)
Read compact run identity during authentication and compact turn state during conversation recovery. Preserve fresh checks, durable steering and reply evidence, locking, and complete stored payloads. Validate 184 targeted PostgreSQL and lifecycle tests, workspace typecheck, server build, full CI, and Apex 5/5. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
f9581e533e
commit
9f3a5456ac
8 files changed
+265
-18
No files matched your search
@@ -282,7 +282,7 @@ export async function prepareConversationTurn(
|
||||
/** Finalizers only park a turn with a durable response, interaction, or processed /new. */
|
||||
export async function settleConversationTurn(
|
||||
db: Db,
|
||||
run: typeof heartbeatRuns.$inferSelect,
|
||||
run: Pick<typeof heartbeatRuns.$inferSelect, "id" | "companyId" | "status" | "contextSnapshot">,
|
||||
) {
|
||||
if (run.status !== "succeeded") return false;
|
||||
const context = run.contextSnapshot ?? {};
|
||||
@@ -291,7 +291,12 @@ export async function settleConversationTurn(
|
||||
let publication: ActivityPublication | null = null;
|
||||
const settled = await db.transaction(async (tx) => {
|
||||
const [issue] = await tx
|
||||
.select()
|
||||
.select({
|
||||
id: issues.id, companyId: issues.companyId, status: issues.status,
|
||||
conversationAgentId: issues.conversationAgentId, conversationUserId: issues.conversationUserId,
|
||||
conversationState: issues.conversationState, conversationSessionGeneration: issues.conversationSessionGeneration,
|
||||
executionRunId: issues.executionRunId,
|
||||
})
|
||||
.from(issues)
|
||||
.where(and(eq(issues.id, issueId), eq(issues.companyId, run.companyId)))
|
||||
.for("update");
|
||||
@@ -344,7 +349,7 @@ export async function settleConversationTurn(
|
||||
const [wake] =
|
||||
typeof wakeId === "string"
|
||||
? await tx
|
||||
.select()
|
||||
.select({ id: issueComments.id })
|
||||
.from(issueComments)
|
||||
.where(eq(issueComments.id, wakeId))
|
||||
: [];
|
||||
@@ -407,6 +412,26 @@ export async function settleConversationTurn(
|
||||
return settled;
|
||||
}
|
||||
|
||||
export async function settleConversationTurnForRun(
|
||||
db: Db,
|
||||
companyId: string,
|
||||
runId: string,
|
||||
) {
|
||||
const [run] = await db.select({
|
||||
id: heartbeatRuns.id, companyId: heartbeatRuns.companyId, status: heartbeatRuns.status,
|
||||
contextSnapshot: sql<Record<string, unknown> | null>`case when jsonb_typeof(${heartbeatRuns.contextSnapshot}) = 'object'
|
||||
then jsonb_build_object(
|
||||
'issueId', ${heartbeatRuns.contextSnapshot}->'issueId',
|
||||
'conversationReset', ${heartbeatRuns.contextSnapshot}->'conversationReset',
|
||||
'conversationSessionGeneration', ${heartbeatRuns.contextSnapshot}->'conversationSessionGeneration',
|
||||
'conversationReplyBoundaryCommentId', ${heartbeatRuns.contextSnapshot}->'conversationReplyBoundaryCommentId',
|
||||
'wakeCommentId', ${heartbeatRuns.contextSnapshot}->'wakeCommentId',
|
||||
'commentId', ${heartbeatRuns.contextSnapshot}->'commentId'
|
||||
) else null end`,
|
||||
}).from(heartbeatRuns).where(and(eq(heartbeatRuns.id, runId), eq(heartbeatRuns.companyId, companyId)));
|
||||
return run ? settleConversationTurn(db, run) : false;
|
||||
}
|
||||
|
||||
/** Fresh provider context uses only messages in this session, up to this turn. */
|
||||
export async function conversationReplay(
|
||||
db: Db,
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
import { CONTROLLER_LEASE_LOST_FAILURE_CODE, hasLiveLegacyController } from "../legacy-controller-lease.js";
|
||||
import { isCompletedOnboardingHandoffWake } from "../chat-completion-delivery.js";
|
||||
import { instanceSettingsService } from "../instance-settings.js";
|
||||
import { isWaitingConversation, settleConversationTurn, deliverConversationComments } from "../agent-conversations.js";
|
||||
import { isWaitingConversation, settleConversationTurnForRun, deliverConversationComments } from "../agent-conversations.js";
|
||||
import {
|
||||
and,
|
||||
asc,
|
||||
@@ -4582,7 +4582,7 @@ export function recoveryService(
|
||||
if (issue.conversationAgentId) {
|
||||
const lastRun = await getLatestIssueRun(issue.companyId, issue.id);
|
||||
if (lastRun?.status === "succeeded") {
|
||||
if (await settleConversationTurn(db, (await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, lastRun.id)))[0]!)) {
|
||||
if (await settleConversationTurnForRun(db, issue.companyId, lastRun.id)) {
|
||||
const [current] = await db.select().from(issues).where(eq(issues.id, issue.id));
|
||||
if (current) Object.assign(issue, current);
|
||||
}
|
||||
|
||||
@@ -434,13 +434,13 @@ export async function acceptSteeredIdentity(
|
||||
export async function captureRunIdentity(
|
||||
db: Db,
|
||||
input: { companyId: string; runId: string; agentId: string },
|
||||
options?: { readScope: "github_credentials" },
|
||||
options?: { readScope: "github_credentials" | "request_authentication" },
|
||||
) {
|
||||
// Lock acquisition serializes with steering delivery and its durable acknowledgement.
|
||||
return db.transaction(async (tx) => {
|
||||
await lockIdentityTask(tx, input.companyId, input.runId);
|
||||
const [run] = await tx
|
||||
.select(options?.readScope === "github_credentials" ? {
|
||||
.select(options?.readScope === "github_credentials" || options?.readScope === "request_authentication" ? {
|
||||
...getTableColumns(heartbeatRuns),
|
||||
contextSnapshot: sql<Record<string, unknown> | null>`case when jsonb_typeof(${heartbeatRuns.contextSnapshot}) = 'object'
|
||||
then jsonb_build_object('issueId', ${heartbeatRuns.contextSnapshot}->'issueId',
|
||||
|
||||
Reference in new issue
Block a user