| ... | @@ -2605,10 +2605,12 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio | ... | @@ -2605,10 +2605,12 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio |
| 2605 | const automaticQueuedRuns = projection.runs.filter( | 2605 | const automaticQueuedRuns = projection.runs.filter( |
| 2606 | (run) => run.status === "queued" && automaticMessageIds.has(run.userMessageId), | 2606 | (run) => run.status === "queued" && automaticMessageIds.has(run.userMessageId), |
| 2607 | ); | 2607 | ); |
| 2608 | const activeRunExists = projection.runs.some( | 2608 | const runningRunExists = projection.runs.some((run) => |
| 2609 | (run) => | 2609 | ["preparing", "starting", "running", "waiting"].includes(run.status), |
| 2610 | ["preparing", "queued", "starting", "running", "waiting"].includes(run.status) && | 2610 | ); |
| 2611 | !automaticQueuedRuns.includes(run), | 2611 | // Includes a queue held after a Stop, which otherwise shows no activity. |
| | 2612 | const queuedMessageExists = projection.runs.some( |
| | 2613 | (run) => run.status === "queued" && !automaticQueuedRuns.includes(run), |
| 2612 | ); | 2614 | ); |
| 2613 | const pendingRequests = projection.runtimeRequests.filter( | 2615 | const pendingRequests = projection.runtimeRequests.filter( |
| 2614 | (request) => request.status === "pending", | 2616 | (request) => request.status === "pending", |
| ... | @@ -2616,11 +2618,19 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio | ... | @@ -2616,11 +2618,19 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio |
| 2616 | const blockingRequestExists = pendingRequests.some( | 2618 | const blockingRequestExists = pendingRequests.some( |
| 2617 | (request) => request.kind !== "user_input" || request.responseCapability.type !== "message", | 2619 | (request) => request.kind !== "user_input" || request.responseCapability.type !== "message", |
| 2618 | ); | 2620 | ); |
| 2619 | if (activeRunExists || blockingRequestExists) { | 2621 | // The reason is shown to the user, so it names what to clear first. |
| | 2622 | const blocker = blockingRequestExists |
| | 2623 | ? "is waiting on an approval or question. Answer it before settling." |
| | 2624 | : runningRunExists |
| | 2625 | ? "is still running. Stop it before settling." |
| | 2626 | : queuedMessageExists |
| | 2627 | ? "has a queued message. Send it or remove it from the queue before settling." |
| | 2628 | : null; |
| | 2629 | if (blocker !== null) { |
| 2620 | return yield* new OrchestratorDispatchError({ | 2630 | return yield* new OrchestratorDispatchError({ |
| 2621 | commandId: command.commandId, | 2631 | commandId: command.commandId, |
| 2622 | commandType: command.type, | 2632 | commandType: command.type, |
| 2623 | cause: `Thread ${command.threadId} has active or blocked work and cannot be settled.`, | 2633 | cause: `Thread ${command.threadId} ${blocker}`, |
| 2624 | }); | 2634 | }); |
| 2625 | } | 2635 | } |
| 2626 | | 2636 | |