@@ -663,6 +663,74 @@ describe("doctor repair sequencing", () => {
|
663 | 663 | ]); |
664 | 664 | }); |
665 | 665 | |
| 666 | +it("repairs #94184 stale Codex model-map refs before missing plugin install repair", async () => { |
| 667 | +mocks.repairMissingConfiguredPluginInstalls.mockImplementationOnce( |
| 668 | +async (params: { cfg: OpenClawConfig }) => { |
| 669 | +expect(params.cfg.plugins?.entries?.codex?.enabled).toBe(true); |
| 670 | +expect(params.cfg.agents?.defaults?.model).toBe("openai/gpt-5.5"); |
| 671 | +expect(params.cfg.agents?.defaults?.models?.["openai/gpt-5.5"]?.agentRuntime).toEqual({ |
| 672 | +id: "codex", |
| 673 | +}); |
| 674 | +expect(params.cfg.agents?.defaults?.models?.["openai-codex/gpt-5.5"]).toBeUndefined(); |
| 675 | +return { |
| 676 | +changes: [], |
| 677 | +warnings: [], |
| 678 | +}; |
| 679 | +}, |
| 680 | +); |
| 681 | + |
| 682 | +const staleUpgradeConfig = { |
| 683 | +plugins: { |
| 684 | +allow: ["openai"], |
| 685 | +entries: { |
| 686 | +codex: { enabled: true }, |
| 687 | +}, |
| 688 | +}, |
| 689 | +agents: { |
| 690 | +defaults: { |
| 691 | +model: "openai-codex/gpt-5.5", |
| 692 | +models: { |
| 693 | +"openai-codex/gpt-5.5": { |
| 694 | +params: { reasoning_effort: "high" }, |
| 695 | +}, |
| 696 | +}, |
| 697 | +}, |
| 698 | +}, |
| 699 | +} as OpenClawConfig; |
| 700 | + |
| 701 | +const result = await runDoctorRepairSequence({ |
| 702 | +state: { |
| 703 | +cfg: staleUpgradeConfig, |
| 704 | +candidate: staleUpgradeConfig, |
| 705 | +pendingChanges: false, |
| 706 | +fixHints: [], |
| 707 | +}, |
| 708 | +doctorFixCommand: "openclaw doctor --fix", |
| 709 | +env: {}, |
| 710 | +}); |
| 711 | + |
| 712 | +expect(result.state.pendingChanges).toBe(true); |
| 713 | +expect(result.state.candidate.plugins?.allow).toEqual(["openai", "codex"]); |
| 714 | +expect(result.state.candidate.plugins?.entries?.codex?.enabled).toBe(true); |
| 715 | +expect(result.state.candidate.agents?.defaults?.model).toBe("openai/gpt-5.5"); |
| 716 | +expect( |
| 717 | +result.state.candidate.agents?.defaults?.models?.["openai-codex/gpt-5.5"], |
| 718 | +).toBeUndefined(); |
| 719 | +expect(result.state.candidate.agents?.defaults?.models?.["openai/gpt-5.5"]).toMatchObject({ |
| 720 | +params: { reasoning_effort: "high" }, |
| 721 | +agentRuntime: { id: "codex" }, |
| 722 | +}); |
| 723 | +const changeNotes = result.changeNotes.join("\n"); |
| 724 | +expect(changeNotes).toContain("agents.defaults.model: openai-codex/gpt-5.5 -> openai/gpt-5.5"); |
| 725 | +expect(changeNotes).toContain( |
| 726 | +"agents.defaults.models.openai-codex/gpt-5.5: openai-codex/gpt-5.5 -> openai/gpt-5.5", |
| 727 | +); |
| 728 | +expect(changeNotes).toContain( |
| 729 | +'Set agents.defaults.models.openai/gpt-5.5.agentRuntime.id to "codex"', |
| 730 | +); |
| 731 | +expect(changeNotes).toContain("Added codex to plugins.allow"); |
| 732 | +}); |
| 733 | + |
666 | 734 | it("runs group allowFrom fallback migration after open-policy allowFrom repair", async () => { |
667 | 735 | const events: string[] = []; |
668 | 736 | mocks.maybeRepairOpenPolicyAllowFrom.mockImplementationOnce((cfg: OpenClawConfig) => { |
|