Unified · Split
| @@ -180,6 +180,7 @@ function makeDeterministicAdapter(input: { · 179 unmodified lines | ||
| 180 | 180 | readonly capabilities: OrchestrationV2ProviderCapabilities; |
| 181 | 181 | readonly capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>; |
| 182 | 182 | readonly shouldComplete: (turn: ProviderAdapterV2TurnInput) => boolean; |
| 183 | + readonly startedGate?: (turn: ProviderAdapterV2TurnInput) => Deferred.Deferred<void> | undefined; | |
| 183 | 184 | readonly terminalGate?: (turn: ProviderAdapterV2TurnInput) => Deferred.Deferred<void> | undefined; |
| 184 | 185 | readonly response: (turn: ProviderAdapterV2TurnInput) => string; |
| 185 | 186 | }): ProviderAdapterV2Shape { |
| @@ -282,6 +283,10 @@ function makeDeterministicAdapter(input: { · 96 unmodified lines | ||
| 282 | 283 | }, |
| 283 | 284 | }, |
| 284 | 285 | ]); |
| 286 | + const startedGate = input.startedGate?.(turnInput); | |
| 287 | + if (startedGate !== undefined) { | |
| 288 | + yield* Deferred.succeed(startedGate, undefined); | |
| 289 | + } | |
| 285 | 290 | const terminalGate = input.terminalGate?.(turnInput); |
| 286 | 291 | if (terminalGate !== undefined) { |
| 287 | 292 | yield* Deferred.await(terminalGate); |
| @@ -477,6 +482,7 @@ describe("orchestrator MCP toolkit", () => { · 189 unmodified lines | ||
| 477 | 482 | Effect.gen(function* () { |
| 478 | 483 | const cwd = yield* checkpointWorkspace("orchestrator-mcp-toolkit"); |
| 479 | 484 | const capturedTurns = yield* Ref.make<ReadonlyArray<CapturedTurn>>([]); |
| 485 | + const providerStartGates = new Map<string, Deferred.Deferred<void>>(); | |
| 480 | 486 | const parentTerminalGates = new Map<ThreadId, Deferred.Deferred<void>>(); |
| 481 | 487 | const deliveryTerminalGates = new Map<ThreadId, Deferred.Deferred<void>>(); |
| 482 | 488 | const registryLayer = makeProviderAdapterRegistryLayer([ |
| @@ -487,6 +493,7 @@ describe("orchestrator MCP toolkit", () => { · 4 unmodified lines | ||
| 487 | 493 | capturedTurns, |
| 488 | 494 | shouldComplete: (turn) => |
| 489 | 495 | turn.threadId !== parentThreadId && turn.message.text !== cancellationPrompt, |
| 496 | + startedGate: (turn) => providerStartGates.get(turn.message.text), | |
| 490 | 497 | terminalGate: (turn) => |
| 491 | 498 | turn.message.text.startsWith("Delegated task") || |
| 492 | 499 | turn.message.text.startsWith("Delegated tasks") |
| @@ -2236,6 +2243,8 @@ describe("orchestrator MCP toolkit", () => { · 1743 unmodified lines | ||
| 2236 | 2243 | state: { archivedAt: null }, |
| 2237 | 2244 | }); |
| 2238 | 2245 | |
| 2246 | + const blockedThreadStarted = yield* Deferred.make<void>(); | |
| 2247 | + providerStartGates.set(cancellationPrompt, blockedThreadStarted); | |
| 2239 | 2248 | const blockedThreadCall = yield* invoke("create_threads", { |
| 2240 | 2249 | threads: [{ prompt: cancellationPrompt, title: "Organization blockers" }], |
| 2241 | 2250 | clientRequestId: "organize-blockers-create", |
| @@ -2243,9 +2252,8 @@ describe("orchestrator MCP toolkit", () => { · 1 unmodified line | ||
| 2243 | 2252 | const blockedThread = (yield* decodeCreateThreadsResult( |
| 2244 | 2253 | blockedThreadCall.structuredContent, |
| 2245 | 2254 | ).pipe(Effect.orDie)).threads[0]!; |
| 2246 | − yield* waitForProjection(orchestrator, blockedThread.threadId, (projection) => | |
| 2247 | − projection.runs.some((run) => run.status === "running"), | |
| 2248 | − ); | |
| 2255 | + yield* Deferred.await(blockedThreadStarted); | |
| 2256 | + providerStartGates.delete(cancellationPrompt); | |
| 2249 | 2257 | yield* invoke("t3_thread_send", { |
| 2250 | 2258 | threadId: blockedThread.threadId, |
| 2251 | 2259 | message: "Queue this behind the active run.", |