@@ -1149,6 +1149,68 @@ describe("tui command handlers", () => {
|
1149 | 1149 | expect(closeOverlay).toHaveBeenCalledTimes(1); |
1150 | 1150 | }); |
1151 | 1151 | |
| 1152 | +it("shows resolved canonical model ref after /model alias, not raw alias string", async () => { |
| 1153 | +// When the user types `/model gpt4` (a bare alias), the gateway resolves it |
| 1154 | +// server-side and returns the canonical ref in result.resolved. The TUI must |
| 1155 | +// display the canonical ref, not the raw alias, so the user knows which model |
| 1156 | +// was actually applied. |
| 1157 | +const patchSession = vi.fn().mockResolvedValue({ |
| 1158 | +ok: true, |
| 1159 | +path: "/sessions/patch", |
| 1160 | +key: "agent:main:main", |
| 1161 | +entry: {}, |
| 1162 | +resolved: { modelProvider: "openai", model: "gpt-5.5" }, |
| 1163 | +}); |
| 1164 | +const refreshSessionInfo = vi.fn().mockResolvedValue(undefined); |
| 1165 | +const applySessionInfoFromPatch = vi.fn(); |
| 1166 | +const { handleCommand, addSystem } = createHarness({ |
| 1167 | + patchSession, |
| 1168 | + refreshSessionInfo, |
| 1169 | + applySessionInfoFromPatch, |
| 1170 | +}); |
| 1171 | + |
| 1172 | +await handleCommand("/model gpt4"); |
| 1173 | + |
| 1174 | +expect(patchSession).toHaveBeenCalledWith(expect.objectContaining({ model: "gpt4" })); |
| 1175 | +// Should show the canonical resolved ref, not the raw alias "gpt4" |
| 1176 | +expect(addSystem).toHaveBeenCalledWith("model set to openai/gpt-5.5"); |
| 1177 | +}); |
| 1178 | + |
| 1179 | +it("falls back to raw input in /model confirmation when resolved ref unavailable", async () => { |
| 1180 | +// Older gateway versions may not return resolved; fall back to raw arg. |
| 1181 | +const patchSession = vi.fn().mockResolvedValue({ |
| 1182 | +ok: true, |
| 1183 | +path: "/sessions/patch", |
| 1184 | +key: "agent:main:main", |
| 1185 | +entry: {}, |
| 1186 | +// No `resolved` field |
| 1187 | +}); |
| 1188 | +const { handleCommand, addSystem } = createHarness({ patchSession }); |
| 1189 | + |
| 1190 | +await handleCommand("/model openai/gpt-5.5"); |
| 1191 | + |
| 1192 | +expect(addSystem).toHaveBeenCalledWith("model set to openai/gpt-5.5"); |
| 1193 | +}); |
| 1194 | +it("preserves provider prefix for nested model ids in /model confirmation", async () => { |
| 1195 | +// Some providers route to nested model ids that themselves contain a slash |
| 1196 | +// (e.g. resolved.model: "moonshotai/kimi-k2.5" with modelProvider: "nvidia"). |
| 1197 | +// The confirmation must still show the full nvidia/moonshotai/kimi-k2.5 ref |
| 1198 | +// to match the footer/status bar, not strip the provider just because the |
| 1199 | +// model id already contains a slash. |
| 1200 | +const patchSession = vi.fn().mockResolvedValue({ |
| 1201 | +ok: true, |
| 1202 | +path: "/sessions/patch", |
| 1203 | +key: "agent:main:main", |
| 1204 | +entry: {}, |
| 1205 | +resolved: { modelProvider: "nvidia", model: "moonshotai/kimi-k2.5" }, |
| 1206 | +}); |
| 1207 | +const { handleCommand, addSystem } = createHarness({ patchSession }); |
| 1208 | + |
| 1209 | +await handleCommand("/model nvidia/moonshotai/kimi-k2.5"); |
| 1210 | + |
| 1211 | +expect(addSystem).toHaveBeenCalledWith("model set to nvidia/moonshotai/kimi-k2.5"); |
| 1212 | +}); |
| 1213 | + |
1152 | 1214 | it("renders model listing feedback before the backend list resolves", async () => { |
1153 | 1215 | let resolveModels: ( |
1154 | 1216 | value: Array<{ provider: string; id: string; name?: string }>, |
|