From 6dcc052bb4407fe440a4e53aef8eeefb5903cab4 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 18 Feb 2026 02:09:40 +0100 Subject: [PATCH] fix: stabilize model catalog and pi discovery auth storage compatibility --- .../matrix/src/matrix/monitor/replies.test.ts | 13 +++--- src/agents/anthropic.setup-token.live.test.ts | 2 +- src/agents/model-catalog.ts | 25 ++++++++--- src/agents/model-forward-compat.ts | 2 +- src/agents/model-selection.ts | 2 +- src/agents/openclaw-tools.ts | 6 +-- src/agents/pi-embedded-runner/compact.ts | 12 ++--- src/agents/pi-embedded-runner/google.ts | 4 +- src/agents/pi-embedded-runner/run/attempt.ts | 6 +-- src/agents/pi-embedded-runner/run/images.ts | 6 +-- src/agents/pi-model-discovery.ts | 10 ++++- ...iases-schemas-without-dropping.e2e.test.ts | 25 ++++++----- src/agents/pi-tools.read.ts | 6 +-- src/agents/pi-tools.ts | 6 +-- src/agents/skills/workspace.ts | 6 ++- src/agents/tools/canvas-tool.ts | 4 +- src/agents/tools/common.ts | 4 +- src/agents/tools/nodes-tool.ts | 4 +- src/auto-reply/reply/dispatch-from-config.ts | 8 ++-- src/cli/cron-cli.test.ts | 4 ++ src/cli/cron-cli/register.cron-add.ts | 2 +- src/cli/cron-cli/shared.ts | 4 +- src/commands/configure.gateway-auth.ts | 2 +- src/config/defaults.ts | 4 +- src/cron/normalize.ts | 2 +- src/cron/service.issue-regressions.test.ts | 2 +- src/cron/service.jobs.test.ts | 4 +- .../service.jobs.top-of-hour-stagger.test.ts | 2 +- src/cron/service/jobs.ts | 16 +++---- src/cron/service/store.ts | 4 +- src/gateway/channel-health-monitor.test.ts | 6 +-- src/gateway/channel-health-monitor.ts | 2 +- src/gateway/server-node-events.test.ts | 45 ++++++++++++------- src/gateway/server-node-events.ts | 2 +- src/imessage/monitor/deliver.test.ts | 11 ++--- src/infra/outbound/deliver.test.ts | 2 +- src/infra/outbound/deliver.ts | 28 ++++++------ src/media-understanding/runner.entries.ts | 8 ++-- src/memory/embeddings-gemini.ts | 2 +- src/slack/monitor/message-handler/dispatch.ts | 10 +++-- src/telegram/send.test.ts | 8 +--- 41 files changed, 184 insertions(+), 137 deletions(-) diff --git a/extensions/matrix/src/matrix/monitor/replies.test.ts b/extensions/matrix/src/matrix/monitor/replies.test.ts index 22e9b58561..3dda8fac9b 100644 --- a/extensions/matrix/src/matrix/monitor/replies.test.ts +++ b/extensions/matrix/src/matrix/monitor/replies.test.ts @@ -5,7 +5,8 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; const sendMessageMatrixMock = vi.hoisted(() => vi.fn().mockResolvedValue({ messageId: "mx-1" })); vi.mock("../send.js", () => ({ - sendMessageMatrix: (...args: unknown[]) => sendMessageMatrixMock(...args), + sendMessageMatrix: (to: string, message: string, opts?: unknown) => + sendMessageMatrixMock(to, message, opts), })); import { setMatrixRuntime } from "../../runtime.js"; @@ -20,14 +21,14 @@ describe("deliverMatrixReplies", () => { const runtimeStub = { config: { - loadConfig: (...args: unknown[]) => loadConfigMock(...args), + loadConfig: () => loadConfigMock(), }, channel: { text: { - resolveMarkdownTableMode: (...args: unknown[]) => resolveMarkdownTableModeMock(...args), - convertMarkdownTables: (...args: unknown[]) => convertMarkdownTablesMock(...args), - resolveChunkMode: (...args: unknown[]) => resolveChunkModeMock(...args), - chunkMarkdownTextWithMode: (...args: unknown[]) => chunkMarkdownTextWithModeMock(...args), + resolveMarkdownTableMode: () => resolveMarkdownTableModeMock(), + convertMarkdownTables: (text: string) => convertMarkdownTablesMock(text), + resolveChunkMode: () => resolveChunkModeMock(), + chunkMarkdownTextWithMode: (text: string) => chunkMarkdownTextWithModeMock(text), }, }, logging: { diff --git a/src/agents/anthropic.setup-token.live.test.ts b/src/agents/anthropic.setup-token.live.test.ts index 182a20e5c2..cf34f78d56 100644 --- a/src/agents/anthropic.setup-token.live.test.ts +++ b/src/agents/anthropic.setup-token.live.test.ts @@ -1,8 +1,8 @@ -import { type Api, completeSimple, type Model } from "@mariozechner/pi-ai"; import { randomUUID } from "node:crypto"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +import { type Api, completeSimple, type Model } from "@mariozechner/pi-ai"; import { describe, expect, it } from "vitest"; import { ANTHROPIC_SETUP_TOKEN_PREFIX, diff --git a/src/agents/model-catalog.ts b/src/agents/model-catalog.ts index c1c12db555..1ebb78c8ef 100644 --- a/src/agents/model-catalog.ts +++ b/src/agents/model-catalog.ts @@ -67,6 +67,14 @@ export function __setModelCatalogImportForTest(loader?: () => Promise unknown }; + if (typeof withFactory.create === "function") { + return withFactory.create(path); + } + return new (AuthStorageLike as { new (path: string): unknown })(path); +} + export async function loadModelCatalog(params?: { config?: OpenClawConfig; useCache?: boolean; @@ -101,12 +109,17 @@ export async function loadModelCatalog(params?: { const piSdk = await importPiSdk(); const agentDir = resolveOpenClawAgentDir(); const { join } = await import("node:path"); - const authStorage = new piSdk.AuthStorage(join(agentDir, "auth.json")); - const registry = new piSdk.ModelRegistry(authStorage, join(agentDir, "models.json")) as - | { - getAll: () => Array; - } - | Array; + const authStorage = createAuthStorage(piSdk.AuthStorage, join(agentDir, "auth.json")); + const registry = new (piSdk.ModelRegistry as unknown as { + new ( + authStorage: unknown, + modelsFile: string, + ): + | Array + | { + getAll: () => Array; + }; + })(authStorage, join(agentDir, "models.json")); const entries = Array.isArray(registry) ? registry : registry.getAll(); for (const entry of entries) { const id = String(entry?.id ?? "").trim(); diff --git a/src/agents/model-forward-compat.ts b/src/agents/model-forward-compat.ts index dc566f9da5..ee88ea5974 100644 --- a/src/agents/model-forward-compat.ts +++ b/src/agents/model-forward-compat.ts @@ -1,8 +1,8 @@ import type { Api, Model } from "@mariozechner/pi-ai"; -import type { ModelRegistry } from "./pi-model-discovery.js"; import { DEFAULT_CONTEXT_TOKENS } from "./defaults.js"; import { normalizeModelCompat } from "./model-compat.js"; import { normalizeProviderId } from "./model-selection.js"; +import type { ModelRegistry } from "./pi-model-discovery.js"; const OPENAI_CODEX_GPT_53_MODEL_ID = "gpt-5.3-codex"; const OPENAI_CODEX_TEMPLATE_MODEL_IDS = ["gpt-5.2-codex"] as const; diff --git a/src/agents/model-selection.ts b/src/agents/model-selection.ts index 4cba18fcc9..1912d6048a 100644 --- a/src/agents/model-selection.ts +++ b/src/agents/model-selection.ts @@ -1,7 +1,7 @@ import type { OpenClawConfig } from "../config/config.js"; -import type { ModelCatalogEntry } from "./model-catalog.js"; import { resolveAgentModelPrimary } from "./agent-scope.js"; import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "./defaults.js"; +import type { ModelCatalogEntry } from "./model-catalog.js"; import { normalizeGoogleModelId } from "./models-config.providers.js"; export type ModelRef = { diff --git a/src/agents/openclaw-tools.ts b/src/agents/openclaw-tools.ts index cad2b88d94..9c9fb72240 100644 --- a/src/agents/openclaw-tools.ts +++ b/src/agents/openclaw-tools.ts @@ -1,12 +1,12 @@ import type { OpenClawConfig } from "../config/config.js"; -import type { GatewayMessageChannel } from "../utils/message-channel.js"; -import type { SandboxFsBridge } from "./sandbox/fs-bridge.js"; -import type { AnyAgentTool } from "./tools/common.js"; import { resolvePluginTools } from "../plugins/tools.js"; +import type { GatewayMessageChannel } from "../utils/message-channel.js"; import { resolveSessionAgentId } from "./agent-scope.js"; +import type { SandboxFsBridge } from "./sandbox/fs-bridge.js"; import { createAgentsListTool } from "./tools/agents-list-tool.js"; import { createBrowserTool } from "./tools/browser-tool.js"; import { createCanvasTool } from "./tools/canvas-tool.js"; +import type { AnyAgentTool } from "./tools/common.js"; import { createCronTool } from "./tools/cron-tool.js"; import { createGatewayTool } from "./tools/gateway-tool.js"; import { createImageTool } from "./tools/image-tool.js"; diff --git a/src/agents/pi-embedded-runner/compact.ts b/src/agents/pi-embedded-runner/compact.ts index 94e48576e6..5dd39a53e7 100644 --- a/src/agents/pi-embedded-runner/compact.ts +++ b/src/agents/pi-embedded-runner/compact.ts @@ -1,3 +1,5 @@ +import fs from "node:fs/promises"; +import os from "node:os"; import type { AgentMessage } from "@mariozechner/pi-agent-core"; import { createAgentSession, @@ -5,14 +7,10 @@ import { SessionManager, SettingsManager, } from "@mariozechner/pi-coding-agent"; -import fs from "node:fs/promises"; -import os from "node:os"; -import type { ReasoningLevel, ThinkLevel } from "../../auto-reply/thinking.js"; -import type { OpenClawConfig } from "../../config/config.js"; -import type { ExecElevatedDefaults } from "../bash-tools.js"; -import type { EmbeddedPiCompactResult } from "./types.js"; import { resolveHeartbeatPrompt } from "../../auto-reply/heartbeat.js"; +import type { ReasoningLevel, ThinkLevel } from "../../auto-reply/thinking.js"; import { resolveChannelCapabilities } from "../../config/channel-capabilities.js"; +import type { OpenClawConfig } from "../../config/config.js"; import { getMachineDisplayName } from "../../infra/machine-name.js"; import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js"; import { type enqueueCommand, enqueueCommandInLane } from "../../process/command-queue.js"; @@ -26,6 +24,7 @@ import { normalizeMessageChannel } from "../../utils/message-channel.js"; import { isReasoningTagProvider } from "../../utils/provider-utils.js"; import { resolveOpenClawAgentDir } from "../agent-paths.js"; import { resolveSessionAgentIds } from "../agent-scope.js"; +import type { ExecElevatedDefaults } from "../bash-tools.js"; import { makeBootstrapWarn, resolveBootstrapContextForRun } from "../bootstrap-files.js"; import { listChannelSupportedActions, resolveChannelMessageToolHints } from "../channel-tools.js"; import { formatUserTime, resolveUserTimeFormat, resolveUserTimezone } from "../date-time.js"; @@ -82,6 +81,7 @@ import { createSystemPromptOverride, } from "./system-prompt.js"; import { splitSdkTools } from "./tool-split.js"; +import type { EmbeddedPiCompactResult } from "./types.js"; import { describeUnknownError, mapThinkingLevel } from "./utils.js"; import { flushPendingToolResultsAfterIdle } from "./wait-for-idle-before-flush.js"; diff --git a/src/agents/pi-embedded-runner/google.ts b/src/agents/pi-embedded-runner/google.ts index 7c3767fbc1..07ad4a5189 100644 --- a/src/agents/pi-embedded-runner/google.ts +++ b/src/agents/pi-embedded-runner/google.ts @@ -1,9 +1,8 @@ +import { EventEmitter } from "node:events"; import type { AgentMessage, AgentTool } from "@mariozechner/pi-agent-core"; import type { SessionManager } from "@mariozechner/pi-coding-agent"; import type { TSchema } from "@sinclair/typebox"; -import { EventEmitter } from "node:events"; import type { OpenClawConfig } from "../../config/config.js"; -import type { TranscriptPolicy } from "../transcript-policy.js"; import { registerUnhandledRejectionHandler } from "../../infra/unhandled-rejections.js"; import { hasInterSessionUserProvenance, @@ -23,6 +22,7 @@ import { stripToolResultDetails, sanitizeToolUseResultPairing, } from "../session-transcript-repair.js"; +import type { TranscriptPolicy } from "../transcript-policy.js"; import { resolveTranscriptPolicy } from "../transcript-policy.js"; import { log } from "./logger.js"; import { describeUnknownError } from "./utils.js"; diff --git a/src/agents/pi-embedded-runner/run/attempt.ts b/src/agents/pi-embedded-runner/run/attempt.ts index e9901636ca..364448eb89 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -1,10 +1,9 @@ +import fs from "node:fs/promises"; +import os from "node:os"; import type { AgentMessage } from "@mariozechner/pi-agent-core"; import type { ImageContent } from "@mariozechner/pi-ai"; import { streamSimple } from "@mariozechner/pi-ai"; import { createAgentSession, SessionManager, SettingsManager } from "@mariozechner/pi-coding-agent"; -import fs from "node:fs/promises"; -import os from "node:os"; -import type { EmbeddedRunAttemptParams, EmbeddedRunAttemptResult } from "./types.js"; import { resolveHeartbeatPrompt } from "../../../auto-reply/heartbeat.js"; import { resolveChannelCapabilities } from "../../../config/channel-capabilities.js"; import { getMachineDisplayName } from "../../../infra/machine-name.js"; @@ -107,6 +106,7 @@ import { shouldFlagCompactionTimeout, } from "./compaction-timeout.js"; import { detectAndLoadPromptImages } from "./images.js"; +import type { EmbeddedRunAttemptParams, EmbeddedRunAttemptResult } from "./types.js"; export function injectHistoryImagesIntoMessages( messages: AgentMessage[], diff --git a/src/agents/pi-embedded-runner/run/images.ts b/src/agents/pi-embedded-runner/run/images.ts index c328fd53f7..c11f191e4f 100644 --- a/src/agents/pi-embedded-runner/run/images.ts +++ b/src/agents/pi-embedded-runner/run/images.ts @@ -1,10 +1,10 @@ -import type { ImageContent } from "@mariozechner/pi-ai"; import path from "node:path"; import { fileURLToPath } from "node:url"; -import type { ImageSanitizationLimits } from "../../image-sanitization.js"; -import type { SandboxFsBridge } from "../../sandbox/fs-bridge.js"; +import type { ImageContent } from "@mariozechner/pi-ai"; import { resolveUserPath } from "../../../utils.js"; import { loadWebMedia } from "../../../web/media.js"; +import type { ImageSanitizationLimits } from "../../image-sanitization.js"; +import type { SandboxFsBridge } from "../../sandbox/fs-bridge.js"; import { sanitizeImageBlocks } from "../../tool-images.js"; import { log } from "../logger.js"; diff --git a/src/agents/pi-model-discovery.ts b/src/agents/pi-model-discovery.ts index 012e89e501..51ac1aeb8e 100644 --- a/src/agents/pi-model-discovery.ts +++ b/src/agents/pi-model-discovery.ts @@ -3,9 +3,17 @@ import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent"; export { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent"; +function createAuthStorage(AuthStorageLike: unknown, path: string) { + const withFactory = AuthStorageLike as { create?: (path: string) => unknown }; + if (typeof withFactory.create === "function") { + return withFactory.create(path) as AuthStorage; + } + return new (AuthStorageLike as { new (path: string): unknown })(path) as AuthStorage; +} + // Compatibility helpers for pi-coding-agent 0.50+ (discover* helpers removed). export function discoverAuthStorage(agentDir: string): AuthStorage { - return new AuthStorage(path.join(agentDir, "auth.json")); + return createAuthStorage(AuthStorage, path.join(agentDir, "auth.json")); } export function discoverModels(authStorage: AuthStorage, agentDir: string): ModelRegistry { diff --git a/src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping.e2e.test.ts b/src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping.e2e.test.ts index a352dcbe33..b6584da114 100644 --- a/src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping.e2e.test.ts +++ b/src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping.e2e.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import type { AgentTool } from "@mariozechner/pi-agent-core"; +import type { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core"; import { Type } from "@sinclair/typebox"; import { describe, expect, it, vi } from "vitest"; import "./test-helpers/fast-coding-tools.js"; @@ -577,6 +577,17 @@ describe("createOpenClawCodingTools", () => { }); it("strips truncation.content details from read results while preserving other fields", async () => { + const readResult: AgentToolResult = { + content: [{ type: "text" as const, text: "line-0001" }], + details: { + truncation: { + truncated: true, + outputLines: 1, + firstLineExceedsLimit: false, + content: "hidden duplicate payload", + }, + }, + }; const baseRead: AgentTool = { name: "read", label: "read", @@ -586,17 +597,7 @@ describe("createOpenClawCodingTools", () => { offset: Type.Optional(Type.Number()), limit: Type.Optional(Type.Number()), }), - execute: vi.fn(async () => ({ - content: [{ type: "text", text: "line-0001" }], - details: { - truncation: { - truncated: true, - outputLines: 1, - firstLineExceedsLimit: false, - content: "hidden duplicate payload", - }, - }, - })), + execute: vi.fn(async () => readResult), }; const wrapped = createOpenClawReadTool( diff --git a/src/agents/pi-tools.read.ts b/src/agents/pi-tools.read.ts index 1fac1190ba..5dc70817c8 100644 --- a/src/agents/pi-tools.read.ts +++ b/src/agents/pi-tools.read.ts @@ -1,11 +1,11 @@ import type { AgentToolResult } from "@mariozechner/pi-agent-core"; import { createEditTool, createReadTool, createWriteTool } from "@mariozechner/pi-coding-agent"; -import type { ImageSanitizationLimits } from "./image-sanitization.js"; -import type { AnyAgentTool } from "./pi-tools.types.js"; -import type { SandboxFsBridge } from "./sandbox/fs-bridge.js"; import { detectMime } from "../media/mime.js"; import { sniffMimeFromBase64 } from "../media/sniff-mime-from-base64.js"; +import type { ImageSanitizationLimits } from "./image-sanitization.js"; +import type { AnyAgentTool } from "./pi-tools.types.js"; import { assertSandboxPath } from "./sandbox-paths.js"; +import type { SandboxFsBridge } from "./sandbox/fs-bridge.js"; import { sanitizeToolResultImages } from "./tool-images.js"; // NOTE(steipete): Upstream read now does file-magic MIME detection; we keep the wrapper diff --git a/src/agents/pi-tools.ts b/src/agents/pi-tools.ts index f4c4eec6cc..32808a2a8c 100644 --- a/src/agents/pi-tools.ts +++ b/src/agents/pi-tools.ts @@ -7,9 +7,6 @@ import { } from "@mariozechner/pi-coding-agent"; import type { OpenClawConfig } from "../config/config.js"; import type { ToolLoopDetectionConfig } from "../config/types.tools.js"; -import type { ModelAuthMode } from "./model-auth.js"; -import type { AnyAgentTool } from "./pi-tools.types.js"; -import type { SandboxContext } from "./sandbox.js"; import { logWarn } from "../logger.js"; import { getPluginToolMeta } from "../plugins/tools.js"; import { isSubagentSessionKey } from "../routing/session-key.js"; @@ -24,6 +21,7 @@ import { } from "./bash-tools.js"; import { listChannelAgentTools } from "./channel-tools.js"; import { resolveImageSanitizationLimits } from "./image-sanitization.js"; +import type { ModelAuthMode } from "./model-auth.js"; import { createOpenClawTools } from "./openclaw-tools.js"; import { wrapToolWithAbortSignal } from "./pi-tools.abort.js"; import { wrapToolWithBeforeToolCallHook } from "./pi-tools.before-tool-call.js"; @@ -46,6 +44,8 @@ import { wrapToolParamNormalization, } from "./pi-tools.read.js"; import { cleanToolSchemaForGemini, normalizeToolParameters } from "./pi-tools.schema.js"; +import type { AnyAgentTool } from "./pi-tools.types.js"; +import type { SandboxContext } from "./sandbox.js"; import { getSubagentDepthFromSessionStore } from "./subagent-depth.js"; import { applyToolPolicyPipeline, diff --git a/src/agents/skills/workspace.ts b/src/agents/skills/workspace.ts index 033221f4d2..5e2123941a 100644 --- a/src/agents/skills/workspace.ts +++ b/src/agents/skills/workspace.ts @@ -477,7 +477,11 @@ export function buildWorkspaceSkillSnapshot( ? `⚠️ Skills truncated: included ${skillsForPrompt.length} of ${resolvedSkills.length}. Run \`openclaw skills check\` to audit.` : ""; - const prompt = [remoteNote, truncationNote, formatSkillsForPrompt(compactSkillPaths(skillsForPrompt))] + const prompt = [ + remoteNote, + truncationNote, + formatSkillsForPrompt(compactSkillPaths(skillsForPrompt)), + ] .filter(Boolean) .join("\n"); const skillFilter = normalizeSkillFilter(opts?.skillFilter); diff --git a/src/agents/tools/canvas-tool.ts b/src/agents/tools/canvas-tool.ts index 41475f668b..1e38192ec7 100644 --- a/src/agents/tools/canvas-tool.ts +++ b/src/agents/tools/canvas-tool.ts @@ -1,9 +1,9 @@ -import { Type } from "@sinclair/typebox"; import crypto from "node:crypto"; import fs from "node:fs/promises"; -import type { OpenClawConfig } from "../../config/config.js"; +import { Type } from "@sinclair/typebox"; import { writeBase64ToFile } from "../../cli/nodes-camera.js"; import { canvasSnapshotTempPath, parseCanvasSnapshotPayload } from "../../cli/nodes-canvas.js"; +import type { OpenClawConfig } from "../../config/config.js"; import { imageMimeFromFormat } from "../../media/mime.js"; import { resolveImageSanitizationLimits } from "../image-sanitization.js"; import { optionalStringEnum, stringEnum } from "../schema/typebox.js"; diff --git a/src/agents/tools/common.ts b/src/agents/tools/common.ts index 5dbfd9e6b6..bca56ceada 100644 --- a/src/agents/tools/common.ts +++ b/src/agents/tools/common.ts @@ -1,7 +1,7 @@ -import type { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core"; import fs from "node:fs/promises"; -import type { ImageSanitizationLimits } from "../image-sanitization.js"; +import type { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core"; import { detectMime } from "../../media/mime.js"; +import type { ImageSanitizationLimits } from "../image-sanitization.js"; import { sanitizeToolResultImages } from "../tool-images.js"; // oxlint-disable-next-line typescript/no-explicit-any diff --git a/src/agents/tools/nodes-tool.ts b/src/agents/tools/nodes-tool.ts index 902a453288..187e7c8fcb 100644 --- a/src/agents/tools/nodes-tool.ts +++ b/src/agents/tools/nodes-tool.ts @@ -1,7 +1,6 @@ +import crypto from "node:crypto"; import type { AgentToolResult } from "@mariozechner/pi-agent-core"; import { Type } from "@sinclair/typebox"; -import crypto from "node:crypto"; -import type { OpenClawConfig } from "../../config/config.js"; import { type CameraFacing, cameraTempPath, @@ -17,6 +16,7 @@ import { writeScreenRecordToFile, } from "../../cli/nodes-screen.js"; import { parseDurationMs } from "../../cli/parse-duration.js"; +import type { OpenClawConfig } from "../../config/config.js"; import { imageMimeFromFormat } from "../../media/mime.js"; import { resolveSessionAgentId } from "../agent-scope.js"; import { resolveImageSanitizationLimits } from "../image-sanitization.js"; diff --git a/src/auto-reply/reply/dispatch-from-config.ts b/src/auto-reply/reply/dispatch-from-config.ts index 5965594cbd..e4e66c16a5 100644 --- a/src/auto-reply/reply/dispatch-from-config.ts +++ b/src/auto-reply/reply/dispatch-from-config.ts @@ -1,8 +1,5 @@ -import type { OpenClawConfig } from "../../config/config.js"; -import type { FinalizedMsgContext } from "../templating.js"; -import type { GetReplyOptions, ReplyPayload } from "../types.js"; -import type { ReplyDispatcher, ReplyDispatchKind } from "./reply-dispatcher.js"; import { resolveSessionAgentId } from "../../agents/agent-scope.js"; +import type { OpenClawConfig } from "../../config/config.js"; import { loadSessionStore, resolveStorePath } from "../../config/sessions.js"; import { logVerbose } from "../../globals.js"; import { createInternalHookEvent, triggerInternalHook } from "../../hooks/internal-hooks.js"; @@ -15,8 +12,11 @@ import { import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js"; import { maybeApplyTtsToPayload, normalizeTtsAutoMode, resolveTtsConfig } from "../../tts/tts.js"; import { getReplyFromConfig } from "../reply.js"; +import type { FinalizedMsgContext } from "../templating.js"; +import type { GetReplyOptions, ReplyPayload } from "../types.js"; import { formatAbortReplyText, tryFastAbortFromMessage } from "./abort.js"; import { shouldSkipDuplicateInbound } from "./inbound-dedupe.js"; +import type { ReplyDispatcher, ReplyDispatchKind } from "./reply-dispatcher.js"; import { isRoutableChannel, routeReply } from "./route-reply.js"; const AUDIO_PLACEHOLDER_RE = /^(\s*\([^)]*\))?$/i; diff --git a/src/cli/cron-cli.test.ts b/src/cli/cron-cli.test.ts index f86c96ad1f..3eb5a2711a 100644 --- a/src/cli/cron-cli.test.ts +++ b/src/cli/cron-cli.test.ts @@ -545,6 +545,8 @@ describe("cron cli", () => { } if (method === "cron.list") { return { + ok: true, + params: {}, jobs: [ { id: "job-1", @@ -581,6 +583,8 @@ describe("cron cli", () => { } if (method === "cron.list") { return { + ok: true, + params: {}, jobs: [{ id: "job-1", schedule: { kind: "every", everyMs: 60_000 } }], }; } diff --git a/src/cli/cron-cli/register.cron-add.ts b/src/cli/cron-cli/register.cron-add.ts index 1e489bd0a5..2388b00a38 100644 --- a/src/cli/cron-cli/register.cron-add.ts +++ b/src/cli/cron-cli/register.cron-add.ts @@ -1,9 +1,9 @@ import type { Command } from "commander"; import type { CronJob } from "../../cron/types.js"; -import type { GatewayRpcOpts } from "../gateway-rpc.js"; import { danger } from "../../globals.js"; import { sanitizeAgentId } from "../../routing/session-key.js"; import { defaultRuntime } from "../../runtime.js"; +import type { GatewayRpcOpts } from "../gateway-rpc.js"; import { addGatewayClientOptions, callGatewayFromCli } from "../gateway-rpc.js"; import { parsePositiveIntOrUndefined } from "../program/helpers.js"; import { diff --git a/src/cli/cron-cli/shared.ts b/src/cli/cron-cli/shared.ts index 97b2bbae85..8c50ebcdb9 100644 --- a/src/cli/cron-cli/shared.ts +++ b/src/cli/cron-cli/shared.ts @@ -1,11 +1,11 @@ -import type { CronJob, CronSchedule } from "../../cron/types.js"; -import type { GatewayRpcOpts } from "../gateway-rpc.js"; import { listChannelPlugins } from "../../channels/plugins/index.js"; import { parseAbsoluteTimeMs } from "../../cron/parse.js"; import { resolveCronStaggerMs } from "../../cron/stagger.js"; +import type { CronJob, CronSchedule } from "../../cron/types.js"; import { formatDurationHuman } from "../../infra/format-time/format-duration.ts"; import { defaultRuntime } from "../../runtime.js"; import { colorize, isRich, theme } from "../../terminal/theme.js"; +import type { GatewayRpcOpts } from "../gateway-rpc.js"; import { callGatewayFromCli } from "../gateway-rpc.js"; export const getCronChannelOptions = () => diff --git a/src/commands/configure.gateway-auth.ts b/src/commands/configure.gateway-auth.ts index a5560cd63a..14f39229d7 100644 --- a/src/commands/configure.gateway-auth.ts +++ b/src/commands/configure.gateway-auth.ts @@ -1,7 +1,7 @@ +import { ensureAuthProfileStore } from "../agents/auth-profiles.js"; import type { OpenClawConfig, GatewayAuthConfig } from "../config/config.js"; import type { RuntimeEnv } from "../runtime.js"; import type { WizardPrompter } from "../wizard/prompts.js"; -import { ensureAuthProfileStore } from "../agents/auth-profiles.js"; import { promptAuthChoiceGrouped } from "./auth-choice-prompt.js"; import { applyAuthChoice, resolvePreferredProviderForAuthChoice } from "./auth-choice.js"; import { diff --git a/src/config/defaults.ts b/src/config/defaults.ts index c8c1c84192..09605388ac 100644 --- a/src/config/defaults.ts +++ b/src/config/defaults.ts @@ -1,9 +1,9 @@ -import type { OpenClawConfig } from "./types.js"; -import type { ModelDefinitionConfig } from "./types.models.js"; import { DEFAULT_CONTEXT_TOKENS } from "../agents/defaults.js"; import { parseModelRef } from "../agents/model-selection.js"; import { DEFAULT_AGENT_MAX_CONCURRENT, DEFAULT_SUBAGENT_MAX_CONCURRENT } from "./agent-limits.js"; import { resolveTalkApiKey } from "./talk.js"; +import type { OpenClawConfig } from "./types.js"; +import type { ModelDefinitionConfig } from "./types.models.js"; type WarnState = { warned: boolean }; diff --git a/src/cron/normalize.ts b/src/cron/normalize.ts index 198bccf8ec..1cba881b75 100644 --- a/src/cron/normalize.ts +++ b/src/cron/normalize.ts @@ -1,4 +1,3 @@ -import type { CronJobCreate, CronJobPatch } from "./types.js"; import { sanitizeAgentId } from "../routing/session-key.js"; import { isRecord } from "../utils.js"; import { @@ -10,6 +9,7 @@ import { parseAbsoluteTimeMs } from "./parse.js"; import { migrateLegacyCronPayload } from "./payload-migration.js"; import { inferLegacyName } from "./service/normalize.js"; import { normalizeCronStaggerMs, resolveDefaultCronStaggerMs } from "./stagger.js"; +import type { CronJobCreate, CronJobPatch } from "./types.js"; type UnknownRecord = Record; diff --git a/src/cron/service.issue-regressions.test.ts b/src/cron/service.issue-regressions.test.ts index 63f76dd98b..e4ebc413bd 100644 --- a/src/cron/service.issue-regressions.test.ts +++ b/src/cron/service.issue-regressions.test.ts @@ -3,12 +3,12 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import type { CronJob, CronJobState } from "./types.js"; import * as schedule from "./schedule.js"; import { CronService } from "./service.js"; import { computeJobNextRunAtMs } from "./service/jobs.js"; import { createCronServiceState, type CronEvent } from "./service/state.js"; import { onTimer } from "./service/timer.js"; +import type { CronJob, CronJobState } from "./types.js"; const noopLogger = { info: vi.fn(), diff --git a/src/cron/service.jobs.test.ts b/src/cron/service.jobs.test.ts index 7cfe651e8b..4dd44ca010 100644 --- a/src/cron/service.jobs.test.ts +++ b/src/cron/service.jobs.test.ts @@ -1,8 +1,8 @@ import { describe, expect, it } from "vitest"; -import type { CronServiceState } from "./service/state.js"; -import type { CronJob, CronJobPatch } from "./types.js"; import { applyJobPatch, createJob } from "./service/jobs.js"; +import type { CronServiceState } from "./service/state.js"; import { DEFAULT_TOP_OF_HOUR_STAGGER_MS } from "./stagger.js"; +import type { CronJob, CronJobPatch } from "./types.js"; describe("applyJobPatch", () => { it("clears delivery when switching to main session", () => { diff --git a/src/cron/service.jobs.top-of-hour-stagger.test.ts b/src/cron/service.jobs.top-of-hour-stagger.test.ts index c88de3fb8d..9f66acc59a 100644 --- a/src/cron/service.jobs.top-of-hour-stagger.test.ts +++ b/src/cron/service.jobs.top-of-hour-stagger.test.ts @@ -1,8 +1,8 @@ import crypto from "node:crypto"; import { describe, expect, it } from "vitest"; -import type { CronJob } from "./types.js"; import { computeJobNextRunAtMs } from "./service/jobs.js"; import { DEFAULT_TOP_OF_HOUR_STAGGER_MS } from "./stagger.js"; +import type { CronJob } from "./types.js"; function stableOffsetMs(jobId: string, windowMs: number) { const digest = crypto.createHash("sha256").update(jobId).digest(); diff --git a/src/cron/service/jobs.ts b/src/cron/service/jobs.ts index e547ed9de4..623ee9132d 100644 --- a/src/cron/service/jobs.ts +++ b/src/cron/service/jobs.ts @@ -1,4 +1,11 @@ import crypto from "node:crypto"; +import { parseAbsoluteTimeMs } from "../parse.js"; +import { computeNextRunAtMs } from "../schedule.js"; +import { + normalizeCronStaggerMs, + resolveCronStaggerMs, + resolveDefaultCronStaggerMs, +} from "../stagger.js"; import type { CronDelivery, CronDeliveryPatch, @@ -8,14 +15,6 @@ import type { CronPayload, CronPayloadPatch, } from "../types.js"; -import type { CronServiceState } from "./state.js"; -import { parseAbsoluteTimeMs } from "../parse.js"; -import { computeNextRunAtMs } from "../schedule.js"; -import { - normalizeCronStaggerMs, - resolveCronStaggerMs, - resolveDefaultCronStaggerMs, -} from "../stagger.js"; import { normalizeHttpWebhookUrl } from "../webhook-url.js"; import { normalizeOptionalAgentId, @@ -24,6 +23,7 @@ import { normalizePayloadToSystemText, normalizeRequiredName, } from "./normalize.js"; +import type { CronServiceState } from "./state.js"; const STUCK_RUN_MS = 2 * 60 * 60 * 1000; diff --git a/src/cron/service/store.ts b/src/cron/service/store.ts index 506f7a55a4..d150708642 100644 --- a/src/cron/service/store.ts +++ b/src/cron/service/store.ts @@ -1,6 +1,4 @@ import fs from "node:fs"; -import type { CronJob } from "../types.js"; -import type { CronServiceState } from "./state.js"; import { buildDeliveryFromLegacyPayload, hasLegacyDeliveryHints, @@ -10,8 +8,10 @@ import { parseAbsoluteTimeMs } from "../parse.js"; import { migrateLegacyCronPayload } from "../payload-migration.js"; import { normalizeCronStaggerMs, resolveDefaultCronStaggerMs } from "../stagger.js"; import { loadCronStore, saveCronStore } from "../store.js"; +import type { CronJob } from "../types.js"; import { recomputeNextRuns } from "./jobs.js"; import { inferLegacyName, normalizeOptionalText } from "./normalize.js"; +import type { CronServiceState } from "./state.js"; function buildDeliveryPatchFromLegacyPayload(payload: Record) { const deliver = payload.deliver; diff --git a/src/gateway/channel-health-monitor.test.ts b/src/gateway/channel-health-monitor.test.ts index d37ad95d48..4f3992dabb 100644 --- a/src/gateway/channel-health-monitor.test.ts +++ b/src/gateway/channel-health-monitor.test.ts @@ -1,8 +1,8 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { ChannelId } from "../channels/plugins/types.js"; import type { ChannelAccountSnapshot } from "../channels/plugins/types.js"; -import type { ChannelManager, ChannelRuntimeSnapshot } from "./server-channels.js"; import { startChannelHealthMonitor } from "./channel-health-monitor.js"; +import type { ChannelManager, ChannelRuntimeSnapshot } from "./server-channels.js"; function createMockChannelManager(overrides?: Partial): ChannelManager { return { @@ -322,9 +322,9 @@ describe("channel-health-monitor", () => { }); it("runs checks single-flight when restart work is still in progress", async () => { - let releaseStart: (() => void) | null = null; + let releaseStart: (() => void) | undefined; const startGate = new Promise((resolve) => { - releaseStart = resolve; + releaseStart = () => resolve(); }); const manager = createMockChannelManager({ getRuntimeSnapshot: vi.fn(() => diff --git a/src/gateway/channel-health-monitor.ts b/src/gateway/channel-health-monitor.ts index 4461c4cb91..980f652ea3 100644 --- a/src/gateway/channel-health-monitor.ts +++ b/src/gateway/channel-health-monitor.ts @@ -1,6 +1,6 @@ import type { ChannelId } from "../channels/plugins/types.js"; -import type { ChannelManager } from "./server-channels.js"; import { createSubsystemLogger } from "../logging/subsystem.js"; +import type { ChannelManager } from "./server-channels.js"; const log = createSubsystemLogger("gateway/health-monitor"); diff --git a/src/gateway/server-node-events.test.ts b/src/gateway/server-node-events.test.ts index 1874b7e070..a68e72fbd6 100644 --- a/src/gateway/server-node-events.test.ts +++ b/src/gateway/server-node-events.test.ts @@ -1,4 +1,28 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; +import type { OpenClawConfig } from "../config/config.js"; +import type { loadSessionEntry as loadSessionEntryType } from "./session-utils.js"; + +const buildSessionLookup = ( + sessionKey: string, + entry: { + sessionId?: string; + lastChannel?: string; + lastTo?: string; + updatedAt?: number; + } = {}, +): ReturnType => ({ + cfg: { session: { mainKey: "agent:main:main" } } as OpenClawConfig, + storePath: "/tmp/sessions.json", + store: {} as ReturnType["store"], + entry: { + sessionId: entry.sessionId ?? `sid-${sessionKey}`, + updatedAt: entry.updatedAt ?? Date.now(), + lastChannel: entry.lastChannel, + lastTo: entry.lastTo, + }, + canonicalKey: sessionKey, + legacyKey: undefined, +}); vi.mock("../infra/system-events.js", () => ({ enqueueSystemEvent: vi.fn(), @@ -17,11 +41,7 @@ vi.mock("../config/sessions.js", () => ({ updateSessionStore: vi.fn(), })); vi.mock("./session-utils.js", () => ({ - loadSessionEntry: vi.fn((sessionKey: string) => ({ - storePath: "/tmp/sessions.json", - entry: { sessionId: `sid-${sessionKey}` }, - canonicalKey: sessionKey, - })), + loadSessionEntry: vi.fn((sessionKey: string) => buildSessionLookup(sessionKey)), pruneLegacyStoreKeys: vi.fn(), resolveGatewaySessionStoreTarget: vi.fn(({ key }: { key: string }) => ({ canonicalKey: key, @@ -30,12 +50,12 @@ vi.mock("./session-utils.js", () => ({ })); import type { CliDeps } from "../cli/deps.js"; -import type { HealthSummary } from "../commands/health.js"; -import type { NodeEventContext } from "./server-node-events-types.js"; import { agentCommand } from "../commands/agent.js"; +import type { HealthSummary } from "../commands/health.js"; import { updateSessionStore } from "../config/sessions.js"; import { requestHeartbeatNow } from "../infra/heartbeat-wake.js"; import { enqueueSystemEvent } from "../infra/system-events.js"; +import type { NodeEventContext } from "./server-node-events-types.js"; import { handleNodeEvent } from "./server-node-events.js"; import { loadSessionEntry } from "./session-utils.js"; @@ -279,11 +299,7 @@ describe("agent request events", () => { updateSessionStoreMock.mockImplementation(async (_storePath, update) => { update({}); }); - loadSessionEntryMock.mockImplementation((sessionKey: string) => ({ - storePath: "/tmp/sessions.json", - entry: { sessionId: `sid-${sessionKey}` }, - canonicalKey: sessionKey, - })); + loadSessionEntryMock.mockImplementation((sessionKey: string) => buildSessionLookup(sessionKey)); }); it("disables delivery when route is unresolved instead of falling back globally", async () => { @@ -317,12 +333,11 @@ describe("agent request events", () => { it("reuses the current session route when delivery target is omitted", async () => { const ctx = buildCtx(); loadSessionEntryMock.mockReturnValueOnce({ - storePath: "/tmp/sessions.json", - entry: { + ...buildSessionLookup("agent:main:main", { sessionId: "sid-current", lastChannel: "telegram", lastTo: "123", - }, + }), canonicalKey: "agent:main:main", }); diff --git a/src/gateway/server-node-events.ts b/src/gateway/server-node-events.ts index 85f21bf4aa..6ee2714ac8 100644 --- a/src/gateway/server-node-events.ts +++ b/src/gateway/server-node-events.ts @@ -1,5 +1,4 @@ import { randomUUID } from "node:crypto"; -import type { NodeEvent, NodeEventContext } from "./server-node-events-types.js"; import { resolveSessionAgentId } from "../agents/agent-scope.js"; import { normalizeChannelId } from "../channels/plugins/index.js"; import { createOutboundSendDeps } from "../cli/outbound-send-deps.js"; @@ -14,6 +13,7 @@ import { normalizeMainKey } from "../routing/session-key.js"; import { defaultRuntime } from "../runtime.js"; import { parseMessageWithAttachments } from "./chat-attachments.js"; import { normalizeRpcAttachmentsToChatAttachments } from "./server-methods/attachment-normalize.js"; +import type { NodeEvent, NodeEventContext } from "./server-node-events-types.js"; import { loadSessionEntry, pruneLegacyStoreKeys, diff --git a/src/imessage/monitor/deliver.test.ts b/src/imessage/monitor/deliver.test.ts index 51fda4de76..4c771b5fe5 100644 --- a/src/imessage/monitor/deliver.test.ts +++ b/src/imessage/monitor/deliver.test.ts @@ -10,12 +10,13 @@ const convertMarkdownTablesMock = vi.hoisted(() => vi.fn((text: string) => text) const resolveMarkdownTableModeMock = vi.hoisted(() => vi.fn(() => "code")); vi.mock("../send.js", () => ({ - sendMessageIMessage: (...args: unknown[]) => sendMessageIMessageMock(...args), + sendMessageIMessage: (to: string, message: string, opts?: unknown) => + sendMessageIMessageMock(to, message, opts), })); vi.mock("../../auto-reply/chunk.js", () => ({ - chunkTextWithMode: (...args: unknown[]) => chunkTextWithModeMock(...args), - resolveChunkMode: (...args: unknown[]) => resolveChunkModeMock(...args), + chunkTextWithMode: (text: string) => chunkTextWithModeMock(text), + resolveChunkMode: () => resolveChunkModeMock(), })); vi.mock("../../config/config.js", () => ({ @@ -23,11 +24,11 @@ vi.mock("../../config/config.js", () => ({ })); vi.mock("../../config/markdown-tables.js", () => ({ - resolveMarkdownTableMode: (...args: unknown[]) => resolveMarkdownTableModeMock(...args), + resolveMarkdownTableMode: () => resolveMarkdownTableModeMock(), })); vi.mock("../../markdown/tables.js", () => ({ - convertMarkdownTables: (...args: unknown[]) => convertMarkdownTablesMock(...args), + convertMarkdownTables: (text: string) => convertMarkdownTablesMock(text), })); import { deliverReplies } from "./deliver.js"; diff --git a/src/infra/outbound/deliver.test.ts b/src/infra/outbound/deliver.test.ts index f2d01f4d60..cfe6b8a488 100644 --- a/src/infra/outbound/deliver.test.ts +++ b/src/infra/outbound/deliver.test.ts @@ -1,9 +1,9 @@ import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import type { OpenClawConfig } from "../../config/config.js"; import { signalOutbound } from "../../channels/plugins/outbound/signal.js"; import { telegramOutbound } from "../../channels/plugins/outbound/telegram.js"; import { whatsappOutbound } from "../../channels/plugins/outbound/whatsapp.js"; +import type { OpenClawConfig } from "../../config/config.js"; import { STATE_DIR } from "../../config/paths.js"; import { setActivePluginRegistry } from "../../plugins/runtime.js"; import { markdownToSignalTextChunks } from "../../signal/format.js"; diff --git a/src/infra/outbound/deliver.ts b/src/infra/outbound/deliver.ts index 018eea5cc1..908b786e5e 100644 --- a/src/infra/outbound/deliver.ts +++ b/src/infra/outbound/deliver.ts @@ -1,38 +1,38 @@ -import type { ReplyPayload } from "../../auto-reply/types.js"; -import type { - ChannelOutboundAdapter, - ChannelOutboundContext, -} from "../../channels/plugins/types.js"; -import type { OpenClawConfig } from "../../config/config.js"; -import type { sendMessageDiscord } from "../../discord/send.js"; -import type { sendMessageIMessage } from "../../imessage/send.js"; -import type { sendMessageSlack } from "../../slack/send.js"; -import type { sendMessageTelegram } from "../../telegram/send.js"; -import type { sendMessageWhatsApp } from "../../web/outbound.js"; -import type { OutboundIdentity } from "./identity.js"; -import type { NormalizedOutboundPayload } from "./payloads.js"; -import type { OutboundChannel } from "./targets.js"; import { chunkByParagraph, chunkMarkdownTextWithMode, resolveChunkMode, resolveTextChunkLimit, } from "../../auto-reply/chunk.js"; +import type { ReplyPayload } from "../../auto-reply/types.js"; import { resolveChannelMediaMaxBytes } from "../../channels/plugins/media-limits.js"; import { loadChannelOutboundAdapter } from "../../channels/plugins/outbound/load.js"; +import type { + ChannelOutboundAdapter, + ChannelOutboundContext, +} from "../../channels/plugins/types.js"; +import type { OpenClawConfig } from "../../config/config.js"; import { resolveMarkdownTableMode } from "../../config/markdown-tables.js"; import { appendAssistantMessageToSessionTranscript, resolveMirroredTranscriptText, } from "../../config/sessions.js"; +import type { sendMessageDiscord } from "../../discord/send.js"; import { createInternalHookEvent, triggerInternalHook } from "../../hooks/internal-hooks.js"; +import type { sendMessageIMessage } from "../../imessage/send.js"; import { getAgentScopedMediaLocalRoots } from "../../media/local-roots.js"; import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js"; import { markdownToSignalTextChunks, type SignalTextStyleRange } from "../../signal/format.js"; import { sendMessageSignal } from "../../signal/send.js"; +import type { sendMessageSlack } from "../../slack/send.js"; +import type { sendMessageTelegram } from "../../telegram/send.js"; +import type { sendMessageWhatsApp } from "../../web/outbound.js"; import { throwIfAborted } from "./abort.js"; import { ackDelivery, enqueueDelivery, failDelivery } from "./delivery-queue.js"; +import type { OutboundIdentity } from "./identity.js"; +import type { NormalizedOutboundPayload } from "./payloads.js"; import { normalizeReplyPayloadsForDelivery } from "./payloads.js"; +import type { OutboundChannel } from "./targets.js"; export type { NormalizedOutboundPayload } from "./payloads.js"; export { normalizeOutboundPayloads } from "./payloads.js"; diff --git a/src/media-understanding/runner.entries.ts b/src/media-understanding/runner.entries.ts index 138f9c20cc..8f76bd2da7 100644 --- a/src/media-understanding/runner.entries.ts +++ b/src/media-understanding/runner.entries.ts @@ -1,11 +1,11 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { requireApiKey, resolveApiKeyForProvider } from "../agents/model-auth.js"; import { collectProviderApiKeysForExecution, executeWithApiKeyRotation, } from "../agents/api-key-rotation.js"; +import { requireApiKey, resolveApiKeyForProvider } from "../agents/model-auth.js"; import type { MsgContext } from "../auto-reply/templating.js"; import { applyTemplate } from "../auto-reply/templating.js"; import type { OpenClawConfig } from "../config/config.js"; @@ -400,6 +400,7 @@ export async function runProviderEntry(params: { if (!provider.transcribeAudio) { throw new Error(`Audio transcription provider "${providerId}" not available.`); } + const transcribeAudio = provider.transcribeAudio; const media = await params.cache.getBuffer({ attachmentIndex: params.attachmentIndex, maxBytes, @@ -434,7 +435,7 @@ export async function runProviderEntry(params: { provider: providerId, apiKeys, execute: async (apiKey) => - provider.transcribeAudio({ + transcribeAudio({ buffer: media.buffer, fileName: media.fileName, mime: media.mime, @@ -460,6 +461,7 @@ export async function runProviderEntry(params: { if (!provider.describeVideo) { throw new Error(`Video understanding provider "${providerId}" not available.`); } + const describeVideo = provider.describeVideo; const media = await params.cache.getBuffer({ attachmentIndex: params.attachmentIndex, maxBytes, @@ -489,7 +491,7 @@ export async function runProviderEntry(params: { provider: providerId, apiKeys, execute: (apiKey) => - provider.describeVideo({ + describeVideo({ buffer: media.buffer, fileName: media.fileName, mime: media.mime, diff --git a/src/memory/embeddings-gemini.ts b/src/memory/embeddings-gemini.ts index 64612ed7f5..414ad9075b 100644 --- a/src/memory/embeddings-gemini.ts +++ b/src/memory/embeddings-gemini.ts @@ -1,4 +1,3 @@ -import type { EmbeddingProvider, EmbeddingProviderOptions } from "./embeddings.js"; import { collectProviderApiKeysForExecution, executeWithApiKeyRotation, @@ -6,6 +5,7 @@ import { import { requireApiKey, resolveApiKeyForProvider } from "../agents/model-auth.js"; import { parseGeminiAuth } from "../infra/gemini-auth.js"; import { debugEmbeddingsLog } from "./embeddings-debug.js"; +import type { EmbeddingProvider, EmbeddingProviderOptions } from "./embeddings.js"; export type GeminiEmbeddingClient = { baseUrl: string; diff --git a/src/slack/monitor/message-handler/dispatch.ts b/src/slack/monitor/message-handler/dispatch.ts index 8397e5505c..b9c88e3448 100644 --- a/src/slack/monitor/message-handler/dispatch.ts +++ b/src/slack/monitor/message-handler/dispatch.ts @@ -1,10 +1,8 @@ -import type { ReplyPayload } from "../../../auto-reply/types.js"; -import type { SlackStreamSession } from "../../streaming.js"; -import type { PreparedSlackMessage } from "./types.js"; import { resolveHumanDelayConfig } from "../../../agents/identity.js"; import { dispatchInboundMessage } from "../../../auto-reply/dispatch.js"; import { clearHistoryEntriesIfEnabled } from "../../../auto-reply/reply/history.js"; import { createReplyDispatcherWithTyping } from "../../../auto-reply/reply/reply-dispatcher.js"; +import type { ReplyPayload } from "../../../auto-reply/types.js"; import { removeAckReactionAfterReply } from "../../../channels/ack-reactions.js"; import { logAckFailure, logTypingFailure } from "../../../channels/logging.js"; import { createReplyPrefixOptions } from "../../../channels/reply-prefix.js"; @@ -18,9 +16,11 @@ import { buildStatusFinalPreviewText, resolveSlackStreamMode, } from "../../stream-mode.js"; +import type { SlackStreamSession } from "../../streaming.js"; import { appendSlackStream, startSlackStream, stopSlackStream } from "../../streaming.js"; import { resolveSlackThreadTargets } from "../../threading.js"; import { createSlackReplyDeliveryPlan, deliverReplies, resolveSlackThreadTs } from "../replies.js"; +import type { PreparedSlackMessage } from "./types.js"; function hasMedia(payload: ReplyPayload): boolean { return Boolean(payload.mediaUrl) || (payload.mediaUrls?.length ?? 0) > 0; @@ -180,9 +180,11 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag } const text = payload.text.trim(); + let plannedThreadTs: string | undefined; try { if (!streamSession) { const streamThreadTs = replyPlan.nextThreadTs(); + plannedThreadTs = streamThreadTs; if (!streamThreadTs) { logVerbose( "slack-stream: no reply thread target for stream start, falling back to normal delivery", @@ -211,7 +213,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag danger(`slack-stream: streaming API call failed: ${String(err)}, falling back`), ); streamFailed = true; - await deliverNormally(payload, streamSession?.threadTs); + await deliverNormally(payload, streamSession?.threadTs ?? plannedThreadTs); } }; diff --git a/src/telegram/send.test.ts b/src/telegram/send.test.ts index 44c8c694a9..6f955de32b 100644 --- a/src/telegram/send.test.ts +++ b/src/telegram/send.test.ts @@ -1442,9 +1442,7 @@ describe("createForumTopicTelegram", () => { message_thread_id: 272, name: "Build Updates", }); - const api = { createForumTopic } as unknown as { - createForumTopic: typeof createForumTopic; - }; + const api = { createForumTopic } as unknown as Bot["api"]; const result = await createForumTopicTelegram("telegram:group:-1001234567890:topic:271", "x", { token: "tok", @@ -1464,9 +1462,7 @@ describe("createForumTopicTelegram", () => { message_thread_id: 300, name: "Roadmap", }); - const api = { createForumTopic } as unknown as { - createForumTopic: typeof createForumTopic; - }; + const api = { createForumTopic } as unknown as Bot["api"]; await createForumTopicTelegram("-1001234567890", "Roadmap", { token: "tok",