Skip to content
234 changes: 124 additions & 110 deletions apps/server/src/orchestration-v2/Orchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,90 @@ function nextQueuedRun(
return queuedRunsInDeliveryOrder(projection)[0];
}

function pullRequestSnapshotEndsWatch(
snapshot: ThreadPullRequestLink["snapshot"],
watchStartedAt: ThreadPullRequestWatch["startedAt"],
): boolean {
return (
snapshot?.state === "merged" ||
// A restarted watch can see a closed snapshot from before the PR reopened.
(snapshot?.state === "closed" &&
DateTime.isGreaterThanOrEqualTo(
DateTime.makeUnsafe(snapshot.syncedAt),
DateTime.makeUnsafe(watchStartedAt),
))
);
}

function isStalePullRequestWatchWake(
projection: Pick<OrchestrationV2ThreadProjection, "thread" | "messages">,
run: OrchestrationV2Run,
): boolean {
const source = projection.messages.find((message) => message.id === run.userMessageId)
?.notification?.source;
if (source?.kind !== "monitor" || source.pullRequest === undefined) return false;
const stamp = source.pullRequest;
const link = threadPullRequestsOf(projection.thread).find(
(candidate) =>
candidate.source !== "stack-dismissed" && threadPullRequestKeysEqual(candidate, stamp),
);
if (link === undefined) return true;
if (stamp.watchEnded === true) {
// The final notice outlives its watch, but a replacement watch supersedes it.
const latestWatch = link.watch ?? link.endedWatch;
return latestWatch?.startedAt !== stamp.watchStartedAt;
}
return (
link.watch === undefined ||
link.watch.startedAt !== stamp.watchStartedAt ||
link.watch.headSha !== stamp.headSha ||
pullRequestSnapshotEndsWatch(link.snapshot, link.watch.startedAt)
);
}

function queuedRunCancellationEvents(
projection: Pick<OrchestrationV2ThreadProjection, "nodes" | "attempts">,
run: OrchestrationV2Run,
now: DateTime.Utc,
): ReadonlyArray<Omit<OrchestrationV2DomainEvent, "id">> {
const rootNode = projection.nodes.find((node) => node.id === run.rootNodeId);
const attempt = projection.attempts.find((candidate) => candidate.id === run.activeAttemptId);
const events: Array<Omit<OrchestrationV2DomainEvent, "id">> = [
{
type: "run.updated",
threadId: run.threadId,
runId: run.id,
...(run.rootNodeId === null ? {} : { nodeId: run.rootNodeId }),
providerInstanceId: run.providerInstanceId,
occurredAt: now,
payload: { ...run, status: "cancelled", queuePosition: null, completedAt: now },
},
];
if (attempt !== undefined && rootNode !== undefined) {
events.push({
type: "run-attempt.updated",
threadId: run.threadId,
runId: run.id,
nodeId: rootNode.id,
providerInstanceId: run.providerInstanceId,
occurredAt: now,
payload: { ...attempt, status: "cancelled", completedAt: now },
});
}
if (rootNode !== undefined) {
events.push({
type: "node.updated",
threadId: run.threadId,
runId: run.id,
nodeId: rootNode.id,
providerInstanceId: run.providerInstanceId,
occurredAt: now,
payload: { ...rootNode, status: "cancelled", completedAt: now },
});
}
return events;
}

