mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-31 06:04:11 +01:00
fix(server): wake chat milestones after confirmation verdicts
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
20f8c301f5
commit
a406da5bf6
4 files changed
+79
-4
No files matched your search
@@ -633,13 +633,23 @@ const support = external ? { supported: true } : await getEmbeddedPostgresTestSu
|
||||
expect((await publications()).map(row => row.idempotencyKey)).toContain(`run:${targetId}:queued:${f.endpointId}`);
|
||||
await quiet();
|
||||
});
|
||||
it.each(["answer", "skip", "cancel", "withdraw", "expire", "historical answer"] as const)("rechecks an unsent question after %s", async action => {
|
||||
it.each([
|
||||
["question", "answer"], ["question", "skip"], ["question", "cancel"],
|
||||
["question", "withdraw"], ["question", "expire"], ["question", "historical answer"],
|
||||
["confirmation", "accept"], ["confirmation", "reject"], ["confirmation", "skip"],
|
||||
["confirmation", "withdraw"], ["confirmation", "expire"],
|
||||
["question", "comment"], ["question", "historical comment"],
|
||||
["confirmation", "comment"], ["confirmation", "historical comment"],
|
||||
] as const)("rechecks an unsent %s after %s", async (kind, action) => {
|
||||
const source = await run(f, { status: "failed", errorCode: "test_run_failed" });
|
||||
const interactions = issueThreadInteractionService(db);
|
||||
const issue = { id: f.issueId, companyId: f.companyId, status: "in_progress" };
|
||||
const interaction = await interactions.create(issue, {
|
||||
const interaction = await interactions.create(issue, kind === "confirmation" ? {
|
||||
kind: "request_confirmation", continuationPolicy: "wake_assignee", sourceRunId: source.id,
|
||||
title: "Confirm", payload: { version: 1, prompt: "Continue?", supersedeOnUserComment: true },
|
||||
} : {
|
||||
kind: "ask_user_questions", continuationPolicy: "wake_assignee", sourceRunId: source.id,
|
||||
title: "Choose", payload: { version: 1, title: "Choose", questions: [{
|
||||
title: "Choose", payload: { version: 1, title: "Choose", supersedeOnUserComment: true, questions: [{
|
||||
id: "choice", prompt: "Continue?", selectionMode: "single", required: true,
|
||||
options: [{ id: "yes", label: "Yes" }],
|
||||
}] },
|
||||
@@ -651,7 +661,18 @@ const support = external ? { supported: true } : await getEmbeddedPostgresTestSu
|
||||
await start(); await flush();
|
||||
expect((await publications()).some(row => row.idempotencyKey.startsWith(`run:${source.id}:`))).toBe(false);
|
||||
await quiet();
|
||||
if (action === "skip") {
|
||||
if (action === "comment" || action === "historical comment") {
|
||||
const [comment] = await db.insert(issueComments).values({ companyId: f.companyId, issueId: f.issueId,
|
||||
authorType: "user", authorUserId: f.userId, origin: "comment", body: "Ignore the earlier request" }).returning();
|
||||
const expired = action === "comment"
|
||||
? await interactions.expireRequestConfirmationsSupersededByComment(issue, comment, { userId: f.userId })
|
||||
: await interactions.expireRequestConfirmationsSupersededByHistoricalComments(issue);
|
||||
expect(expired.map(row => row.id)).toContain(interaction.id);
|
||||
} else if (action === "accept") {
|
||||
await interactions.acceptInteraction({ ...issue, projectId: null, goalId: null }, interaction.id, {}, { userId: f.userId });
|
||||
} else if (action === "reject") {
|
||||
await interactions.rejectInteraction(issue, interaction.id, { reason: "No longer needed" }, { userId: f.userId });
|
||||
} else if (action === "skip") {
|
||||
await interactions.skipInteraction(issue, interaction.id, { reason: "No longer needed" }, { userId: f.userId });
|
||||
} else if (action === "cancel") {
|
||||
await interactions.cancelQuestions(issue, interaction.id, { reason: "No longer needed" }, { userId: f.userId });
|
||||
|
||||
@@ -2175,6 +2175,7 @@ export function issueThreadInteractionService(
|
||||
const postCommitActivityPublications: ActivityPublication[] = [];
|
||||
const result = await db.transaction(async (tx) => {
|
||||
await assertInteractionRunWriteAllowed(tx as unknown as Db, args.issue, args.actor);
|
||||
if (args.current.kind === "request_confirmation") await notifyChatRunMilestones(tx, args.current);
|
||||
await args.mutationOptions?.beforeResolveInTransaction?.(tx);
|
||||
// Policy mutations and review transitions use the same issue-row lock,
|
||||
// so the authoritative review policy and requester are stable through
|
||||
@@ -2458,6 +2459,7 @@ export function issueThreadInteractionService(
|
||||
const now = new Date();
|
||||
const updated = await db.transaction(async (tx) => {
|
||||
await assertInteractionRunWriteAllowed(tx as unknown as Db, args.issue, args.actor);
|
||||
if (args.current.kind === "request_confirmation") await notifyChatRunMilestones(tx, args.current);
|
||||
await args.mutationOptions?.beforeResolveInTransaction?.(tx);
|
||||
const issueContext = await tx
|
||||
.select({
|
||||
@@ -4401,6 +4403,7 @@ export function issueThreadInteractionService(
|
||||
const expired: IssueThreadInteraction[] = [];
|
||||
for (const row of superseded) {
|
||||
const updated = await db.transaction(async (tx) => {
|
||||
if (["ask_user_questions", "request_confirmation"].includes(row.kind)) await notifyChatRunMilestones(tx, row);
|
||||
if (row.kind === "connection_intent") {
|
||||
await tx
|
||||
.delete(toolOauthStates)
|
||||
@@ -4537,6 +4540,7 @@ export function issueThreadInteractionService(
|
||||
);
|
||||
if (!sampleQuestionRow) continue;
|
||||
const updatedInteractions = await db.transaction(async (tx) => {
|
||||
await notifyChatRunMilestones(tx, sampleQuestionRow);
|
||||
const updatedRows = await tx
|
||||
.update(issueThreadInteractions)
|
||||
.set({
|
||||
@@ -4575,6 +4579,7 @@ export function issueThreadInteractionService(
|
||||
);
|
||||
if (!sampleConfirmationRow) continue;
|
||||
const updatedInteractions = await db.transaction(async (tx) => {
|
||||
if (commentRows.some(row => row.kind === "request_confirmation")) await notifyChatRunMilestones(tx, sampleConfirmationRow);
|
||||
await notifyDeliveryWork(tx, DELIVERY_QUEUES.toolReviewProjection);
|
||||
const updatedRows = await tx
|
||||
.update(issueThreadInteractions)
|
||||
|
||||
Reference in new issue
Block a user