From 9fc62cefa21b8ce275b1f3b649007312430b31b2 Mon Sep 17 00:00:00 2001 From: ThanhNguyxn Date: Thu, 29 Jan 2026 19:53:01 +0700 Subject: [PATCH] fix(compact): return clear error for CLI providers (#3874) --- src/agents/pi-embedded-runner/compact.test.ts | 69 +++++++++++++++++++ src/agents/pi-embedded-runner/compact.ts | 13 ++++ 2 files changed, 82 insertions(+) create mode 100644 src/agents/pi-embedded-runner/compact.test.ts diff --git a/src/agents/pi-embedded-runner/compact.test.ts b/src/agents/pi-embedded-runner/compact.test.ts new file mode 100644 index 000000000..5dc6852ca --- /dev/null +++ b/src/agents/pi-embedded-runner/compact.test.ts @@ -0,0 +1,69 @@ +import { describe, expect, it, vi, beforeEach } from "vitest"; + +// Mock model-selection to control isCliProvider behavior +vi.mock("../model-selection.js", () => ({ + isCliProvider: vi.fn((provider: string) => { + return provider === "claude-cli" || provider === "codex-cli"; + }), +})); + +// Mock defaults +vi.mock("../defaults.js", () => ({ + DEFAULT_PROVIDER: "anthropic", + DEFAULT_MODEL: "claude-sonnet-4", +})); + +// Mock models-config - ensure async function returns +vi.mock("../models-config.js", () => ({ + ensureMoltbotModelsJson: vi.fn(async () => {}), +})); + +// Mock agent-paths +vi.mock("../agent-paths.js", () => ({ + resolveMoltbotAgentDir: vi.fn(() => "/tmp/test-agent"), +})); + +// Mock utils +vi.mock("../../utils.js", () => ({ + resolveUserPath: vi.fn((p: string) => p), +})); + +// Import after mocks are set up +import { compactEmbeddedPiSessionDirect } from "./compact.js"; + +describe("compactEmbeddedPiSessionDirect", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("returns error for CLI provider claude-cli", async () => { + const result = await compactEmbeddedPiSessionDirect({ + sessionId: "test-session", + sessionFile: "/tmp/test.jsonl", + workspaceDir: "/tmp/workspace", + provider: "claude-cli", + model: "opus", + }); + + expect(result.ok).toBe(false); + expect(result.compacted).toBe(false); + expect(result.reason).toContain("Compaction is not supported for CLI providers"); + expect(result.reason).toContain("claude-cli"); + expect(result.reason).toContain("Use /model to switch to an API provider"); + }); + + it("returns error for CLI provider codex-cli", async () => { + const result = await compactEmbeddedPiSessionDirect({ + sessionId: "test-session", + sessionFile: "/tmp/test.jsonl", + workspaceDir: "/tmp/workspace", + provider: "codex-cli", + model: "default", + }); + + expect(result.ok).toBe(false); + expect(result.compacted).toBe(false); + expect(result.reason).toContain("Compaction is not supported for CLI providers"); + expect(result.reason).toContain("codex-cli"); + }); +}); diff --git a/src/agents/pi-embedded-runner/compact.ts b/src/agents/pi-embedded-runner/compact.ts index dc68561c2..4af6a80e7 100644 --- a/src/agents/pi-embedded-runner/compact.ts +++ b/src/agents/pi-embedded-runner/compact.ts @@ -29,6 +29,7 @@ import { resolveMoltbotDocsPath } from "../docs-path.js"; import type { ExecElevatedDefaults } from "../bash-tools.js"; import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../defaults.js"; import { getApiKeyForModel, resolveModelAuthMode } from "../model-auth.js"; +import { isCliProvider } from "../model-selection.js"; import { ensureMoltbotModelsJson } from "../models-config.js"; import { ensureSessionHeader, @@ -114,6 +115,18 @@ export async function compactEmbeddedPiSessionDirect( const provider = (params.provider ?? DEFAULT_PROVIDER).trim() || DEFAULT_PROVIDER; const modelId = (params.model ?? DEFAULT_MODEL).trim() || DEFAULT_MODEL; + + // CLI providers (claude-cli, codex-cli, etc.) are not supported for compaction + // because they don't use the embedded Pi runner. Users need to switch to an + // API provider before compacting. + if (isCliProvider(provider, params.config)) { + return { + ok: false, + compacted: false, + reason: `Compaction is not supported for CLI providers (${provider}/${modelId}). Use /model to switch to an API provider before compacting.`, + }; + } + const agentDir = params.agentDir ?? resolveMoltbotAgentDir(); await ensureMoltbotModelsJson(params.config, agentDir); const { model, error, authStorage, modelRegistry } = resolveModel(