fix(messaging): deliver delegated tool-only replies before mirroring (#60)
deliverDelegatedReplies used allowSilence and marked teamChatMirroredAt even when no Slack/Teams message was sent, leaving tool-only delegated runs silently dropped with no retry path. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
This commit is contained in:
parent
ec597a5c68
commit
492e0f0965
@ -41,6 +41,65 @@ describe("team chat bridge", () => {
|
|||||||
expect(teamChatResponseText([])).toBe("Bot completed the request without a written reply.");
|
expect(teamChatResponseText([])).toBe("Bot completed the request without a written reply.");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("delivers delegated tool-only replies before marking them mirrored", async () => {
|
||||||
|
const send = vi.fn(async () => ({ handle: "reply-delegated" }));
|
||||||
|
const runUpdateMany = vi.fn(async () => ({ count: 1 }));
|
||||||
|
const bridge = new TeamChatBridge({
|
||||||
|
prisma: {
|
||||||
|
run: {
|
||||||
|
findMany: vi.fn(async () => [
|
||||||
|
{
|
||||||
|
id: "run-delegated",
|
||||||
|
status: "completed",
|
||||||
|
sourceMessageId: "msg-source",
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
findUnique: vi.fn(async () => null),
|
||||||
|
updateMany: runUpdateMany,
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
findUnique: vi.fn(async () => ({ replyTo: { runId: "parent-run-1" } })),
|
||||||
|
findFirst: vi.fn(async () => ({
|
||||||
|
blocks: [{ kind: "progress", text: "Searching files" }],
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
externalMessage: {
|
||||||
|
findUnique: vi.fn(async () => ({
|
||||||
|
replyThreadId: "100.1",
|
||||||
|
externalConversation: { conversationId: "C-1" },
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
} as unknown as PrismaClient,
|
||||||
|
events: { sendUserMessage: vi.fn() },
|
||||||
|
jobs: { enqueue: vi.fn() },
|
||||||
|
send,
|
||||||
|
providerId: "slack",
|
||||||
|
botId: "bot-1",
|
||||||
|
});
|
||||||
|
(
|
||||||
|
bridge as unknown as {
|
||||||
|
target: { id: string; spaceId: string; userId: string; name: string };
|
||||||
|
}
|
||||||
|
).target = { id: "bot-1", spaceId: "space-1", userId: "owner-1", name: "Analyst" };
|
||||||
|
|
||||||
|
await (
|
||||||
|
bridge as unknown as {
|
||||||
|
deliverDelegatedReplies: (target: { id: string; name: string }) => Promise<void>;
|
||||||
|
}
|
||||||
|
).deliverDelegatedReplies({ id: "bot-1", name: "Analyst" });
|
||||||
|
|
||||||
|
expect(send).toHaveBeenCalledWith({
|
||||||
|
conversationId: "C-1",
|
||||||
|
replyThreadId: "100.1",
|
||||||
|
content: "Analyst completed the request without a written reply.",
|
||||||
|
idempotencyKey: "team-chat-run:run-delegated",
|
||||||
|
});
|
||||||
|
expect(runUpdateMany).toHaveBeenCalledWith({
|
||||||
|
where: { id: "run-delegated", teamChatMirroredAt: null },
|
||||||
|
data: { teamChatMirroredAt: expect.any(Date) },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps deferred messages outside reconciliation until routine routing resolves them", async () => {
|
it("keeps deferred messages outside reconciliation until routine routing resolves them", async () => {
|
||||||
const upsert = vi.fn(async ({ create }: { create: Record<string, unknown> }) => ({
|
const upsert = vi.fn(async ({ create }: { create: Record<string, unknown> }) => ({
|
||||||
id: "external-deferred",
|
id: "external-deferred",
|
||||||
|
|||||||
@ -710,15 +710,14 @@ export class TeamChatBridge {
|
|||||||
const content =
|
const content =
|
||||||
run.status === "failed"
|
run.status === "failed"
|
||||||
? `${target.name} could not complete the delegated request. Open Rakazo for details.`
|
? `${target.name} could not complete the delegated request. Open Rakazo for details.`
|
||||||
: teamChatResponseText(blocks, target.name, true);
|
: teamChatResponseText(blocks, target.name);
|
||||||
if (content) {
|
if (!content) continue;
|
||||||
await this.deps.send({
|
await this.deps.send({
|
||||||
conversationId: origin.externalConversation.conversationId,
|
conversationId: origin.externalConversation.conversationId,
|
||||||
replyThreadId: origin.replyThreadId,
|
replyThreadId: origin.replyThreadId,
|
||||||
content,
|
content,
|
||||||
idempotencyKey: `team-chat-run:${run.id}`,
|
idempotencyKey: `team-chat-run:${run.id}`,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
await this.markTeamChatMirrored(run.id);
|
await this.markTeamChatMirrored(run.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user