| ... | ... | @@ -648,6 +648,78 @@ it.effect("ProviderServiceLive catches stopAll failures during shutdown", () => |
| 648 | 648 | }), |
| 649 | 649 | ); |
| 650 | 650 | |
| 651 | it.effect("ProviderServiceLive shutdown leaves settled session rows untouched", () => |
| 652 | Effect.gen(function* () { |
| 653 | const recordedAnalytics = makeRecordingAnalytics(); |
| 654 | const codex = makeFakeCodexAdapter(); |
| 655 | const persistence = yield* Layer.build( |
| 656 | ProviderSessionDirectoryLive.pipe( |
| 657 | Layer.provide(ProviderSessionRuntime.layer.pipe(Layer.provide(SqlitePersistenceMemory))), |
| 658 | ), |
| 659 | ); |
| 660 | const directory = yield* ProviderSessionDirectory.ProviderSessionDirectory.pipe( |
| 661 | Effect.provide(persistence), |
| 662 | ); |
| 663 | const seed = (threadId: ThreadId, status: "running" | "stopped", activeTurnId: TurnId | null) => |
| 664 | directory.upsert({ |
| 665 | threadId, |
| 666 | provider: CODEX_DRIVER, |
| 667 | providerInstanceId: codexInstanceId, |
| 668 | status, |
| 669 | runtimePayload: { cwd: "/repo", activeTurnId }, |
| 670 | }); |
| 671 | const readBindings = directory |
| 672 | .listBindings() |
| 673 | .pipe( |
| 674 | Effect.map((bindings) => new Map(bindings.map((binding) => [binding.threadId, binding]))), |
| 675 | ); |
| 676 | const settledId = asThreadId("shutdown-settled"); |
| 677 | const runningId = asThreadId("shutdown-running"); |
| 678 | const stoppedWithTurnId = asThreadId("shutdown-stopped-with-turn"); |
| 679 | yield* seed(settledId, "stopped", null); |
| 680 | yield* seed(runningId, "running", asTurnId("running-turn")); |
| 681 | yield* seed(stoppedWithTurnId, "stopped", asTurnId("stale-turn")); |
| 682 | const settledBefore = (yield* readBindings).get(settledId); |
| 683 | assert(settledBefore !== undefined); |
| 684 | |
| 685 | const scope = yield* Scope.make(); |
| 686 | yield* Layer.build( |
| 687 | makeProviderServiceLive().pipe( |
| 688 | Layer.provide(NodeServices.layer), |
| 689 | Layer.provide(Layer.succeed(ProviderSessionDirectory.ProviderSessionDirectory, directory)), |
| 690 | Layer.provide( |
| 691 | Layer.succeed( |
| 692 | ProviderAdapterRegistry.ProviderAdapterRegistry, |
| 693 | makeStaticInstanceRegistry([[codexInstanceId, codex.adapter]]), |
| 694 | ), |
| 695 | ), |
| 696 | Layer.provide(defaultServerSettingsLayer), |
| 697 | Layer.provide(serverConfigTestLayer), |
| 698 | Layer.provide(recordedAnalytics.layer), |
| 699 | Layer.provide( |
| 700 | Layer.succeed( |
| 701 | ProviderEventLoggers.ProviderEventLoggers, |
| 702 | ProviderEventLoggers.NoOpProviderEventLoggers, |
| 703 | ), |
| 704 | ), |
| 705 | ), |
| 706 | ).pipe(Scope.provide(scope)); |
| 707 | yield* TestClock.adjust("1 minute"); |
| 708 | yield* Scope.close(scope, Exit.void); |
| 709 | |
| 710 | const byThread = yield* readBindings; |
| 711 | assert.deepStrictEqual(byThread.get(settledId), settledBefore); |
| 712 | for (const threadId of [runningId, stoppedWithTurnId]) { |
| 713 | const binding = byThread.get(threadId); |
| 714 | assert.equal(binding?.status, "stopped"); |
| 715 | assert.propertyVal(binding?.runtimePayload, "activeTurnId", null); |
| 716 | assert.propertyVal(binding?.runtimePayload, "lastRuntimeEvent", "provider.stopAll"); |
| 717 | } |
| 718 | const [stoppedAll] = recordedAnalytics.eventsByName("provider.sessions.stopped_all"); |
| 719 | assert.equal(stoppedAll?.properties?.stoppedSessionCount, 2); |
| 720 | }).pipe(Effect.provide(NodeServices.layer)), |
| 721 | ); |
| 722 | |
| 651 | 723 | it.effect("ProviderServiceLive flushes deferred completions during shutdown", () => |
| 652 | 724 | Effect.gen(function* () { |
| 653 | 725 | const recordedAnalytics = makeRecordingAnalytics(); |