fix: enforce Mistral tool call ids (#1372) (thanks @zerone0x)

This commit is contained in:
Peter Steinberger
2026-01-22 00:38:48 +00:00
parent d51eca64cc
commit 0704fe7dbb
13 changed files with 193 additions and 51 deletions

View File

@@ -60,7 +60,7 @@ describe("directive behavior", () => {
vi.restoreAllMocks();
});
it("lists allowlisted models on /model list", async () => {
it("moves /model list to /models", async () => {
await withTempHome(async (home) => {
vi.mocked(runEmbeddedPiAgent).mockReset();
const storePath = path.join(home, "sessions.json");
@@ -90,7 +90,7 @@ describe("directive behavior", () => {
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("falls back to configured models when catalog is unavailable", async () => {
it("shows summary on /model when catalog is unavailable", async () => {
await withTempHome(async (home) => {
vi.mocked(runEmbeddedPiAgent).mockReset();
vi.mocked(loadModelCatalog).mockResolvedValueOnce([]);
@@ -116,12 +116,13 @@ describe("directive behavior", () => {
const text = Array.isArray(res) ? res[0]?.text : res?.text;
expect(text).toContain("Current: anthropic/claude-opus-4-5");
expect(text).toContain("Switch: /model <provider/model>");
expect(text).toContain("Browse: /models (providers) or /models <provider> (models)");
expect(text).toContain("More: /model status");
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("includes catalog models when no allowlist is set", async () => {
it("moves /model list to /models even when no allowlist is set", async () => {
await withTempHome(async (home) => {
vi.mocked(runEmbeddedPiAgent).mockReset();
vi.mocked(loadModelCatalog).mockResolvedValueOnce([
@@ -156,7 +157,7 @@ describe("directive behavior", () => {
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("merges config allowlist models even when catalog is present", async () => {
it("moves /model list to /models even when catalog is present", async () => {
await withTempHome(async (home) => {
vi.mocked(runEmbeddedPiAgent).mockReset();
// Catalog present but missing custom providers: /model should still include
@@ -207,7 +208,7 @@ describe("directive behavior", () => {
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("does not repeat missing auth labels on /model list", async () => {
it("moves /model list to /models without listing auth labels", async () => {
await withTempHome(async (home) => {
vi.mocked(runEmbeddedPiAgent).mockReset();
const storePath = path.join(home, "sessions.json");
@@ -231,6 +232,8 @@ describe("directive behavior", () => {
const text = Array.isArray(res) ? res[0]?.text : res?.text;
expect(text).toContain("Model listing moved.");
expect(text).toContain("Use: /models (providers) or /models <provider> (models)");
expect(text).toContain("Switch: /model <provider/model>");
expect(text).not.toContain("missing (missing)");
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});

View File

@@ -67,7 +67,7 @@ describe("directive behavior", () => {
vi.mocked(runEmbeddedPiAgent).mockReset();
const storePath = path.join(home, "sessions.json");
await getReplyFromConfig(
const res = await getReplyFromConfig(
{ Body: "/model ki", From: "+1222", To: "+1222", CommandAuthorized: true },
{},
{
@@ -103,10 +103,11 @@ describe("directive behavior", () => {
},
);
assertModelSelection(storePath, {
model: "kimi-k2-0905-preview",
provider: "moonshot",
});
const text = Array.isArray(res) ? res[0]?.text : res?.text;
expect(text).toContain("Unrecognized model: ki");
expect(text).toContain("Did you mean: moonshot/kimi-k2-0905-preview");
expect(text).toContain("Try: /model moonshot/kimi-k2-0905-preview");
assertModelSelection(storePath);
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});

View File

@@ -65,7 +65,7 @@ describe("directive behavior", () => {
vi.mocked(runEmbeddedPiAgent).mockReset();
const storePath = path.join(home, "sessions.json");
await getReplyFromConfig(
const res = await getReplyFromConfig(
{ Body: "/model kimi", From: "+1222", To: "+1222", CommandAuthorized: true },
{},
{
@@ -94,10 +94,11 @@ describe("directive behavior", () => {
},
);
assertModelSelection(storePath, {
model: "kimi-k2-0905-preview",
provider: "moonshot",
});
const text = Array.isArray(res) ? res[0]?.text : res?.text;
expect(text).toContain("Unrecognized model: kimi");
expect(text).toContain("Did you mean: moonshot/kimi-k2-0905-preview");
expect(text).toContain("Try: /model moonshot/kimi-k2-0905-preview");
assertModelSelection(storePath);
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
@@ -106,7 +107,7 @@ describe("directive behavior", () => {
vi.mocked(runEmbeddedPiAgent).mockReset();
const storePath = path.join(home, "sessions.json");
await getReplyFromConfig(
const res = await getReplyFromConfig(
{
Body: "/model kimi-k2-0905-preview",
From: "+1222",
@@ -140,10 +141,11 @@ describe("directive behavior", () => {
},
);
assertModelSelection(storePath, {
model: "kimi-k2-0905-preview",
provider: "moonshot",
});
const text = Array.isArray(res) ? res[0]?.text : res?.text;
expect(text).toContain("Unrecognized model: kimi-k2-0905-preview");
expect(text).toContain("Did you mean: moonshot/kimi-k2-0905-preview");
expect(text).toContain("Try: /model moonshot/kimi-k2-0905-preview");
assertModelSelection(storePath);
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
@@ -152,7 +154,7 @@ describe("directive behavior", () => {
vi.mocked(runEmbeddedPiAgent).mockReset();
const storePath = path.join(home, "sessions.json");
await getReplyFromConfig(
const res = await getReplyFromConfig(
{ Body: "/model moonshot/kimi", From: "+1222", To: "+1222", CommandAuthorized: true },
{},
{
@@ -181,10 +183,11 @@ describe("directive behavior", () => {
},
);
assertModelSelection(storePath, {
model: "kimi-k2-0905-preview",
provider: "moonshot",
});
const text = Array.isArray(res) ? res[0]?.text : res?.text;
expect(text).toContain("Unrecognized model: moonshot/kimi");
expect(text).toContain("Did you mean: moonshot/kimi-k2-0905-preview");
expect(text).toContain("Try: /model moonshot/kimi-k2-0905-preview");
assertModelSelection(storePath);
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});

View File

@@ -187,7 +187,7 @@ describe("directive behavior", () => {
expect(runEmbeddedPiAgent).toHaveBeenCalledOnce();
});
});
it("lists allowlisted models on /model", async () => {
it("shows summary on /model", async () => {
await withTempHome(async (home) => {
vi.mocked(runEmbeddedPiAgent).mockReset();
const storePath = path.join(home, "sessions.json");
@@ -212,6 +212,7 @@ describe("directive behavior", () => {
const text = Array.isArray(res) ? res[0]?.text : res?.text;
expect(text).toContain("Current: anthropic/claude-opus-4-5");
expect(text).toContain("Switch: /model <provider/model>");
expect(text).toContain("Browse: /models (providers) or /models <provider> (models)");
expect(text).toContain("More: /model status");
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();