From e5f5030570d2444106e537f579706f25aa33b3ad Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Fri, 7 Aug 2026 08:25:32 +0800 Subject: [PATCH 1/5] fix(plugins): align Fish Audio extension directory --- .github/labeler.yml | 2 +- extensions/{fish-audio => fish-audio-speech}/README.md | 0 extensions/{fish-audio => fish-audio-speech}/index.ts | 0 .../{fish-audio => fish-audio-speech}/openclaw.plugin.json | 0 extensions/{fish-audio => fish-audio-speech}/package.json | 0 .../{fish-audio => fish-audio-speech}/speech-provider.test.ts | 0 extensions/{fish-audio => fish-audio-speech}/speech-provider.ts | 0 extensions/{fish-audio => fish-audio-speech}/tts.ts | 0 package.json | 2 +- pnpm-lock.yaml | 2 +- 10 files changed, 3 insertions(+), 3 deletions(-) rename extensions/{fish-audio => fish-audio-speech}/README.md (100%) rename extensions/{fish-audio => fish-audio-speech}/index.ts (100%) rename extensions/{fish-audio => fish-audio-speech}/openclaw.plugin.json (100%) rename extensions/{fish-audio => fish-audio-speech}/package.json (100%) rename extensions/{fish-audio => fish-audio-speech}/speech-provider.test.ts (100%) rename extensions/{fish-audio => fish-audio-speech}/speech-provider.ts (100%) rename extensions/{fish-audio => fish-audio-speech}/tts.ts (100%) diff --git a/.github/labeler.yml b/.github/labeler.yml index 1a245d4c8219..d53da58026f3 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -505,7 +505,7 @@ "extensions: fish-audio": - changed-files: - any-glob-to-any-file: - - "extensions/fish-audio/**" + - "extensions/fish-audio-speech/**" - "docs/providers/fish-audio.md" - "docs/tools/tts.md" "extensions: kilocode": diff --git a/extensions/fish-audio/README.md b/extensions/fish-audio-speech/README.md similarity index 100% rename from extensions/fish-audio/README.md rename to extensions/fish-audio-speech/README.md diff --git a/extensions/fish-audio/index.ts b/extensions/fish-audio-speech/index.ts similarity index 100% rename from extensions/fish-audio/index.ts rename to extensions/fish-audio-speech/index.ts diff --git a/extensions/fish-audio/openclaw.plugin.json b/extensions/fish-audio-speech/openclaw.plugin.json similarity index 100% rename from extensions/fish-audio/openclaw.plugin.json rename to extensions/fish-audio-speech/openclaw.plugin.json diff --git a/extensions/fish-audio/package.json b/extensions/fish-audio-speech/package.json similarity index 100% rename from extensions/fish-audio/package.json rename to extensions/fish-audio-speech/package.json diff --git a/extensions/fish-audio/speech-provider.test.ts b/extensions/fish-audio-speech/speech-provider.test.ts similarity index 100% rename from extensions/fish-audio/speech-provider.test.ts rename to extensions/fish-audio-speech/speech-provider.test.ts diff --git a/extensions/fish-audio/speech-provider.ts b/extensions/fish-audio-speech/speech-provider.ts similarity index 100% rename from extensions/fish-audio/speech-provider.ts rename to extensions/fish-audio-speech/speech-provider.ts diff --git a/extensions/fish-audio/tts.ts b/extensions/fish-audio-speech/tts.ts similarity index 100% rename from extensions/fish-audio/tts.ts rename to extensions/fish-audio-speech/tts.ts diff --git a/package.json b/package.json index 610a81d19506..35d32c1dc706 100644 --- a/package.json +++ b/package.json @@ -270,7 +270,7 @@ "!dist/extensions/featherless/**", "!dist/extensions/firecrawl/**", "!dist/extensions/fireworks/**", - "!dist/extensions/fish-audio/**", + "!dist/extensions/fish-audio-speech/**", "!dist/extensions/google-meet/**", "!dist/extensions/googlechat/**", "!dist/extensions/gmi/**", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee8af1ed7255..fc0fc6038487 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -947,7 +947,7 @@ importers: specifier: workspace:* version: link:../../packages/plugin-sdk - extensions/fish-audio: + extensions/fish-audio-speech: devDependencies: '@openclaw/plugin-sdk': specifier: workspace:* From 67618e273dc351bc2b454f00c6f121ae0fcf471b Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Fri, 7 Aug 2026 08:49:24 +0800 Subject: [PATCH 2/5] fix(docs): preserve legacy plugin reference links --- scripts/generate-plugin-inventory-doc.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generate-plugin-inventory-doc.mjs b/scripts/generate-plugin-inventory-doc.mjs index 796c70e085f4..3253bd572546 100644 --- a/scripts/generate-plugin-inventory-doc.mjs +++ b/scripts/generate-plugin-inventory-doc.mjs @@ -302,7 +302,7 @@ function resolveDocs({ dirName, manifest, packageJson }) { } } - for (const candidate of [manifest.id, dirName]) { + for (const candidate of [manifest.id, dirName, ...(manifest.legacyPluginIds ?? [])]) { if (typeof candidate !== "string") { continue; } From daca53c9856b3c5fee68f1b2b50eb782ad72841a Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Fri, 7 Aug 2026 21:52:42 +0800 Subject: [PATCH 3/5] test(codex): align file-backed session fixtures --- .../event-projector.test-harness.ts | 2 +- .../run-attempt.context-engine.test.ts | 32 +++++++++++-------- .../src/app-server/run-attempt.hooks.test.ts | 10 ++++-- .../codex/src/app-server/run-attempt.test.ts | 4 +++ ...session-manager.persistence-compat.test.ts | 11 ++++++- 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/extensions/codex/src/app-server/event-projector.test-harness.ts b/extensions/codex/src/app-server/event-projector.test-harness.ts index 49618e9793db..983287b8de0e 100644 --- a/extensions/codex/src/app-server/event-projector.test-harness.ts +++ b/extensions/codex/src/app-server/event-projector.test-harness.ts @@ -91,7 +91,7 @@ export async function createParams(): Promise { const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-projector-")); tempDirs.add(tempDir); const sessionFile = path.join(tempDir, "session.jsonl"); - openFileBackedSessionManagerForTest(sessionFile).appendMessage( + openFileBackedSessionManagerForTest(sessionFile, { sessionId: "session-1" }).appendMessage( assistantMessage("history", Date.now()), ); return { diff --git a/extensions/codex/src/app-server/run-attempt.context-engine.test.ts b/extensions/codex/src/app-server/run-attempt.context-engine.test.ts index fb653e7337b7..adcd616102e6 100644 --- a/extensions/codex/src/app-server/run-attempt.context-engine.test.ts +++ b/extensions/codex/src/app-server/run-attempt.context-engine.test.ts @@ -278,7 +278,7 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { it("bootstraps and assembles non-legacy context before the Codex turn starts", async () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); - openFileBackedSessionManagerForTest(sessionFile).appendMessage( + openFileBackedSessionManagerForTest(sessionFile, { sessionId: "session-1" }).appendMessage( assistantMessage("existing context", Date.now()) as never, ); const openSpy = vi.spyOn(SessionManager, "open"); @@ -356,7 +356,7 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { it("keeps context-engine history bound to the run session when sandbox key differs", async () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); - openFileBackedSessionManagerForTest(sessionFile).appendMessage( + openFileBackedSessionManagerForTest(sessionFile, { sessionId: "session-1" }).appendMessage( assistantMessage("canonical main context", Date.now()) as never, ); const contextEngine = createContextEngine(); @@ -547,7 +547,7 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { const info = vi.spyOn(embeddedAgentLog, "info").mockImplementation(() => undefined); const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); - openFileBackedSessionManagerForTest(sessionFile).appendMessage( + openFileBackedSessionManagerForTest(sessionFile, { sessionId: "session-1" }).appendMessage( assistantMessage("bootstrap-only context", Date.now()) as never, ); const contextEngine = createContextEngine({ @@ -799,7 +799,9 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); const agentDir = path.join(tempDir, "agent"); - const sessionManager = openFileBackedSessionManagerForTest(sessionFile); + const sessionManager = openFileBackedSessionManagerForTest(sessionFile, { + sessionId: "session-1", + }); sessionManager.appendMessage( userMessage("previous stale-bootstrap request", Date.now()) as never, ); @@ -888,7 +890,9 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { it("keeps mirrored history when an inactive per-turn context-engine binding starts fresh", async () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); - const sessionManager = openFileBackedSessionManagerForTest(sessionFile); + const sessionManager = openFileBackedSessionManagerForTest(sessionFile, { + sessionId: "session-1", + }); sessionManager.appendMessage(userMessage("previous per-turn request", 10) as never); sessionManager.appendMessage(assistantMessage("previous per-turn answer", 11) as never); await writeCodexAppServerBinding(sessionFile, { @@ -1266,7 +1270,7 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { it("retries a resumed context-engine thread on a fresh Codex thread without plugin compaction", async () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); - openFileBackedSessionManagerForTest(sessionFile).appendMessage( + openFileBackedSessionManagerForTest(sessionFile, { sessionId: "session-1" }).appendMessage( assistantMessage("pre-compaction context", Date.now()) as never, ); await writeCodexAppServerBinding(sessionFile, { @@ -1368,7 +1372,7 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { it("returns a replay-safe recovery result when the executable owner changes during overflow retry", async () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); - openFileBackedSessionManagerForTest(sessionFile).appendMessage( + openFileBackedSessionManagerForTest(sessionFile, { sessionId: "session-1" }).appendMessage( assistantMessage("pre-compaction context", Date.now()) as never, ); await writeCodexAppServerBinding(sessionFile, { @@ -1435,7 +1439,7 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { it("preserves a newer context-engine binding when a stale resumed thread overflows", async () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); - openFileBackedSessionManagerForTest(sessionFile).appendMessage( + openFileBackedSessionManagerForTest(sessionFile, { sessionId: "session-1" }).appendMessage( assistantMessage("pre-compaction context", Date.now()) as never, ); await writeCodexAppServerBinding(sessionFile, { @@ -1507,7 +1511,7 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { it("clears a resumed context-engine binding when a turn terminally overflows", async () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); - openFileBackedSessionManagerForTest(sessionFile).appendMessage( + openFileBackedSessionManagerForTest(sessionFile, { sessionId: "session-1" }).appendMessage( assistantMessage("pre-compaction context", Date.now()) as never, ); await writeCodexAppServerBinding(sessionFile, { @@ -1585,7 +1589,7 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { it("does not pre-compact over-budget rendered context-engine prompts before Codex turn/start", async () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); - openFileBackedSessionManagerForTest(sessionFile).appendMessage( + openFileBackedSessionManagerForTest(sessionFile, { sessionId: "session-1" }).appendMessage( assistantMessage("pre-compaction context", Date.now()) as never, ); const hugePayload = { @@ -1667,7 +1671,7 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { it("does not call hung owning context-engine compaction during Codex overflow recovery", async () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); - openFileBackedSessionManagerForTest(sessionFile).appendMessage( + openFileBackedSessionManagerForTest(sessionFile, { sessionId: "session-1" }).appendMessage( assistantMessage("pre-compaction context", Date.now()) as never, ); await writeCodexAppServerBinding(sessionFile, { @@ -1748,7 +1752,7 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { it("keeps current inbound context at the front of the Codex context-engine prompt", async () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); - openFileBackedSessionManagerForTest(sessionFile).appendMessage( + openFileBackedSessionManagerForTest(sessionFile, { sessionId: "session-1" }).appendMessage( assistantMessage("older context", Date.now()) as never, ); const contextEngine = createContextEngine(); @@ -1850,7 +1854,7 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { it("reloads mirrored history after bootstrap mutates the session transcript", async () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); - openFileBackedSessionManagerForTest(sessionFile).appendMessage( + openFileBackedSessionManagerForTest(sessionFile, { sessionId: "session-1" }).appendMessage( assistantMessage("existing context", Date.now()) as never, ); const afterTurn = vi.fn( @@ -1858,7 +1862,7 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { ); const bootstrap = vi.fn( async ({ sessionFile: file }: Parameters>[0]) => { - openFileBackedSessionManagerForTest(file).appendMessage( + openFileBackedSessionManagerForTest(file, { sessionId: "session-1" }).appendMessage( assistantMessage("bootstrap context", Date.now() + 1) as never, ); return { bootstrapped: true }; diff --git a/extensions/codex/src/app-server/run-attempt.hooks.test.ts b/extensions/codex/src/app-server/run-attempt.hooks.test.ts index b2596985023c..a92cef252ea5 100644 --- a/extensions/codex/src/app-server/run-attempt.hooks.test.ts +++ b/extensions/codex/src/app-server/run-attempt.hooks.test.ts @@ -115,7 +115,9 @@ describe("runCodexAppServerAttempt hooks and model diagnostics", () => { ); const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); - const sessionManager = openFileBackedSessionManagerForTest(sessionFile); + const sessionManager = openFileBackedSessionManagerForTest(sessionFile, { + sessionId: "session-1", + }); sessionManager.appendMessage(assistantMessage("existing context", Date.now())); const harness = createStartedThreadHarness(); @@ -311,7 +313,9 @@ describe("runCodexAppServerAttempt hooks and model diagnostics", () => { return {}; }); const params = createParams(sessionFile, workspaceDir); - const sessionManager = openFileBackedSessionManagerForTest(sessionFile); + const sessionManager = openFileBackedSessionManagerForTest(sessionFile, { + sessionId: "diagnostic-session-1", + }); sessionManager.appendMessage(assistantMessage("existing context", Date.now())); params.runtimePlan = createCodexRuntimePlanFixture(); params.config = { @@ -943,7 +947,7 @@ describe("runCodexAppServerAttempt hooks and model diagnostics", () => { ); const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); - openFileBackedSessionManagerForTest(sessionFile).appendMessage( + openFileBackedSessionManagerForTest(sessionFile, { sessionId: "session-1" }).appendMessage( assistantMessage("existing context", Date.now()), ); createStartedThreadHarness(async (method) => { diff --git a/extensions/codex/src/app-server/run-attempt.test.ts b/extensions/codex/src/app-server/run-attempt.test.ts index b7b72ee81fca..02fc544e6887 100644 --- a/extensions/codex/src/app-server/run-attempt.test.ts +++ b/extensions/codex/src/app-server/run-attempt.test.ts @@ -643,6 +643,10 @@ function createRunParams() { return createParams(sessionFile, workspaceDir); } +function openRunSession(sessionFile: string) { + return openFileBackedSessionManagerForTest(sessionFile, { sessionId: "session-1" }); +} + const GOOGLE_CALENDAR_PLUGIN_CONFIG = { codexPlugins: { enabled: true, diff --git a/src/agents/sessions/session-manager.persistence-compat.test.ts b/src/agents/sessions/session-manager.persistence-compat.test.ts index 5a996ac81167..ba09e491f74f 100644 --- a/src/agents/sessions/session-manager.persistence-compat.test.ts +++ b/src/agents/sessions/session-manager.persistence-compat.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { afterEach, describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it, vi } from "vitest"; import { openFileBackedSessionManagerForTest } from "../../../test/helpers/session-manager-file-fixture.js"; import { formatSqliteSessionFileMarker, @@ -140,6 +140,15 @@ describe("SessionManager persistence compatibility", () => { expect(() => openFileBackedSessionManagerForTest(sessionFile, { sessionId: "session-2" }), ).toThrow("belongs to session-1, not session-2"); + const inMemory = vi.fn((cwd?: string) => SessionManager.inMemory(cwd)); + const ManagerClass = { inMemory } as unknown as typeof SessionManager; + openFileBackedSessionManagerForTest( + path.join(dir, "legacy.jsonl"), + undefined, + dir, + ManagerClass, + ); + expect(inMemory).toHaveBeenCalledWith(dir); }); it("separates appended records from a final unterminated JSONL record", async () => { From cdd621eaeebe8eff602eebc31e2829a944d7203f Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Fri, 7 Aug 2026 21:53:34 +0800 Subject: [PATCH 4/5] test(telegram): use model selection rejection seam --- .../telegram/src/bot.create-telegram-bot.test.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/extensions/telegram/src/bot.create-telegram-bot.test.ts b/extensions/telegram/src/bot.create-telegram-bot.test.ts index 3d9944d15602..fb6af5ce6daa 100644 --- a/extensions/telegram/src/bot.create-telegram-bot.test.ts +++ b/extensions/telegram/src/bot.create-telegram-bot.test.ts @@ -43,7 +43,6 @@ const harness = await import("./bot.create-telegram-bot.test-harness.js"); const pluginStateTestRuntime = await import("openclaw/plugin-sdk/plugin-state-test-runtime"); const configMutation = await import("openclaw/plugin-sdk/config-mutation"); const modelSessionRuntime = await import("openclaw/plugin-sdk/model-session-runtime"); -const sessionStoreRuntime = await import("openclaw/plugin-sdk/session-store-runtime"); const EYES_EMOJI = "\u{1F440}"; const tempStateDirs: string[] = []; let previousStateDir: string | undefined; @@ -5407,15 +5406,14 @@ describe("createTelegramBot", () => { it("shows a permanent rejection when model selection is locked", async () => { createTelegramBot({ token: "tok" }); const callbackHandler = getOnHandler("callback_query"); - const patchSessionEntrySpy = vi - .spyOn(sessionStoreRuntime, "patchSessionEntry") - .mockImplementationOnce(async (params) => { + const getSessionEntrySpy = vi + .spyOn(telegramBotDepsForTest, "getSessionEntry") + .mockImplementationOnce(() => { const entry = { sessionId: "locked-session", updatedAt: Date.now(), modelSelectionLocked: true, }; - await params.update(entry, { existingEntry: entry }); return entry; }); const ctx = makeCallbackRetryContext({ @@ -5427,7 +5425,7 @@ describe("createTelegramBot", () => { try { await expect(callbackHandler(ctx)).resolves.toBeUndefined(); } finally { - patchSessionEntrySpy.mockRestore(); + getSessionEntrySpy.mockRestore(); } expect(editMessageTextSpy).toHaveBeenCalledTimes(1); From 6e100ca56a3fb06a9bd49544a2ceabe94992d61d Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 8 Aug 2026 18:47:18 +0800 Subject: [PATCH 5/5] test(codex): remove duplicate session helper --- extensions/codex/src/app-server/run-attempt.test.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/extensions/codex/src/app-server/run-attempt.test.ts b/extensions/codex/src/app-server/run-attempt.test.ts index 02fc544e6887..b7b72ee81fca 100644 --- a/extensions/codex/src/app-server/run-attempt.test.ts +++ b/extensions/codex/src/app-server/run-attempt.test.ts @@ -643,10 +643,6 @@ function createRunParams() { return createParams(sessionFile, workspaceDir); } -function openRunSession(sessionFile: string) { - return openFileBackedSessionManagerForTest(sessionFile, { sessionId: "session-1" }); -} - const GOOGLE_CALENDAR_PLUGIN_CONFIG = { codexPlugins: { enabled: true,