mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 14:01:24 +08:00
Compare commits
1 Commits
codex/tele
...
fix-plugin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d35b54fe7d |
@@ -318,11 +318,10 @@ curl "https://api.telegram.org/bot<bot_token>/getUpdates"
|
||||
- `channels.telegram.streaming` is `off | partial | block | progress` (default: `partial`)
|
||||
- `progress` keeps one editable status draft for tool progress, clears it at completion, and sends the final answer as a normal message
|
||||
- `streaming.preview.toolProgress` controls whether tool/progress updates reuse the same edited preview message (default: `true` when preview streaming is active)
|
||||
- `streaming.progress.commentary` (default `false`) opts into Codex preamble/commentary text in the temporary progress draft. Commentary is cleaned before display, stays transient, and does not change final answer delivery.
|
||||
- `streaming.preview.commandText` controls command/exec detail inside those tool-progress lines: `raw` (default, preserves released behavior) or `status` (tool label only)
|
||||
- legacy `channels.telegram.streamMode` and boolean `streaming` values are detected; run `openclaw doctor --fix` to migrate them to `channels.telegram.streaming.mode`
|
||||
|
||||
Tool-progress preview updates are the short status lines shown while tools run, for example command execution, file reads, planning updates, and patch summaries. Telegram keeps these enabled by default to match released OpenClaw behavior from `v2026.4.22` and later.
|
||||
Tool-progress preview updates are the short status lines shown while tools run, for example command execution, file reads, planning updates, patch summaries, or Codex preamble/commentary text in Codex app-server mode. Telegram keeps these enabled by default to match released OpenClaw behavior from `v2026.4.22` and later.
|
||||
|
||||
Direct chats can use native Telegram drafts for these tool-progress lines without persisting tool chatter into chat history. Native drafts stop before answer text starts; final answers stay on the normal persistent delivery path. This lane is off by default and should be gated to trusted DM IDs first:
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import { EmbeddedBlockChunker } from "openclaw/plugin-sdk/agent-runtime";
|
||||
import {
|
||||
buildChannelCommentaryProgressDraftLine,
|
||||
createChannelProgressDraftGate,
|
||||
type ChannelProgressDraftLine,
|
||||
formatChannelProgressDraftText,
|
||||
isChannelProgressDraftWorkToolName,
|
||||
mergeChannelProgressDraftLine,
|
||||
normalizeChannelProgressDraftLineIdentity,
|
||||
removeChannelProgressDraftLine,
|
||||
resolveChannelCommentaryProgressLineId,
|
||||
resolveChannelProgressDraftMaxLines,
|
||||
resolveChannelStreamingBlockEnabled,
|
||||
resolveChannelStreamingProgressCommentary,
|
||||
@@ -126,8 +123,10 @@ export function createDiscordDraftPreviewController(params: {
|
||||
});
|
||||
|
||||
const clearProgressDraftLine = async (lineId: string) => {
|
||||
const nextLines = removeChannelProgressDraftLine(previewToolProgressLines, lineId);
|
||||
if (nextLines === previewToolProgressLines) {
|
||||
const nextLines = previewToolProgressLines.filter(
|
||||
(line) => typeof line !== "object" || line.id?.trim() !== lineId,
|
||||
);
|
||||
if (nextLines.length === previewToolProgressLines.length) {
|
||||
return;
|
||||
}
|
||||
previewToolProgressLines = nextLines;
|
||||
@@ -308,20 +307,25 @@ export function createDiscordDraftPreviewController(params: {
|
||||
if (finalReplyStarted || finalReplyDelivered) {
|
||||
return;
|
||||
}
|
||||
const line = buildChannelCommentaryProgressDraftLine({
|
||||
text,
|
||||
itemId: options?.itemId,
|
||||
});
|
||||
if (!line) {
|
||||
const lineId = resolveChannelCommentaryProgressLineId({
|
||||
text,
|
||||
itemId: options?.itemId,
|
||||
});
|
||||
const itemId = options?.itemId?.trim();
|
||||
if (!text && !itemId) {
|
||||
return;
|
||||
}
|
||||
const normalized = normalizeCommentaryProgressText(text ?? "");
|
||||
const lineId = itemId ? `commentary:${itemId}` : normalized ? `commentary:${normalized}` : "";
|
||||
if (!normalized) {
|
||||
if (lineId) {
|
||||
await clearProgressDraftLine(lineId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const line: ChannelProgressDraftLine = {
|
||||
id: lineId,
|
||||
kind: "item",
|
||||
text: normalized,
|
||||
label: "Commentary",
|
||||
prefix: false,
|
||||
};
|
||||
previewToolProgressLines = mergeChannelProgressDraftLine(previewToolProgressLines, line, {
|
||||
maxLines: resolveChannelProgressDraftMaxLines(params.discordConfig),
|
||||
});
|
||||
@@ -465,6 +469,24 @@ function normalizeReasoningProgressLine(text: string): string {
|
||||
.trim();
|
||||
}
|
||||
|
||||
function normalizeCommentaryProgressText(text: string): string {
|
||||
const cleaned = stripInlineDirectiveTagsForDelivery(text).text.trim();
|
||||
if (!cleaned || isSilentCommentaryProgressText(cleaned)) {
|
||||
return "";
|
||||
}
|
||||
return cleaned
|
||||
.split(/\r?\n/u)
|
||||
.map((line) => line.replace(/\s+/g, " ").trim())
|
||||
.filter(Boolean)
|
||||
.map((line) => `_${line}_`)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
function isSilentCommentaryProgressText(text: string): boolean {
|
||||
const normalized = text.replace(/^[\s*_`~]+|[\s*_`~]+$/gu, "").trim();
|
||||
return /^NO_REPLY$/iu.test(normalized);
|
||||
}
|
||||
|
||||
function mergeReasoningProgressText(
|
||||
current: string,
|
||||
incoming: string,
|
||||
|
||||
@@ -2252,40 +2252,4 @@ describe("shouldIgnoreBoundThreadWebhookMessage", () => {
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("does not suppress unbound thread webhook echoes when echo expiry overflows", async () => {
|
||||
const manager = createThreadBindingManager({
|
||||
cfg: DEFAULT_PREFLIGHT_CFG,
|
||||
accountId: "default",
|
||||
persist: false,
|
||||
enableSweeper: false,
|
||||
});
|
||||
const binding = await manager.bindTarget({
|
||||
threadId: "thread-overflow",
|
||||
channelId: "parent-1",
|
||||
targetKind: "subagent",
|
||||
targetSessionKey: "agent:main:subagent:child-1",
|
||||
agentId: "main",
|
||||
webhookId: "wh-overflow",
|
||||
webhookToken: "tok-1",
|
||||
});
|
||||
expect(binding).not.toBeNull();
|
||||
const nowSpy = vi.spyOn(Date, "now").mockReturnValue(8_640_000_000_000_000);
|
||||
try {
|
||||
manager.unbindThread({
|
||||
threadId: "thread-overflow",
|
||||
sendFarewell: false,
|
||||
});
|
||||
} finally {
|
||||
nowSpy.mockRestore();
|
||||
}
|
||||
|
||||
expect(
|
||||
shouldIgnoreBoundThreadWebhookMessage({
|
||||
accountId: "default",
|
||||
threadId: "thread-overflow",
|
||||
webhookId: "wh-overflow",
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { loadJsonFile, saveJsonFile } from "openclaw/plugin-sdk/json-store";
|
||||
import {
|
||||
isFutureDateTimestampMs,
|
||||
resolveExpiresAtMsFromDurationMs,
|
||||
} from "openclaw/plugin-sdk/number-runtime";
|
||||
import { normalizeAccountId, resolveAgentIdFromSessionKey } from "openclaw/plugin-sdk/routing";
|
||||
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
||||
import {
|
||||
@@ -349,14 +345,9 @@ export function rememberRecentUnboundWebhookEcho(record: ThreadBindingRecord) {
|
||||
if (!bindingKey) {
|
||||
return;
|
||||
}
|
||||
const expiresAt = resolveExpiresAtMsFromDurationMs(RECENT_UNBOUND_WEBHOOK_ECHO_WINDOW_MS);
|
||||
if (expiresAt === undefined) {
|
||||
RECENT_UNBOUND_WEBHOOK_ECHOES_BY_BINDING_KEY.delete(bindingKey);
|
||||
return;
|
||||
}
|
||||
RECENT_UNBOUND_WEBHOOK_ECHOES_BY_BINDING_KEY.set(bindingKey, {
|
||||
webhookId,
|
||||
expiresAt,
|
||||
expiresAt: Date.now() + RECENT_UNBOUND_WEBHOOK_ECHO_WINDOW_MS,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -417,7 +408,7 @@ export function isRecentlyUnboundThreadWebhookMessage(params: {
|
||||
if (!suppressed) {
|
||||
return false;
|
||||
}
|
||||
if (!isFutureDateTimestampMs(suppressed.expiresAt)) {
|
||||
if (suppressed.expiresAt <= Date.now()) {
|
||||
RECENT_UNBOUND_WEBHOOK_ECHOES_BY_BINDING_KEY.delete(bindingKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { MAX_DATE_TIMESTAMP_MS } from "openclaw/plugin-sdk/number-runtime";
|
||||
import {
|
||||
testing as sessionBindingTesting,
|
||||
registerSessionBindingAdapter,
|
||||
@@ -322,58 +321,6 @@ describe("matrix monitor handler pairing account scope", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("does not reuse account-scoped allowFrom cache while the process clock is invalid", async () => {
|
||||
const readAllowFromStore = vi.fn(async () => [] as string[]);
|
||||
const nowSpy = vi.spyOn(Date, "now");
|
||||
const { handler } = createMatrixHandlerTestHarness({
|
||||
readAllowFromStore,
|
||||
dmPolicy: "pairing",
|
||||
buildPairingReply: () => "pairing",
|
||||
});
|
||||
const makeEvent = (id: string): MatrixRawEvent =>
|
||||
createMatrixTextMessageEvent({
|
||||
eventId: id,
|
||||
body: "@room hello",
|
||||
mentions: { room: true },
|
||||
});
|
||||
|
||||
try {
|
||||
nowSpy.mockReturnValue(Number.NaN);
|
||||
await handler("!room:example.org", makeEvent("$event1"));
|
||||
await handler("!room:example.org", makeEvent("$event2"));
|
||||
|
||||
expect(readAllowFromStore).toHaveBeenCalledTimes(2);
|
||||
} finally {
|
||||
nowSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("does not cache account-scoped allowFrom reads when cache expiry overflows", async () => {
|
||||
const readAllowFromStore = vi.fn(async () => [] as string[]);
|
||||
const nowSpy = vi.spyOn(Date, "now");
|
||||
const { handler } = createMatrixHandlerTestHarness({
|
||||
readAllowFromStore,
|
||||
dmPolicy: "pairing",
|
||||
buildPairingReply: () => "pairing",
|
||||
});
|
||||
const makeEvent = (id: string): MatrixRawEvent =>
|
||||
createMatrixTextMessageEvent({
|
||||
eventId: id,
|
||||
body: "@room hello",
|
||||
mentions: { room: true },
|
||||
});
|
||||
|
||||
try {
|
||||
nowSpy.mockReturnValue(MAX_DATE_TIMESTAMP_MS);
|
||||
await handler("!room:example.org", makeEvent("$event1"));
|
||||
await handler("!room:example.org", makeEvent("$event2"));
|
||||
|
||||
expect(readAllowFromStore).toHaveBeenCalledTimes(2);
|
||||
} finally {
|
||||
nowSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("pins direct-message main route updates to the configured owner", async () => {
|
||||
const { handler, recordInboundSession } = createMatrixHandlerTestHarness({
|
||||
cfg: {
|
||||
|
||||
@@ -27,10 +27,6 @@ import {
|
||||
resolveChannelContextVisibilityMode,
|
||||
} from "openclaw/plugin-sdk/context-visibility-runtime";
|
||||
import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
|
||||
import {
|
||||
isFutureDateTimestampMs,
|
||||
resolveExpiresAtMsFromDurationMs,
|
||||
} from "openclaw/plugin-sdk/number-runtime";
|
||||
import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
|
||||
import { buildInboundHistoryFromEntries } from "openclaw/plugin-sdk/reply-history";
|
||||
import {
|
||||
@@ -514,13 +510,9 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
||||
|
||||
const readStoreAllowFrom = async (): Promise<string[]> => {
|
||||
const now = Date.now();
|
||||
if (
|
||||
cachedStoreAllowFrom &&
|
||||
isFutureDateTimestampMs(cachedStoreAllowFrom.expiresAtMs, { nowMs: now })
|
||||
) {
|
||||
if (cachedStoreAllowFrom && now < cachedStoreAllowFrom.expiresAtMs) {
|
||||
return cachedStoreAllowFrom.value;
|
||||
}
|
||||
cachedStoreAllowFrom = null;
|
||||
const value = await core.channel.pairing
|
||||
.readAllowFromStore({
|
||||
channel: "matrix",
|
||||
@@ -528,10 +520,10 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
||||
accountId,
|
||||
})
|
||||
.catch(() => []);
|
||||
const expiresAtMs = resolveExpiresAtMsFromDurationMs(ALLOW_FROM_STORE_CACHE_TTL_MS, {
|
||||
nowMs: now,
|
||||
});
|
||||
cachedStoreAllowFrom = expiresAtMs === undefined ? null : { value, expiresAtMs };
|
||||
cachedStoreAllowFrom = {
|
||||
value,
|
||||
expiresAtMs: now + ALLOW_FROM_STORE_CACHE_TTL_MS,
|
||||
};
|
||||
return value;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { computeFileHash, getCachedFileInfo, setCachedFileInfo } from "./upload-cache.js";
|
||||
|
||||
describe("qqbot upload-cache", () => {
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("reuses cached file info before expiry", () => {
|
||||
const hash = computeFileHash("qqbot-cache-hit");
|
||||
|
||||
setCachedFileInfo(hash, "group", "target-hit", 1, "file-info-hit", "uuid-hit", 3600);
|
||||
|
||||
expect(getCachedFileInfo(hash, "group", "target-hit", 1)).toBe("file-info-hit");
|
||||
});
|
||||
|
||||
it("drops cached file info when the current clock is invalid", () => {
|
||||
const hash = computeFileHash("qqbot-invalid-clock");
|
||||
setCachedFileInfo(hash, "group", "target-invalid-clock", 1, "file-info-invalid", "uuid", 3600);
|
||||
vi.spyOn(Date, "now").mockReturnValue(Number.NaN);
|
||||
|
||||
expect(getCachedFileInfo(hash, "group", "target-invalid-clock", 1)).toBeNull();
|
||||
});
|
||||
|
||||
it("does not cache file info when ttl expiry exceeds the Date range", () => {
|
||||
vi.spyOn(Date, "now").mockReturnValue(8_640_000_000_000_000);
|
||||
const hash = computeFileHash("qqbot-overflow");
|
||||
|
||||
setCachedFileInfo(hash, "group", "target-overflow", 1, "file-info-overflow", "uuid", 3600);
|
||||
|
||||
expect(getCachedFileInfo(hash, "group", "target-overflow", 1)).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -4,10 +4,6 @@
|
||||
*/
|
||||
|
||||
import * as crypto from "node:crypto";
|
||||
import {
|
||||
isFutureDateTimestampMs,
|
||||
resolveExpiresAtMsFromDurationSeconds,
|
||||
} from "openclaw/plugin-sdk/number-runtime";
|
||||
import type { ChatScope } from "../types.js";
|
||||
import { debugLog } from "./log.js";
|
||||
|
||||
@@ -50,7 +46,7 @@ export function getCachedFileInfo(
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isFutureDateTimestampMs(entry.expiresAt)) {
|
||||
if (Date.now() >= entry.expiresAt) {
|
||||
cache.delete(key);
|
||||
return null;
|
||||
}
|
||||
@@ -72,7 +68,7 @@ export function setCachedFileInfo(
|
||||
if (cache.size >= MAX_CACHE_SIZE) {
|
||||
const now = Date.now();
|
||||
for (const [k, v] of cache) {
|
||||
if (!isFutureDateTimestampMs(v.expiresAt, { nowMs: now })) {
|
||||
if (now >= v.expiresAt) {
|
||||
cache.delete(k);
|
||||
}
|
||||
}
|
||||
@@ -87,16 +83,11 @@ export function setCachedFileInfo(
|
||||
const key = buildCacheKey(contentHash, scope, targetId, fileType);
|
||||
const safetyMargin = 60;
|
||||
const effectiveTtl = Math.max(ttl - safetyMargin, 10);
|
||||
const expiresAt = resolveExpiresAtMsFromDurationSeconds(effectiveTtl);
|
||||
if (expiresAt === undefined) {
|
||||
cache.delete(key);
|
||||
return;
|
||||
}
|
||||
|
||||
cache.set(key, {
|
||||
fileInfo,
|
||||
fileUuid,
|
||||
expiresAt,
|
||||
expiresAt: Date.now() + effectiveTtl * 1000,
|
||||
});
|
||||
|
||||
debugLog(
|
||||
|
||||
@@ -2076,118 +2076,6 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
expect(deliverReplies).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("hides Telegram commentary progress unless explicitly enabled", async () => {
|
||||
const { answerDraftStream } = setupDraftStreams({ answerMessageId: 2001 });
|
||||
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ replyOptions }) => {
|
||||
await replyOptions?.onItemEvent?.({
|
||||
itemId: "preamble-1",
|
||||
kind: "preamble",
|
||||
progressText: "Checking private context before replying.",
|
||||
});
|
||||
return { queuedFinal: false };
|
||||
});
|
||||
|
||||
await dispatchWithContext({
|
||||
context: createContext(),
|
||||
streamMode: "progress",
|
||||
telegramCfg: { streaming: { mode: "progress", progress: { label: false } } },
|
||||
});
|
||||
|
||||
expect(answerDraftStream.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows opt-in Telegram commentary progress through the shared progress draft", async () => {
|
||||
const { answerDraftStream } = setupDraftStreams({ answerMessageId: 2001 });
|
||||
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ replyOptions }) => {
|
||||
await replyOptions?.onItemEvent?.({
|
||||
itemId: "preamble-1",
|
||||
kind: "preamble",
|
||||
progressText: "Checking the current weather source before summarizing.",
|
||||
});
|
||||
await replyOptions?.onItemEvent?.({
|
||||
itemId: "preamble-1",
|
||||
kind: "preamble",
|
||||
progressText: "Checking the current weather source before summarizing clearly.",
|
||||
});
|
||||
await replyOptions?.onItemEvent?.({
|
||||
itemId: "preamble-2",
|
||||
kind: "preamble",
|
||||
progressText: "[[reply_to_current]] Checking route impacts.",
|
||||
});
|
||||
await replyOptions?.onItemEvent?.({
|
||||
itemId: "preamble-2",
|
||||
kind: "preamble",
|
||||
progressText: "NO_REPLY",
|
||||
});
|
||||
await replyOptions?.onToolStart?.({ name: "exec", phase: "start" });
|
||||
return { queuedFinal: false };
|
||||
});
|
||||
|
||||
await dispatchWithContext({
|
||||
context: createContext(),
|
||||
streamMode: "progress",
|
||||
telegramCfg: {
|
||||
streaming: {
|
||||
mode: "progress",
|
||||
progress: {
|
||||
label: false,
|
||||
toolProgress: false,
|
||||
commentary: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(answerDraftStream.update).toHaveBeenLastCalledWith(
|
||||
"_Checking the current weather source before summarizing clearly._",
|
||||
);
|
||||
const updates = answerDraftStream.update.mock.calls.map((call) => call[0]).join("\n");
|
||||
expect(updates).not.toContain("Exec");
|
||||
expect(updates).not.toContain("reply_to_current");
|
||||
expect(updates).not.toContain("NO_REPLY");
|
||||
});
|
||||
|
||||
it("keeps Telegram progress drafts usable after the last commentary line becomes silent", async () => {
|
||||
const { answerDraftStream } = setupDraftStreams({ answerMessageId: 2001 });
|
||||
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ replyOptions }) => {
|
||||
await replyOptions?.onItemEvent?.({
|
||||
itemId: "preamble-1",
|
||||
kind: "preamble",
|
||||
progressText: "Temporary note.",
|
||||
});
|
||||
await replyOptions?.onItemEvent?.({
|
||||
itemId: "preamble-1",
|
||||
kind: "preamble",
|
||||
progressText: "NO_REPLY",
|
||||
});
|
||||
await replyOptions?.onToolStart?.({ name: "exec", phase: "start" });
|
||||
return { queuedFinal: false };
|
||||
});
|
||||
|
||||
await dispatchWithContext({
|
||||
context: createContext(),
|
||||
streamMode: "progress",
|
||||
telegramCfg: {
|
||||
streaming: {
|
||||
mode: "progress",
|
||||
progress: {
|
||||
label: false,
|
||||
commentary: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(answerDraftStream.clear).toHaveBeenCalled();
|
||||
expect(answerDraftStream.forceNewMessage).toHaveBeenCalled();
|
||||
const clearOrder = answerDraftStream.clear.mock.invocationCallOrder[0];
|
||||
const forceNewMessageOrder = answerDraftStream.forceNewMessage.mock.invocationCallOrder[0];
|
||||
const lastUpdateOrder = answerDraftStream.update.mock.invocationCallOrder.at(-1)!;
|
||||
expect(clearOrder).toBeLessThan(forceNewMessageOrder);
|
||||
expect(forceNewMessageOrder).toBeLessThan(lastUpdateOrder);
|
||||
expect(answerDraftStream.update).toHaveBeenLastCalledWith("`🛠️ Exec`");
|
||||
});
|
||||
|
||||
it("does not restart progress drafts after final answer delivery", async () => {
|
||||
const { answerDraftStream } = setupDraftStreams({ answerMessageId: 2001 });
|
||||
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
projectOutboundPayloadPlanForDelivery,
|
||||
} from "openclaw/plugin-sdk/channel-outbound";
|
||||
import {
|
||||
buildChannelCommentaryProgressDraftLine,
|
||||
buildChannelProgressDraftLineForEntry,
|
||||
createChannelProgressDraftGate,
|
||||
type ChannelProgressDraftLine,
|
||||
@@ -32,11 +31,8 @@ import {
|
||||
formatChannelProgressDraftText,
|
||||
isChannelProgressDraftWorkToolName,
|
||||
mergeChannelProgressDraftLine,
|
||||
removeChannelProgressDraftLine,
|
||||
resolveChannelCommentaryProgressLineId,
|
||||
resolveChannelProgressDraftMaxLines,
|
||||
resolveChannelStreamingBlockEnabled,
|
||||
resolveChannelStreamingProgressCommentary,
|
||||
resolveChannelStreamingPreviewNativeToolProgress,
|
||||
resolveChannelStreamingPreviewNativeToolProgressAllowFrom,
|
||||
resolveChannelStreamingPreviewToolProgress,
|
||||
@@ -924,8 +920,6 @@ export const dispatchTelegramMessage = async ({
|
||||
const reasoningLane = lanes.reasoning;
|
||||
const streamToolProgressEnabled =
|
||||
Boolean(answerLane.stream) && resolveChannelStreamingPreviewToolProgress(telegramCfg);
|
||||
const commentaryProgressEnabled =
|
||||
Boolean(answerLane.stream) && resolveChannelStreamingProgressCommentary(telegramCfg);
|
||||
const nativeToolProgressDraft =
|
||||
streamToolProgressEnabled &&
|
||||
!isRoomEvent &&
|
||||
@@ -988,26 +982,6 @@ export const dispatchTelegramMessage = async ({
|
||||
});
|
||||
let finalAnswerDeliveryStarted = false;
|
||||
let finalAnswerDelivered = false;
|
||||
const clearStreamProgressDraftLine = async (lineId: string) => {
|
||||
const nextLines = removeChannelProgressDraftLine(streamToolProgressLines, lineId);
|
||||
if (nextLines === streamToolProgressLines) {
|
||||
return false;
|
||||
}
|
||||
streamToolProgressLines = nextLines;
|
||||
if (!progressDraftGate.hasStarted) {
|
||||
return true;
|
||||
}
|
||||
if (await renderProgressDraft()) {
|
||||
return true;
|
||||
}
|
||||
answerLane.lastPartialText = "";
|
||||
answerLane.hasStreamedMessage = false;
|
||||
answerLane.finalized = false;
|
||||
resetAnswerToolProgressDraft();
|
||||
await answerLane.stream?.clear();
|
||||
answerLane.stream?.forceNewMessage();
|
||||
return true;
|
||||
};
|
||||
const pushStreamToolProgress = async (
|
||||
line?: string | ChannelProgressDraftLine,
|
||||
options?: { toolName?: string; startImmediately?: boolean },
|
||||
@@ -1088,34 +1062,6 @@ export const dispatchTelegramMessage = async ({
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const pushCommentaryProgress = async (text?: string, options?: { itemId?: string }) => {
|
||||
if (!answerLane.stream || streamMode !== "progress" || !commentaryProgressEnabled) {
|
||||
return false;
|
||||
}
|
||||
if (answerLane.finalized || finalAnswerDeliveryStarted || finalAnswerDelivered) {
|
||||
return false;
|
||||
}
|
||||
const line = buildChannelCommentaryProgressDraftLine({
|
||||
text,
|
||||
itemId: options?.itemId,
|
||||
});
|
||||
if (!line) {
|
||||
const lineId = resolveChannelCommentaryProgressLineId({
|
||||
text,
|
||||
itemId: options?.itemId,
|
||||
});
|
||||
return lineId ? await clearStreamProgressDraftLine(lineId) : false;
|
||||
}
|
||||
const nextLines = mergeChannelProgressDraftLine(streamToolProgressLines, line, {
|
||||
maxLines: resolveChannelProgressDraftMaxLines(telegramCfg),
|
||||
});
|
||||
if (nextLines === streamToolProgressLines) {
|
||||
return false;
|
||||
}
|
||||
streamToolProgressLines = nextLines;
|
||||
await progressDraftGate.startNow();
|
||||
return await renderProgressDraft();
|
||||
};
|
||||
let splitReasoningOnNextStream = false;
|
||||
let draftLaneEventQueue = Promise.resolve();
|
||||
const reasoningStepState = createTelegramReasoningStepState();
|
||||
@@ -2057,12 +2003,6 @@ export const dispatchTelegramMessage = async ({
|
||||
await progressPromise;
|
||||
},
|
||||
onItemEvent: async (payload) => {
|
||||
if (payload.kind === "preamble") {
|
||||
await pushCommentaryProgress(payload.progressText, {
|
||||
itemId: payload.itemId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
await pushStreamToolProgress(
|
||||
buildChannelProgressDraftLineForEntry(telegramCfg, {
|
||||
event: "item",
|
||||
|
||||
@@ -89,10 +89,6 @@ export const telegramChannelConfigUiHints = {
|
||||
label: "Telegram Progress Tool Lines",
|
||||
help: "Show compact tool/progress lines in progress draft mode (default: true). Set false to keep only the label until final delivery.",
|
||||
},
|
||||
"streaming.progress.commentary": {
|
||||
label: "Telegram Progress Commentary",
|
||||
help: "Show assistant commentary/preamble text in the temporary progress draft. Final answer delivery is unchanged.",
|
||||
},
|
||||
"streaming.progress.commandText": {
|
||||
label: "Telegram Progress Command Text",
|
||||
help: 'Command/exec detail in progress draft lines: "raw" preserves released behavior; "status" shows only the tool label.',
|
||||
|
||||
@@ -336,37 +336,6 @@ describe("RealtimeCallHandler path routing", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects stream sessions when token expiry would exceed the Date range", async () => {
|
||||
const processEvent = vi.fn();
|
||||
const createBridge = vi.fn(() => makeBridge());
|
||||
const handler = makeHandler(undefined, {
|
||||
manager: {
|
||||
processEvent,
|
||||
},
|
||||
provider: {
|
||||
name: "telnyx",
|
||||
},
|
||||
realtimeProvider: makeRealtimeProvider(createBridge),
|
||||
});
|
||||
handler.setPublicUrl("https://public.example/voice/webhook");
|
||||
const nowSpy = vi.spyOn(Date, "now").mockReturnValue(8_640_000_000_000_000);
|
||||
const session = handler.issueStreamSession({
|
||||
providerName: "telnyx",
|
||||
callId: "call-overflow",
|
||||
direction: "inbound",
|
||||
});
|
||||
nowSpy.mockRestore();
|
||||
const server = await startStreamSessionServer(handler, session.streamUrl);
|
||||
|
||||
try {
|
||||
await expect(connectWs(server.url)).rejects.toThrow("Unexpected server response: 401");
|
||||
expect(createBridge).not.toHaveBeenCalled();
|
||||
expect(processEvent).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
await server.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects Telnyx stream starts that do not match the token-bound call", async () => {
|
||||
const processEvent = vi.fn();
|
||||
const getCall = vi.fn(
|
||||
|
||||
@@ -3,10 +3,6 @@ import http from "node:http";
|
||||
import type { Duplex } from "node:stream";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||
import {
|
||||
isFutureDateTimestampMs,
|
||||
resolveExpiresAtMsFromDurationMs,
|
||||
} from "openclaw/plugin-sdk/number-runtime";
|
||||
import {
|
||||
buildRealtimeVoiceAgentConsultWorkingResponse,
|
||||
createRealtimeVoiceForcedConsultCoordinator,
|
||||
@@ -506,13 +502,9 @@ export class RealtimeCallHandler {
|
||||
|
||||
private issueStreamToken(meta: Omit<PendingStreamToken, "expiry"> = {}): string {
|
||||
const token = randomUUID();
|
||||
const now = Date.now();
|
||||
const expiry = resolveExpiresAtMsFromDurationMs(STREAM_TOKEN_TTL_MS, { nowMs: now });
|
||||
if (expiry !== undefined) {
|
||||
this.pendingStreamTokens.set(token, { expiry, ...meta });
|
||||
}
|
||||
this.pendingStreamTokens.set(token, { expiry: Date.now() + STREAM_TOKEN_TTL_MS, ...meta });
|
||||
for (const [candidate, entry] of this.pendingStreamTokens) {
|
||||
if (!isFutureDateTimestampMs(entry.expiry, { nowMs: now })) {
|
||||
if (Date.now() > entry.expiry) {
|
||||
this.pendingStreamTokens.delete(candidate);
|
||||
}
|
||||
}
|
||||
@@ -525,7 +517,7 @@ export class RealtimeCallHandler {
|
||||
return null;
|
||||
}
|
||||
this.pendingStreamTokens.delete(token);
|
||||
if (!isFutureDateTimestampMs(entry.expiry)) {
|
||||
if (Date.now() > entry.expiry) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -5,10 +5,8 @@ import path from "node:path";
|
||||
import { extensionForMime } from "openclaw/plugin-sdk/media-mime";
|
||||
import {
|
||||
asFiniteNumberInRange,
|
||||
isFutureDateTimestampMs,
|
||||
parseStrictFiniteNumber,
|
||||
parseStrictNonNegativeInteger,
|
||||
resolveExpiresAtMsFromDurationMs,
|
||||
resolveTimerTimeoutMs,
|
||||
} from "openclaw/plugin-sdk/number-runtime";
|
||||
import { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
|
||||
@@ -849,7 +847,7 @@ function readCachedGroupContext(profile: string, groupId: string): ZaloGroupCont
|
||||
if (!cached) {
|
||||
return null;
|
||||
}
|
||||
if (!isFutureDateTimestampMs(cached.expiresAt)) {
|
||||
if (cached.expiresAt <= Date.now()) {
|
||||
groupContextCache.delete(key);
|
||||
return null;
|
||||
}
|
||||
@@ -861,7 +859,7 @@ function readCachedGroupContext(profile: string, groupId: string): ZaloGroupCont
|
||||
|
||||
function trimGroupContextCache(now: number): void {
|
||||
for (const [key, value] of groupContextCache) {
|
||||
if (isFutureDateTimestampMs(value.expiresAt, { nowMs: now })) {
|
||||
if (value.expiresAt > now) {
|
||||
continue;
|
||||
}
|
||||
groupContextCache.delete(key);
|
||||
@@ -881,13 +879,9 @@ function writeCachedGroupContext(profile: string, context: ZaloGroupContext): vo
|
||||
if (groupContextCache.has(key)) {
|
||||
groupContextCache.delete(key);
|
||||
}
|
||||
const expiresAt = resolveExpiresAtMsFromDurationMs(GROUP_CONTEXT_CACHE_TTL_MS, { nowMs: now });
|
||||
if (expiresAt === undefined) {
|
||||
return;
|
||||
}
|
||||
groupContextCache.set(key, {
|
||||
value: context,
|
||||
expiresAt,
|
||||
expiresAt: now + GROUP_CONTEXT_CACHE_TTL_MS,
|
||||
});
|
||||
trimGroupContextCache(now);
|
||||
}
|
||||
@@ -985,9 +979,6 @@ function toInboundMessage(message: Message, ownUserId?: string): ZaloInboundMess
|
||||
|
||||
export const testing = {
|
||||
toInboundMessage,
|
||||
readCachedGroupContext,
|
||||
writeCachedGroupContext,
|
||||
clearCachedGroupContext,
|
||||
};
|
||||
export { testing as __testing };
|
||||
|
||||
|
||||
@@ -75,30 +75,3 @@ describe("Zalo inbound timestamp normalization", () => {
|
||||
expect(inboundTimestamp("9007199254740993")).toBe(1_700_000_000_000);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Zalo group context cache", () => {
|
||||
afterEach(() => {
|
||||
zaloTesting.clearCachedGroupContext("cache-profile");
|
||||
});
|
||||
|
||||
it("drops cached group context when the current clock is invalid", () => {
|
||||
zaloTesting.writeCachedGroupContext("cache-profile", {
|
||||
groupId: "group-invalid-clock",
|
||||
name: "Cached",
|
||||
});
|
||||
vi.spyOn(Date, "now").mockReturnValue(Number.NaN);
|
||||
|
||||
expect(zaloTesting.readCachedGroupContext("cache-profile", "group-invalid-clock")).toBeNull();
|
||||
});
|
||||
|
||||
it("does not cache group context when ttl expiry exceeds the Date range", () => {
|
||||
vi.spyOn(Date, "now").mockReturnValue(8_640_000_000_000_000);
|
||||
|
||||
zaloTesting.writeCachedGroupContext("cache-profile", {
|
||||
groupId: "group-overflow",
|
||||
name: "Overflow",
|
||||
});
|
||||
|
||||
expect(zaloTesting.readCachedGroupContext("cache-profile", "group-overflow")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { coerceSecretRef } from "../config/types.secrets.js";
|
||||
import { normalizeOptionalString } from "../shared/string-coerce.js";
|
||||
import type { AuthProfileCredential, AuthProfileStore } from "./auth-profiles.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
type AgentApiKeyCredential = { type: "api_key"; key: string };
|
||||
type AgentOAuthCredential = {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import path from "node:path";
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { Model } from "../llm/types.js";
|
||||
import { normalizeModelCompat } from "../plugins/provider-model-compat.js";
|
||||
@@ -15,6 +14,7 @@ import {
|
||||
} from "./agent-auth-discovery.js";
|
||||
import { resolveModelPluginMetadataSnapshot } from "./model-discovery-context.js";
|
||||
import type { PluginModelCatalogMetadataSnapshot } from "./plugin-model-catalog.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
import {
|
||||
AuthStorage,
|
||||
ModelRegistry,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { AgentCompactionMode } from "../config/types.agent-defaults.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { ContextEngineInfo } from "../context-engine/types.js";
|
||||
import { MIN_PROMPT_BUDGET_RATIO, MIN_PROMPT_BUDGET_TOKENS } from "./agent-compaction-constants.js";
|
||||
import { resolveProviderEndpoint } from "./provider-attribution.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
export const DEFAULT_AGENT_COMPACTION_RESERVE_TOKENS_FLOOR = 20_000;
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { getLoadedChannelPlugin } from "../channels/plugins/index.js";
|
||||
import { resolveSessionConversation } from "../channels/plugins/session-conversation.js";
|
||||
import { DEFAULT_SUBAGENT_MAX_SPAWN_DEPTH } from "../config/agent-limits.js";
|
||||
@@ -22,6 +21,7 @@ import {
|
||||
import { normalizeMessageChannel } from "../utils/message-channel.js";
|
||||
import { resolveAgentConfig, resolveAgentIdFromSessionKey } from "./agent-scope.js";
|
||||
import type { AnyAgentTool } from "./agent-tools.types.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
import { pickSandboxToolPolicy } from "./sandbox-tool-policy.js";
|
||||
import type { SandboxToolPolicy } from "./sandbox.js";
|
||||
import {
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import {
|
||||
findNormalizedProviderValue,
|
||||
normalizeProviderId,
|
||||
} from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { normalizeUniqueStringEntries } from "../shared/string-normalization.js";
|
||||
import {
|
||||
@@ -15,6 +11,7 @@ import { resolveEffectiveOAuthCredential } from "./auth-profiles/effective-oauth
|
||||
import { resolveAuthProfileOrder } from "./auth-profiles/order.js";
|
||||
import type { AuthProfileCredential, AuthProfileStore } from "./auth-profiles/types.js";
|
||||
import { resolveProviderIdForAuth } from "./provider-auth-aliases.js";
|
||||
import { findNormalizedProviderValue, normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
type AuthProfileSource = "store";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { buildProviderAuthDoctorHintWithPlugin } from "../../plugins/provider-runtime.runtime.js";
|
||||
import { normalizeProviderId } from "../provider-id.js";
|
||||
import type { AuthProfileStore } from "./types.js";
|
||||
|
||||
const QWEN_PORTAL_OAUTH_MIGRATION_HINT =
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import {
|
||||
findNormalizedProviderValue,
|
||||
normalizeProviderId,
|
||||
} from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { resolveCliRuntimeExecutionProvider } from "../model-runtime-aliases.js";
|
||||
import { resolveProviderIdForAuth } from "../provider-auth-aliases.js";
|
||||
import { findNormalizedProviderValue, normalizeProviderId } from "../provider-id.js";
|
||||
import { CLAUDE_CLI_PROFILE_ID } from "./constants.js";
|
||||
import type { AuthProfileStore } from "./types.js";
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import {
|
||||
resolveAgentModelFallbackValues,
|
||||
resolveAgentModelPrimaryValue,
|
||||
} from "../../config/model-input.js";
|
||||
import type { AgentModelConfig } from "../../config/types.agents-shared.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { normalizeProviderId } from "../provider-id.js";
|
||||
|
||||
export type ExternalCliAuthScope = {
|
||||
providerIds: string[];
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import {
|
||||
readClaudeCliCredentialsCached,
|
||||
readCodexCliCredentialsCached,
|
||||
readMiniMaxCliCredentialsCached,
|
||||
} from "../cli-credentials.js";
|
||||
import { normalizeProviderId } from "../provider-id.js";
|
||||
import {
|
||||
CLAUDE_CLI_PROFILE_ID,
|
||||
EXTERNAL_CLI_SYNC_TTL_MS,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { sanitizeForLog } from "../../../packages/terminal-core/src/ansi.js";
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import { normalizeProviderId } from "../provider-id.js";
|
||||
|
||||
export type OAuthRefreshFailureReason =
|
||||
| "refresh_token_reused"
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import {
|
||||
findNormalizedProviderValue,
|
||||
normalizeProviderId,
|
||||
} from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { resolveProviderIdForAuth } from "../provider-auth-aliases.js";
|
||||
import { findNormalizedProviderValue, normalizeProviderId } from "../provider-id.js";
|
||||
import {
|
||||
evaluateStoredCredentialEligibility,
|
||||
type AuthCredentialReasonCode,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { resolveOAuthPath } from "../../config/paths.js";
|
||||
import { coerceSecretRef } from "../../config/types.secrets.js";
|
||||
import { loadJsonFile } from "../../infra/json-file.js";
|
||||
import { isRecord } from "../../shared/record-coerce.js";
|
||||
import { uniqueStrings } from "../../shared/string-normalization.js";
|
||||
import { asBoolean } from "../../utils/boolean.js";
|
||||
import { normalizeProviderId } from "../provider-id.js";
|
||||
import { AUTH_STORE_VERSION, log } from "./constants.js";
|
||||
import {
|
||||
isLegacyOAuthRef,
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import {
|
||||
findNormalizedProviderKey,
|
||||
normalizeProviderId,
|
||||
} from "@openclaw/model-catalog-core/provider-id";
|
||||
import { normalizeStringEntries } from "../../shared/string-normalization.js";
|
||||
import { normalizeSecretInput } from "../../utils/normalize-secret-input.js";
|
||||
import { resolveProviderIdForAuth } from "../provider-auth-aliases.js";
|
||||
import { findNormalizedProviderKey, normalizeProviderId } from "../provider-id.js";
|
||||
import { dedupeProfileIds, listProfilesForProvider } from "./profile-list.js";
|
||||
import {
|
||||
ensureAuthProfileStoreForLocalUpdate,
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import {
|
||||
findNormalizedProviderKey,
|
||||
normalizeProviderId,
|
||||
} from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { AuthProfileConfig } from "../../config/types.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { findNormalizedProviderKey, normalizeProviderId } from "../provider-id.js";
|
||||
import { resolveAuthProfileMetadata } from "./identity.js";
|
||||
import { dedupeProfileIds, listProfilesForProvider } from "./profile-list.js";
|
||||
import type { AuthProfileIdRepairResult, AuthProfileStore } from "./types.js";
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import fs from "node:fs";
|
||||
import { isDeepStrictEqual } from "node:util";
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { loadJsonFile, repairJsonFilePermissions, saveJsonFile } from "../../infra/json-file.js";
|
||||
import { asFiniteNumber } from "../../shared/number-coercion.js";
|
||||
import { isRecord } from "../../shared/record-coerce.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import { normalizeTrimmedStringList } from "../../shared/string-normalization.js";
|
||||
import { normalizeProviderId } from "../provider-id.js";
|
||||
import { AUTH_STORE_VERSION } from "./constants.js";
|
||||
import { resolveAuthStatePath } from "./paths.js";
|
||||
import type {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { normalizeProviderId } from "../provider-id.js";
|
||||
import type { AuthProfileStore, ProfileUsageStats } from "./types.js";
|
||||
|
||||
export function isAuthCooldownBypassedForProvider(provider: string | undefined): boolean {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||
import {
|
||||
positiveSecondsToSafeMilliseconds,
|
||||
resolveExpiresAtMsFromEpochSeconds,
|
||||
} from "../../shared/number-coercion.js";
|
||||
import { normalizeProviderId } from "../provider-id.js";
|
||||
import { resolveProviderRequestHeaders } from "../provider-request-config.js";
|
||||
import { logAuthProfileFailureStateChange } from "./state-observation.js";
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { MAX_DATE_TIMESTAMP_MS } from "../shared/number-coercion.js";
|
||||
import {
|
||||
consumeExecApprovalFollowupRuntimeHandoff,
|
||||
resetExecApprovalFollowupRuntimeHandoffsForTests,
|
||||
} from "./bash-tools.exec-approval-followup-state.js";
|
||||
import {
|
||||
buildExecApprovalPendingToolResult,
|
||||
createExecApprovalPendingState,
|
||||
enforceStrictInlineEvalApprovalBoundary,
|
||||
MAX_EXEC_APPROVAL_FOLLOWUP_FAILURE_LOG_KEYS as maxExecApprovalFollowupFailureLogKeys,
|
||||
resolveExecApprovalUnavailableState,
|
||||
@@ -41,60 +39,6 @@ vi.mock("../infra/exec-approvals.js", async (importOriginal) => {
|
||||
};
|
||||
});
|
||||
|
||||
describe("createExecApprovalPendingState", () => {
|
||||
it("sets a valid pending approval expiry from the current clock", () => {
|
||||
const nowSpy = vi.spyOn(Date, "now");
|
||||
try {
|
||||
nowSpy.mockReturnValue(1_800_000_000_000);
|
||||
|
||||
expect(
|
||||
createExecApprovalPendingState({
|
||||
warnings: ["careful"],
|
||||
timeoutMs: 60_000,
|
||||
}),
|
||||
).toMatchObject({
|
||||
warningText: "careful\n\n",
|
||||
expiresAtMs: 1_800_000_060_000,
|
||||
preResolvedDecision: undefined,
|
||||
});
|
||||
} finally {
|
||||
nowSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("expires pending approvals immediately while the process clock is invalid", () => {
|
||||
const nowSpy = vi.spyOn(Date, "now");
|
||||
try {
|
||||
nowSpy.mockReturnValue(Number.NaN);
|
||||
|
||||
expect(
|
||||
createExecApprovalPendingState({
|
||||
warnings: [],
|
||||
timeoutMs: 60_000,
|
||||
}).expiresAtMs,
|
||||
).toBe(0);
|
||||
} finally {
|
||||
nowSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("expires pending approvals immediately when expiry would exceed Date bounds", () => {
|
||||
const nowSpy = vi.spyOn(Date, "now");
|
||||
try {
|
||||
nowSpy.mockReturnValue(MAX_DATE_TIMESTAMP_MS);
|
||||
|
||||
expect(
|
||||
createExecApprovalPendingState({
|
||||
warnings: [],
|
||||
timeoutMs: 60_000,
|
||||
}).expiresAtMs,
|
||||
).toBe(0);
|
||||
} finally {
|
||||
nowSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("sendExecApprovalFollowupResult", () => {
|
||||
const sendExecApprovalFollowup = vi.fn();
|
||||
const logWarn = vi.fn();
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
type ExecSecurity,
|
||||
} from "../infra/exec-approvals.js";
|
||||
import { logWarn } from "../logger.js";
|
||||
import { resolveExpiresAtMsFromDurationMs } from "../shared/number-coercion.js";
|
||||
import { registerExecApprovalFollowupRuntimeHandoff } from "./bash-tools.exec-approval-followup-state.js";
|
||||
import { sendExecApprovalFollowup } from "./bash-tools.exec-approval-followup.js";
|
||||
import {
|
||||
@@ -64,8 +63,6 @@ export type ExecApprovalRequestState = ExecApprovalPendingState & {
|
||||
noticeSeconds: number;
|
||||
};
|
||||
|
||||
const EXPIRED_EXEC_APPROVAL_EXPIRES_AT_MS = 0;
|
||||
|
||||
export type ExecApprovalUnavailableReason =
|
||||
| "no-approval-route"
|
||||
| "initiating-platform-disabled"
|
||||
@@ -114,11 +111,9 @@ export function createExecApprovalPendingState(params: {
|
||||
warnings: string[];
|
||||
timeoutMs: number;
|
||||
}): ExecApprovalPendingState {
|
||||
const expiresAtMs =
|
||||
resolveExpiresAtMsFromDurationMs(params.timeoutMs) ?? EXPIRED_EXEC_APPROVAL_EXPIRES_AT_MS;
|
||||
return {
|
||||
warningText: params.warnings.length ? `${params.warnings.join("\n")}\n\n` : "",
|
||||
expiresAtMs,
|
||||
expiresAtMs: Date.now() + params.timeoutMs,
|
||||
preResolvedDecision: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { CliBackendConfig } from "../config/types.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { ContextEngineHostCapability } from "../context-engine/types.js";
|
||||
@@ -19,6 +18,7 @@ import type {
|
||||
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
|
||||
import { uniqueStrings } from "../shared/string-normalization.js";
|
||||
import { mergePluginTextTransforms } from "./plugin-text-transforms.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
type CliBackendsDeps = {
|
||||
resolvePluginSetupCliBackend: typeof resolvePluginSetupCliBackend;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { findNormalizedProviderValue } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { resolveProviderEndpoint } from "./provider-attribution.js";
|
||||
import { findNormalizedProviderValue } from "./provider-id.js";
|
||||
|
||||
export const CONTEXT_WINDOW_HARD_MIN_TOKENS = 4_000;
|
||||
export const CONTEXT_WINDOW_WARN_BELOW_TOKENS = 8_000;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { normalizeOptionalLowercaseString } from "../../shared/string-coerce.js";
|
||||
import { normalizeProviderId } from "../provider-id.js";
|
||||
import type { AgentMessage } from "../runtime/index.js";
|
||||
|
||||
const THREAD_SUFFIX_REGEX = /^(.*)(?::(?:thread|topic):\d+)$/i;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { planManifestModelCatalogRows } from "../../model-catalog/manifest-planner.js";
|
||||
import type { NormalizedModelCatalogRow } from "../../model-catalog/types.js";
|
||||
import type { ProviderRuntimeModel } from "../../plugins/provider-runtime-model.types.js";
|
||||
import { DEFAULT_CONTEXT_TOKENS } from "../defaults.js";
|
||||
import { listOpenClawPluginManifestMetadata } from "../../plugins/manifest-metadata-scan.js";
|
||||
import { loadPluginManifestRegistry } from "../../plugins/manifest-registry.js";
|
||||
import type { PluginManifestRecord } from "../../plugins/manifest-registry.js";
|
||||
import { loadPluginManifest } from "../../plugins/manifest.js";
|
||||
import type { ProviderRuntimeModel } from "../../plugins/provider-runtime-model.types.js";
|
||||
import { DEFAULT_CONTEXT_TOKENS } from "../defaults.js";
|
||||
import { normalizeStaticProviderModelId } from "../model-ref-shared.js";
|
||||
import { normalizeProviderId } from "../provider-id.js";
|
||||
|
||||
function rowMatchesModel(params: {
|
||||
row: NormalizedModelCatalogRow;
|
||||
@@ -28,8 +28,8 @@ function rowMatchesModel(params: {
|
||||
function normalizeStaticCatalogInput(
|
||||
input: NormalizedModelCatalogRow["input"],
|
||||
): ProviderRuntimeModel["input"] {
|
||||
const normalizedInput = input.filter(
|
||||
(item): item is "text" | "image" => item === "text" || item === "image",
|
||||
const normalizedInput = input.filter((item): item is "text" | "image" =>
|
||||
item === "text" || item === "image"
|
||||
);
|
||||
return normalizedInput.length > 0 ? normalizedInput : ["text"];
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { isRecord } from "../utils.js";
|
||||
import { OPENCLAW_AGENT_RUNTIME_ID, isDefaultAgentRuntimeId } from "./agent-runtime-id.js";
|
||||
import { normalizeOptionalAgentRuntimeId } from "./agent-runtime-id.js";
|
||||
import { resolveAgentHarnessPolicy } from "./harness/policy.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
function normalizeConfiguredRuntimeId(value: unknown): string | undefined {
|
||||
return normalizeOptionalAgentRuntimeId(value);
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import {
|
||||
findNormalizedProviderValue,
|
||||
normalizeProviderId,
|
||||
} from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { Model } from "../llm/types.js";
|
||||
import type {
|
||||
@@ -12,6 +8,7 @@ import type { ProviderResolveDynamicModelContext } from "../plugins/types.js";
|
||||
import { createLazyImportLoader } from "../shared/lazy-promise.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
|
||||
import { listPrioritizedHighSignalLiveModelRefs } from "./live-model-filter.js";
|
||||
import { findNormalizedProviderValue, normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
type ProviderRuntimeModule = typeof import("../plugins/provider-runtime.js");
|
||||
type DynamicModelResolver = typeof runProviderDynamicModel;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { resolveProviderModernModelRef } from "../plugins/provider-runtime.js";
|
||||
import { parseStrictNonNegativeInteger } from "../shared/number-coercion.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
|
||||
import { liveProvidersShareOwningPlugin } from "./live-provider-owner.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
type ModelRef = {
|
||||
provider?: string | null;
|
||||
|
||||
@@ -14,8 +14,8 @@ import {
|
||||
} from "./model-selection.js";
|
||||
export { LiveSessionModelSwitchError } from "./live-model-switch-error.js";
|
||||
export type LiveSessionModelSelection = EmbeddedRunModelSwitchRequest;
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { normalizeOptionalString } from "../shared/string-coerce.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
const OPENAI_PROVIDER_ID = "openai";
|
||||
const OPENAI_CODEX_PROVIDER_ID = "openai-codex";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { resolveOwningPluginIdsForProviderRef } from "../plugins/providers.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
type LiveProviderOwnerContext = {
|
||||
config?: OpenClawConfig;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { normalizeGooglePreviewModelId } from "@openclaw/model-catalog-core/provider-model-id-normalize";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { normalizeGooglePreviewModelId } from "../plugin-sdk/provider-model-id-normalize.js";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalLowercaseString,
|
||||
} from "../shared/string-coerce.js";
|
||||
import { liveProvidersShareOwningPlugin } from "./live-provider-owner.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
type ModelTarget = {
|
||||
raw: string;
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import {
|
||||
findNormalizedProviderValue,
|
||||
normalizeProviderId,
|
||||
} from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig, MemorySearchConfig } from "../config/config.js";
|
||||
import { resolveStateDir } from "../config/paths.js";
|
||||
import type { SecretInput } from "../config/types.secrets.js";
|
||||
@@ -17,6 +13,7 @@ import { getMemoryEmbeddingProvider } from "../plugins/memory-embedding-provider
|
||||
import { normalizeStringEntries, uniqueStrings } from "../shared/string-normalization.js";
|
||||
import { clampInt, clampNumber, resolveUserPath } from "../utils.js";
|
||||
import { resolveAgentConfig } from "./agent-scope.js";
|
||||
import { findNormalizedProviderValue, normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
export type ResolvedMemorySearchConfig = {
|
||||
enabled: boolean;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import { normalizeProviderIdForAuth } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { getShellEnvAppliedKeys } from "../infra/shell-env.js";
|
||||
import { resolvePluginSetupProvider } from "../plugins/setup-registry.js";
|
||||
@@ -9,6 +8,7 @@ import { normalizeOptionalString as normalizeOptionalPathInput } from "../shared
|
||||
import { normalizeOptionalSecretInput } from "../utils/normalize-secret-input.js";
|
||||
import { resolveProviderEnvAuthLookupMaps } from "./model-auth-env-vars.js";
|
||||
import { GCP_VERTEX_CREDENTIALS_MARKER } from "./model-auth-markers.js";
|
||||
import { normalizeProviderIdForAuth } from "./provider-id.js";
|
||||
|
||||
export type EnvApiKeyResult = {
|
||||
apiKey: string;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalString,
|
||||
} from "../shared/string-coerce.js";
|
||||
import type { ModelCatalogEntry, ModelInputType } from "./model-catalog.types.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
export function modelSupportsInput(
|
||||
entry: ModelCatalogEntry | undefined,
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import {
|
||||
findNormalizedProviderValue,
|
||||
normalizeProviderId,
|
||||
} from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { normalizeUniqueSingleOrTrimmedStringList } from "../shared/string-normalization.js";
|
||||
import { findNormalizedProviderValue, normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
function dedupeCatalogScopeRefs(values: Array<string | undefined>): string[] {
|
||||
return normalizeUniqueSingleOrTrimmedStringList(values);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { getRuntimeConfig } from "../config/config.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
@@ -38,6 +37,7 @@ import {
|
||||
listPluginModelCatalogFiles,
|
||||
type PluginModelCatalogMetadataSnapshot,
|
||||
} from "./plugin-model-catalog.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
const log = createSubsystemLogger("model-catalog");
|
||||
const AGENT_CUSTOM_MODEL_DEFAULT_CONTEXT_WINDOW = 128_000;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { listCliRuntimeProviderIds } from "./cli-backends.js";
|
||||
import { isCliRuntimeProvider } from "./model-runtime-aliases.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
const RETIRED_MODEL_PICKER_PROVIDERS = new Set(["codex", "codex-cli"]);
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { normalizeProviderModelIdWithManifest } from "../plugins/manifest-model-id-normalization.js";
|
||||
import {
|
||||
collectManifestModelIdNormalizationPolicies,
|
||||
type ManifestModelIdNormalizationRecord,
|
||||
normalizeBuiltInProviderModelId,
|
||||
normalizeConfiguredProviderCatalogModelRef,
|
||||
normalizeConfiguredProviderCatalogModelId as normalizeConfiguredProviderCatalogModelIdShared,
|
||||
normalizeStaticProviderModelIdWithPolicies,
|
||||
} from "@openclaw/model-catalog-core/provider-model-id-normalization";
|
||||
import { normalizeProviderModelIdWithManifest } from "../plugins/manifest-model-id-normalization.js";
|
||||
} from "../shared/provider-model-id-normalization.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
type StaticModelRef = {
|
||||
provider: string;
|
||||
@@ -19,22 +20,6 @@ export type ProviderModelIdNormalizationOptions = {
|
||||
manifestPlugins?: readonly ManifestModelIdNormalizationRecord[];
|
||||
};
|
||||
|
||||
export type ManifestModelIdNormalizationProvider = {
|
||||
aliases?: Record<string, string>;
|
||||
stripPrefixes?: string[];
|
||||
prefixWhenBare?: string;
|
||||
prefixWhenBareAfterAliasStartsWith?: {
|
||||
modelPrefix: string;
|
||||
prefix: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
export type ManifestModelIdNormalizationRecord = {
|
||||
modelIdNormalization?: {
|
||||
providers?: Record<string, ManifestModelIdNormalizationProvider>;
|
||||
};
|
||||
};
|
||||
|
||||
export function modelKey(provider: string, model: string): string {
|
||||
const providerId = provider.trim();
|
||||
const modelId = model.trim();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
|
||||
import {
|
||||
@@ -9,6 +8,7 @@ import {
|
||||
} from "./cli-backends.js";
|
||||
import { resolveModelRuntimePolicy } from "./model-runtime-policy.js";
|
||||
import { resolveProviderIdForAuth } from "./provider-auth-aliases.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
const RUNTIME_COMPARISON_PROVIDER_ALIASES = new Map<string, string>([["openai-codex", "openai"]]);
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { AgentModelEntryConfig } from "../config/types.agent-defaults.js";
|
||||
import type { AgentRuntimePolicyConfig } from "../config/types.agents-shared.js";
|
||||
import type { ModelDefinitionConfig, ModelProviderConfig } from "../config/types.models.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { normalizeAgentId } from "../routing/session-key.js";
|
||||
import { listAgentEntries, resolveSessionAgentIds } from "./agent-scope.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
export type ModelRuntimePolicySource = "model" | "provider";
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { Type } from "typebox";
|
||||
import { formatErrorMessage } from "../infra/errors.js";
|
||||
import { getEnvApiKey } from "../llm/env-api-keys.js";
|
||||
@@ -12,6 +11,7 @@ import {
|
||||
normalizeOptionalString,
|
||||
} from "../shared/string-coerce.js";
|
||||
import { normalizeStringEntries, uniqueStrings } from "../shared/string-normalization.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
const OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
|
||||
const DEFAULT_TIMEOUT_MS = 12_000;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import {
|
||||
findNormalizedProviderKey as findNormalizedProviderKeyCore,
|
||||
findNormalizedProviderValue as findNormalizedProviderValueCore,
|
||||
normalizeProviderId as normalizeProviderIdCore,
|
||||
normalizeProviderIdForAuth as normalizeProviderIdForAuthCore,
|
||||
} from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
|
||||
import { modelKey as sharedModelKey, normalizeStaticProviderModelId } from "./model-ref-shared.js";
|
||||
import {
|
||||
findNormalizedProviderKey,
|
||||
findNormalizedProviderValue,
|
||||
normalizeProviderId,
|
||||
normalizeProviderIdForAuth,
|
||||
} from "./provider-id.js";
|
||||
import { normalizeProviderModelIdWithRuntime } from "./provider-model-normalization.runtime.js";
|
||||
|
||||
export type ModelRef = {
|
||||
@@ -33,27 +33,12 @@ export function legacyModelKey(provider: string, model: string): string | null {
|
||||
return rawKey === canonicalKey ? null : rawKey;
|
||||
}
|
||||
|
||||
export function normalizeProviderId(provider: string): string {
|
||||
return normalizeProviderIdCore(provider);
|
||||
}
|
||||
|
||||
export function normalizeProviderIdForAuth(provider: string): string {
|
||||
return normalizeProviderIdForAuthCore(provider);
|
||||
}
|
||||
|
||||
export function findNormalizedProviderValue<T>(
|
||||
entries: Record<string, T> | undefined,
|
||||
provider: string,
|
||||
): T | undefined {
|
||||
return findNormalizedProviderValueCore(entries, provider);
|
||||
}
|
||||
|
||||
export function findNormalizedProviderKey(
|
||||
entries: Record<string, unknown> | undefined,
|
||||
provider: string,
|
||||
): string | undefined {
|
||||
return findNormalizedProviderKeyCore(entries, provider);
|
||||
}
|
||||
export {
|
||||
findNormalizedProviderKey,
|
||||
findNormalizedProviderValue,
|
||||
normalizeProviderId,
|
||||
normalizeProviderIdForAuth,
|
||||
};
|
||||
|
||||
function normalizeProviderModelId(
|
||||
provider: string,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import {
|
||||
buildManifestBuiltInModelSuppressionResolver,
|
||||
resolveManifestBuiltInModelSuppression,
|
||||
} from "../plugins/manifest-model-suppression.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
function resolveBuiltInModelSuppressionFromManifest(params: {
|
||||
provider?: string | null;
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import {
|
||||
findNormalizedProviderValue,
|
||||
normalizeProviderId,
|
||||
} from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { formatErrorMessage } from "../infra/errors.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
@@ -31,6 +27,7 @@ import {
|
||||
createProviderApiKeyResolver,
|
||||
createProviderAuthResolver,
|
||||
} from "./models-config.providers.secrets.js";
|
||||
import { findNormalizedProviderValue, normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
const log = createSubsystemLogger("agents/model-providers");
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { resolveSecretInputRef } from "../config/types.secrets.js";
|
||||
import { resolveProviderSyntheticAuthWithPlugin } from "../plugins/provider-runtime.js";
|
||||
@@ -21,6 +20,7 @@ import {
|
||||
type ProviderAuthResolver,
|
||||
} from "./models-config.providers.secret-helpers.js";
|
||||
import { resolveProviderIdForAuth } from "./provider-auth-aliases.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
export type {
|
||||
ProfileApiKeyResolution,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { writeSync } from "node:fs";
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { type Api, completeSimple, type Model } from "openclaw/plugin-sdk/llm";
|
||||
import { Type } from "typebox";
|
||||
import { describe, expect, it } from "vitest";
|
||||
@@ -63,6 +62,7 @@ import {
|
||||
import { getApiKeyForModel, requireApiKey } from "./model-auth.js";
|
||||
import { shouldSuppressBuiltInModel } from "./model-suppression.js";
|
||||
import { ensureOpenClawModelsJson } from "./models-config.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
import { prepareModelForSimpleCompletion } from "./simple-completion-transport.js";
|
||||
|
||||
const LIVE = isLiveTestEnabled();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
export const OPENAI_PROVIDER_ID = "openai";
|
||||
export const OPENAI_CODEX_PROVIDER_ID = "openai-codex";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { existsSync, readdirSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.types.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
export const PLUGIN_MODEL_CATALOG_FILE = "catalog.json";
|
||||
export const PLUGIN_MODEL_CATALOG_GENERATED_BY = "openclaw-plugin-model-catalog-v1";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { listOpenClawPluginManifestMetadata } from "../plugins/manifest-metadata-scan.js";
|
||||
import { isRecord } from "../shared/record-coerce.js";
|
||||
import {
|
||||
@@ -10,6 +9,7 @@ import { normalizeTrimmedStringList } from "../shared/string-normalization.js";
|
||||
import { asBoolean } from "../utils/boolean.js";
|
||||
import type { RuntimeVersionEnv } from "../version.js";
|
||||
import { resolveRuntimeServiceVersion } from "../version.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
type ProviderAttributionVerification =
|
||||
| "vendor-documented"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { normalizePluginsConfig } from "../plugins/config-state.js";
|
||||
import { getCurrentPluginMetadataSnapshot } from "../plugins/current-plugin-metadata-snapshot.js";
|
||||
@@ -11,6 +10,7 @@ import { resolvePluginControlPlaneFingerprint } from "../plugins/plugin-control-
|
||||
import { loadPluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js";
|
||||
import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.types.js";
|
||||
import type { PluginOrigin } from "../plugins/plugin-origin.types.js";
|
||||
import { normalizeProviderId } from "./provider-id.js";
|
||||
|
||||
export type ProviderAuthAliasLookupParams = {
|
||||
config?: OpenClawConfig;
|
||||
|
||||
6
src/agents/provider-id.ts
Normal file
6
src/agents/provider-id.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export {
|
||||
findNormalizedProviderKey,
|
||||
findNormalizedProviderValue,
|
||||
normalizeProviderId,
|
||||
normalizeProviderIdForAuth,
|
||||
} from "../../packages/model-catalog-core/src/provider-id.js";
|
||||
@@ -1,7 +1,3 @@
|
||||
import {
|
||||
findNormalizedProviderValue,
|
||||
normalizeProviderId,
|
||||
} from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { extractModelCompat } from "../plugins/provider-model-compat.js";
|
||||
import type { ProviderRuntimeModel } from "../plugins/provider-runtime-model.types.js";
|
||||
@@ -16,6 +12,7 @@ import { resolveEffectiveToolPolicy } from "./agent-tools.policy.js";
|
||||
import { resolveModel } from "./embedded-agent-runner/model.js";
|
||||
import { resolveBundledStaticCatalogModel } from "./embedded-agent-runner/model.static-catalog.js";
|
||||
import { normalizeStaticProviderModelId } from "./model-ref-shared.js";
|
||||
import { findNormalizedProviderValue, normalizeProviderId } from "./provider-id.js";
|
||||
import { normalizeToolName } from "./tool-policy.js";
|
||||
import {
|
||||
buildEffectiveToolInventoryGroups,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import {
|
||||
findCapabilityProviderById,
|
||||
resolveCapabilityModelRefForProviders,
|
||||
@@ -20,6 +19,7 @@ import {
|
||||
import { uniqueStrings } from "../../shared/string-normalization.js";
|
||||
import type { AuthProfileStore } from "../auth-profiles/types.js";
|
||||
import { normalizeModelRef } from "../model-selection.js";
|
||||
import { normalizeProviderId } from "../provider-id.js";
|
||||
import {
|
||||
ToolInputError,
|
||||
readPositiveIntegerParam,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { resolveAgentDir, resolveSessionAgentId } from "../../agents/agent-scope.js";
|
||||
import { resolveContextTokensForModel } from "../../agents/context.js";
|
||||
import { resolveAgentHarnessPolicy } from "../../agents/harness/selection.js";
|
||||
@@ -7,6 +6,7 @@ import {
|
||||
OPENAI_PROVIDER_ID,
|
||||
resolveContextConfigProviderForRuntime,
|
||||
} from "../../agents/openai-codex-routing.js";
|
||||
import { normalizeProviderId } from "../../agents/provider-id.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { logVerbose } from "../../globals.js";
|
||||
import { createLazyImportLoader } from "../../shared/lazy-promise.js";
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import crypto from "node:crypto";
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import {
|
||||
clearAutoFallbackPrimaryProbeSelection,
|
||||
hasSessionAutoModelFallbackProvenance,
|
||||
@@ -13,6 +12,7 @@ import { resolveFastModeState } from "../../agents/fast-mode.js";
|
||||
import { runAgentHarnessBeforeMessageWriteHook } from "../../agents/harness/hook-helpers.js";
|
||||
import { resolveAgentHarnessPolicy } from "../../agents/harness/selection.js";
|
||||
import { listOpenAIAuthProfileProvidersForAgentRuntime } from "../../agents/openai-codex-routing.js";
|
||||
import { normalizeProviderId } from "../../agents/provider-id.js";
|
||||
import { resolveIngressWorkspaceOverrideForSpawnedRun } from "../../agents/spawned-context.js";
|
||||
import type { SilentReplyPromptMode } from "../../agents/system-prompt.types.js";
|
||||
import { normalizeChatType } from "../../channels/chat-type.js";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { splitTrailingAuthProfile } from "../../agents/model-ref-profile.js";
|
||||
import { isModelKeyAllowedBySet } from "../../agents/model-selection-shared.js";
|
||||
import { normalizeProviderId } from "../../agents/provider-id.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js";
|
||||
|
||||
export type ModelAliasIndex = {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { ModelCatalogEntry } from "../../agents/model-catalog.types.js";
|
||||
import {
|
||||
buildAllowedModelSetWithFallbacks,
|
||||
isModelKeyAllowedBySet,
|
||||
} from "../../agents/model-selection-shared.js";
|
||||
import { normalizeProviderId } from "../../agents/provider-id.js";
|
||||
import { resolveAgentModelFallbackValues } from "../../config/model-input.js";
|
||||
import type { SessionEntry } from "../../config/sessions.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { normalizeProviderId } from "../agents/provider-id.js";
|
||||
import {
|
||||
BASE_THINKING_LEVELS,
|
||||
normalizeThinkLevel,
|
||||
|
||||
@@ -12,7 +12,6 @@ import type {
|
||||
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
|
||||
import { normalizeTrimmedStringList } from "../shared/string-normalization.js";
|
||||
import { asBoolean } from "../utils/boolean.js";
|
||||
import { stripInlineDirectiveTagsForDelivery } from "../utils/directive-tags.js";
|
||||
|
||||
export type {
|
||||
ChannelDeliveryStreamingConfig,
|
||||
@@ -276,7 +275,6 @@ export type ChannelProgressDraftLine = {
|
||||
status?: string;
|
||||
toolName?: string;
|
||||
prefix?: boolean;
|
||||
format?: boolean;
|
||||
};
|
||||
|
||||
function compactStrings(values: readonly (string | undefined | null)[]): string[] {
|
||||
@@ -515,70 +513,6 @@ export function buildChannelProgressDraftLine(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function normalizeChannelCommentaryProgressText(text: string): string {
|
||||
const cleaned = stripInlineDirectiveTagsForDelivery(text).text.trim();
|
||||
if (!cleaned || isSilentChannelCommentaryProgressText(cleaned)) {
|
||||
return "";
|
||||
}
|
||||
return cleaned
|
||||
.split(/\r?\n/u)
|
||||
.map((line) => line.replace(/\s+/g, " ").trim())
|
||||
.filter(Boolean)
|
||||
.map((line) => `_${line}_`)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
export function resolveChannelCommentaryProgressLineId(params: {
|
||||
text?: string;
|
||||
itemId?: string;
|
||||
}): string | undefined {
|
||||
const itemId = params.itemId?.trim();
|
||||
if (itemId) {
|
||||
return `commentary:${itemId}`;
|
||||
}
|
||||
const normalized = normalizeChannelCommentaryProgressText(params.text ?? "");
|
||||
return normalized ? `commentary:${normalized}` : undefined;
|
||||
}
|
||||
|
||||
export function buildChannelCommentaryProgressDraftLine(params: {
|
||||
text?: string;
|
||||
itemId?: string;
|
||||
}): ChannelProgressDraftLine | undefined {
|
||||
const normalized = normalizeChannelCommentaryProgressText(params.text ?? "");
|
||||
const itemId = params.itemId?.trim();
|
||||
const lineId = itemId ? `commentary:${itemId}` : normalized ? `commentary:${normalized}` : "";
|
||||
if (!normalized || !lineId) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
id: lineId,
|
||||
kind: "item",
|
||||
text: normalized,
|
||||
label: "Commentary",
|
||||
prefix: false,
|
||||
format: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function removeChannelProgressDraftLine<TLine extends string | ChannelProgressDraftLine>(
|
||||
lines: TLine[],
|
||||
lineId: string,
|
||||
): TLine[] {
|
||||
const normalizedLineId = lineId.trim();
|
||||
if (!normalizedLineId) {
|
||||
return lines;
|
||||
}
|
||||
const nextLines = lines.filter(
|
||||
(line) => typeof line !== "object" || line.id?.trim() !== normalizedLineId,
|
||||
);
|
||||
return nextLines.length === lines.length ? lines : nextLines;
|
||||
}
|
||||
|
||||
function isSilentChannelCommentaryProgressText(text: string): boolean {
|
||||
const normalized = text.replace(/^[\s*_`~]+|[\s*_`~]+$/gu, "").trim();
|
||||
return /^NO_REPLY$/iu.test(normalized);
|
||||
}
|
||||
|
||||
export function createChannelProgressDraftGate(params: {
|
||||
onStart: () => void | Promise<void>;
|
||||
initialDelayMs?: number;
|
||||
@@ -1055,17 +989,14 @@ export function formatChannelProgressDraftText(params: {
|
||||
? line
|
||||
: getProgressDraftLineText(line);
|
||||
const text = compactChannelProgressDraftLine(rawText, maxLineChars);
|
||||
const format =
|
||||
!isLabelLine && typeof line === "object" && line !== null ? line.format !== false : true;
|
||||
return text ? { text, isLabelLine, prefix, format } : undefined;
|
||||
return text ? { text, isLabelLine, prefix } : undefined;
|
||||
})
|
||||
.filter(
|
||||
(line): line is { text: string; isLabelLine: boolean; prefix: boolean; format: boolean } =>
|
||||
Boolean(line),
|
||||
.filter((line): line is { text: string; isLabelLine: boolean; prefix: boolean } =>
|
||||
Boolean(line),
|
||||
)
|
||||
.slice(-maxLines)
|
||||
.map(({ text, isLabelLine, prefix, format }) => {
|
||||
const formatted = isLabelLine || !format ? text : formatLine(text);
|
||||
.map(({ text, isLabelLine, prefix }) => {
|
||||
const formatted = isLabelLine ? text : formatLine(text);
|
||||
return {
|
||||
text:
|
||||
!isLabelLine && prefix && shouldPrefixProgressLine(text)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { collectConfiguredModelRefs } from "@openclaw/model-catalog-core/configured-model-refs";
|
||||
import { splitTrailingAuthProfile } from "../agents/model-ref-profile.js";
|
||||
import { collectConfiguredModelRefs } from "../config/model-refs.js";
|
||||
import type { AuthProfileConfig } from "../config/types.auth.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import {
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import fsSync from "node:fs";
|
||||
import {
|
||||
findNormalizedProviderValue,
|
||||
normalizeProviderId,
|
||||
} from "@openclaw/model-catalog-core/provider-id";
|
||||
import { note } from "../../packages/terminal-core/src/note.js";
|
||||
import {
|
||||
resolveAgentDir,
|
||||
@@ -16,6 +12,7 @@ import {
|
||||
resolveEnvApiKey,
|
||||
resolveUsableCustomProviderApiKey,
|
||||
} from "../agents/model-auth.js";
|
||||
import { findNormalizedProviderValue, normalizeProviderId } from "../agents/provider-id.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { formatErrorMessage } from "../infra/errors.js";
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import fs from "node:fs";
|
||||
import { AGENT_MODEL_CONFIG_KEYS } from "@openclaw/model-catalog-core/configured-model-refs";
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { normalizeOptionalAgentRuntimeId } from "../../../agents/agent-runtime-id.js";
|
||||
import { resolveConfiguredProviderFallback } from "../../../agents/configured-provider-fallback.js";
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../../../agents/defaults.js";
|
||||
@@ -8,6 +6,8 @@ import { splitTrailingAuthProfile } from "../../../agents/model-ref-profile.js";
|
||||
import { normalizeConfiguredProviderCatalogModelId } from "../../../agents/model-ref-shared.js";
|
||||
import { resolveModelRuntimePolicy } from "../../../agents/model-runtime-policy.js";
|
||||
import { openAIProviderUsesCodexRuntimeByDefault } from "../../../agents/openai-codex-routing.js";
|
||||
import { normalizeProviderId } from "../../../agents/provider-id.js";
|
||||
import { AGENT_MODEL_CONFIG_KEYS } from "../../../config/model-refs.js";
|
||||
import { loadSessionStore, updateSessionStore } from "../../../config/sessions/store.js";
|
||||
import { resolveAllAgentSessionStoreTargetsSync } from "../../../config/sessions/targets.js";
|
||||
import type { SessionEntry } from "../../../config/sessions/types.js";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { normalizeEmbeddedAgentRuntime } from "../../../agents/agent-runtime-id.js";
|
||||
import { resolveDefaultAgentDir } from "../../../agents/agent-scope-config.js";
|
||||
import { resolveCliBackendConfig } from "../../../agents/cli-backends.js";
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../../../agents/defaults.js";
|
||||
import { resolveAgentHarnessPolicy } from "../../../agents/harness/policy.js";
|
||||
import { getRegisteredAgentHarness } from "../../../agents/harness/registry.js";
|
||||
import { normalizeProviderId } from "../../../agents/provider-id.js";
|
||||
import type { OpenClawConfig } from "../../../config/types.openclaw.js";
|
||||
import {
|
||||
buildGenericCliContextEngineHostSupport,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { sanitizeForLog } from "../../../../packages/terminal-core/src/ansi.js";
|
||||
import { normalizeProviderId } from "../../../agents/provider-id.js";
|
||||
import { resolveSingleAccountKeysToMove } from "../../../channels/plugins/setup-promotion-helpers.js";
|
||||
import { resolveNormalizedProviderModelMaxTokens } from "../../../config/defaults.js";
|
||||
import type { OpenClawConfig } from "../../../config/types.openclaw.js";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { normalizeProviderId } from "../../../agents/provider-id.js";
|
||||
import { isKnownCoreToolId } from "../../../agents/tool-catalog.js";
|
||||
import { isToolAllowedByPolicyName } from "../../../agents/tool-policy-match.js";
|
||||
import { resolveToolProfilePolicy } from "../../../agents/tool-policy-shared.js";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { splitTrailingAuthProfile } from "../../../agents/model-ref-profile.js";
|
||||
import { normalizeProviderId } from "../../../agents/provider-id.js";
|
||||
import {
|
||||
defineLegacyConfigMigration,
|
||||
ensureRecord,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { normalizeProviderId } from "../../../agents/provider-id.js";
|
||||
import type { ModelDefinitionConfig } from "../../../config/types.models.js";
|
||||
|
||||
const LEGACY_MODELS_ADD_CODEX_MODEL_IDS = new Set(["gpt-5.5", "gpt-5.5-pro"]);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { normalizeStaticProviderModelId } from "../../../agents/model-ref-shared.js";
|
||||
import { normalizeProviderId } from "../../../agents/provider-id.js";
|
||||
|
||||
type LegacyRuntimeModelProviderAlias = {
|
||||
/** Legacy provider id that encoded the runtime in the model ref. */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import fs from "node:fs";
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { normalizeProviderId } from "../../../agents/provider-id.js";
|
||||
import {
|
||||
extractShippedPluginInstallConfigRecords,
|
||||
stripShippedPluginInstallConfigRecords,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { sanitizeServerName, TOOL_NAME_SEPARATOR } from "../../../agents/agent-bundle-mcp-names.js";
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../../../agents/defaults.js";
|
||||
import { compileGlobPatterns, matchesAnyGlobPattern } from "../../../agents/glob-pattern.js";
|
||||
import { parseModelRef } from "../../../agents/model-selection-normalize.js";
|
||||
import { normalizeProviderId } from "../../../agents/provider-id.js";
|
||||
import {
|
||||
mergeAlsoAllowPolicy,
|
||||
normalizeToolName,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { resolveAgentConfig } from "../../../agents/agent-scope-config.js";
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../../../agents/defaults.js";
|
||||
import { parseModelRef } from "../../../agents/model-selection-normalize.js";
|
||||
import { normalizeProviderId } from "../../../agents/provider-id.js";
|
||||
import { pickSandboxToolPolicy } from "../../../agents/sandbox-tool-policy.js";
|
||||
import {
|
||||
isToolAllowedByPolicies,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { collectConfiguredModelRefs } from "@openclaw/model-catalog-core/configured-model-refs";
|
||||
import { collectConfiguredAgentHarnessRuntimes } from "../../../agents/harness-runtimes.js";
|
||||
import { listPotentialConfiguredChannelPresenceSignals } from "../../../channels/config-presence.js";
|
||||
import { normalizeChatChannelId } from "../../../channels/registry.js";
|
||||
import { isChannelConfigured } from "../../../config/channel-configured.js";
|
||||
import { collectConfiguredModelRefs } from "../../../config/model-refs.js";
|
||||
import { detectPluginAutoEnableCandidates } from "../../../config/plugin-auto-enable.js";
|
||||
import type { OpenClawConfig } from "../../../config/types.openclaw.js";
|
||||
import { compareOpenClawVersions } from "../../../config/version.js";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { normalizeProviderIdForAuth } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { AuthProfileStore } from "../../agents/auth-profiles/types.js";
|
||||
import {
|
||||
listProviderEnvAuthLookupKeys,
|
||||
@@ -15,6 +14,7 @@ import {
|
||||
OPENAI_PROVIDER_ID,
|
||||
openAIProviderUsesCodexRuntimeByDefault,
|
||||
} from "../../agents/openai-codex-routing.js";
|
||||
import { normalizeProviderIdForAuth } from "../../agents/provider-id.js";
|
||||
import { resolveAgentModelPrimaryValue } from "../../config/model-input.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import type { PluginMetadataSnapshot } from "../../plugins/plugin-metadata-snapshot.types.js";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { normalizeProviderIdForAuth } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { formatRemainingShort } from "../../agents/auth-health.js";
|
||||
import { resolveAuthProfileDisplayLabel } from "../../agents/auth-profiles/display.js";
|
||||
import { resolveAuthStorePathForDisplay } from "../../agents/auth-profiles/paths.js";
|
||||
@@ -12,6 +11,7 @@ import {
|
||||
resolveEnvApiKey,
|
||||
resolveUsableCustomProviderApiKey,
|
||||
} from "../../agents/model-auth.js";
|
||||
import { normalizeProviderIdForAuth } from "../../agents/provider-id.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import type { ProviderAuthEvidence } from "../../secrets/provider-env-vars.js";
|
||||
import {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { loadAuthProfileStoreWithoutExternalProfiles } from "../../agents/auth-profiles/store.js";
|
||||
import {
|
||||
createProviderApiKeyResolver,
|
||||
createProviderAuthResolver,
|
||||
} from "../../agents/models-config.providers.secrets.js";
|
||||
import { normalizeProviderId } from "../../agents/provider-id.js";
|
||||
import type { ModelProviderConfig } from "../../config/types.models.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { formatErrorMessage } from "../../infra/errors.js";
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { DEFAULT_CONTEXT_TOKENS } from "../../agents/defaults.js";
|
||||
import {
|
||||
shouldSuppressBuiltInModel,
|
||||
shouldSuppressBuiltInModelFromManifest,
|
||||
} from "../../agents/model-suppression.js";
|
||||
import { normalizeProviderId } from "../../agents/provider-id.js";
|
||||
import type { ModelDefinitionConfig, ModelProviderConfig } from "../../config/types.models.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import type { ModelRegistry } from "../../llm/model-registry.js";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { resolveModelAgentRuntimeMetadata } from "../agents/agent-runtime-metadata.js";
|
||||
import { resolveConfiguredProviderFallback } from "../agents/configured-provider-fallback.js";
|
||||
import { DEFAULT_CONTEXT_TOKENS, DEFAULT_MODEL, DEFAULT_PROVIDER } from "../agents/defaults.js";
|
||||
import { parseModelRef, resolvePersistedSelectedModelRef } from "../agents/model-selection.js";
|
||||
import { normalizeProviderId } from "../agents/provider-id.js";
|
||||
import { resolveAgentModelPrimaryValue } from "../config/model-input.js";
|
||||
import type { SessionEntry } from "../config/sessions/types.js";
|
||||
import type { OpenClawConfig } from "../config/types.js";
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,10 +1,10 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { DEFAULT_CONTEXT_TOKENS } from "../agents/defaults.js";
|
||||
import { normalizeProviderId } from "../agents/provider-id.js";
|
||||
import type { PluginManifestRegistry } from "../plugins/manifest-registry.js";
|
||||
import {
|
||||
collectManifestModelIdNormalizationPolicies,
|
||||
normalizeConfiguredProviderCatalogModelId,
|
||||
} from "@openclaw/model-catalog-core/provider-model-id-normalization";
|
||||
import { DEFAULT_CONTEXT_TOKENS } from "../agents/defaults.js";
|
||||
import type { PluginManifestRegistry } from "../plugins/manifest-registry.js";
|
||||
} from "../shared/provider-model-id-normalization.js";
|
||||
import { isRecord } from "../shared/record-coerce.js";
|
||||
import {
|
||||
DEFAULT_AGENT_MAX_CONCURRENT,
|
||||
|
||||
@@ -2,7 +2,6 @@ import crypto from "node:crypto";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { collectManifestModelIdNormalizationPolicies } from "@openclaw/model-catalog-core/provider-model-id-normalization";
|
||||
import JSON5 from "json5";
|
||||
import { sanitizeTerminalText } from "../../packages/terminal-core/src/safe-text.js";
|
||||
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope-config.js";
|
||||
@@ -28,6 +27,7 @@ import {
|
||||
resolvePluginMetadataSnapshot,
|
||||
type PluginMetadataSnapshot,
|
||||
} from "../plugins/plugin-metadata-snapshot.js";
|
||||
import { collectManifestModelIdNormalizationPolicies } from "../shared/provider-model-id-normalization.js";
|
||||
import { isRecord } from "../utils.js";
|
||||
import { VERSION } from "../version.js";
|
||||
import { DuplicateAgentDirError, findDuplicateAgentDirs } from "./agent-dirs.js";
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { isDeepStrictEqual } from "node:util";
|
||||
import { normalizeConfiguredProviderCatalogModelId } from "@openclaw/model-catalog-core/provider-model-id-normalization";
|
||||
import { parseConfigPathArrayIndex } from "../shared/path-array-index.js";
|
||||
import {
|
||||
type ManifestModelIdNormalizationProvider,
|
||||
normalizeConfiguredProviderCatalogModelId,
|
||||
} from "../shared/provider-model-id-normalization.js";
|
||||
import { isRecord } from "../utils.js";
|
||||
import { applyMergePatch } from "./merge-patch.js";
|
||||
import { normalizeAgentModelMapForConfig, normalizeAgentModelRefForConfig } from "./model-input.js";
|
||||
@@ -12,16 +15,6 @@ const OPEN_DM_POLICY_ALLOW_FROM_RE =
|
||||
|
||||
const MANAGED_CONFIG_UNSET_PATHS = [["plugins", "installs"]] as const;
|
||||
|
||||
type ManifestModelIdNormalizationProvider = {
|
||||
aliases?: Record<string, string>;
|
||||
stripPrefixes?: string[];
|
||||
prefixWhenBare?: string;
|
||||
prefixWhenBareAfterAliasStartsWith?: {
|
||||
modelPrefix: string;
|
||||
prefix: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
function cloneUnknown<T>(value: T): T {
|
||||
return structuredClone(value);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { normalizeProviderId } from "../agents/provider-id.js";
|
||||
import {
|
||||
normalizeGooglePreviewModelId,
|
||||
normalizeTogetherModelId,
|
||||
} from "@openclaw/model-catalog-core/provider-model-id-normalize";
|
||||
} from "../plugin-sdk/provider-model-id-normalize.js";
|
||||
import { isRecord as isPlainRecord } from "../shared/record-coerce.js";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
|
||||
7
src/config/model-refs.ts
Normal file
7
src/config/model-refs.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export {
|
||||
AGENT_MODEL_CONFIG_KEYS,
|
||||
collectConfiguredModelRefs,
|
||||
collectConfiguredModelRefValues,
|
||||
extractProviderFromModelRef,
|
||||
type ConfiguredModelRef,
|
||||
} from "../../packages/model-catalog-core/src/configured-model-refs.js";
|
||||
@@ -1,6 +1,5 @@
|
||||
import { collectConfiguredModelRefs } from "@openclaw/model-catalog-core/configured-model-refs";
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import { collectConfiguredAgentHarnessRuntimes } from "../agents/harness-runtimes.js";
|
||||
import { normalizeProviderId } from "../agents/provider-id.js";
|
||||
import {
|
||||
listPotentialConfiguredChannelPresenceSignals,
|
||||
type ChannelPresenceSignalSource,
|
||||
@@ -24,6 +23,7 @@ import { resolvePluginSetupAutoEnableReasons } from "../plugins/setup-registry.j
|
||||
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
|
||||
import { isRecord } from "../utils.js";
|
||||
import { isChannelConfigured } from "./channel-configured.js";
|
||||
import { collectConfiguredModelRefs } from "./model-refs.js";
|
||||
import { shouldSkipPreferredPluginAutoEnable } from "./plugin-auto-enable.prefer-over.js";
|
||||
import type {
|
||||
PluginAutoEnableCandidate,
|
||||
|
||||
@@ -238,7 +238,7 @@ describe("config schema", () => {
|
||||
expect(progressPropsFor("discord")).not.toHaveProperty("nativeTaskCards");
|
||||
expect(progressPropsFor("telegram")).not.toHaveProperty("nativeTaskCards");
|
||||
expect(progressPropsFor("discord")).toHaveProperty("commentary");
|
||||
expect(progressPropsFor("telegram")).toHaveProperty("commentary");
|
||||
expect(progressPropsFor("telegram")).not.toHaveProperty("commentary");
|
||||
expect(res.uiHints["channels.matrix"]?.label).toBe("Matrix");
|
||||
expect(res.uiHints["channels.matrix.accessToken"]?.sensitive).toBe(true);
|
||||
expect(res.uiHints["channels.matrix.streaming.progress.label"]?.label).toBe(
|
||||
@@ -252,9 +252,7 @@ describe("config schema", () => {
|
||||
expect(res.uiHints["channels.discord.streaming.progress.toolProgress"]?.label).toBe(
|
||||
"Discord Progress Tool Lines",
|
||||
);
|
||||
expect(res.uiHints["channels.telegram.streaming.progress.commentary"]?.label).toBe(
|
||||
"Telegram Progress Commentary",
|
||||
);
|
||||
expect(res.uiHints["channels.telegram.streaming.progress.commentary"]).toBeUndefined();
|
||||
expect(res.uiHints["channels.mattermost.streaming.progress.label"]?.label).toBe(
|
||||
"Mattermost Progress Label",
|
||||
);
|
||||
@@ -412,7 +410,7 @@ describe("config schema", () => {
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("accepts progress commentary for channels that render commentary drafts", () => {
|
||||
it("accepts progress commentary only for Discord streaming config", () => {
|
||||
expect(
|
||||
DiscordConfigSchema.safeParse({
|
||||
streaming: {
|
||||
@@ -429,7 +427,7 @@ describe("config schema", () => {
|
||||
progress: { commentary: true },
|
||||
},
|
||||
}).success,
|
||||
).toBe(true);
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps per-agent model overrides limited to model selection", () => {
|
||||
|
||||
@@ -120,13 +120,6 @@ function readValidPromptBlob(storePath: string, ref: SessionSkillPromptRef): str
|
||||
}
|
||||
}
|
||||
|
||||
export function isSessionSkillPromptBlobReadable(
|
||||
storePath: string,
|
||||
ref: SessionSkillPromptRef,
|
||||
): boolean {
|
||||
return readValidPromptBlob(storePath, ref) !== null;
|
||||
}
|
||||
|
||||
async function ensurePromptBlob(storePath: string, prompt: string): Promise<SessionSkillPromptRef> {
|
||||
const ref = buildPromptRef(prompt);
|
||||
const blobPath = resolveSessionSkillPromptBlobPath(storePath, ref.hash);
|
||||
|
||||
@@ -19,7 +19,6 @@ import { deriveSessionMetaPatch } from "./metadata.js";
|
||||
import { resolveStorePath } from "./paths.js";
|
||||
import {
|
||||
ensureSessionStorePromptBlobsForPersistence,
|
||||
isSessionSkillPromptBlobReadable,
|
||||
projectSessionStoreForPersistence,
|
||||
type SessionSkillPromptBlobProjection,
|
||||
} from "./skill-prompt-blobs.js";
|
||||
@@ -59,7 +58,6 @@ import {
|
||||
mergeSessionEntry,
|
||||
mergeSessionEntryPreserveActivity,
|
||||
type SessionEntry,
|
||||
type SessionSkillPromptRef,
|
||||
} from "./types.js";
|
||||
|
||||
export {
|
||||
@@ -91,6 +89,12 @@ const writerStoreFileStats = new WeakMap<
|
||||
Record<string, SessionEntry>,
|
||||
ReturnType<typeof getFileStatSnapshot> | null
|
||||
>();
|
||||
let serializedPromptRefKeyCache:
|
||||
| {
|
||||
serialized: string;
|
||||
keys: Set<string>;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
function loadSessionArchiveRuntime() {
|
||||
sessionArchiveRuntimePromise ??= import("../../gateway/session-archive.runtime.js");
|
||||
@@ -367,20 +371,23 @@ function buildSingleEntrySerializedStore(params: {
|
||||
};
|
||||
}
|
||||
|
||||
function collectSerializedPromptRefs(serialized: string): Map<string, SessionSkillPromptRef> {
|
||||
const refs = new Map<string, SessionSkillPromptRef>();
|
||||
function collectSerializedPromptRefKeys(serialized: string): Set<string> {
|
||||
if (serializedPromptRefKeyCache?.serialized === serialized) {
|
||||
return serializedPromptRefKeyCache.keys;
|
||||
}
|
||||
const keys = new Set<string>();
|
||||
try {
|
||||
const parsed = JSON.parse(serialized) as Record<string, SessionEntry>;
|
||||
for (const [key, entry] of Object.entries(parsed)) {
|
||||
const ref = entry?.skillsSnapshot?.promptRef;
|
||||
if (ref) {
|
||||
refs.set(key, ref);
|
||||
if (entry?.skillsSnapshot?.promptRef) {
|
||||
keys.add(key);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Malformed serialized cache cannot prove prompt refs are already durable.
|
||||
}
|
||||
return refs;
|
||||
serializedPromptRefKeyCache = { serialized, keys };
|
||||
return keys;
|
||||
}
|
||||
|
||||
function storeHasUnsafeUntouchedHydratedSkillPrompts(
|
||||
@@ -389,15 +396,15 @@ function storeHasUnsafeUntouchedHydratedSkillPrompts(
|
||||
changedSessionKey: string,
|
||||
): boolean {
|
||||
const currentSerialized = getSerializedSessionStore(storePath);
|
||||
const serializedPromptRefs = currentSerialized
|
||||
? collectSerializedPromptRefs(currentSerialized)
|
||||
const serializedPromptRefKeys = currentSerialized
|
||||
? collectSerializedPromptRefKeys(currentSerialized)
|
||||
: undefined;
|
||||
for (const [key, entry] of Object.entries(store)) {
|
||||
if (key === changedSessionKey || typeof entry.skillsSnapshot?.prompt !== "string") {
|
||||
continue;
|
||||
}
|
||||
const ref = serializedPromptRefs?.get(key);
|
||||
if (!ref || !isSessionSkillPromptBlobReadable(storePath, ref)) {
|
||||
if (
|
||||
key !== changedSessionKey &&
|
||||
typeof entry.skillsSnapshot?.prompt === "string" &&
|
||||
!serializedPromptRefKeys?.has(key)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user