1import {
2 latestProviderTurnForAttempt,
3 type MessageId,
4 type OrchestrationV2ConversationMessage,
5 type OrchestrationV2ThreadProjection,
6} from "@t3tools/contracts";
7
8/** A wake attaches to output held in provider memory, which Stop or process loss discards. */
9export function isBufferedProviderNotification(
10 message: OrchestrationV2ConversationMessage,
11): boolean {
12 return (
13 message.createdBy === "agent" &&
14 message.creationSource === "provider" &&
15 message.notification !== undefined &&
16 message.delegatedCompletion === undefined
17 );
18}
19
20/**
21 * A persisted steer without an acceptance receipt can be retried as a continuation.
22 * Delivery is at least once: provider acceptance and our receipt cannot commit
23 * atomically. Reusing the message ID keeps recovery from duplicating timeline items.
24 */
25export function isUndeliveredMailboxSteer(
26 projection: Pick<OrchestrationV2ThreadProjection, "messages" | "runs" | "providerTurns">,
27 messageId: MessageId,
28): boolean {
29 const message = projection.messages.find((candidate) => candidate.id === messageId);
30 if (message?.delegatedCompletion === undefined) return false;
31 const run = projection.runs.find((candidate) => candidate.id === message.runId);
32 return (
33 run !== undefined &&
34 run.userMessageId !== message.id &&
35 (["completed", "failed", "interrupted", "cancelled", "rolled_back"].includes(run.status) ||
36 latestProviderTurnForAttempt(projection.providerTurns, run.activeAttemptId)?.status ===
37 "completed")
38 );
39}