| ... | @@ -4925,9 +4925,13 @@ describe("AcpAdapterV2", () => { | ... | @@ -4925,9 +4925,13 @@ describe("AcpAdapterV2", () => { |
| 4925 | const mockAgentPath = yield* path.fromFileUrl( | 4925 | const mockAgentPath = yield* path.fromFileUrl( |
| 4926 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), | 4926 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 4927 | ); | 4927 | ); |
| 4928 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); | | |
| 4929 | const instanceId = ProviderInstanceId.make("acp-test"); | 4928 | const instanceId = ProviderInstanceId.make("acp-test"); |
| | 4929 | const promptSettled = yield* Deferred.make<void>(); |
| 4930 | const adapter = makeAcpAdapterV2({ | 4930 | const adapter = makeAcpAdapterV2({ |
| | 4931 | testHooks: { |
| | 4932 | afterPromptSettledWithBackgroundWork: () => |
| | 4933 | Deferred.succeed(promptSettled, undefined).pipe(Effect.asVoid), |
| | 4934 | }, |
| 4931 | crypto: yield* Crypto.Crypto, | 4935 | crypto: yield* Crypto.Crypto, |
| 4932 | instanceId, | 4936 | instanceId, |
| 4933 | flavor: { | 4937 | flavor: { |
| ... | @@ -4950,7 +4954,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -4950,7 +4954,6 @@ describe("AcpAdapterV2", () => { |
| 4950 | childProcessSpawner, | 4954 | childProcessSpawner, |
| 4951 | mockAgentPath, | 4955 | mockAgentPath, |
| 4952 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, | 4956 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, |
| 4953 | protocolEvents, | | |
| 4954 | }), | 4957 | }), |
| 4955 | }, | 4958 | }, |
| 4956 | fileSystem, | 4959 | fileSystem, |
| ... | @@ -4986,28 +4989,13 @@ describe("AcpAdapterV2", () => { | ... | @@ -4986,28 +4989,13 @@ describe("AcpAdapterV2", () => { |
| 4986 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), | 4989 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), |
| 4987 | ); | 4990 | ); |
| 4988 | // The still-running subagent defers finalize after session/prompt returns. | 4991 | // The still-running subagent defers finalize after session/prompt returns. |
| 4989 | yield* Stream.fromQueue(protocolEvents).pipe( | 4992 | yield* Deferred.await(promptSettled); |
| 4990 | Stream.filter( | | |
| 4991 | (event) => | | |
| 4992 | event.direction === "incoming" && | | |
| 4993 | event.stage === "raw" && | | |
| 4994 | typeof event.payload === "string" && | | |
| 4995 | event.payload.includes('"stopReason"'), | | |
| 4996 | ), | | |
| 4997 | Stream.runHead, | | |
| 4998 | ); | | |
| 4999 | yield* Effect.yieldNow; | | |
| 5000 | yield* Effect.yieldNow; | | |
| 5001 | | 4993 | |
| 5002 | const providerTurnId = idAllocator.derive.providerTurn({ | 4994 | const providerTurnId = idAllocator.derive.providerTurn({ |
| 5003 | driver: ACP_TEST_DRIVER, | 4995 | driver: ACP_TEST_DRIVER, |
| 5004 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), | 4996 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), |
| 5005 | }); | 4997 | }); |
| 5006 | const interruptFiber = yield* runtime | 4998 | yield* runtime.interruptTurn({ providerThread, providerTurnId }); |
| 5007 | .interruptTurn({ providerThread, providerTurnId }) | | |
| 5008 | .pipe(Effect.forkScoped); | | |
| 5009 | yield* TestClock.adjust("10 seconds"); | | |
| 5010 | yield* Fiber.join(interruptFiber); | | |
| 5011 | | 4999 | |
| 5012 | let terminalStatus: string | null = null; | 5000 | let terminalStatus: string | null = null; |
| 5013 | while (terminalStatus === null) { | 5001 | while (terminalStatus === null) { |
| ... | @@ -5033,10 +5021,14 @@ describe("AcpAdapterV2", () => { | ... | @@ -5033,10 +5021,14 @@ describe("AcpAdapterV2", () => { |
| 5033 | const mockAgentPath = yield* path.fromFileUrl( | 5021 | const mockAgentPath = yield* path.fromFileUrl( |
| 5034 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), | 5022 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 5035 | ); | 5023 | ); |
| 5036 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); | | |
| 5037 | const instanceId = ProviderInstanceId.make("acp-test"); | 5024 | const instanceId = ProviderInstanceId.make("acp-test"); |
| 5038 | let subagentPhase: "spawn" | "complete" = "spawn"; | 5025 | let subagentPhase: "spawn" | "complete" = "spawn"; |
| | 5026 | const promptSettled = yield* Deferred.make<void>(); |
| 5039 | const adapter = makeAcpAdapterV2({ | 5027 | const adapter = makeAcpAdapterV2({ |
| | 5028 | testHooks: { |
| | 5029 | afterPromptSettledWithBackgroundWork: () => |
| | 5030 | Deferred.succeed(promptSettled, undefined).pipe(Effect.asVoid), |
| | 5031 | }, |
| 5040 | crypto: yield* Crypto.Crypto, | 5032 | crypto: yield* Crypto.Crypto, |
| 5041 | instanceId, | 5033 | instanceId, |
| 5042 | flavor: { | 5034 | flavor: { |
| ... | @@ -5072,7 +5064,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -5072,7 +5064,6 @@ describe("AcpAdapterV2", () => { |
| 5072 | childProcessSpawner, | 5064 | childProcessSpawner, |
| 5073 | mockAgentPath, | 5065 | mockAgentPath, |
| 5074 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, | 5066 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, |
| 5075 | protocolEvents, | | |
| 5076 | }), | 5067 | }), |
| 5077 | }, | 5068 | }, |
| 5078 | fileSystem, | 5069 | fileSystem, |
| ... | @@ -5107,28 +5098,13 @@ describe("AcpAdapterV2", () => { | ... | @@ -5107,28 +5098,13 @@ describe("AcpAdapterV2", () => { |
| 5107 | yield* runtime.startTurn( | 5098 | yield* runtime.startTurn( |
| 5108 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), | 5099 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), |
| 5109 | ); | 5100 | ); |
| 5110 | yield* Stream.fromQueue(protocolEvents).pipe( | 5101 | yield* Deferred.await(promptSettled); |
| 5111 | Stream.filter( | | |
| 5112 | (event) => | | |
| 5113 | event.direction === "incoming" && | | |
| 5114 | event.stage === "raw" && | | |
| 5115 | typeof event.payload === "string" && | | |
| 5116 | event.payload.includes('"stopReason"'), | | |
| 5117 | ), | | |
| 5118 | Stream.runHead, | | |
| 5119 | ); | | |
| 5120 | yield* Effect.yieldNow; | | |
| 5121 | yield* Effect.yieldNow; | | |
| 5122 | | 5102 | |
| 5123 | const firstProviderTurnId = idAllocator.derive.providerTurn({ | 5103 | const firstProviderTurnId = idAllocator.derive.providerTurn({ |
| 5124 | driver: ACP_TEST_DRIVER, | 5104 | driver: ACP_TEST_DRIVER, |
| 5125 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), | 5105 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), |
| 5126 | }); | 5106 | }); |
| 5127 | const interruptFiber = yield* runtime | 5107 | yield* runtime.interruptTurn({ providerThread, providerTurnId: firstProviderTurnId }); |
| 5128 | .interruptTurn({ providerThread, providerTurnId: firstProviderTurnId }) | | |
| 5129 | .pipe(Effect.forkScoped); | | |
| 5130 | yield* TestClock.adjust("10 seconds"); | | |
| 5131 | yield* Fiber.join(interruptFiber); | | |
| 5132 | | 5108 | |
| 5133 | let subagentTurnItemId: string | null = null; | 5109 | let subagentTurnItemId: string | null = null; |
| 5134 | let firstTerminalStatus: string | null = null; | 5110 | let firstTerminalStatus: string | null = null; |
| ... | @@ -5193,10 +5169,14 @@ describe("AcpAdapterV2", () => { | ... | @@ -5193,10 +5169,14 @@ describe("AcpAdapterV2", () => { |
| 5193 | const mockAgentPath = yield* path.fromFileUrl( | 5169 | const mockAgentPath = yield* path.fromFileUrl( |
| 5194 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), | 5170 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 5195 | ); | 5171 | ); |
| 5196 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); | | |
| 5197 | const instanceId = ProviderInstanceId.make("acp-test"); | 5172 | const instanceId = ProviderInstanceId.make("acp-test"); |
| 5198 | let subagentPhase: "spawn" | "complete" = "spawn"; | 5173 | let subagentPhase: "spawn" | "complete" = "spawn"; |
| | 5174 | const promptSettled = yield* Deferred.make<void>(); |
| 5199 | const adapter = makeAcpAdapterV2({ | 5175 | const adapter = makeAcpAdapterV2({ |
| | 5176 | testHooks: { |
| | 5177 | afterPromptSettledWithBackgroundWork: () => |
| | 5178 | Deferred.succeed(promptSettled, undefined).pipe(Effect.asVoid), |
| | 5179 | }, |
| 5200 | crypto: yield* Crypto.Crypto, | 5180 | crypto: yield* Crypto.Crypto, |
| 5201 | instanceId, | 5181 | instanceId, |
| 5202 | flavor: { | 5182 | flavor: { |
| ... | @@ -5230,7 +5210,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -5230,7 +5210,6 @@ describe("AcpAdapterV2", () => { |
| 5230 | childProcessSpawner, | 5210 | childProcessSpawner, |
| 5231 | mockAgentPath, | 5211 | mockAgentPath, |
| 5232 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, | 5212 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, |
| 5233 | protocolEvents, | | |
| 5234 | }), | 5213 | }), |
| 5235 | }, | 5214 | }, |
| 5236 | fileSystem, | 5215 | fileSystem, |
| ... | @@ -5272,28 +5251,13 @@ describe("AcpAdapterV2", () => { | ... | @@ -5272,28 +5251,13 @@ describe("AcpAdapterV2", () => { |
| 5272 | yield* runtime.startTurn( | 5251 | yield* runtime.startTurn( |
| 5273 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), | 5252 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), |
| 5274 | ); | 5253 | ); |
| 5275 | yield* Stream.fromQueue(protocolEvents).pipe( | 5254 | yield* Deferred.await(promptSettled); |
| 5276 | Stream.filter( | | |
| 5277 | (event) => | | |
| 5278 | event.direction === "incoming" && | | |
| 5279 | event.stage === "raw" && | | |
| 5280 | typeof event.payload === "string" && | | |
| 5281 | event.payload.includes('"stopReason"'), | | |
| 5282 | ), | | |
| 5283 | Stream.runHead, | | |
| 5284 | ); | | |
| 5285 | yield* Effect.yieldNow; | | |
| 5286 | yield* Effect.yieldNow; | | |
| 5287 | | 5255 | |
| 5288 | const firstProviderTurnId = idAllocator.derive.providerTurn({ | 5256 | const firstProviderTurnId = idAllocator.derive.providerTurn({ |
| 5289 | driver: ACP_TEST_DRIVER, | 5257 | driver: ACP_TEST_DRIVER, |
| 5290 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), | 5258 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), |
| 5291 | }); | 5259 | }); |
| 5292 | const interruptFiber = yield* runtime | 5260 | yield* runtime.interruptTurn({ providerThread, providerTurnId: firstProviderTurnId }); |
| 5293 | .interruptTurn({ providerThread, providerTurnId: firstProviderTurnId }) | | |
| 5294 | .pipe(Effect.forkScoped); | | |
| 5295 | yield* TestClock.adjust("10 seconds"); | | |
| 5296 | yield* Fiber.join(interruptFiber); | | |
| 5297 | | 5261 | |
| 5298 | let firstTerminalStatus: string | null = null; | 5262 | let firstTerminalStatus: string | null = null; |
| 5299 | while (firstTerminalStatus === null) { | 5263 | while (firstTerminalStatus === null) { |
| ... | @@ -5352,7 +5316,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -5352,7 +5316,6 @@ describe("AcpAdapterV2", () => { |
| 5352 | const mockAgentPath = yield* path.fromFileUrl( | 5316 | const mockAgentPath = yield* path.fromFileUrl( |
| 5353 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), | 5317 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 5354 | ); | 5318 | ); |
| 5355 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); | | |
| 5356 | const secondPromptWireReturned = yield* Deferred.make<void>(); | 5319 | const secondPromptWireReturned = yield* Deferred.make<void>(); |
| 5357 | const releaseSecondPromptCompletion = yield* Deferred.make<void>(); | 5320 | const releaseSecondPromptCompletion = yield* Deferred.make<void>(); |
| 5358 | const instanceId = ProviderInstanceId.make("acp-test"); | 5321 | const instanceId = ProviderInstanceId.make("acp-test"); |
| ... | @@ -5361,7 +5324,12 @@ describe("AcpAdapterV2", () => { | ... | @@ -5361,7 +5324,12 @@ describe("AcpAdapterV2", () => { |
| 5361 | let subagentPhase: "spawn" | "complete" = "spawn"; | 5324 | let subagentPhase: "spawn" | "complete" = "spawn"; |
| 5362 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; | 5325 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; |
| 5363 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; | 5326 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; |
| | 5327 | const promptSettled = yield* Deferred.make<void>(); |
| 5364 | const adapter = makeAcpAdapterV2({ | 5328 | const adapter = makeAcpAdapterV2({ |
| | 5329 | testHooks: { |
| | 5330 | afterPromptSettledWithBackgroundWork: () => |
| | 5331 | Deferred.succeed(promptSettled, undefined).pipe(Effect.asVoid), |
| | 5332 | }, |
| 5365 | crypto: yield* Crypto.Crypto, | 5333 | crypto: yield* Crypto.Crypto, |
| 5366 | instanceId, | 5334 | instanceId, |
| 5367 | flavor: { | 5335 | flavor: { |
| ... | @@ -5395,7 +5363,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -5395,7 +5363,6 @@ describe("AcpAdapterV2", () => { |
| 5395 | childProcessSpawner, | 5363 | childProcessSpawner, |
| 5396 | mockAgentPath, | 5364 | mockAgentPath, |
| 5397 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, | 5365 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, |
| 5398 | protocolEvents, | | |
| 5399 | wrapRuntime: (runtime) => ({ | 5366 | wrapRuntime: (runtime) => ({ |
| 5400 | ...runtime, | 5367 | ...runtime, |
| 5401 | handleSessionUpdate: (handler) => | 5368 | handleSessionUpdate: (handler) => |
| ... | @@ -5456,28 +5423,13 @@ describe("AcpAdapterV2", () => { | ... | @@ -5456,28 +5423,13 @@ describe("AcpAdapterV2", () => { |
| 5456 | yield* runtime.startTurn( | 5423 | yield* runtime.startTurn( |
| 5457 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), | 5424 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), |
| 5458 | ); | 5425 | ); |
| 5459 | yield* Stream.fromQueue(protocolEvents).pipe( | 5426 | yield* Deferred.await(promptSettled); |
| 5460 | Stream.filter( | | |
| 5461 | (event) => | | |
| 5462 | event.direction === "incoming" && | | |
| 5463 | event.stage === "raw" && | | |
| 5464 | typeof event.payload === "string" && | | |
| 5465 | event.payload.includes('"stopReason"'), | | |
| 5466 | ), | | |
| 5467 | Stream.runHead, | | |
| 5468 | ); | | |
| 5469 | yield* Effect.yieldNow; | | |
| 5470 | yield* Effect.yieldNow; | | |
| 5471 | | 5427 | |
| 5472 | const firstProviderTurnId = idAllocator.derive.providerTurn({ | 5428 | const firstProviderTurnId = idAllocator.derive.providerTurn({ |
| 5473 | driver: ACP_TEST_DRIVER, | 5429 | driver: ACP_TEST_DRIVER, |
| 5474 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), | 5430 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), |
| 5475 | }); | 5431 | }); |
| 5476 | const interruptFiber = yield* runtime | 5432 | yield* runtime.interruptTurn({ providerThread, providerTurnId: firstProviderTurnId }); |
| 5477 | .interruptTurn({ providerThread, providerTurnId: firstProviderTurnId }) | | |
| 5478 | .pipe(Effect.forkScoped); | | |
| 5479 | yield* TestClock.adjust("10 seconds"); | | |
| 5480 | yield* Fiber.join(interruptFiber); | | |
| 5481 | | 5433 | |
| 5482 | let firstTerminalStatus: string | null = null; | 5434 | let firstTerminalStatus: string | null = null; |
| 5483 | while (firstTerminalStatus === null) { | 5435 | while (firstTerminalStatus === null) { |
| ... | @@ -5601,7 +5553,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -5601,7 +5553,6 @@ describe("AcpAdapterV2", () => { |
| 5601 | const mockAgentPath = yield* path.fromFileUrl( | 5553 | const mockAgentPath = yield* path.fromFileUrl( |
| 5602 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), | 5554 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 5603 | ); | 5555 | ); |
| 5604 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); | | |
| 5605 | const baseClock = yield* Clock.Clock; | 5556 | const baseClock = yield* Clock.Clock; |
| 5606 | const finalizationClockRead = yield* Deferred.make<void>(); | 5557 | const finalizationClockRead = yield* Deferred.make<void>(); |
| 5607 | const releaseFinalizationClockRead = yield* Deferred.make<void>(); | 5558 | const releaseFinalizationClockRead = yield* Deferred.make<void>(); |
| ... | @@ -5629,7 +5580,12 @@ describe("AcpAdapterV2", () => { | ... | @@ -5629,7 +5580,12 @@ describe("AcpAdapterV2", () => { |
| 5629 | let sessionUpdateHandler: | 5580 | let sessionUpdateHandler: |
| 5630 | | Parameters<RuntimeService["handleSessionUpdate"]>[0] | 5581 | | Parameters<RuntimeService["handleSessionUpdate"]>[0] |
| 5631 | | undefined; | 5582 | | undefined; |
| | 5583 | const promptSettled = yield* Deferred.make<void>(); |
| 5632 | const adapter = makeAcpAdapterV2({ | 5584 | const adapter = makeAcpAdapterV2({ |
| | 5585 | testHooks: { |
| | 5586 | afterPromptSettledWithBackgroundWork: () => |
| | 5587 | Deferred.succeed(promptSettled, undefined).pipe(Effect.asVoid), |
| | 5588 | }, |
| 5633 | crypto: yield* Crypto.Crypto, | 5589 | crypto: yield* Crypto.Crypto, |
| 5634 | instanceId, | 5590 | instanceId, |
| 5635 | flavor: { | 5591 | flavor: { |
| ... | @@ -5668,7 +5624,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -5668,7 +5624,6 @@ describe("AcpAdapterV2", () => { |
| 5668 | childProcessSpawner, | 5624 | childProcessSpawner, |
| 5669 | mockAgentPath, | 5625 | mockAgentPath, |
| 5670 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, | 5626 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, |
| 5671 | protocolEvents, | | |
| 5672 | wrapRuntime: (runtime) => ({ | 5627 | wrapRuntime: (runtime) => ({ |
| 5673 | ...runtime, | 5628 | ...runtime, |
| 5674 | handleSessionUpdate: (handler) => | 5629 | handleSessionUpdate: (handler) => |
| ... | @@ -5719,18 +5674,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -5719,18 +5674,7 @@ describe("AcpAdapterV2", () => { |
| 5719 | yield* runtime.startTurn( | 5674 | yield* runtime.startTurn( |
| 5720 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), | 5675 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), |
| 5721 | ); | 5676 | ); |
| 5722 | yield* Stream.fromQueue(protocolEvents).pipe( | 5677 | yield* Deferred.await(promptSettled); |
| 5723 | Stream.filter( | | |
| 5724 | (event) => | | |
| 5725 | event.direction === "incoming" && | | |
| 5726 | event.stage === "raw" && | | |
| 5727 | typeof event.payload === "string" && | | |
| 5728 | event.payload.includes('"stopReason"'), | | |
| 5729 | ), | | |
| 5730 | Stream.runHead, | | |
| 5731 | ); | | |
| 5732 | yield* Effect.yieldNow; | | |
| 5733 | yield* Effect.yieldNow; | | |
| 5734 | yield* TestClock.adjust("1 second"); | 5678 | yield* TestClock.adjust("1 second"); |
| 5735 | assert.isDefined(sessionUpdateHandler, "session update handler must be wired"); | 5679 | assert.isDefined(sessionUpdateHandler, "session update handler must be wired"); |
| 5736 | | 5680 | |
| ... | @@ -6451,14 +6395,18 @@ describe("AcpAdapterV2", () => { | ... | @@ -6451,14 +6395,18 @@ describe("AcpAdapterV2", () => { |
| 6451 | const mockAgentPath = yield* path.fromFileUrl( | 6395 | const mockAgentPath = yield* path.fromFileUrl( |
| 6452 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), | 6396 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 6453 | ); | 6397 | ); |
| 6454 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); | | |
| 6455 | const continuationRequests: Array<ProviderContinuationRequest> = []; | 6398 | const continuationRequests: Array<ProviderContinuationRequest> = []; |
| 6456 | const bufferedAssistantText = "BUFFERED_WAKE_AFTER_USER_ATTACH"; | 6399 | const bufferedAssistantText = "BUFFERED_WAKE_AFTER_USER_ATTACH"; |
| 6457 | const instanceId = ProviderInstanceId.make("acp-test"); | 6400 | const instanceId = ProviderInstanceId.make("acp-test"); |
| 6458 | let subagentPhase: "spawn" | "complete" = "spawn"; | 6401 | let subagentPhase: "spawn" | "complete" = "spawn"; |
| 6459 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; | 6402 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; |
| 6460 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; | 6403 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; |
| | 6404 | const promptSettled = yield* Deferred.make<void>(); |
| 6461 | const adapter = makeAcpAdapterV2({ | 6405 | const adapter = makeAcpAdapterV2({ |
| | 6406 | testHooks: { |
| | 6407 | afterPromptSettledWithBackgroundWork: () => |
| | 6408 | Deferred.succeed(promptSettled, undefined).pipe(Effect.asVoid), |
| | 6409 | }, |
| 6462 | crypto: yield* Crypto.Crypto, | 6410 | crypto: yield* Crypto.Crypto, |
| 6463 | instanceId, | 6411 | instanceId, |
| 6464 | flavor: { | 6412 | flavor: { |
| ... | @@ -6492,7 +6440,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -6492,7 +6440,6 @@ describe("AcpAdapterV2", () => { |
| 6492 | childProcessSpawner, | 6440 | childProcessSpawner, |
| 6493 | mockAgentPath, | 6441 | mockAgentPath, |
| 6494 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, | 6442 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, |
| 6495 | protocolEvents, | | |
| 6496 | wrapRuntime: (runtime) => ({ | 6443 | wrapRuntime: (runtime) => ({ |
| 6497 | ...runtime, | 6444 | ...runtime, |
| 6498 | handleSessionUpdate: (handler) => | 6445 | handleSessionUpdate: (handler) => |
| ... | @@ -6548,28 +6495,13 @@ describe("AcpAdapterV2", () => { | ... | @@ -6548,28 +6495,13 @@ describe("AcpAdapterV2", () => { |
| 6548 | yield* runtime.startTurn( | 6495 | yield* runtime.startTurn( |
| 6549 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), | 6496 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), |
| 6550 | ); | 6497 | ); |
| 6551 | yield* Stream.fromQueue(protocolEvents).pipe( | 6498 | yield* Deferred.await(promptSettled); |
| 6552 | Stream.filter( | | |
| 6553 | (event) => | | |
| 6554 | event.direction === "incoming" && | | |
| 6555 | event.stage === "raw" && | | |
| 6556 | typeof event.payload === "string" && | | |
| 6557 | event.payload.includes('"stopReason"'), | | |
| 6558 | ), | | |
| 6559 | Stream.runHead, | | |
| 6560 | ); | | |
| 6561 | yield* Effect.yieldNow; | | |
| 6562 | yield* Effect.yieldNow; | | |
| 6563 | | 6499 | |
| 6564 | const firstProviderTurnId = idAllocator.derive.providerTurn({ | 6500 | const firstProviderTurnId = idAllocator.derive.providerTurn({ |
| 6565 | driver: ACP_TEST_DRIVER, | 6501 | driver: ACP_TEST_DRIVER, |
| 6566 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), | 6502 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), |
| 6567 | }); | 6503 | }); |
| 6568 | const interruptFiber = yield* runtime | 6504 | yield* runtime.interruptTurn({ providerThread, providerTurnId: firstProviderTurnId }); |
| 6569 | .interruptTurn({ providerThread, providerTurnId: firstProviderTurnId }) | | |
| 6570 | .pipe(Effect.forkScoped); | | |
| 6571 | yield* TestClock.adjust("10 seconds"); | | |
| 6572 | yield* Fiber.join(interruptFiber); | | |
| 6573 | | 6505 | |
| 6574 | let firstTerminalStatus: string | null = null; | 6506 | let firstTerminalStatus: string | null = null; |
| 6575 | while (firstTerminalStatus === null) { | 6507 | while (firstTerminalStatus === null) { |
| ... | @@ -6752,14 +6684,18 @@ describe("AcpAdapterV2", () => { | ... | @@ -6752,14 +6684,18 @@ describe("AcpAdapterV2", () => { |
| 6752 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), | 6684 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 6753 | ); | 6685 | ); |
| 6754 | const continuationRequests: Array<ProviderContinuationRequest> = []; | 6686 | const continuationRequests: Array<ProviderContinuationRequest> = []; |
| 6755 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); | | |
| 6756 | const promptWireReturned = yield* Deferred.make<void>(); | 6687 | const promptWireReturned = yield* Deferred.make<void>(); |
| 6757 | const releasePromptCompletion = yield* Deferred.make<void>(); | 6688 | const releasePromptCompletion = yield* Deferred.make<void>(); |
| 6758 | const instanceId = ProviderInstanceId.make("acp-test"); | 6689 | const instanceId = ProviderInstanceId.make("acp-test"); |
| 6759 | let subagentPhase: "spawn" | "complete" = "spawn"; | 6690 | let subagentPhase: "spawn" | "complete" = "spawn"; |
| 6760 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; | 6691 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; |
| 6761 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; | 6692 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; |
| | 6693 | const promptSettled = yield* Deferred.make<void>(); |
| 6762 | const adapter = makeAcpAdapterV2({ | 6694 | const adapter = makeAcpAdapterV2({ |
| | 6695 | testHooks: { |
| | 6696 | afterPromptSettledWithBackgroundWork: () => |
| | 6697 | Deferred.succeed(promptSettled, undefined).pipe(Effect.asVoid), |
| | 6698 | }, |
| 6763 | crypto: yield* Crypto.Crypto, | 6699 | crypto: yield* Crypto.Crypto, |
| 6764 | instanceId, | 6700 | instanceId, |
| 6765 | flavor: { | 6701 | flavor: { |
| ... | @@ -6795,7 +6731,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -6795,7 +6731,6 @@ describe("AcpAdapterV2", () => { |
| 6795 | childProcessSpawner, | 6731 | childProcessSpawner, |
| 6796 | mockAgentPath, | 6732 | mockAgentPath, |
| 6797 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, | 6733 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, |
| 6798 | protocolEvents, | | |
| 6799 | wrapRuntime: (runtime) => ({ | 6734 | wrapRuntime: (runtime) => ({ |
| 6800 | ...runtime, | 6735 | ...runtime, |
| 6801 | handleSessionUpdate: (handler) => | 6736 | handleSessionUpdate: (handler) => |
| ... | @@ -6875,18 +6810,13 @@ describe("AcpAdapterV2", () => { | ... | @@ -6875,18 +6810,13 @@ describe("AcpAdapterV2", () => { |
| 6875 | }, | 6810 | }, |
| 6876 | }); | 6811 | }); |
| 6877 | yield* Deferred.succeed(releasePromptCompletion, undefined); | 6812 | yield* Deferred.succeed(releasePromptCompletion, undefined); |
| 6878 | yield* Effect.yieldNow; | 6813 | yield* Deferred.await(promptSettled); |
| 6879 | yield* Effect.yieldNow; | | |
| 6880 | | 6814 | |
| 6881 | const firstProviderTurnId = idAllocator.derive.providerTurn({ | 6815 | const firstProviderTurnId = idAllocator.derive.providerTurn({ |
| 6882 | driver: ACP_TEST_DRIVER, | 6816 | driver: ACP_TEST_DRIVER, |
| 6883 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), | 6817 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), |
| 6884 | }); | 6818 | }); |
| 6885 | const interruptFiber = yield* runtime | 6819 | yield* runtime.interruptTurn({ providerThread, providerTurnId: firstProviderTurnId }); |
| 6886 | .interruptTurn({ providerThread, providerTurnId: firstProviderTurnId }) | | |
| 6887 | .pipe(Effect.forkScoped); | | |
| 6888 | yield* TestClock.adjust("10 seconds"); | | |
| 6889 | yield* Fiber.join(interruptFiber); | | |
| 6890 | | 6820 | |
| 6891 | let firstTerminalStatus: string | null = null; | 6821 | let firstTerminalStatus: string | null = null; |
| 6892 | while (firstTerminalStatus === null) { | 6822 | while (firstTerminalStatus === null) { |
| ... | @@ -7223,7 +7153,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -7223,7 +7153,6 @@ describe("AcpAdapterV2", () => { |
| 7223 | const mockAgentPath = yield* path.fromFileUrl( | 7153 | const mockAgentPath = yield* path.fromFileUrl( |
| 7224 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), | 7154 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 7225 | ); | 7155 | ); |
| 7226 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); | | |
| 7227 | const continuationRequests: Array<ProviderContinuationRequest> = []; | 7156 | const continuationRequests: Array<ProviderContinuationRequest> = []; |
| 7228 | const instanceId = ProviderInstanceId.make("acp-test"); | 7157 | const instanceId = ProviderInstanceId.make("acp-test"); |
| 7229 | const childSessionId = "mock-child-session-pending-continuation"; | 7158 | const childSessionId = "mock-child-session-pending-continuation"; |
| ... | @@ -7231,7 +7160,12 @@ describe("AcpAdapterV2", () => { | ... | @@ -7231,7 +7160,12 @@ describe("AcpAdapterV2", () => { |
| 7231 | let subagentPhase: "spawn" | "complete" = "spawn"; | 7160 | let subagentPhase: "spawn" | "complete" = "spawn"; |
| 7232 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; | 7161 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; |
| 7233 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; | 7162 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; |
| | 7163 | const promptSettled = yield* Deferred.make<void>(); |
| 7234 | const adapter = makeAcpAdapterV2({ | 7164 | const adapter = makeAcpAdapterV2({ |
| | 7165 | testHooks: { |
| | 7166 | afterPromptSettledWithBackgroundWork: () => |
| | 7167 | Deferred.succeed(promptSettled, undefined).pipe(Effect.asVoid), |
| | 7168 | }, |
| 7235 | crypto: yield* Crypto.Crypto, | 7169 | crypto: yield* Crypto.Crypto, |
| 7236 | instanceId, | 7170 | instanceId, |
| 7237 | flavor: { | 7171 | flavor: { |
| ... | @@ -7265,7 +7199,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -7265,7 +7199,6 @@ describe("AcpAdapterV2", () => { |
| 7265 | childProcessSpawner, | 7199 | childProcessSpawner, |
| 7266 | mockAgentPath, | 7200 | mockAgentPath, |
| 7267 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, | 7201 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, |
| 7268 | protocolEvents, | | |
| 7269 | wrapRuntime: (runtime) => ({ | 7202 | wrapRuntime: (runtime) => ({ |
| 7270 | ...runtime, | 7203 | ...runtime, |
| 7271 | handleSessionUpdate: (handler) => | 7204 | handleSessionUpdate: (handler) => |
| ... | @@ -7321,28 +7254,13 @@ describe("AcpAdapterV2", () => { | ... | @@ -7321,28 +7254,13 @@ describe("AcpAdapterV2", () => { |
| 7321 | yield* runtime.startTurn( | 7254 | yield* runtime.startTurn( |
| 7322 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), | 7255 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), |
| 7323 | ); | 7256 | ); |
| 7324 | yield* Stream.fromQueue(protocolEvents).pipe( | 7257 | yield* Deferred.await(promptSettled); |
| 7325 | Stream.filter( | | |
| 7326 | (event) => | | |
| 7327 | event.direction === "incoming" && | | |
| 7328 | event.stage === "raw" && | | |
| 7329 | typeof event.payload === "string" && | | |
| 7330 | event.payload.includes('"stopReason"'), | | |
| 7331 | ), | | |
| 7332 | Stream.runHead, | | |
| 7333 | ); | | |
| 7334 | yield* Effect.yieldNow; | | |
| 7335 | yield* Effect.yieldNow; | | |
| 7336 | | 7258 | |
| 7337 | const firstProviderTurnId = idAllocator.derive.providerTurn({ | 7259 | const firstProviderTurnId = idAllocator.derive.providerTurn({ |
| 7338 | driver: ACP_TEST_DRIVER, | 7260 | driver: ACP_TEST_DRIVER, |
| 7339 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), | 7261 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), |
| 7340 | }); | 7262 | }); |
| 7341 | const interruptFiber = yield* runtime | 7263 | yield* runtime.interruptTurn({ providerThread, providerTurnId: firstProviderTurnId }); |
| 7342 | .interruptTurn({ providerThread, providerTurnId: firstProviderTurnId }) | | |
| 7343 | .pipe(Effect.forkScoped); | | |
| 7344 | yield* TestClock.adjust("10 seconds"); | | |
| 7345 | yield* Fiber.join(interruptFiber); | | |
| 7346 | | 7264 | |
| 7347 | let firstTerminalStatus: string | null = null; | 7265 | let firstTerminalStatus: string | null = null; |
| 7348 | while (firstTerminalStatus === null) { | 7266 | while (firstTerminalStatus === null) { |
| ... | @@ -7506,14 +7424,18 @@ describe("AcpAdapterV2", () => { | ... | @@ -7506,14 +7424,18 @@ describe("AcpAdapterV2", () => { |
| 7506 | const mockAgentPath = yield* path.fromFileUrl( | 7424 | const mockAgentPath = yield* path.fromFileUrl( |
| 7507 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), | 7425 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 7508 | ); | 7426 | ); |
| 7509 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); | | |
| 7510 | const continuationRequests: Array<ProviderContinuationRequest> = []; | 7427 | const continuationRequests: Array<ProviderContinuationRequest> = []; |
| 7511 | const instanceId = ProviderInstanceId.make("acp-test"); | 7428 | const instanceId = ProviderInstanceId.make("acp-test"); |
| 7512 | const childSessionId = "mock-child-session-root-then-child"; | 7429 | const childSessionId = "mock-child-session-root-then-child"; |
| 7513 | let subagentPhase: "spawn" | "complete" = "spawn"; | 7430 | let subagentPhase: "spawn" | "complete" = "spawn"; |
| 7514 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; | 7431 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; |
| 7515 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; | 7432 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; |
| | 7433 | const promptSettled = yield* Deferred.make<void>(); |
| 7516 | const adapter = makeAcpAdapterV2({ | 7434 | const adapter = makeAcpAdapterV2({ |
| | 7435 | testHooks: { |
| | 7436 | afterPromptSettledWithBackgroundWork: () => |
| | 7437 | Deferred.succeed(promptSettled, undefined).pipe(Effect.asVoid), |
| | 7438 | }, |
| 7517 | crypto: yield* Crypto.Crypto, | 7439 | crypto: yield* Crypto.Crypto, |
| 7518 | instanceId, | 7440 | instanceId, |
| 7519 | flavor: { | 7441 | flavor: { |
| ... | @@ -7547,7 +7469,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -7547,7 +7469,6 @@ describe("AcpAdapterV2", () => { |
| 7547 | childProcessSpawner, | 7469 | childProcessSpawner, |
| 7548 | mockAgentPath, | 7470 | mockAgentPath, |
| 7549 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, | 7471 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, |
| 7550 | protocolEvents, | | |
| 7551 | wrapRuntime: (runtime) => ({ | 7472 | wrapRuntime: (runtime) => ({ |
| 7552 | ...runtime, | 7473 | ...runtime, |
| 7553 | handleSessionUpdate: (handler) => | 7474 | handleSessionUpdate: (handler) => |
| ... | @@ -7603,28 +7524,13 @@ describe("AcpAdapterV2", () => { | ... | @@ -7603,28 +7524,13 @@ describe("AcpAdapterV2", () => { |
| 7603 | yield* runtime.startTurn( | 7524 | yield* runtime.startTurn( |
| 7604 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), | 7525 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), |
| 7605 | ); | 7526 | ); |
| 7606 | yield* Stream.fromQueue(protocolEvents).pipe( | 7527 | yield* Deferred.await(promptSettled); |
| 7607 | Stream.filter( | | |
| 7608 | (event) => | | |
| 7609 | event.direction === "incoming" && | | |
| 7610 | event.stage === "raw" && | | |
| 7611 | typeof event.payload === "string" && | | |
| 7612 | event.payload.includes('"stopReason"'), | | |
| 7613 | ), | | |
| 7614 | Stream.runHead, | | |
| 7615 | ); | | |
| 7616 | yield* Effect.yieldNow; | | |
| 7617 | yield* Effect.yieldNow; | | |
| 7618 | | 7528 | |
| 7619 | const firstProviderTurnId = idAllocator.derive.providerTurn({ | 7529 | const firstProviderTurnId = idAllocator.derive.providerTurn({ |
| 7620 | driver: ACP_TEST_DRIVER, | 7530 | driver: ACP_TEST_DRIVER, |
| 7621 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), | 7531 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), |
| 7622 | }); | 7532 | }); |
| 7623 | const interruptFiber = yield* runtime | 7533 | yield* runtime.interruptTurn({ providerThread, providerTurnId: firstProviderTurnId }); |
| 7624 | .interruptTurn({ providerThread, providerTurnId: firstProviderTurnId }) | | |
| 7625 | .pipe(Effect.forkScoped); | | |
| 7626 | yield* TestClock.adjust("10 seconds"); | | |
| 7627 | yield* Fiber.join(interruptFiber); | | |
| 7628 | | 7534 | |
| 7629 | let firstTerminalStatus: string | null = null; | 7535 | let firstTerminalStatus: string | null = null; |
| 7630 | while (firstTerminalStatus === null) { | 7536 | while (firstTerminalStatus === null) { |
| ... | @@ -7760,12 +7666,16 @@ describe("AcpAdapterV2", () => { | ... | @@ -7760,12 +7666,16 @@ describe("AcpAdapterV2", () => { |
| 7760 | const mockAgentPath = yield* path.fromFileUrl( | 7666 | const mockAgentPath = yield* path.fromFileUrl( |
| 7761 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), | 7667 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 7762 | ); | 7668 | ); |
| 7763 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); | | |
| 7764 | const instanceId = ProviderInstanceId.make("acp-test"); | 7669 | const instanceId = ProviderInstanceId.make("acp-test"); |
| 7765 | let subagentPhase: "spawn" | "complete" = "spawn"; | 7670 | let subagentPhase: "spawn" | "complete" = "spawn"; |
| 7766 | let cancelCalled = false; | 7671 | let cancelCalled = false; |
| 7767 | let runtimeOrdinalSeen = 0; | 7672 | let runtimeOrdinalSeen = 0; |
| | 7673 | const promptSettled = yield* Deferred.make<void>(); |
| 7768 | const adapter = makeAcpAdapterV2({ | 7674 | const adapter = makeAcpAdapterV2({ |
| | 7675 | testHooks: { |
| | 7676 | afterPromptSettledWithBackgroundWork: () => |
| | 7677 | Deferred.succeed(promptSettled, undefined).pipe(Effect.asVoid), |
| | 7678 | }, |
| 7769 | crypto: yield* Crypto.Crypto, | 7679 | crypto: yield* Crypto.Crypto, |
| 7770 | instanceId, | 7680 | instanceId, |
| 7771 | flavor: { | 7681 | flavor: { |
| ... | @@ -7812,7 +7722,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -7812,7 +7722,6 @@ describe("AcpAdapterV2", () => { |
| 7812 | runtimeOrdinalSeen = Math.max(runtimeOrdinalSeen, runtimeOrdinal); | 7722 | runtimeOrdinalSeen = Math.max(runtimeOrdinalSeen, runtimeOrdinal); |
| 7813 | return { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }; | 7723 | return { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }; |
| 7814 | }, | 7724 | }, |
| 7815 | protocolEvents, | | |
| 7816 | wrapCancel: (cancel) => | 7725 | wrapCancel: (cancel) => |
| 7817 | Effect.sync(() => { | 7726 | Effect.sync(() => { |
| 7818 | cancelCalled = true; | 7727 | cancelCalled = true; |
| ... | @@ -7853,28 +7762,13 @@ describe("AcpAdapterV2", () => { | ... | @@ -7853,28 +7762,13 @@ describe("AcpAdapterV2", () => { |
| 7853 | ); | 7762 | ); |
| 7854 | // The still-running subagent defers finalize after session/prompt returns, | 7763 | // The still-running subagent defers finalize after session/prompt returns, |
| 7855 | // so the interrupt below hits a settled turn held open for background work. | 7764 | // so the interrupt below hits a settled turn held open for background work. |
| 7856 | yield* Stream.fromQueue(protocolEvents).pipe( | 7765 | yield* Deferred.await(promptSettled); |
| 7857 | Stream.filter( | | |
| 7858 | (event) => | | |
| 7859 | event.direction === "incoming" && | | |
| 7860 | event.stage === "raw" && | | |
| 7861 | typeof event.payload === "string" && | | |
| 7862 | event.payload.includes('"stopReason"'), | | |
| 7863 | ), | | |
| 7864 | Stream.runHead, | | |
| 7865 | ); | | |
| 7866 | yield* Effect.yieldNow; | | |
| 7867 | yield* Effect.yieldNow; | | |
| 7868 | | 7766 | |
| 7869 | const firstProviderTurnId = idAllocator.derive.providerTurn({ | 7767 | const firstProviderTurnId = idAllocator.derive.providerTurn({ |
| 7870 | driver: ACP_TEST_DRIVER, | 7768 | driver: ACP_TEST_DRIVER, |
| 7871 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), | 7769 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), |
| 7872 | }); | 7770 | }); |
| 7873 | const interruptFiber = yield* runtime | 7771 | yield* runtime.interruptTurn({ providerThread, providerTurnId: firstProviderTurnId }); |
| 7874 | .interruptTurn({ providerThread, providerTurnId: firstProviderTurnId }) | | |
| 7875 | .pipe(Effect.forkScoped); | | |
| 7876 | yield* TestClock.adjust("10 seconds"); | | |
| 7877 | yield* Fiber.join(interruptFiber); | | |
| 7878 | assert.isFalse( | 7772 | assert.isFalse( |
| 7879 | cancelCalled, | 7773 | cancelCalled, |
| 7880 | "settled soft steer must not send session/cancel (the real Grok CLI kills background subagents on cancel)", | 7774 | "settled soft steer must not send session/cancel (the real Grok CLI kills background subagents on cancel)", |
| ... | @@ -13725,10 +13619,14 @@ describe("AcpAdapterV2", () => { | ... | @@ -13725,10 +13619,14 @@ describe("AcpAdapterV2", () => { |
| 13725 | const mockAgentPath = yield* path.fromFileUrl( | 13619 | const mockAgentPath = yield* path.fromFileUrl( |
| 13726 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), | 13620 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 13727 | ); | 13621 | ); |
| 13728 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); | | |
| 13729 | const instanceId = ProviderInstanceId.make("acp-test"); | 13622 | const instanceId = ProviderInstanceId.make("acp-test"); |
| 13730 | let subagentPhase: "spawn" | "complete" = "spawn"; | 13623 | let subagentPhase: "spawn" | "complete" = "spawn"; |
| | 13624 | const promptSettled = yield* Deferred.make<void>(); |
| 13731 | const adapter = makeAcpAdapterV2({ | 13625 | const adapter = makeAcpAdapterV2({ |
| | 13626 | testHooks: { |
| | 13627 | afterPromptSettledWithBackgroundWork: () => |
| | 13628 | Deferred.succeed(promptSettled, undefined).pipe(Effect.asVoid), |
| | 13629 | }, |
| 13732 | crypto: yield* Crypto.Crypto, | 13630 | crypto: yield* Crypto.Crypto, |
| 13733 | instanceId, | 13631 | instanceId, |
| 13734 | flavor: { | 13632 | flavor: { |
| ... | @@ -13762,7 +13660,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -13762,7 +13660,6 @@ describe("AcpAdapterV2", () => { |
| 13762 | childProcessSpawner, | 13660 | childProcessSpawner, |
| 13763 | mockAgentPath, | 13661 | mockAgentPath, |
| 13764 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, | 13662 | environment: { T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS: "1" }, |
| 13765 | protocolEvents, | | |
| 13766 | }), | 13663 | }), |
| 13767 | }, | 13664 | }, |
| 13768 | fileSystem, | 13665 | fileSystem, |
| ... | @@ -13799,32 +13696,17 @@ describe("AcpAdapterV2", () => { | ... | @@ -13799,32 +13696,17 @@ describe("AcpAdapterV2", () => { |
| 13799 | yield* runtime.startTurn( | 13696 | yield* runtime.startTurn( |
| 13800 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), | 13697 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now }), |
| 13801 | ); | 13698 | ); |
| 13802 | yield* Stream.fromQueue(protocolEvents).pipe( | 13699 | yield* Deferred.await(promptSettled); |
| 13803 | Stream.filter( | | |
| 13804 | (event) => | | |
| 13805 | event.direction === "incoming" && | | |
| 13806 | event.stage === "raw" && | | |
| 13807 | typeof event.payload === "string" && | | |
| 13808 | event.payload.includes('"stopReason"'), | | |
| 13809 | ), | | |
| 13810 | Stream.runHead, | | |
| 13811 | ); | | |
| 13812 | yield* Effect.yieldNow; | | |
| 13813 | yield* Effect.yieldNow; | | |
| 13814 | | 13700 | |
| 13815 | const firstProviderTurnId = idAllocator.derive.providerTurn({ | 13701 | const firstProviderTurnId = idAllocator.derive.providerTurn({ |
| 13816 | driver: ACP_TEST_DRIVER, | 13702 | driver: ACP_TEST_DRIVER, |
| 13817 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), | 13703 | nativeTurnId: acpScopedNativeId(instanceId, "mock-session-1:turn:1"), |
| 13818 | }); | 13704 | }); |
| 13819 | const interruptFiber = yield* runtime | 13705 | yield* runtime.interruptTurn({ |
| 13820 | .interruptTurn({ | 13706 | providerThread, |
| 13821 | providerThread, | 13707 | providerTurnId: firstProviderTurnId, |
| 13822 | providerTurnId: firstProviderTurnId, | 13708 | requestRuntimeRestart: true, |
| 13823 | requestRuntimeRestart: true, | 13709 | }); |
| 13824 | }) | | |
| 13825 | .pipe(Effect.forkScoped); | | |
| 13826 | yield* TestClock.adjust("10 seconds"); | | |
| 13827 | yield* Fiber.join(interruptFiber); | | |
| 13828 | | 13710 | |
| 13829 | let subagentStatus: string | null = null; | 13711 | let subagentStatus: string | null = null; |
| 13830 | let firstTerminalStatus: string | null = null; | 13712 | let firstTerminalStatus: string | null = null; |