Download src/agents/embedded-agent-runner/compaction-runtime-context.test.ts from SaylorTwift/openclaw: direct link, hf CLI and curl.
- Browser
- Download file 34.8 kB
-
https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/agents/embedded-agent-runner/compaction-runtime-context.test.ts
- Command line
-
hf download hf://SaylorTwift/openclaw/src/agents/embedded-agent-runner/compaction-runtime-context.test.ts
-
curl -L -o compaction-runtime-context.test.ts https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/agents/embedded-agent-runner/compaction-runtime-context.test.ts
34.8 kB
| // Coverage for building compaction runtime context from active runner state. | |
| import path from "node:path"; | |
| import { afterEach, describe, expect, it, vi } from "vitest"; | |
| import { useAutoCleanupTempDirTracker } from "../../../test/helpers/temp-dir.js"; | |
| import type { OpenClawConfig } from "../../config/config.js"; | |
| import { formatSqliteSessionFileMarker } from "../../config/sessions/legacy-sqlite-marker.js"; | |
| import * as manifestModelIdNormalization from "../../plugins/manifest-model-id-normalization.js"; | |
| import { addSession, deleteSession } from "../bash-process-registry.js"; | |
| import { createProcessSessionFixture } from "../bash-process-registry.test-helpers.js"; | |
| import * as providerModelNormalizationRuntime from "../provider-model-normalization.runtime.js"; | |
| import { | |
| buildEmbeddedCompactionRuntimeContext, | |
| resolveCompactionContextTokenBudget, | |
| resolveCompactionHarnessRuntime, | |
| resolveEmbeddedCompactionThinkingLevel, | |
| resolveEmbeddedCompactionTarget, | |
| } from "./compaction-runtime-context.js"; | |
| import { buildContextEngineCompactionSessionTarget } from "./run/session-bootstrap.js"; | |
| const compactionTempDirs = useAutoCleanupTempDirTracker(afterEach); | |
| describe("resolveCompactionContextTokenBudget", () => { | |
| const cfg = {} as OpenClawConfig; | |
| const modelWithWindow = (contextWindow: number) => | |
| ({ contextWindow }) as Parameters<typeof resolveCompactionContextTokenBudget>[0]["model"]; | |
| it.each([ | |
| { requested: 500_000, modelWindow: 500_000, expected: 500_000 }, | |
| { requested: 100_000, modelWindow: 500_000, expected: 100_000 }, | |
| { requested: 500_000, modelWindow: 64_000, expected: 64_000 }, | |
| ])( | |
| "caps requested=$requested by the model ceiling to $expected", | |
| ({ requested, modelWindow, expected }) => { | |
| const budget = resolveCompactionContextTokenBudget({ | |
| config: cfg, | |
| provider: "openai", | |
| modelId: "mock-model", | |
| model: modelWithWindow(modelWindow), | |
| requestedTokenBudget: requested, | |
| }); | |
| expect(budget).toBe(expected); | |
| }, | |
| ); | |
| it.each([ | |
| { requested: 16_000, expected: 3_000 }, | |
| { requested: 2_000, expected: 2_000 }, | |
| ])( | |
| "caps requested=$requested by the authored native window despite a larger contextTokens cap", | |
| ({ requested, expected }) => { | |
| const budget = resolveCompactionContextTokenBudget({ | |
| config: { | |
| models: { | |
| providers: { | |
| custom: { | |
| baseUrl: "https://models.example.test/v1", | |
| models: [ | |
| { | |
| id: "tiny-model", | |
| name: "Tiny model", | |
| reasoning: false, | |
| input: ["text"], | |
| cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, | |
| contextWindow: 3_000, | |
| contextTokens: 16_000, | |
| maxTokens: 256, | |
| }, | |
| ], | |
| }, | |
| }, | |
| }, | |
| }, | |
| provider: "custom", | |
| modelId: "tiny-model", | |
| model: modelWithWindow(3_000), | |
| requestedTokenBudget: requested, | |
| }); | |
| expect(budget).toBe(expected); | |
| }, | |
| ); | |
| }); | |
| describe("resolveEmbeddedCompactionThinkingLevel", () => { | |
| it.each([ | |
| { configured: undefined, inherited: "high", expected: "off" }, | |
| { configured: "low", inherited: "high", expected: "low" }, | |
| { configured: "inherit", inherited: "high", expected: "high" }, | |
| { configured: "inherit", inherited: undefined, expected: "off" }, | |
| ] as const)( | |
| "uses the prepared default only when compaction thinking is unset ($configured)", | |
| ({ configured, inherited, expected }) => { | |
| expect( | |
| resolveEmbeddedCompactionThinkingLevel({ | |
| config: configured | |
| ? { agents: { defaults: { compaction: { thinkingLevel: configured } } } } | |
| : {}, | |
| provider: "demo", | |
| modelId: "demo-model", | |
| inheritedLevel: inherited, | |
| compactionThinkingDefault: "off", | |
| }), | |
| ).toBe(expected); | |
| }, | |
| ); | |
| it("lets the compaction override replace the inherited session level", () => { | |
| expect( | |
| resolveEmbeddedCompactionThinkingLevel({ | |
| config: { | |
| agents: { defaults: { compaction: { thinkingLevel: "low" } } }, | |
| }, | |
| provider: "demo", | |
| modelId: "demo-model", | |
| inheritedLevel: "high", | |
| }), | |
| ).toBe("low"); | |
| }); | |
| it("revalidates an unsupported configured level for the actual candidate", () => { | |
| expect( | |
| resolveEmbeddedCompactionThinkingLevel({ | |
| config: { | |
| agents: { defaults: { compaction: { thinkingLevel: "ultra" } } }, | |
| }, | |
| provider: "demo", | |
| modelId: "demo-model", | |
| }), | |
| ).toBe("high"); | |
| }); | |
| it("revalidates the compaction default against provider-denied thinking levels", () => { | |
| expect( | |
| resolveEmbeddedCompactionThinkingLevel({ | |
| provider: "custom", | |
| modelId: "reasoning-model", | |
| catalog: [ | |
| { | |
| provider: "custom", | |
| id: "reasoning-model", | |
| reasoning: true, | |
| thinkingLevelMap: { minimal: null, low: null, medium: null }, | |
| }, | |
| ], | |
| }), | |
| ).toBe("high"); | |
| }); | |
| it("defaults compaction to low without inheriting the session level", () => { | |
| expect( | |
| resolveEmbeddedCompactionThinkingLevel({ | |
| provider: "demo", | |
| modelId: "demo-model", | |
| inheritedLevel: "medium", | |
| }), | |
| ).toBe("low"); | |
| expect( | |
| resolveEmbeddedCompactionThinkingLevel({ | |
| provider: "demo", | |
| modelId: "demo-model", | |
| }), | |
| ).toBe("low"); | |
| }); | |
| it("inherits the session level only when explicitly configured", () => { | |
| expect( | |
| resolveEmbeddedCompactionThinkingLevel({ | |
| config: { | |
| agents: { defaults: { compaction: { thinkingLevel: "inherit" } } }, | |
| } as unknown as OpenClawConfig, | |
| provider: "demo", | |
| modelId: "demo-model", | |
| inheritedLevel: "medium", | |
| }), | |
| ).toBe("medium"); | |
| }); | |
| it("preserves thinking when the resolved Ollama model reports reasoning support", () => { | |
| expect( | |
| resolveEmbeddedCompactionThinkingLevel({ | |
| config: { | |
| agents: { defaults: { compaction: { thinkingLevel: "inherit" } } }, | |
| }, | |
| provider: "ollama", | |
| modelId: "qwen3.5:4b", | |
| inheritedLevel: "high", | |
| catalog: [{ provider: "ollama", id: "qwen3.5:4b", reasoning: true }], | |
| }), | |
| ).toBe("high"); | |
| }); | |
| }); | |
| describe("buildEmbeddedCompactionRuntimeContext", () => { | |
| it("preserves sender and current message routing for compaction", () => { | |
| const result = buildEmbeddedCompactionRuntimeContext({ | |
| sessionKey: "agent:main:thread:1", | |
| pinnedWidgetAuthoring: true, | |
| messageChannel: "slack", | |
| messageProvider: "slack", | |
| chatType: "channel", | |
| agentAccountId: "acct-1", | |
| conversationRoutePeerId: "peer", | |
| currentChannelId: "C123", | |
| currentThreadTs: "thread-9", | |
| currentMessageId: "msg-42", | |
| authProfileId: "openai:p1", | |
| workspaceDir: "/tmp/workspace", | |
| cwd: "/tmp/task-repo", | |
| requireWorkspaceOnly: true, | |
| requireWritableSandbox: true, | |
| agentDir: "/tmp/agent", | |
| config: {} as unknown as OpenClawConfig, | |
| senderIsOwner: true, | |
| senderId: "user-123", | |
| provider: "openai", | |
| modelId: "gpt-5.4", | |
| thinkLevel: "off", | |
| reasoningLevel: "on", | |
| extraSystemPrompt: "extra", | |
| ownerNumbers: ["+15555550123"], | |
| }); | |
| expect(result.sessionKey).toBe("agent:main:thread:1"); | |
| expect(result.pinnedWidgetAuthoring).toBe(true); | |
| expect(result.messageChannel).toBe("slack"); | |
| expect(result.messageProvider).toBe("slack"); | |
| expect(result.chatType).toBe("channel"); | |
| expect(result.agentAccountId).toBe("acct-1"); | |
| expect(result.conversationRoutePeerId).toBe("peer"); | |
| expect(result.currentChannelId).toBe("C123"); | |
| expect(result.currentThreadTs).toBe("thread-9"); | |
| expect(result.currentMessageId).toBe("msg-42"); | |
| expect(result.authProfileId).toBe("openai:p1"); | |
| expect(result.workspaceDir).toBe("/tmp/workspace"); | |
| expect(result.cwd).toBe("/tmp/task-repo"); | |
| expect(result.requireWorkspaceOnly).toBe(true); | |
| expect(result.requireWritableSandbox).toBe(true); | |
| expect(result.agentDir).toBe("/tmp/agent"); | |
| expect(result.senderIsOwner).toBe(true); | |
| expect(result.senderId).toBe("user-123"); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.model).toBe("gpt-5.4"); | |
| }); | |
| it("preserves the finite tool allowlist for delegated compaction", () => { | |
| const result = buildEmbeddedCompactionRuntimeContext({ | |
| workspaceDir: "/tmp/workspace", | |
| agentDir: "/tmp/agent", | |
| provider: "openai", | |
| modelId: "gpt-5.4", | |
| toolsAllow: ["read"], | |
| }); | |
| expect(result.toolsAllow).toEqual(["read"]); | |
| }); | |
| it("normalizes nullable compaction routing fields to undefined", () => { | |
| const result = buildEmbeddedCompactionRuntimeContext({ | |
| sessionKey: null, | |
| messageChannel: null, | |
| messageProvider: null, | |
| chatType: null, | |
| agentAccountId: null, | |
| currentChannelId: null, | |
| currentThreadTs: null, | |
| currentMessageId: null, | |
| authProfileId: null, | |
| workspaceDir: "/tmp/workspace", | |
| agentDir: "/tmp/agent", | |
| senderId: null, | |
| provider: null, | |
| modelId: null, | |
| }); | |
| expect(result.sessionKey).toBeUndefined(); | |
| expect(result.messageChannel).toBeUndefined(); | |
| expect(result.messageProvider).toBeUndefined(); | |
| expect(result.chatType).toBeUndefined(); | |
| expect(result.agentAccountId).toBeUndefined(); | |
| expect(result.currentChannelId).toBeUndefined(); | |
| expect(result.currentThreadTs).toBeUndefined(); | |
| expect(result.currentMessageId).toBeUndefined(); | |
| expect(result.authProfileId).toBeUndefined(); | |
| expect(result.senderId).toBeUndefined(); | |
| expect(result.provider).toBeUndefined(); | |
| expect(result.model).toBeUndefined(); | |
| }); | |
| it("applies compaction.model override with provider/model format", () => { | |
| const result = buildEmbeddedCompactionRuntimeContext({ | |
| workspaceDir: "/tmp/workspace", | |
| agentDir: "/tmp/agent", | |
| config: { | |
| agents: { defaults: { compaction: { model: "anthropic/claude-opus-4-6" } } }, | |
| } as unknown as OpenClawConfig, | |
| provider: "ollama", | |
| modelId: "minimax-m2.7:cloud", | |
| authProfileId: "ollama:default", | |
| }); | |
| expect(result.provider).toBe("anthropic"); | |
| expect(result.model).toBe("claude-opus-4-6"); | |
| // Auth profile must be dropped when provider changes; otherwise compaction | |
| // could send a stale credential to the override provider. | |
| expect(result.authProfileId).toBeUndefined(); | |
| }); | |
| it("applies compaction.model override with model-only format", () => { | |
| const result = buildEmbeddedCompactionRuntimeContext({ | |
| workspaceDir: "/tmp/workspace", | |
| agentDir: "/tmp/agent", | |
| config: { | |
| agents: { defaults: { compaction: { model: "gpt-4o" } } }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-3.5-turbo", | |
| authProfileId: "openai:p1", | |
| }); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.model).toBe("gpt-4o"); | |
| // Auth profile preserved because provider didn't change | |
| expect(result.authProfileId).toBe("openai:p1"); | |
| }); | |
| it.each([ | |
| { name: "without configured aliases", models: undefined }, | |
| { | |
| name: "with an unrelated configured alias", | |
| models: { "openai/gpt-5.4-mini": { alias: "fast" } }, | |
| }, | |
| ])( | |
| "resolves literal compaction overrides without discovering provider plugins $name", | |
| ({ models }) => { | |
| const manifestNormalization = vi | |
| .spyOn(manifestModelIdNormalization, "resolveManifestModelIdNormalizationPolicies") | |
| .mockImplementation(() => { | |
| throw new Error("literal compaction overrides must not discover plugin manifests"); | |
| }); | |
| const runtimeNormalization = vi | |
| .spyOn(providerModelNormalizationRuntime, "normalizeProviderModelIdWithRuntime") | |
| .mockImplementation(() => { | |
| throw new Error("literal compaction overrides must not activate provider plugins"); | |
| }); | |
| try { | |
| const result = buildEmbeddedCompactionRuntimeContext({ | |
| workspaceDir: "/tmp/workspace", | |
| agentDir: "/tmp/agent", | |
| config: { | |
| agents: { | |
| defaults: { | |
| ...(models ? { models } : {}), | |
| compaction: { model: "gpt-4o" }, | |
| }, | |
| }, | |
| } as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-3.5-turbo", | |
| authProfileId: "openai:p1", | |
| }); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.model).toBe("gpt-4o"); | |
| expect(result.authProfileId).toBe("openai:p1"); | |
| expect(manifestNormalization).not.toHaveBeenCalled(); | |
| expect(runtimeNormalization).not.toHaveBeenCalled(); | |
| } finally { | |
| runtimeNormalization.mockRestore(); | |
| manifestNormalization.mockRestore(); | |
| } | |
| }, | |
| ); | |
| it("uses session model when no compaction.model override configured", () => { | |
| const result = buildEmbeddedCompactionRuntimeContext({ | |
| workspaceDir: "/tmp/workspace", | |
| agentDir: "/tmp/agent", | |
| config: {} as unknown as OpenClawConfig, | |
| provider: "ollama", | |
| modelId: "minimax-m2.7:cloud", | |
| authProfileId: "ollama:default", | |
| }); | |
| expect(result.provider).toBe("ollama"); | |
| expect(result.model).toBe("minimax-m2.7:cloud"); | |
| expect(result.authProfileId).toBe("ollama:default"); | |
| }); | |
| it("preserves scoped active process session references for compaction", () => { | |
| // Only sessions tied to the same scope are summarized; cross-session process | |
| // state would leak unrelated task context into the compaction prompt. | |
| const scopeKey = "agent:main:compaction-runtime-context"; | |
| const startedAt = Date.now() - 1_000; | |
| const active = createProcessSessionFixture({ | |
| id: "compaction-runtime-active", | |
| command: "sleep 600", | |
| backgrounded: true, | |
| pid: 1234, | |
| startedAt, | |
| }); | |
| active.scopeKey = scopeKey; | |
| const other = createProcessSessionFixture({ | |
| id: "compaction-runtime-other", | |
| command: "sleep 600", | |
| backgrounded: true, | |
| }); | |
| other.scopeKey = "agent:other"; | |
| addSession(active); | |
| addSession(other); | |
| try { | |
| const result = buildEmbeddedCompactionRuntimeContext({ | |
| sessionKey: scopeKey, | |
| workspaceDir: "/tmp/workspace", | |
| agentDir: "/tmp/agent", | |
| config: {} as unknown as OpenClawConfig, | |
| }); | |
| expect(result.activeProcessSessions).toEqual([ | |
| { | |
| command: "sleep 600", | |
| cwd: "/tmp", | |
| name: "sleep 600", | |
| pid: 1234, | |
| runtimeMs: expect.any(Number), | |
| sessionId: "compaction-runtime-active", | |
| startedAt, | |
| status: "running", | |
| tail: "", | |
| truncated: false, | |
| }, | |
| ]); | |
| } finally { | |
| deleteSession(active.id); | |
| deleteSession(other.id); | |
| } | |
| }); | |
| it("omits active process session references when no safe scope is available", () => { | |
| const result = buildEmbeddedCompactionRuntimeContext({ | |
| workspaceDir: "/tmp/workspace", | |
| agentDir: "/tmp/agent", | |
| config: {} as unknown as OpenClawConfig, | |
| }); | |
| expect(result.activeProcessSessions).toBeUndefined(); | |
| }); | |
| it("applies runtime defaults when resolving the effective compaction target", () => { | |
| expect( | |
| resolveEmbeddedCompactionTarget({ | |
| config: { | |
| agents: { defaults: { compaction: { model: "anthropic/" } } }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-5.4", | |
| authProfileId: "openai:p1", | |
| defaultProvider: "openai", | |
| defaultModel: "gpt-5.4", | |
| }), | |
| ).toEqual({ | |
| provider: "anthropic", | |
| model: "gpt-5.4", | |
| authProfileId: undefined, | |
| }); | |
| }); | |
| it("ignores compaction model overrides for model-locked sessions", () => { | |
| expect( | |
| resolveEmbeddedCompactionTarget({ | |
| config: { | |
| agents: { defaults: { compaction: { model: "anthropic/claude-opus-4-6" } } }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| authProfileId: "openai:default", | |
| modelSelectionLocked: true, | |
| }), | |
| ).toEqual({ | |
| provider: "openai", | |
| model: "gpt-5.5", | |
| authProfileId: "openai:default", | |
| }); | |
| }); | |
| it("keeps configured OpenAI provider with legacy Codex auth profiles (#86373)", () => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| provider: "openai", | |
| modelId: "gpt-5.4", | |
| authProfileId: "openai:default", | |
| defaultProvider: "openai", | |
| defaultModel: "gpt-5.4", | |
| }); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.runtimeProvider).toBeUndefined(); | |
| expect(result.contextProvider).toBeUndefined(); | |
| expect(result.model).toBe("gpt-5.4"); | |
| expect(result.authProfileId).toBe("openai:default"); | |
| }); | |
| it("keeps openai auth order with Codex profile on canonical OpenAI", () => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| config: { | |
| auth: { order: { openai: ["openai:default"] } }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| defaultProvider: "openai", | |
| defaultModel: "gpt-5.5", | |
| }); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.runtimeProvider).toBeUndefined(); | |
| expect(result.contextProvider).toBeUndefined(); | |
| expect(result.model).toBe("gpt-5.5"); | |
| expect(result.authProfileId).toBeUndefined(); | |
| }); | |
| it("keeps Codex-runtime OpenAI compaction on the canonical OpenAI provider", () => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| config: { | |
| models: { | |
| providers: { | |
| openai: { models: [{ id: "gpt-5.5" }] }, | |
| }, | |
| }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| harnessRuntime: "codex", | |
| defaultProvider: "openai", | |
| defaultModel: "gpt-5.5", | |
| }); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.runtimeProvider).toBeUndefined(); | |
| expect(result.contextProvider).toBeUndefined(); | |
| expect(result.nativeHarnessCompaction).toBe(true); | |
| expect(result.model).toBe("gpt-5.5"); | |
| expect(result.authProfileId).toBeUndefined(); | |
| }); | |
| it("carries the selected harness id for delegated runtime compaction", () => { | |
| const result = buildEmbeddedCompactionRuntimeContext({ | |
| workspaceDir: "/tmp/workspace", | |
| agentDir: "/tmp/agent", | |
| config: { | |
| agents: { defaults: { compaction: { model: "anthropic/claude-opus-4-6" } } }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| harnessRuntime: "codex", | |
| modelSelectionLocked: true, | |
| }); | |
| expect(result.agentHarnessId).toBe("codex"); | |
| expect(result.modelSelectionLocked).toBe(true); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.model).toBe("gpt-5.5"); | |
| expect(result.runtimeProvider).toBeUndefined(); | |
| }); | |
| it("carries only a target-matching prepared auth plan into compaction context", () => { | |
| const runtimeAuthPlan = { | |
| providerForAuth: "openai", | |
| modelId: "gpt-5.5", | |
| authProfileProviderForAuth: "openai", | |
| forwardedAuthProfileId: "openai:work", | |
| forwardedAuthProfileSource: "user", | |
| modelRoute: { | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| api: "openai-responses", | |
| baseUrl: "https://api.openai.com/v1", | |
| authRequirement: "api-key", | |
| requestTransportOverrides: "none", | |
| }, | |
| } as const; | |
| const matching = buildEmbeddedCompactionRuntimeContext({ | |
| workspaceDir: "/tmp/workspace", | |
| agentDir: "/tmp/agent", | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| authProfileId: "openai:work", | |
| authProfileIdSource: "user", | |
| runtimeAuthPlan, | |
| }); | |
| const mismatched = buildEmbeddedCompactionRuntimeContext({ | |
| workspaceDir: "/tmp/workspace", | |
| agentDir: "/tmp/agent", | |
| provider: "openai", | |
| modelId: "gpt-5.4", | |
| runtimeAuthPlan, | |
| }); | |
| expect(matching.runtimeAuthPlan).toBe(runtimeAuthPlan); | |
| expect(matching.authProfileIdSource).toBe("user"); | |
| expect(mismatched.runtimeAuthPlan).toBeUndefined(); | |
| }); | |
| it("resolves compaction harness ownership from bound, prepared, then configured facts", () => { | |
| const preparedRuntimePlan = { | |
| resolvedRef: { | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| harnessId: "codex", | |
| }, | |
| auth: { | |
| providerForAuth: "openai", | |
| modelId: "gpt-5.5", | |
| authProfileProviderForAuth: "openai", | |
| }, | |
| } as never; | |
| expect( | |
| resolveCompactionHarnessRuntime({ | |
| boundHarnessRuntime: "copilot", | |
| preparedRuntimePlan, | |
| configuredHarnessRuntime: "custom", | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| }), | |
| ).toBe("copilot"); | |
| expect( | |
| resolveCompactionHarnessRuntime({ | |
| preparedRuntimePlan, | |
| configuredHarnessRuntime: "custom", | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| }), | |
| ).toBe("codex"); | |
| expect( | |
| resolveCompactionHarnessRuntime({ | |
| preparedRuntimePlan, | |
| configuredHarnessRuntime: "custom", | |
| provider: "openai", | |
| modelId: "gpt-5.4", | |
| }), | |
| ).toBe("custom"); | |
| }); | |
| it("preserves direct OpenAI compaction for the OpenClaw runtime", () => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| config: { | |
| models: { | |
| providers: { | |
| openai: { models: [{ id: "gpt-5.5" }] }, | |
| }, | |
| }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| harnessRuntime: "openclaw", | |
| defaultProvider: "openai", | |
| defaultModel: "gpt-5.5", | |
| }); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.runtimeProvider).toBeUndefined(); | |
| expect(result.contextProvider).toBeUndefined(); | |
| expect(result.model).toBe("gpt-5.5"); | |
| expect(result.authProfileId).toBeUndefined(); | |
| }); | |
| it.each([ | |
| { selection: "implicit OpenClaw", harnessRuntime: undefined, nativeCompaction: undefined }, | |
| { selection: "bound OpenClaw", harnessRuntime: "openclaw", nativeCompaction: undefined }, | |
| { selection: "bound Codex", harnessRuntime: "codex", nativeCompaction: true }, | |
| ])("keeps $selection ownership for custom OpenAI Responses compaction", (fixture) => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| config: { | |
| models: { | |
| providers: { | |
| openai: { | |
| api: "openai-responses", | |
| baseUrl: "https://example.test/v1", | |
| models: [{ id: "gpt-5.5" }], | |
| }, | |
| }, | |
| }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| harnessRuntime: fixture.harnessRuntime, | |
| defaultProvider: "openai", | |
| defaultModel: "gpt-5.5", | |
| }); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.runtimeProvider).toBeUndefined(); | |
| expect(result.contextProvider).toBeUndefined(); | |
| expect(result.nativeHarnessCompaction).toBe(fixture.nativeCompaction); | |
| expect(result.model).toBe("gpt-5.5"); | |
| expect(result.authProfileId).toBeUndefined(); | |
| }); | |
| it("keeps a locked Codex harness authoritative over a custom OpenAI base URL", () => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| config: { | |
| models: { | |
| providers: { | |
| openai: { | |
| baseUrl: "https://example.test/v1", | |
| models: [{ id: "gpt-5.5" }], | |
| }, | |
| }, | |
| }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| harnessRuntime: "codex", | |
| modelSelectionLocked: true, | |
| defaultProvider: "openai", | |
| defaultModel: "gpt-5.5", | |
| }); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.runtimeProvider).toBeUndefined(); | |
| expect(result.contextProvider).toBeUndefined(); | |
| expect(result.nativeHarnessCompaction).toBe(true); | |
| expect(result.model).toBe("gpt-5.5"); | |
| }); | |
| it("keeps model-only compaction overrides with legacy Codex auth on OpenAI", () => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| config: { | |
| agents: { defaults: { compaction: { model: "gpt-5.4" } } }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| authProfileId: "openai:default", | |
| defaultProvider: "openai", | |
| defaultModel: "gpt-5.5", | |
| }); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.runtimeProvider).toBeUndefined(); | |
| expect(result.contextProvider).toBeUndefined(); | |
| expect(result.model).toBe("gpt-5.4"); | |
| expect(result.authProfileId).toBe("openai:default"); | |
| }); | |
| it("keeps openai compaction overrides with legacy Codex auth on OpenAI", () => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| config: { | |
| agents: { defaults: { compaction: { model: "openai/gpt-5.4" } } }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| authProfileId: "openai:default", | |
| defaultProvider: "openai", | |
| defaultModel: "gpt-5.5", | |
| }); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.runtimeProvider).toBeUndefined(); | |
| expect(result.contextProvider).toBeUndefined(); | |
| expect(result.model).toBe("gpt-5.4"); | |
| expect(result.authProfileId).toBe("openai:default"); | |
| }); | |
| it("keeps OpenAI compaction model overrides on canonical OpenAI with Codex runtime", () => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| config: { | |
| models: { | |
| providers: { | |
| openai: { models: [{ id: "gpt-5.5" }, { id: "gpt-5.4-mini" }] }, | |
| }, | |
| }, | |
| agents: { defaults: { compaction: { model: "openai/gpt-5.4-mini" } } }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| harnessRuntime: "codex", | |
| defaultProvider: "openai", | |
| defaultModel: "gpt-5.5", | |
| }); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.runtimeProvider).toBeUndefined(); | |
| expect(result.contextProvider).toBeUndefined(); | |
| expect(result.nativeHarnessCompaction).toBe(true); | |
| expect(result.model).toBe("gpt-5.4-mini"); | |
| expect(result.authProfileId).toBeUndefined(); | |
| }); | |
| it("resolves a mixed-case compaction model alias with a trailing profile on its provider", () => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| config: { | |
| agents: { | |
| defaults: { | |
| models: { | |
| "openai/gpt-5.4-mini": { | |
| alias: "gpt54mini", | |
| params: { thinking: "high" }, | |
| }, | |
| }, | |
| compaction: { model: "GPT54MINI@work" }, | |
| }, | |
| }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| authProfileId: "openai:default", | |
| defaultProvider: "openai", | |
| defaultModel: "gpt-5.5", | |
| }); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.model).toBe("gpt-5.4-mini"); | |
| expect(result.authProfileId).toBe("openai:default"); | |
| }); | |
| it("resolves compaction.model alias to canonical model ref on different provider", () => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| config: { | |
| agents: { | |
| defaults: { | |
| models: { | |
| "anthropic/claude-opus-4-6": { | |
| alias: "thinky", | |
| }, | |
| }, | |
| compaction: { model: "thinky" }, | |
| }, | |
| }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| authProfileId: "openai:default", | |
| defaultProvider: "openai", | |
| defaultModel: "gpt-5.5", | |
| }); | |
| expect(result.provider).toBe("anthropic"); | |
| expect(result.model).toBe("claude-opus-4-6"); | |
| // Auth profile must be dropped when provider changes | |
| expect(result.authProfileId).toBeUndefined(); | |
| }); | |
| it("preserves the full literal model and profile when no configured alias matches", () => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| config: { | |
| agents: { | |
| defaults: { | |
| models: { | |
| "openai/gpt-5.4-mini": { | |
| alias: "gpt54mini", | |
| }, | |
| }, | |
| compaction: { model: "nonexistent-alias@work" }, | |
| }, | |
| }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| authProfileId: "openai:default", | |
| defaultProvider: "openai", | |
| defaultModel: "gpt-5.5", | |
| }); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.model).toBe("nonexistent-alias@work"); | |
| expect(result.authProfileId).toBe("openai:default"); | |
| }); | |
| it("preserves auth when an omitted provider uses the effective default", () => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| config: { | |
| agents: { | |
| defaults: { | |
| models: { | |
| "openai/gpt-5.4-mini": { | |
| alias: "summary", | |
| }, | |
| }, | |
| compaction: { model: "summary" }, | |
| }, | |
| }, | |
| } as unknown as OpenClawConfig, | |
| authProfileId: "openai:default", | |
| defaultProvider: "openai", | |
| defaultModel: "gpt-5.5", | |
| }); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.model).toBe("gpt-5.4-mini"); | |
| expect(result.authProfileId).toBe("openai:default"); | |
| }); | |
| it("prefers literal configured model ids over alias collisions (#90340)", () => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| config: { | |
| agents: { | |
| defaults: { | |
| models: { | |
| "openai/gpt-5.4-mini": { | |
| alias: "gpt54mini", | |
| }, | |
| "openai/gpt54mini": {}, | |
| }, | |
| compaction: { model: "gpt54mini" }, | |
| }, | |
| }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| authProfileId: "openai:default", | |
| defaultProvider: "openai", | |
| defaultModel: "gpt-5.5", | |
| }); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.model).toBe("gpt54mini"); | |
| expect(result.authProfileId).toBe("openai:default"); | |
| }); | |
| it("keeps current-provider configured model ids over cross-provider alias collisions (#90340)", () => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| config: { | |
| agents: { | |
| defaults: { | |
| models: { | |
| "anthropic/claude-opus-4-6": { | |
| alias: "gpt-5.4-mini", | |
| }, | |
| }, | |
| compaction: { model: "gpt-5.4-mini" }, | |
| }, | |
| }, | |
| models: { | |
| providers: { | |
| openai: { models: [{ id: "gpt-5.4-mini" }] }, | |
| }, | |
| }, | |
| } as unknown as OpenClawConfig, | |
| provider: "openai", | |
| modelId: "gpt-5.5", | |
| authProfileId: "openai:default", | |
| defaultProvider: "openai", | |
| defaultModel: "gpt-5.5", | |
| }); | |
| expect(result.provider).toBe("openai"); | |
| expect(result.model).toBe("gpt-5.4-mini"); | |
| expect(result.authProfileId).toBe("openai:default"); | |
| }); | |
| it.each([ | |
| { | |
| name: "infers a different provider for a uniquely configured bare literal", | |
| config: { | |
| models: { | |
| providers: { | |
| anthropic: { models: [{ id: "compact-model" }] }, | |
| }, | |
| }, | |
| agents: { defaults: { compaction: { model: "compact-model" } } }, | |
| }, | |
| provider: "openai", | |
| authProfileId: "openai:default", | |
| expectedProvider: "anthropic", | |
| expectedModel: "compact-model", | |
| expectedAuthProfileId: undefined, | |
| }, | |
| { | |
| name: "keeps an ambiguous configured bare literal on the current provider", | |
| config: { | |
| models: { | |
| providers: { | |
| openai: { models: [{ id: "shared-model" }] }, | |
| anthropic: { models: [{ id: "shared-model" }] }, | |
| }, | |
| }, | |
| agents: { defaults: { compaction: { model: "shared-model" } } }, | |
| }, | |
| provider: "google", | |
| authProfileId: "google:default", | |
| expectedProvider: "google", | |
| expectedModel: "shared-model", | |
| expectedAuthProfileId: "google:default", | |
| }, | |
| { | |
| name: "preserves a multi-segment model id and trailing profile suffix", | |
| config: { | |
| agents: { | |
| defaults: { | |
| compaction: { model: "openrouter/meta-llama/llama-3.3-70b:free@work" }, | |
| }, | |
| }, | |
| }, | |
| provider: "openrouter", | |
| authProfileId: "openrouter:default", | |
| expectedProvider: "openrouter", | |
| expectedModel: "meta-llama/llama-3.3-70b:free@work", | |
| expectedAuthProfileId: "openrouter:default", | |
| }, | |
| ])("$name", (fixture) => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| config: fixture.config as unknown as OpenClawConfig, | |
| provider: fixture.provider, | |
| modelId: "current-model", | |
| authProfileId: fixture.authProfileId, | |
| defaultProvider: fixture.provider, | |
| defaultModel: "current-model", | |
| }); | |
| expect(result.provider).toBe(fixture.expectedProvider); | |
| expect(result.model).toBe(fixture.expectedModel); | |
| expect(result.authProfileId).toBe(fixture.expectedAuthProfileId); | |
| }); | |
| it("leaves non-openai providers unchanged", () => { | |
| const result = resolveEmbeddedCompactionTarget({ | |
| provider: "anthropic", | |
| modelId: "claude-opus-4-5", | |
| authProfileId: "anthropic:default", | |
| defaultProvider: "anthropic", | |
| defaultModel: "claude-opus-4-5", | |
| }); | |
| expect(result.provider).toBe("anthropic"); | |
| }); | |
| }); | |
| describe("buildContextEngineCompactionSessionTarget", () => { | |
| it("derives the agent from a scoped session key", () => { | |
| expect( | |
| buildContextEngineCompactionSessionTarget({ | |
| config: { session: { store: "/tmp/agents/{agentId}/sessions.json" } }, | |
| sessionFile: "agent:helper:main", | |
| sessionId: "helper-session", | |
| sessionKey: "agent:helper:main", | |
| }), | |
| ).toMatchObject({ | |
| agentId: "helper", | |
| sessionKey: "agent:helper:main", | |
| storePath: "/tmp/agents/helper/sessions.json", | |
| }); | |
| }); | |
| it("leaves the key absent when a marker store has no mapped row", () => { | |
| const storePath = path.join(compactionTempDirs.make("compaction-marker-"), "sessions.json"); | |
| const sessionId = "legacy-unmapped-session"; | |
| expect( | |
| buildContextEngineCompactionSessionTarget({ | |
| sessionFile: formatSqliteSessionFileMarker({ agentId: "main", sessionId, storePath }), | |
| sessionId, | |
| }), | |
| ).toEqual({ agentId: "main", sessionId, storePath }); | |
| }); | |
| }); | |