function latestStableRun(
projection: Pick<OrchestrationV2ThreadProjection, "runs">,
): OrchestrationV2Run | null {
Expand Down Expand Up @@ -1117,60 +1201,9 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
readonly now: DateTime.Utc;
}) =>
Effect.gen(function* () {
const rootNode =
input.run.rootNodeId === null
? undefined
: input.projection.nodes.find((candidate) => candidate.id === input.run.rootNodeId);
const attempt =
input.run.activeAttemptId === null
? undefined
: input.projection.attempts.find(
(candidate) => candidate.id === input.run.activeAttemptId,
);
const emitEvent = emit(input.events, input.command);
yield* emitEvent({
type: "run.updated",
threadId: input.run.threadId,
runId: input.run.id,
...(input.run.rootNodeId === null ? {} : { nodeId: input.run.rootNodeId }),
providerInstanceId: input.run.providerInstanceId,
occurredAt: input.now,
payload: {
...input.run,
status: "cancelled",
queuePosition: null,
completedAt: input.now,
},
});
if (attempt !== undefined && rootNode !== undefined) {
yield* emitEvent({
type: "run-attempt.updated",
threadId: input.run.threadId,
runId: input.run.id,
nodeId: rootNode.id,
providerInstanceId: input.run.providerInstanceId,
occurredAt: input.now,
payload: {
...attempt,
status: "cancelled",
completedAt: input.now,
},
});
}
if (rootNode !== undefined) {
yield* emitEvent({
type: "node.updated",
threadId: input.run.threadId,
runId: input.run.id,
nodeId: rootNode.id,
providerInstanceId: input.run.providerInstanceId,
occurredAt: input.now,
payload: {
...rootNode,
status: "cancelled",
completedAt: input.now,
},
});
for (const event of queuedRunCancellationEvents(input.projection, input.run, input.now)) {
yield* emitEvent(event);
}
});

Expand Down Expand Up @@ -1285,7 +1318,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
) =>
Effect.gen(function* () {
if (!(yield* projectionStore.canStartQueuedRun(threadId))) return;
const projection = yield* readCommandProjection(threadId);
let projection = yield* readCommandProjection(threadId);
if (
projection.thread.archivedAt !== null ||
projection.thread.deletedAt !== null ||
Expand All @@ -1307,7 +1340,14 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
if (usageLimitBlockedRun(projection.runs, projection.turnItems, sessionError) !== null) {
return;
}
const queuedRun = nextQueuedRun(projection);
let queuedRun = nextQueuedRun(projection);
while (queuedRun !== undefined && isStalePullRequestWatchWake(projection, queuedRun)) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
yield* writeSystemEvents(
queuedRunCancellationEvents(projection, queuedRun, yield* DateTime.now),
);
projection = yield* readCommandProjection(threadId);
queuedRun = nextQueuedRun(projection);
}
if (queuedRun === undefined) {
return;
}
Expand Down Expand Up @@ -2411,14 +2451,36 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
}
yield* dispatchThreadMutation(command, events, effects);
if (command.wake === undefined) return;
// An idle thread dispatches immediately, bypassing queued-wake validation.
if (
command.wake.notification.source.kind === "monitor" &&
command.watch !== null &&
pullRequestSnapshotEndsWatch(link.snapshot, command.startedAt)
) {
return;
}
yield* dispatchMessage(
{
type: "message.dispatch",
commandId: command.commandId,
threadId: command.threadId,
messageId: command.wake.messageId,
text: command.wake.text,
notification: command.wake.notification,
notification: {
...command.wake.notification,
source:
command.wake.notification.source.kind === "monitor"
? {
kind: "monitor",
pullRequest: {
...key,
watchStartedAt: command.startedAt,
headSha: command.watch?.headSha ?? null,
...(command.watch === null ? { watchEnded: true } : {}),
},
}
: command.wake.notification.source,
},
attachments: [],
dispatchMode: { type: "queue_after_active" },
createdBy: "agent",
Expand Down Expand Up @@ -7985,61 +8047,13 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
});
return;
}
const queuedRootNode =
queuedRun.rootNodeId === null
? undefined
: projection.nodes.find((candidate) => candidate.id === queuedRun.rootNodeId);
const queuedAttempt =
queuedRun.activeAttemptId === null
? undefined
: projection.attempts.find((candidate) => candidate.id === queuedRun.activeAttemptId);

const now = yield* DateTime.now;
const emitEvent = emit(events, command);
yield* emitEvent({
type: "run.updated",
threadId: command.threadId,
runId: queuedRun.id,
...(queuedRun.rootNodeId === null ? {} : { nodeId: queuedRun.rootNodeId }),
providerInstanceId: queuedRun.providerInstanceId,
occurredAt: now,
payload: {
...queuedRun,
status: "cancelled",
queuePosition: null,
completedAt: now,
},
yield* emitQueuedRunCancellation({
command,
events,
projection,
run: queuedRun,
now: yield* DateTime.now,
});
if (queuedAttempt !== undefined && queuedRootNode !== undefined) {
yield* emitEvent({
type: "run-attempt.updated",
threadId: command.threadId,
runId: queuedRun.id,
nodeId: queuedRootNode.id,
providerInstanceId: queuedRun.providerInstanceId,
occurredAt: now,
payload: {
...queuedAttempt,
status: "cancelled",
completedAt: now,
},
});
}
if (queuedRootNode !== undefined) {
yield* emitEvent({
type: "node.updated",
threadId: command.threadId,
runId: queuedRun.id,
nodeId: queuedRootNode.id,
providerInstanceId: queuedRun.providerInstanceId,
occurredAt: now,
payload: {
...queuedRootNode,
status: "cancelled",
completedAt: now,
},
});
}
});

const dispatchQueuedRunEdit = (
Expand Down
Loading
Loading