


























@@ -0,0 +1,236 @@
1+import { describe, expect, it } from "vitest";
2+import {
3+applySessionRouteStateRepair,
4+resolveConfiguredDoctorSessionStateRoute,
5+scanSessionRouteStateOwners,
6+} from "./doctor-session-state-providers.js";
7+8+const codexOwner = {
9+id: "codex",
10+label: "Codex",
11+providerIds: ["codex", "codex-cli", "openai-codex"],
12+runtimeIds: ["codex", "codex-cli"],
13+cliSessionKeys: ["codex-cli"],
14+authProfilePrefixes: ["codex:", "codex-cli:", "openai-codex:"],
15+};
16+17+describe("doctor session state provider routes", () => {
18+it("preserves raw configured CLI runtimes before harness policy normalization", () => {
19+expect(
20+resolveConfiguredDoctorSessionStateRoute({
21+cfg: {
22+agents: {
23+defaults: {
24+model: { primary: "openai/gpt-5.5" },
25+agentRuntime: { id: "codex-cli" },
26+},
27+},
28+},
29+sessionKey: "agent:main:telegram:direct:1",
30+env: {},
31+}),
32+).toMatchObject({
33+defaultProvider: "openai",
34+configuredModelRefs: ["openai/gpt-5.5"],
35+runtime: "codex-cli",
36+});
37+});
38+39+it("lets environment CLI runtime overrides reach plugin-owned scanners", () => {
40+expect(
41+resolveConfiguredDoctorSessionStateRoute({
42+cfg: {
43+agents: {
44+defaults: {
45+model: { primary: "openai/gpt-5.5" },
46+agentRuntime: { id: "pi" },
47+},
48+},
49+},
50+sessionKey: "agent:main:telegram:direct:1",
51+env: { OPENCLAW_AGENT_RUNTIME: "codex-cli" },
52+}),
53+).toMatchObject({
54+runtime: "codex-cli",
55+});
56+});
57+58+it("clears auto-created route state when current route no longer uses the owner", () => {
59+const sessionKey = "agent:main:telegram:direct:1";
60+const entry: Record<string, unknown> = {
61+sessionId: "sess-stale-codex",
62+updatedAt: 1,
63+providerOverride: "openai-codex",
64+modelOverride: "gpt-5.4",
65+modelOverrideSource: "auto",
66+modelProvider: "openai-codex",
67+model: "gpt-5.4",
68+contextTokens: 1_050_000,
69+systemPromptReport: { source: "run" },
70+fallbackNoticeSelectedModel: "github-copilot/gpt-5-mini",
71+fallbackNoticeActiveModel: "openai-codex/gpt-5.4",
72+fallbackNoticeReason: "rate-limit",
73+agentHarnessId: "codex",
74+authProfileOverride: "openai-codex:default",
75+authProfileOverrideSource: "auto",
76+authProfileOverrideCompactionCount: 2,
77+cliSessionBindings: {
78+"codex-cli": { sessionId: "codex-session-1" },
79+"claude-cli": { sessionId: "claude-session-1" },
80+},
81+cliSessionIds: {
82+"codex-cli": "codex-session-1",
83+"claude-cli": "claude-session-1",
84+},
85+};
86+87+const scan = scanSessionRouteStateOwners({
88+owners: [codexOwner],
89+store: { [sessionKey]: entry },
90+routes: {
91+[sessionKey]: {
92+defaultProvider: "github-copilot",
93+configuredModelRefs: ["github-copilot/gpt-5-mini"],
94+runtime: "pi",
95+},
96+},
97+});
98+99+expect(scan.manualReview).toEqual([]);
100+expect(scan.repairs).toEqual([
101+{
102+key: sessionKey,
103+ownerId: "codex",
104+ownerLabel: "Codex",
105+cliSessionKeys: ["codex-cli"],
106+reasons: [
107+"auto model override",
108+"runtime model state",
109+"pinned runtime",
110+"CLI session binding",
111+"auto auth profile override",
112+],
113+},
114+]);
115+116+expect(applySessionRouteStateRepair({ entry, repair: scan.repairs[0], now: 123 })).toBe(true);
117+expect(entry).toMatchObject({
118+sessionId: "sess-stale-codex",
119+updatedAt: 123,
120+cliSessionBindings: {
121+"claude-cli": { sessionId: "claude-session-1" },
122+},
123+cliSessionIds: {
124+"claude-cli": "claude-session-1",
125+},
126+});
127+expect(entry.providerOverride).toBeUndefined();
128+expect(entry.modelOverride).toBeUndefined();
129+expect(entry.modelOverrideSource).toBeUndefined();
130+expect(entry.modelProvider).toBeUndefined();
131+expect(entry.model).toBeUndefined();
132+expect(entry.contextTokens).toBeUndefined();
133+expect(entry.systemPromptReport).toBeUndefined();
134+expect(entry.agentHarnessId).toBeUndefined();
135+expect(entry.authProfileOverride).toBeUndefined();
136+expect(entry.authProfileOverrideSource).toBeUndefined();
137+expect(entry.authProfileOverrideCompactionCount).toBeUndefined();
138+expect(entry.fallbackNoticeActiveModel).toBeUndefined();
139+});
140+141+it("leaves explicit user owner model choices for manual review", () => {
142+const sessionKey = "agent:main:telegram:direct:2";
143+const entry: Record<string, unknown> = {
144+sessionId: "sess-user-codex",
145+updatedAt: 1,
146+providerOverride: "openai-codex",
147+modelOverride: "gpt-5.4",
148+modelOverrideSource: "user",
149+modelProvider: "openai-codex",
150+model: "gpt-5.4",
151+agentHarnessId: "codex",
152+cliSessionBindings: {
153+"codex-cli": { sessionId: "codex-session-2" },
154+},
155+};
156+157+const scan = scanSessionRouteStateOwners({
158+owners: [codexOwner],
159+store: { [sessionKey]: entry },
160+routes: {
161+[sessionKey]: {
162+defaultProvider: "github-copilot",
163+configuredModelRefs: ["github-copilot/gpt-5-mini"],
164+runtime: "pi",
165+},
166+},
167+});
168+169+expect(scan.repairs).toEqual([]);
170+expect(scan.manualReview).toEqual([
171+{
172+key: sessionKey,
173+ownerLabel: "Codex",
174+message: `${sessionKey} (openai-codex/gpt-5.4, user)`,
175+},
176+]);
177+});
178+179+it("keeps owner state when owner remains in the configured route", () => {
180+const sessionKey = "agent:main:telegram:direct:3";
181+const entry: Record<string, unknown> = {
182+sessionId: "sess-configured-codex",
183+updatedAt: 1,
184+providerOverride: "openai-codex",
185+modelOverride: "gpt-5.4",
186+modelOverrideSource: "auto",
187+modelProvider: "openai-codex",
188+model: "gpt-5.4",
189+agentHarnessId: "codex",
190+cliSessionBindings: {
191+"codex-cli": { sessionId: "codex-session-3" },
192+},
193+};
194+195+const scan = scanSessionRouteStateOwners({
196+owners: [codexOwner],
197+store: { [sessionKey]: entry },
198+routes: {
199+[sessionKey]: {
200+defaultProvider: "github-copilot",
201+configuredModelRefs: ["github-copilot/gpt-5-mini", "openai-codex/gpt-5.4"],
202+runtime: "pi",
203+},
204+},
205+});
206+207+expect(scan).toEqual({ repairs: [], manualReview: [] });
208+});
209+210+it("keeps owner CLI state when owner runtime is still configured", () => {
211+const sessionKey = "agent:main:telegram:direct:4";
212+const entry: Record<string, unknown> = {
213+sessionId: "sess-codex-cli",
214+updatedAt: 1,
215+modelProvider: "codex-cli",
216+model: "gpt-5.5",
217+cliSessionBindings: {
218+"codex-cli": { sessionId: "codex-cli-session" },
219+},
220+};
221+222+const scan = scanSessionRouteStateOwners({
223+owners: [codexOwner],
224+store: { [sessionKey]: entry },
225+routes: {
226+[sessionKey]: {
227+defaultProvider: "openai",
228+configuredModelRefs: ["openai/gpt-5.5"],
229+runtime: "codex-cli",
230+},
231+},
232+});
233+234+expect(scan).toEqual({ repairs: [], manualReview: [] });
235+});
236+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。