

















1-import { describe, expect, it } from "vitest";
1+import { describe, expect, it, vi } from "vitest";
22import {
33applySessionRouteStateRepair,
44resolveConfiguredDoctorSessionStateRoute,
5+runPluginSessionStateDoctorRepairs,
56scanSessionRouteStateOwners,
67storeMayContainPluginSessionRouteState,
78} from "./doctor-session-state-providers.js";
8910+vi.mock("../plugins/doctor-contract-registry.js", async () => {
11+const actual = await vi.importActual<typeof import("../plugins/doctor-contract-registry.js")>(
12+"../plugins/doctor-contract-registry.js",
13+);
14+return {
15+ ...actual,
16+listPluginDoctorSessionRouteStateOwners: vi.fn(() => [
17+{
18+id: "codex",
19+label: "Codex",
20+providerIds: ["codex", "codex-cli", "openai-codex"],
21+runtimeIds: ["codex", "codex-cli"],
22+cliSessionKeys: ["codex-cli"],
23+authProfilePrefixes: ["codex:", "codex-cli:", "openai-codex:"],
24+},
25+]),
26+};
27+});
28+929const codexOwner = {
1030id: "codex",
1131label: "Codex",
@@ -459,4 +479,80 @@ describe("doctor session state provider routes", () => {
459479expect(entry.agentHarnessId).toBeUndefined();
460480expect(entry.agentRuntimeOverride).toBe("claude-cli");
461481});
482+483+it("skips entries without plugin route state and memoizes routes per agentId", async () => {
484+// Sentinel cfg makes resolveConfiguredDoctorSessionStateRoute cheap and
485+// deterministic. The important assertions are observable through the
486+// resulting scan: entries with no route-state fields contribute no
487+// repairs/manual-review and the run completes immediately.
488+const cfg = {
489+agents: {
490+defaults: {
491+model: { primary: "anthropic/claude-sonnet-4" },
492+},
493+},
494+models: {
495+providers: {
496+anthropic: {},
497+},
498+},
499+} as unknown as Parameters<typeof runPluginSessionStateDoctorRepairs>[0]["cfg"];
500+501+// Build a store with 200 entries belonging to one agent. Two carry route
502+// state that the codex owner cares about; the rest are bare. The old
503+// implementation resolved a route for all 200; the new one only resolves
504+// for the 2 that matter, deduplicated by agentId.
505+const store: Record<string, Record<string, unknown>> = {};
506+for (let i = 0; i < 198; i += 1) {
507+store[`agent:main:bare-${i}`] = {
508+sessionId: `sess-bare-${i}`,
509+updatedAt: i,
510+// No providerOverride/model/agentHarnessId/etc. — must be skipped.
511+};
512+}
513+store["agent:main:codex-1"] = {
514+sessionId: "sess-codex-1",
515+updatedAt: 1,
516+agentHarnessId: "codex-cli",
517+};
518+store["agent:main:codex-2"] = {
519+sessionId: "sess-codex-2",
520+updatedAt: 2,
521+agentHarnessId: "codex-cli",
522+};
523+524+const warnings: string[] = [];
525+const changes: string[] = [];
526+const prompter: Parameters<typeof runPluginSessionStateDoctorRepairs>[0]["prompter"] = {
527+confirmRuntimeRepair: vi.fn(async () => false),
528+note: vi.fn(),
529+};
530+531+const start = Date.now();
532+await runPluginSessionStateDoctorRepairs({
533+ cfg,
534+store: store as unknown as Parameters<typeof runPluginSessionStateDoctorRepairs>[0]["store"],
535+absoluteStorePath: "/tmp/nonexistent-store.json",
536+ prompter,
537+env: {},
538+ warnings,
539+ changes,
540+});
541+const elapsedMs = Date.now() - start;
542+543+// Two entries flagged for pinned-runtime repair; warning emitted once.
544+expect(warnings).toHaveLength(1);
545+expect(warnings[0]).toMatch(/Codex/);
546+expect(warnings[0]).toMatch(/2 sessions?/);
547+548+// User declined the repair so no changes applied.
549+expect(changes).toHaveLength(0);
550+expect(prompter.confirmRuntimeRepair).toHaveBeenCalledOnce();
551+552+// Sanity check: even with 200 entries, this should complete near-
553+// instantly because route resolution is bounded by unique agentIds, not
554+// by store size. A 200-entry x 1.6s-per-call pre-fix run would exceed
555+// 5 minutes; the fixed code should run in well under a second.
556+expect(elapsedMs).toBeLessThan(2000);
557+});
462558});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。