| ... | @@ -448,6 +448,51 @@ describe("ProviderRuntimeIngestion", () => { | ... | @@ -448,6 +448,51 @@ describe("ProviderRuntimeIngestion", () => { |
| 448 | expect(thread.session?.lastError).toBeNull(); | 448 | expect(thread.session?.lastError).toBeNull(); |
| 449 | }); | 449 | }); |
| 450 | | 450 | |
| | 451 | it("clears active turn when provider session becomes ready", async () => { |
| | 452 | const harness = await createHarness(); |
| | 453 | const now = "2026-01-01T00:00:00.000Z"; |
| | 454 | |
| | 455 | harness.emit({ |
| | 456 | type: "turn.started", |
| | 457 | eventId: asEventId("evt-turn-started-session-ready"), |
| | 458 | provider: ProviderDriverKind.make("codex"), |
| | 459 | createdAt: now, |
| | 460 | threadId: asThreadId("thread-1"), |
| | 461 | turnId: asTurnId("turn-session-ready"), |
| | 462 | }); |
| | 463 | |
| | 464 | await waitForThread( |
| | 465 | harness.readModel, |
| | 466 | (thread) => |
| | 467 | thread.session?.status === "running" && |
| | 468 | thread.session?.activeTurnId === "turn-session-ready", |
| | 469 | 10_000, |
| | 470 | ); |
| | 471 | |
| | 472 | harness.emit({ |
| | 473 | type: "session.state.changed", |
| | 474 | eventId: asEventId("evt-session-state-ready-with-active-turn"), |
| | 475 | provider: ProviderDriverKind.make("codex"), |
| | 476 | threadId: asThreadId("thread-1"), |
| | 477 | createdAt: "2026-01-01T00:00:01.000Z", |
| | 478 | payload: { |
| | 479 | state: "ready", |
| | 480 | }, |
| | 481 | }); |
| | 482 | |
| | 483 | const thread = await waitForThread( |
| | 484 | harness.readModel, |
| | 485 | (entry) => |
| | 486 | entry.session?.status === "ready" && |
| | 487 | entry.session?.activeTurnId === null && |
| | 488 | entry.session?.lastError === null, |
| | 489 | 10_000, |
| | 490 | ); |
| | 491 | expect(thread.session?.status).toBe("ready"); |
| | 492 | expect(thread.session?.activeTurnId).toBeNull(); |
| | 493 | expect(thread.session?.lastError).toBeNull(); |
| | 494 | }); |
| | 495 | |
| 451 | it("does not clear active turn when session/thread started arrives mid-turn", async () => { | 496 | it("does not clear active turn when session/thread started arrives mid-turn", async () => { |
| 452 | const harness = await createHarness(); | 497 | const harness = await createHarness(); |
| 453 | const now = "2026-01-01T00:00:00.000Z"; | 498 | const now = "2026-01-01T00:00:00.000Z"; |
| ... | @@ -466,6 +511,7 @@ describe("ProviderRuntimeIngestion", () => { | ... | @@ -466,6 +511,7 @@ describe("ProviderRuntimeIngestion", () => { |
| 466 | (thread) => | 511 | (thread) => |
| 467 | thread.session?.status === "running" && | 512 | thread.session?.status === "running" && |
| 468 | thread.session?.activeTurnId === "turn-midturn-lifecycle", | 513 | thread.session?.activeTurnId === "turn-midturn-lifecycle", |
| | 514 | 10_000, |
| 469 | ); | 515 | ); |
| 470 | | 516 | |
| 471 | harness.emit({ | 517 | harness.emit({ |
| ... | @@ -502,6 +548,7 @@ describe("ProviderRuntimeIngestion", () => { | ... | @@ -502,6 +548,7 @@ describe("ProviderRuntimeIngestion", () => { |
| 502 | await waitForThread( | 548 | await waitForThread( |
| 503 | harness.readModel, | 549 | harness.readModel, |
| 504 | (thread) => thread.session?.status === "ready" && thread.session?.activeTurnId === null, | 550 | (thread) => thread.session?.status === "ready" && thread.session?.activeTurnId === null, |
| | 551 | 10_000, |
| 505 | ); | 552 | ); |
| 506 | }); | 553 | }); |
| 507 | | 554 | |