From a8066ad96d0172030a93db3def0a0b09be3da49f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 27 Mar 2026 06:17:01 +0000 Subject: [PATCH] fix: align skills and compaction api drift --- src/agents/compaction.retry.test.ts | 2 +- src/agents/compaction.ts | 1 + .../pi-extensions/compaction-safeguard.ts | 20 +++++++++++++++++-- src/agents/skills-install.download.test.ts | 5 ++++- src/agents/skills-status.test.ts | 3 ++- .../skills.buildworkspaceskillstatus.test.ts | 5 ++++- .../skills.resolveskillspromptforrun.test.ts | 5 ++++- src/agents/skills/compact-format.test.ts | 8 ++++++-- src/agents/skills/source.ts | 2 +- src/cli/skills-cli.formatting.test.ts | 5 ++++- test/helpers/extensions/plugin-sdk-stub.cjs | 8 ++++---- 11 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/agents/compaction.retry.test.ts b/src/agents/compaction.retry.test.ts index 31404e2e9b2..30f81ca6664 100644 --- a/src/agents/compaction.retry.test.ts +++ b/src/agents/compaction.retry.test.ts @@ -56,7 +56,7 @@ describe("compaction retry integration", () => { } as unknown as NonNullable; const invokeGenerateSummary = (signal = new AbortController().signal) => - mockGenerateSummary(testMessages, testModel, 1000, "test-api-key", signal); + mockGenerateSummary(testMessages, testModel, 1000, "test-api-key", undefined, signal); const runSummaryRetry = (options: Parameters[1]) => retryAsync(() => invokeGenerateSummary(), options); diff --git a/src/agents/compaction.ts b/src/agents/compaction.ts index 20a6d80fa43..0a9f93f81d8 100644 --- a/src/agents/compaction.ts +++ b/src/agents/compaction.ts @@ -257,6 +257,7 @@ async function summarizeChunks(params: { model, params.reserveTokens, params.apiKey, + model.headers, params.signal, effectiveInstructions, summary, diff --git a/src/agents/pi-extensions/compaction-safeguard.ts b/src/agents/pi-extensions/compaction-safeguard.ts index ee136dfbd00..8e2d59d5aa2 100644 --- a/src/agents/pi-extensions/compaction-safeguard.ts +++ b/src/agents/pi-extensions/compaction-safeguard.ts @@ -614,11 +614,27 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void { return { cancel: true }; } - const headers = + const modelHeaders = model.headers && typeof model.headers === "object" && !Array.isArray(model.headers) ? model.headers : undefined; - const apiKey = (await ctx.modelRegistry.getApiKey(model)) ?? ""; + const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model); + const headersRecord = { + ...modelHeaders, + ...(auth.ok ? auth.headers : undefined), + }; + const headers = Object.keys(headersRecord).length > 0 ? headersRecord : undefined; + const apiKey = auth.ok ? (auth.apiKey ?? "") : ""; + if (!auth.ok && !headers) { + log.warn( + `Compaction safeguard: request auth unavailable; cancelling compaction. ${auth.error}`, + ); + setCompactionSafeguardCancelReason( + ctx.sessionManager, + `Compaction safeguard could not resolve request auth for ${model.provider}/${model.id}: ${auth.error}`, + ); + return { cancel: true }; + } if (!apiKey && !headers) { log.warn( "Compaction safeguard: no request auth available; cancelling compaction to preserve history.", diff --git a/src/agents/skills-install.download.test.ts b/src/agents/skills-install.download.test.ts index bd8559b60fe..d61bd115d08 100644 --- a/src/agents/skills-install.download.test.ts +++ b/src/agents/skills-install.download.test.ts @@ -1,6 +1,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +import { createSyntheticSourceInfo } from "@mariozechner/pi-coding-agent"; import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { installDownloadSpec } from "./skills-install-download.js"; import { setTempStateDir } from "./skills-install.download-test-utils.js"; @@ -60,7 +61,9 @@ function buildEntry(name: string): SkillEntry { description: `${name} test skill`, filePath: path.join(skillDir, "SKILL.md"), baseDir: skillDir, - source: "openclaw-workspace", + sourceInfo: createSyntheticSourceInfo(path.join(skillDir, "SKILL.md"), { + source: "openclaw-workspace", + }), disableModelInvocation: false, }, frontmatter: {}, diff --git a/src/agents/skills-status.test.ts b/src/agents/skills-status.test.ts index 3e0b2ab6783..0d5f9ab42ea 100644 --- a/src/agents/skills-status.test.ts +++ b/src/agents/skills-status.test.ts @@ -1,3 +1,4 @@ +import { createSyntheticSourceInfo } from "@mariozechner/pi-coding-agent"; import { describe, expect, it } from "vitest"; import { buildWorkspaceSkillStatus } from "./skills-status.js"; import type { SkillEntry } from "./skills/types.js"; @@ -17,7 +18,7 @@ describe("buildWorkspaceSkillStatus", () => { description: "test", filePath: "/tmp/os-scoped", baseDir: "/tmp", - source: "test", + sourceInfo: createSyntheticSourceInfo("/tmp/os-scoped", { source: "test" }), disableModelInvocation: false, }, frontmatter: {}, diff --git a/src/agents/skills.buildworkspaceskillstatus.test.ts b/src/agents/skills.buildworkspaceskillstatus.test.ts index 4b3cca8808f..fddf6875d34 100644 --- a/src/agents/skills.buildworkspaceskillstatus.test.ts +++ b/src/agents/skills.buildworkspaceskillstatus.test.ts @@ -1,3 +1,4 @@ +import { createSyntheticSourceInfo } from "@mariozechner/pi-coding-agent"; import { describe, expect, it } from "vitest"; import { withEnv } from "../test-utils/env.js"; import { buildWorkspaceSkillStatus } from "./skills-status.js"; @@ -24,7 +25,9 @@ function makeEntry(params: { description: `desc:${params.name}`, filePath: `/tmp/${params.name}/SKILL.md`, baseDir: `/tmp/${params.name}`, - source: params.source ?? "openclaw-workspace", + sourceInfo: createSyntheticSourceInfo(`/tmp/${params.name}/SKILL.md`, { + source: params.source ?? "openclaw-workspace", + }), disableModelInvocation: false, }, frontmatter: {}, diff --git a/src/agents/skills.resolveskillspromptforrun.test.ts b/src/agents/skills.resolveskillspromptforrun.test.ts index 305e11f2f4e..03be4c86c47 100644 --- a/src/agents/skills.resolveskillspromptforrun.test.ts +++ b/src/agents/skills.resolveskillspromptforrun.test.ts @@ -1,3 +1,4 @@ +import { createSyntheticSourceInfo } from "@mariozechner/pi-coding-agent"; import { describe, expect, it } from "vitest"; import { resolveSkillsPromptForRun } from "./skills.js"; import type { SkillEntry } from "./skills/types.js"; @@ -17,7 +18,9 @@ describe("resolveSkillsPromptForRun", () => { description: "Demo", filePath: "/app/skills/demo-skill/SKILL.md", baseDir: "/app/skills/demo-skill", - source: "openclaw-bundled", + sourceInfo: createSyntheticSourceInfo("/app/skills/demo-skill/SKILL.md", { + source: "openclaw-bundled", + }), disableModelInvocation: false, }, frontmatter: {}, diff --git a/src/agents/skills/compact-format.test.ts b/src/agents/skills/compact-format.test.ts index cd9d6f42f15..f532ce1d199 100644 --- a/src/agents/skills/compact-format.test.ts +++ b/src/agents/skills/compact-format.test.ts @@ -1,5 +1,9 @@ import os from "node:os"; -import { formatSkillsForPrompt, type Skill } from "@mariozechner/pi-coding-agent"; +import { + createSyntheticSourceInfo, + formatSkillsForPrompt, + type Skill, +} from "@mariozechner/pi-coding-agent"; import { describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../../config/config.js"; import type { SkillEntry } from "./types.js"; @@ -15,7 +19,7 @@ function makeSkill(name: string, desc = "A skill", filePath = `/skills/${name}/S description: desc, filePath, baseDir: `/skills/${name}`, - source: "workspace", + sourceInfo: createSyntheticSourceInfo(filePath, { source: "workspace" }), disableModelInvocation: false, }; } diff --git a/src/agents/skills/source.ts b/src/agents/skills/source.ts index 076c10bb8ec..5db39e3d440 100644 --- a/src/agents/skills/source.ts +++ b/src/agents/skills/source.ts @@ -1,5 +1,5 @@ import type { Skill } from "@mariozechner/pi-coding-agent"; export function resolveSkillSource(skill: Skill): string { - return skill.source; + return skill.sourceInfo.source; } diff --git a/src/cli/skills-cli.formatting.test.ts b/src/cli/skills-cli.formatting.test.ts index 7002bdc4aec..851e76442e4 100644 --- a/src/cli/skills-cli.formatting.test.ts +++ b/src/cli/skills-cli.formatting.test.ts @@ -1,6 +1,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; +import { createSyntheticSourceInfo } from "@mariozechner/pi-coding-agent"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { buildWorkspaceSkillStatus } from "../agents/skills-status.js"; import type { SkillEntry } from "../agents/skills.js"; @@ -38,7 +39,9 @@ describe("skills-cli (e2e)", () => { description: "Capture UI screenshots", filePath: path.join(baseDir, "SKILL.md"), baseDir, - source: "openclaw-bundled", + sourceInfo: createSyntheticSourceInfo(path.join(baseDir, "SKILL.md"), { + source: "openclaw-bundled", + }), disableModelInvocation: false, }, frontmatter: {}, diff --git a/test/helpers/extensions/plugin-sdk-stub.cjs b/test/helpers/extensions/plugin-sdk-stub.cjs index 1b693f991a0..337ba0f59dd 100644 --- a/test/helpers/extensions/plugin-sdk-stub.cjs +++ b/test/helpers/extensions/plugin-sdk-stub.cjs @@ -37,10 +37,10 @@ stub = new Proxy( } return stub; }, - ownKeys() { - return []; + ownKeys(target) { + return [...new Set([...Reflect.ownKeys(target), "__esModule", "default"])]; }, - getOwnPropertyDescriptor(_target, prop) { + getOwnPropertyDescriptor(target, prop) { if (prop === "__esModule") { return { configurable: true, @@ -57,7 +57,7 @@ stub = new Proxy( writable: false, }; } - return undefined; + return Reflect.getOwnPropertyDescriptor(target, prop); }, }, );