import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; import { logVerbose } from "openclaw/plugin-sdk/runtime-env"; import {
completeWithPreparedSimpleCompletionModel,
extractAssistantText,
prepareSimpleCompletionModelForAgent,
} from "openclaw/plugin-sdk/simple-completion-runtime";
const DEFAULT_THREAD_TITLE_TIMEOUT_MS = 10_000; const MAX_THREAD_TITLE_SOURCE_CHARS = 600; const MAX_THREAD_TITLE_CHANNEL_NAME_CHARS = 120; const MAX_THREAD_TITLE_CHANNEL_DESCRIPTION_CHARS = 320; // Budget generous enough to cover reasoning-model thinking tokens plus the // short text output. Lower values (e.g. 24) starve reasoning models of output // capacity: the entire budget is consumed by the thinking block before any // text is emitted, so extractAssistantText returns empty and the rename is // silently skipped. const DISCORD_THREAD_TITLE_MAX_TOKENS = 512; const DISCORD_THREAD_TITLE_SYSTEM_PROMPT = "Generate a concise Discord thread title (3-6 words). Return only the title. Use channel context when provided and avoid redundant channel-name words unless needed for clarity.";
function truncateThreadTitleSourceText(sourceText: string): string { if (sourceText.length <= MAX_THREAD_TITLE_SOURCE_CHARS) { return sourceText;
} return `${sourceText.slice(0, MAX_THREAD_TITLE_SOURCE_CHARS)}...`;
}
function resolveThreadTitleTimeoutMs(timeoutMs: number | undefined): number { return Math.max(100, Math.floor(timeoutMs ?? DEFAULT_THREAD_TITLE_TIMEOUT_MS));
}
export function normalizeGeneratedThreadTitle(raw: string): string { const lines = raw.replace(/\r/g, "").split("\n");
let firstLine = ""; for (const line of lines) { const trimmed = line.trim(); if (!trimmed) { continue;
} if (!firstLine && trimmed.startsWith("```")) { continue;
}
firstLine = trimmed; break;
} return stripThreadTitleWrappers(firstLine);
}
function stripThreadTitleWrappers(raw: string): string {
let current = raw.trim();
let previous = ""; while (current && current !== previous) {
previous = current;
current = current.replace(/^["'`]+|["'`]+$/g, "").trim();
current = current.replace(/^\*\*(.+)\*\*$/u, "$1").trim();
current = current.replace(/^__(.+)__$/u, "$1").trim();
current = current.replace(/^\*(.+)\*$/u, "$1").trim();
current = current.replace(/^_(.+)_$/u, "$1").trim();
current = current.replace(/^~~(.+)~~$/u, "$1").trim();
} return current;
}
function normalizeTitleContextField(raw: string | undefined, maxChars: number): string | undefined { const value = raw?.trim(); if (!value) { return undefined;
} const singleLine = value.replace(/\s+/g, " "); if (singleLine.length <= maxChars) { return singleLine;
} return `${singleLine.slice(0, maxChars)}...`;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.