mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-19 18:39:20 -05:00
fix: stabilize model catalog and pi discovery auth storage compatibility
This commit is contained in:
@@ -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: {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -67,6 +67,14 @@ export function __setModelCatalogImportForTest(loader?: () => Promise<PiSdkModul
|
||||
importPiSdk = loader ?? defaultImportPiSdk;
|
||||
}
|
||||
|
||||
function createAuthStorage(AuthStorageLike: unknown, path: string) {
|
||||
const withFactory = AuthStorageLike as { create?: (path: string) => 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<DiscoveredModel>;
|
||||
}
|
||||
| Array<DiscoveredModel>;
|
||||
const authStorage = createAuthStorage(piSdk.AuthStorage, join(agentDir, "auth.json"));
|
||||
const registry = new (piSdk.ModelRegistry as unknown as {
|
||||
new (
|
||||
authStorage: unknown,
|
||||
modelsFile: string,
|
||||
):
|
||||
| Array<DiscoveredModel>
|
||||
| {
|
||||
getAll: () => Array<DiscoveredModel>;
|
||||
};
|
||||
})(authStorage, join(agentDir, "models.json"));
|
||||
const entries = Array.isArray(registry) ? registry : registry.getAll();
|
||||
for (const entry of entries) {
|
||||
const id = String(entry?.id ?? "").trim();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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[],
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<unknown> = {
|
||||
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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 = /^<media:audio>(\s*\([^)]*\))?$/i;
|
||||
|
||||
@@ -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 } }],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 = () =>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 };
|
||||
|
||||
|
||||
@@ -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<string, unknown>;
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<string, unknown>) {
|
||||
const deliver = payload.deliver;
|
||||
|
||||
@@ -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>): 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<void>((resolve) => {
|
||||
releaseStart = resolve;
|
||||
releaseStart = () => resolve();
|
||||
});
|
||||
const manager = createMockChannelManager({
|
||||
getRuntimeSnapshot: vi.fn(() =>
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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<typeof loadSessionEntryType> => ({
|
||||
cfg: { session: { mainKey: "agent:main:main" } } as OpenClawConfig,
|
||||
storePath: "/tmp/sessions.json",
|
||||
store: {} as ReturnType<typeof loadSessionEntryType>["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",
|
||||
});
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user