mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 01:29:56 +00:00
refactor(comments): deslop production narration
## What Problem This Solves Production code still carries duplicated narration over function names, types, branches and CSS selectors. Some of that prose has drifted: Zalo polling is described as development-only even though it is the default production route, and a joined hook helper is called fire-and-forget. ## User Impact No user-visible behavior changes. Runtime logic, templates, CSS declarations, configuration, persisted state, wire formats, public API documentation, licenses and lint-suppression reasons remain intact. ## Why This Change Was Made This maintainer-requested cleanup removes redundant internal helper/registrar summaries, repeated section labels, and obsolete inline font-size history. Existing declarations and shared owners already express these facts; no new abstraction is needed. Comments explaining authority, lifecycle, ordering, cleanup, platform constraints, dependencies and public contracts stay. The measured reduction is 696 net production/tooling lines: 604 standalone comment lines and 92 adjacent blank lines, plus 59 inline comment removals without net line savings. No tests or generated files changed. This is a bounded contextual sweep, not a claim of exhaustive repository coverage; the local census records exact findings, retained candidates, and unread files. Filename-header cleanup from #161768 is excluded. ## Evidence Independent review completed; all accepted documentation findings were addressed by restoring base comments. The remaining changed files are byte-identical to the reviewed and remotely frozen candidate. Blacksmith Testbox validation: - Parser comparison: identical non-comment TypeScript tokens and CSS structure. - Both import-cycle checks: 0 cycles. - Focused existing tests: 40 Vitest shards passed (521.71 seconds). - Plugin contracts: 48 files / 1,153 tests passed. - Plugin, source-to-extension, and SDK/package import-boundary checks passed. - Feishu asset hook check: no build hooks; no plugin browser/control-UI source changed. SDK API comparison passed with no API changes. The full changed-file gate passed remotely. The lowered-threshold duplicate census (12 lines / 80 tokens) completed; its raw 155 records include deliberate probe/fixture matches and are not claimed as removable production code. No tests were added or changed. Public JSDoc was audited independently: the SDK API comparison strips comments, while shipped declarations can preserve them, so API-shape equality alone would not prove documentation preservation. ### Inherited hosted CI failure Exact-head [CI run 36815106181](https://github.com/openclaw/openclaw/actions/runs/36815106181) tested `ff26a4c05d3b41d25477df41cb94010c6cac5cb0` merged with main `75d1f82c18`. The only failing test job was `checks-node-compact-small-19`: `test/helpers/openclaw-test-instance.acquisition.test.ts:33`, “keeps an absent Gateway unreachable while retaining its port claims,” expected `free` but received `busy`. The other failure is the aggregate CI gate. This attempt has 84 successful jobs, 16 skipped jobs, and 88 collateral cancellations; cancelled coverage is not counted as passing. The identical assertion and error were independently verified in [job 110052175838](https://github.com/openclaw/openclaw/actions/runs/36763489663/job/110052175838), the latest attempt (1) for unrelated PR #162070's final head `d07a6981d4`. The acquisition test, instance helper, cleanup wrapper, isolated-state writer, port allocator, claim owner, claim-lock owner and TCP probe are byte-identical between that head and this PR. No changed file participates in the failing acquisition/probe path. The failure precedes Gateway startup; the logs do not identify the competing listener, so no root-cause repair is claimed. Landing uses the maintainer-authorized inherited-failure exception pinned to this exact head, backed by the passing remote gates above. The fixture defect remains with the main-CI coordinator. No workflow rerun, test weakening, timeout increase, or source repush was used to obtain green. GitHub's GraphQL writer rejected auto-merge because its quota was exhausted; the request was reconciled as absent before selecting the supported REST squash path.
This commit is contained in:
parent
504d2905d6
commit
799ddbd35d
164 changed files with 59 additions and 755 deletions
|
|
@ -194,7 +194,6 @@ export async function handleDiscordMessageManagementAction(ctx: DiscordMessaging
|
|||
}
|
||||
const channelId = readStringParam(ctx.params, "channelId");
|
||||
const channelIds = readStringArrayParam(ctx.params, "channelIds");
|
||||
// Resolve guildId from channel info when not explicitly provided.
|
||||
if (!guildId) {
|
||||
const rawInferChannelId = channelId ?? channelIds?.[0];
|
||||
if (rawInferChannelId) {
|
||||
|
|
|
|||
|
|
@ -304,9 +304,7 @@ const FeishuConfigSchemaBase = z
|
|||
requireMention: z.boolean().optional(),
|
||||
groupSessionScope: GroupSessionScopeSchema,
|
||||
topicSessionMode: TopicSessionModeSchema,
|
||||
// Dynamic agent creation for DM users
|
||||
dynamicAgentCreation: DynamicAgentCreationSchema,
|
||||
// Optimization flags
|
||||
typingIndicator: z.boolean().optional().default(true),
|
||||
resolveSenderNames: z.boolean().optional().default(true),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -71,7 +71,6 @@ export const FeishuDocSchema = Type.Union([
|
|||
doc_token: Type.String({ description: "Document token" }),
|
||||
block_id: Type.String({ description: "Block ID" }),
|
||||
}),
|
||||
// Table creation (explicit structure)
|
||||
Type.Object({
|
||||
action: Type.Literal("create_table"),
|
||||
...tableCreationProperties,
|
||||
|
|
@ -93,7 +92,6 @@ export const FeishuDocSchema = Type.Union([
|
|||
minItems: 1,
|
||||
}),
|
||||
}),
|
||||
// Table row/column manipulation
|
||||
Type.Object({
|
||||
action: Type.Literal("insert_table_row"),
|
||||
doc_token: Type.String({ description: "Document token" }),
|
||||
|
|
@ -135,7 +133,6 @@ export const FeishuDocSchema = Type.Union([
|
|||
column_start: Type.Number({ description: "Start column index" }),
|
||||
column_end: Type.Number({ description: "End column index (exclusive)" }),
|
||||
}),
|
||||
// Image / file upload
|
||||
Type.Object({
|
||||
action: Type.Literal("upload_image"),
|
||||
doc_token: Type.String({ description: "Document token" }),
|
||||
|
|
@ -168,7 +165,6 @@ export const FeishuDocSchema = Type.Union([
|
|||
),
|
||||
filename: Type.Optional(Type.String({ description: "Optional filename override" })),
|
||||
}),
|
||||
// Text color / style
|
||||
Type.Object({
|
||||
action: Type.Literal("color_text"),
|
||||
doc_token: Type.String({ description: "Document token" }),
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ function calculateAdaptiveColumnWidths(
|
|||
return [];
|
||||
}
|
||||
|
||||
// Use original total width from Convert API, or fall back to default
|
||||
const totalWidth =
|
||||
originalWidths && originalWidths.length > 0
|
||||
? originalWidths.reduce((a: number, b: number) => a + b, 0)
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ import { createFeishuToolClient } from "./tool-account.js";
|
|||
import { registerFeishuTool } from "./tool-registration.js";
|
||||
import { feishuExternalToolResult as jsonResult, unknownToolActionResult } from "./tool-result.js";
|
||||
|
||||
// ============ Actions ============
|
||||
|
||||
type FeishuExplorerRootFolderMetaResponse = {
|
||||
code: number;
|
||||
msg?: string;
|
||||
|
|
@ -632,8 +630,6 @@ export async function deliverCommentThreadText(
|
|||
}
|
||||
}
|
||||
|
||||
// ============ Tool Registration ============
|
||||
|
||||
export function registerFeishuDriveTools(api: OpenClawPluginApi) {
|
||||
registerFeishuTool(api, {
|
||||
family: "drive",
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ type ListTokenType = NonNullable<
|
|||
NonNullable<Parameters<Lark.Client["drive"]["permissionMember"]["list"]>[0]>["params"]
|
||||
>["type"];
|
||||
|
||||
// ============ Actions ============
|
||||
|
||||
async function listMembers(client: Lark.Client, token: string, type: string) {
|
||||
const res = await client.drive.permissionMember.list({
|
||||
path: { token },
|
||||
|
|
@ -66,8 +64,6 @@ async function removeMember(
|
|||
};
|
||||
}
|
||||
|
||||
// ============ Tool Registration ============
|
||||
|
||||
export function registerFeishuPermTools(api: OpenClawPluginApi) {
|
||||
registerFeishuTool(api, {
|
||||
family: "perm",
|
||||
|
|
|
|||
|
|
@ -42,9 +42,6 @@ export async function addReactionFeishu(params: {
|
|||
return { reactionId };
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a reaction from a message.
|
||||
*/
|
||||
export async function removeReactionFeishu(params: {
|
||||
cfg: ClawdbotConfig;
|
||||
messageId: string;
|
||||
|
|
@ -64,9 +61,6 @@ export async function removeReactionFeishu(params: {
|
|||
assertFeishuApiSuccess(response, "Feishu remove reaction failed");
|
||||
}
|
||||
|
||||
/**
|
||||
* List all reactions for a message.
|
||||
*/
|
||||
export async function listReactionsFeishu(params: {
|
||||
cfg: ClawdbotConfig;
|
||||
messageId: string;
|
||||
|
|
|
|||
|
|
@ -115,7 +115,6 @@ function normalizeEpochMs(timestamp: number | undefined): number | undefined {
|
|||
return timestamp < MS_EPOCH_MIN ? timestamp * 1000 : timestamp;
|
||||
}
|
||||
|
||||
/** Build a card header from agent identity config. */
|
||||
function resolveCardHeader(
|
||||
agentId: string,
|
||||
identity: OutboundIdentity | undefined,
|
||||
|
|
@ -132,7 +131,6 @@ function resolveCardHeader(
|
|||
};
|
||||
}
|
||||
|
||||
/** Build a card note footer from agent identity and model context. */
|
||||
function resolveCardNote(
|
||||
agentId: string,
|
||||
identity: OutboundIdentity | undefined,
|
||||
|
|
@ -222,7 +220,6 @@ export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherP
|
|||
accountId,
|
||||
typing: {
|
||||
start: async () => {
|
||||
// Check if typing indicator is enabled (default: true)
|
||||
if (!(account.config.typingIndicator ?? true)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,6 @@ type FeishuGetMessageResponse = {
|
|||
};
|
||||
};
|
||||
|
||||
/** Send a direct message as a fallback when a reply target is unavailable. */
|
||||
async function sendFallbackDirect(
|
||||
client: ReturnType<typeof createFeishuClient>,
|
||||
params: {
|
||||
|
|
@ -270,10 +269,6 @@ function parseFeishuMessageItem(
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a message by its ID.
|
||||
* Useful for fetching quoted/replied message content.
|
||||
*/
|
||||
export async function getMessageFeishu(params: {
|
||||
cfg: ClawdbotConfig;
|
||||
messageId: string;
|
||||
|
|
@ -433,7 +428,6 @@ type SendFeishuMessageParams = {
|
|||
/** When true, reply creates a Feishu topic thread instead of an inline reply */
|
||||
replyInThread?: boolean;
|
||||
allowTopLevelReplyFallback?: boolean;
|
||||
/** Mention target users */
|
||||
mentions?: MentionTarget[];
|
||||
/** Account ID (optional, uses default if not specified) */
|
||||
accountId?: string;
|
||||
|
|
@ -538,17 +532,12 @@ export async function editMessageFeishu(params: {
|
|||
return { messageId, contentType: "post" };
|
||||
}
|
||||
|
||||
/** Header configuration for structured Feishu cards. */
|
||||
export type CardHeaderConfig = {
|
||||
/** Header title text, e.g. "💻 Coder" */
|
||||
title: string;
|
||||
/** Feishu header color template (blue, green, red, orange, purple, grey, etc.). Defaults to "blue". */
|
||||
template?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Build a Feishu interactive card with optional header and note footer.
|
||||
*/
|
||||
function buildStructuredCard(
|
||||
text: string,
|
||||
options?: {
|
||||
|
|
@ -604,9 +593,6 @@ export function chunkFeishuCardMarkdown(
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message as a structured card with optional header and note.
|
||||
*/
|
||||
export async function sendStructuredCardFeishu(
|
||||
params: Omit<SendFeishuMessageParams, "preparedPostText"> & {
|
||||
header?: CardHeaderConfig;
|
||||
|
|
|
|||
|
|
@ -258,7 +258,6 @@ async function runNewAppFlow(params: {
|
|||
scanDomain = scanResult.domain;
|
||||
scanOpenId = scanResult.openId;
|
||||
} else {
|
||||
// Fallback to manual input: collect domain, appId, appSecret.
|
||||
await noteFeishuCredentialHelp(prompter);
|
||||
|
||||
appId = (
|
||||
|
|
@ -288,7 +287,6 @@ async function runNewAppFlow(params: {
|
|||
appSecretProbeValue = appSecretResult.resolvedValue;
|
||||
}
|
||||
|
||||
// Fetch openId via API for manual flow.
|
||||
if (appId && appSecretProbeValue) {
|
||||
const { getAppOwnerOpenId } = await loadAppRegistrationModule();
|
||||
scanOpenId = await getAppOwnerOpenId({
|
||||
|
|
|
|||
|
|
@ -233,7 +233,6 @@ export function mergeStreamingText(
|
|||
return `${previous}${next}`;
|
||||
}
|
||||
|
||||
/** Streaming card session manager */
|
||||
export class FeishuStreamingSession {
|
||||
private client: Client;
|
||||
private creds: Credentials;
|
||||
|
|
@ -573,12 +572,10 @@ export class FeishuStreamingSession {
|
|||
}
|
||||
}
|
||||
|
||||
// Update note with final model/provider info
|
||||
if (options?.note) {
|
||||
await this.updateNoteContent(options.note);
|
||||
}
|
||||
|
||||
// Close streaming mode
|
||||
// A rejected final write must not advertise content that CardKit never accepted.
|
||||
const acceptedText = this.state.sentText;
|
||||
this.state.sequence += 1;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ const DEFAULT_TOOLS_CONFIG: Required<FeishuToolsConfig> = {
|
|||
bitable: true,
|
||||
};
|
||||
|
||||
/** Resolve tools config with defaults. */
|
||||
export function resolveToolsConfig(cfg?: FeishuToolsConfig): Required<FeishuToolsConfig> {
|
||||
return { ...DEFAULT_TOOLS_CONFIG, ...cfg };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ type ObjType = "doc" | "sheet" | "mindnote" | "bitable" | "file" | "docx" | "sli
|
|||
|
||||
const WIKI_PAGE_SIZE = 50;
|
||||
|
||||
// ============ Actions ============
|
||||
|
||||
const WIKI_ACCESS_HINT =
|
||||
"To grant wiki access: Open wiki space → Settings → Members → Add the bot. " +
|
||||
"See: https://open.feishu.cn/document/server-docs/docs/wiki-v2/wiki-qa#a40ad4ca";
|
||||
|
|
@ -186,8 +184,6 @@ async function renameNode(client: Lark.Client, spaceId: string, nodeToken: strin
|
|||
};
|
||||
}
|
||||
|
||||
// ============ Tool Registration ============
|
||||
|
||||
export function registerFeishuWikiTools(api: OpenClawPluginApi) {
|
||||
registerFeishuTool(api, {
|
||||
family: "wiki",
|
||||
|
|
|
|||
|
|
@ -159,7 +159,6 @@ function parseCardArgs(argsStrInput: string): {
|
|||
flags: {},
|
||||
};
|
||||
|
||||
// Extract type (first word)
|
||||
const typeMatch = argsStr.match(/^(\w+)/);
|
||||
if (typeMatch) {
|
||||
result.type = normalizeLowercaseStringOrEmpty(typeMatch[1]);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import type {
|
|||
VideoFileInfo,
|
||||
} from "../sdk.js";
|
||||
|
||||
// Message types
|
||||
export const MsgType = {
|
||||
Text: "m.text",
|
||||
Image: "m.image",
|
||||
|
|
@ -23,14 +22,12 @@ export const MsgType = {
|
|||
Notice: "m.notice",
|
||||
} as const;
|
||||
|
||||
// Relation types
|
||||
export const RelationType = {
|
||||
Annotation: MATRIX_ANNOTATION_RELATION_TYPE,
|
||||
Replace: "m.replace",
|
||||
Thread: "m.thread",
|
||||
} as const;
|
||||
|
||||
// Event types
|
||||
export const EventType = {
|
||||
Direct: "m.direct",
|
||||
Reaction: MATRIX_REACTION_EVENT_TYPE,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ const MATTERMOST_QUESTION_CONTEXT_KEY = "oc_question";
|
|||
|
||||
export type MattermostQuestionSelection = { questionId: string; optionIndex: number };
|
||||
|
||||
/** One interactive button as this plugin hands it to the attachment builder. */
|
||||
type MattermostPresentationButton = {
|
||||
id: string;
|
||||
text: string;
|
||||
|
|
|
|||
|
|
@ -62,9 +62,6 @@ async function findStandardChannelMember(params: {
|
|||
return result.found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a user profile from Microsoft Graph by user ID.
|
||||
*/
|
||||
export async function getMemberInfoMSTeams(params: GetMemberInfoMSTeamsParams) {
|
||||
const isCurrentRequester =
|
||||
normalizeUserId(params.userId) === normalizeUserId(params.currentRequesterId);
|
||||
|
|
|
|||
|
|
@ -8,10 +8,6 @@ import { resolveMSTeamsRouteSessionKey } from "./thread-session.js";
|
|||
|
||||
type ReactionDirection = "added" | "removed";
|
||||
|
||||
/**
|
||||
* Create a handler for MS Teams reaction activities (reactionsAdded / reactionsRemoved).
|
||||
* The returned function accepts a turn context and a direction string.
|
||||
*/
|
||||
export function createMSTeamsReactionHandler(deps: MSTeamsMessageHandlerDeps) {
|
||||
const { cfg, log } = deps;
|
||||
const core = getMSTeamsRuntime();
|
||||
|
|
@ -23,7 +19,6 @@ export function createMSTeamsReactionHandler(deps: MSTeamsMessageHandlerDeps) {
|
|||
): Promise<void> {
|
||||
const activity = context.activity;
|
||||
|
||||
// Reactions are carried in reactionsAdded / reactionsRemoved on the activity.
|
||||
const rawReactions =
|
||||
direction === "added" ? activity.reactionsAdded : activity.reactionsRemoved;
|
||||
const reactions: Array<{ type?: string }> = Array.isArray(rawReactions) ? rawReactions : [];
|
||||
|
|
@ -78,7 +73,6 @@ export function createMSTeamsReactionHandler(deps: MSTeamsMessageHandlerDeps) {
|
|||
}
|
||||
}
|
||||
|
||||
// Resolve the agent route for this conversation/sender.
|
||||
// Extract teamId for team-scoped routing bindings (channel/group reactions).
|
||||
const teamId = isDirectMessage ? undefined : activity.channelData?.team?.id;
|
||||
const route = core.channel.routing.resolveAgentRoute({
|
||||
|
|
|
|||
|
|
@ -60,13 +60,11 @@ type RelayMetrics = {
|
|||
};
|
||||
|
||||
export interface MetricEvent {
|
||||
/** Metric name (e.g., "event.received", "relay.connect") */
|
||||
name: MetricName;
|
||||
/** Metric value (usually 1 for counters, or a measured value) */
|
||||
value: number;
|
||||
/** Unix timestamp in milliseconds */
|
||||
timestamp: number;
|
||||
/** Optional labels for additional context */
|
||||
labels?: Record<string, string | number>;
|
||||
}
|
||||
|
||||
|
|
@ -240,9 +238,6 @@ export function createMetrics(onMetric?: OnMetricCallback) {
|
|||
export type MetricsSnapshot = ReturnType<typeof createMetricsSnapshot>;
|
||||
export type NostrMetrics = ReturnType<typeof createMetrics>;
|
||||
|
||||
/**
|
||||
* Create a no-op metrics instance (for when metrics are disabled).
|
||||
*/
|
||||
export function createNoopMetrics(): NostrMetrics {
|
||||
const emptySnapshot = createMetricsSnapshot(createZeroMetricsState(), 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -748,7 +748,6 @@ export async function containerRpcRequest<T = unknown>(
|
|||
timeoutMs: opts.timeoutMs,
|
||||
maxResponseBytes: opts.maxResponseBytes,
|
||||
});
|
||||
// Convert to native format: { data: base64String }
|
||||
if (!buffer) {
|
||||
return { data: undefined } as T;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1384,7 +1384,6 @@ export async function prepareSlackMessage(params: {
|
|||
});
|
||||
}
|
||||
|
||||
// Use direct media (including forwarded attachment media) if available, else thread starter media
|
||||
const effectiveMedia = effectiveDirectMedia ?? threadStarterMedia;
|
||||
let inboundMedia = await toInboundMediaFactsWithMetadata(effectiveMedia, {
|
||||
transcribed: (entry) =>
|
||||
|
|
|
|||
|
|
@ -485,7 +485,6 @@ export function createTelegramRequestWithDiag(params: {
|
|||
function wrapTelegramChatNotFoundError(err: unknown, params: { chatId: string; input: string }) {
|
||||
const errorMsg = formatErrorMessage(err);
|
||||
|
||||
// Check for 403 "bot is not a member" or "bot was blocked" errors
|
||||
if (/403.*(bot.*not.*member|bot.*blocked|bot.*kicked)/i.test(errorMsg)) {
|
||||
return new Error(
|
||||
[
|
||||
|
|
|
|||
|
|
@ -169,7 +169,6 @@ export async function sendMessageTelegram(
|
|||
const tableMode = opts.tableMode ?? resolveTelegramTableMode(richMessagesParams);
|
||||
const renderHtmlText = (value: string) =>
|
||||
renderTelegramHtmlText(value, { textMode, tableMode });
|
||||
// Resolve link preview setting from config (default: enabled).
|
||||
const linkPreviewEnabled = account.config.linkPreview ?? true;
|
||||
const linkPreviewOptions = linkPreviewEnabled ? undefined : { is_disabled: true };
|
||||
|
||||
|
|
|
|||
|
|
@ -251,7 +251,6 @@ export function extractMessageText(content: unknown): string {
|
|||
return "";
|
||||
}
|
||||
|
||||
// Handle inline content (text, ships, links, etc.)
|
||||
if (Array.isArray(verseRecord.inline)) {
|
||||
return verseRecord.inline
|
||||
.map((item) =>
|
||||
|
|
@ -264,12 +263,10 @@ export function extractMessageText(content: unknown): string {
|
|||
.join("");
|
||||
}
|
||||
|
||||
// Handle block content (images, code blocks, etc.)
|
||||
const block = asNullableRecord(verseRecord.block);
|
||||
if (block) {
|
||||
const image = asNullableRecord(block.image);
|
||||
|
||||
// Image blocks
|
||||
if (image) {
|
||||
const imageSrc = readStringField(image, "src");
|
||||
if (imageSrc) {
|
||||
|
|
@ -279,7 +276,6 @@ export function extractMessageText(content: unknown): string {
|
|||
}
|
||||
}
|
||||
|
||||
// Code blocks
|
||||
const codeBlock = asNullableRecord(block.code);
|
||||
if (codeBlock) {
|
||||
const lang = readStringField(codeBlock, "lang") ?? "";
|
||||
|
|
@ -287,7 +283,6 @@ export function extractMessageText(content: unknown): string {
|
|||
return `\n\`\`\`${lang}\n${code}\n\`\`\`\n`;
|
||||
}
|
||||
|
||||
// Header blocks
|
||||
const header = asNullableRecord(block.header);
|
||||
if (header) {
|
||||
const headerContent = Array.isArray(header.content) ? header.content : [];
|
||||
|
|
@ -296,7 +291,6 @@ export function extractMessageText(content: unknown): string {
|
|||
return `\n## ${text}\n`;
|
||||
}
|
||||
|
||||
// Cite/quote blocks - parse the reference structure
|
||||
const cite = asNullableRecord(block.cite);
|
||||
if (cite) {
|
||||
const chanCite = asNullableRecord(cite.chan);
|
||||
|
|
|
|||
|
|
@ -7,9 +7,6 @@ import type { TwitchClientManager } from "./twitch-client.js";
|
|||
import type { TwitchAccountConfig } from "./types.js";
|
||||
import { stripMarkdownForTwitch } from "./utils/markdown.js";
|
||||
|
||||
/**
|
||||
* Result from sending a message to Twitch.
|
||||
*/
|
||||
interface SendMessageResult {
|
||||
outcome?: "not_sent";
|
||||
/** The message ID (generated for tracking) */
|
||||
|
|
|
|||
|
|
@ -220,7 +220,6 @@ export class CallManager {
|
|||
const elapsed = Date.now() - maxDurationAnchor;
|
||||
const maxDurationMs = resolveVoiceCallSecondsTimerDelayMs(this.config.maxDurationSeconds);
|
||||
if (elapsed >= maxDurationMs) {
|
||||
// Already expired — remove instead of keeping
|
||||
verified.delete(callId);
|
||||
skippedAlreadyElapsedTimers += 1;
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ export const CALL_RECORD_EVENT_CHUNKS_NAMESPACE = "call-record-event-chunks";
|
|||
export const MAX_CALL_RECORD_EVENTS = 1000;
|
||||
/** Extra metadata entries retained so pruning can safely trim oldest rows. */
|
||||
export const CALL_RECORD_EVENT_META_MAX_ENTRIES = MAX_CALL_RECORD_EVENTS + 100;
|
||||
/** Maximum chunks allowed for one persisted call record event. */
|
||||
const MAX_CHUNKS_PER_CALL_RECORD_EVENT = 48;
|
||||
export const CALL_RECORD_CHUNK_MAX_ENTRIES =
|
||||
MAX_CALL_RECORD_EVENTS * MAX_CHUNKS_PER_CALL_RECORD_EVENT + MAX_CHUNKS_PER_CALL_RECORD_EVENT;
|
||||
|
|
@ -51,7 +50,6 @@ type PersistedCallRecord = {
|
|||
orderKey: string;
|
||||
};
|
||||
|
||||
/** Pair of plugin state stores used for call record events. */
|
||||
type CallRecordStateStores = {
|
||||
events: PluginStateKeyedStore<CallRecordEventMeta>;
|
||||
chunks: PluginStateKeyedStore<CallRecordEventChunk>;
|
||||
|
|
@ -66,12 +64,10 @@ export function resolveVoiceCallLegacyCallLogPath(storePath: string): string {
|
|||
return path.join(storePath, "calls.jsonl");
|
||||
}
|
||||
|
||||
/** Build env for plugin state stores rooted at the voice-call store path. */
|
||||
function resolvePluginStateEnv(storePath: string): NodeJS.ProcessEnv {
|
||||
return { ...process.env, OPENCLAW_STATE_DIR: storePath };
|
||||
}
|
||||
|
||||
/** Open the plugin state stores when the runtime is available. */
|
||||
function createCallRecordStateStores(
|
||||
storePath: string,
|
||||
stateRuntime?: VoiceCallStateRuntime["state"],
|
||||
|
|
@ -130,7 +126,6 @@ function buildNewEventKey(order: { persistedAt: number; sequence: number }): str
|
|||
return `event:${order.persistedAt.toString(36)}:${String(order.sequence).padStart(6, "0")}:${randomUUID()}`;
|
||||
}
|
||||
|
||||
/** Recover the sequence segment from newer event keys. */
|
||||
function parseEventKeySequence(key: string): number {
|
||||
const match = /^event:[^:]+:(\d+):/.exec(key);
|
||||
const sequence = match?.[1];
|
||||
|
|
@ -175,7 +170,6 @@ export function parseVoiceCallRecordLine(line: string, sequence = 0): PersistedC
|
|||
}
|
||||
}
|
||||
|
||||
/** Count storage chunks needed for a call record. */
|
||||
function countCallRecordChunks(call: CallRecord): number {
|
||||
return Math.max(
|
||||
1,
|
||||
|
|
@ -310,7 +304,6 @@ function isValidCallRecordChunkCount(chunkCount: number): boolean {
|
|||
);
|
||||
}
|
||||
|
||||
/** Read and reassemble one chunked call record event. */
|
||||
async function readCallRecordEvent(
|
||||
stores: CallRecordStateStores,
|
||||
eventKey: string,
|
||||
|
|
|
|||
|
|
@ -137,9 +137,7 @@ export class MediaStreamHandler {
|
|||
private inflightUpgrades = 0;
|
||||
/** TTS playback queues per stream (serialize audio to prevent overlap) */
|
||||
private ttsQueues = new Map<string, TtsQueueEntry[]>();
|
||||
/** Whether TTS is currently playing per stream */
|
||||
private ttsPlaying = new Map<string, boolean>();
|
||||
/** Active TTS playback controllers per stream */
|
||||
private ttsActiveControllers = new Map<string, AbortController>();
|
||||
private pendingPlaybackMarks = new Map<string, Map<string, PendingPlaybackMark>>();
|
||||
private ignoredPlaybackMarks = new Map<string, Set<string>>();
|
||||
|
|
|
|||
|
|
@ -96,10 +96,8 @@ export class TwilioProvider implements VoiceCallProvider {
|
|||
/** Current public webhook URL (set when tunnel starts or from config) */
|
||||
private currentPublicUrl: string | null = null;
|
||||
|
||||
/** Optional telephony TTS provider for streaming TTS */
|
||||
private ttsProvider: TelephonyTtsProvider | null = null;
|
||||
|
||||
/** Optional media stream handler for sending audio */
|
||||
private mediaStreamHandler: MediaStreamHandler | null = null;
|
||||
private playbackMarkSequence = 0;
|
||||
|
||||
|
|
@ -377,13 +375,11 @@ export class TwilioProvider implements VoiceCallProvider {
|
|||
};
|
||||
}
|
||||
|
||||
// Handle DTMF
|
||||
const digits = params.get("Digits");
|
||||
if (digits) {
|
||||
return { ...baseEvent, type: "call.dtmf", digits };
|
||||
}
|
||||
|
||||
// Handle call status changes
|
||||
const callStatus = normalizeProviderStatus(params.get("CallStatus"));
|
||||
if (callStatus === "initiated") {
|
||||
return { ...baseEvent, type: "call.initiated" };
|
||||
|
|
@ -482,14 +478,11 @@ export class TwilioProvider implements VoiceCallProvider {
|
|||
return null;
|
||||
}
|
||||
|
||||
// Extract just the origin (host) from the public URL, ignoring any path
|
||||
const url = new URL(this.currentPublicUrl);
|
||||
const origin = url.origin;
|
||||
|
||||
// Convert https:// to wss:// for WebSocket
|
||||
const wsOrigin = origin.replace(/^https:\/\//, "wss://").replace(/^http:\/\//, "ws://");
|
||||
|
||||
// Append the stream path
|
||||
const path = this.options.streamPath.startsWith("/")
|
||||
? this.options.streamPath
|
||||
: `/${this.options.streamPath}`;
|
||||
|
|
@ -821,10 +814,6 @@ export class TwilioProvider implements VoiceCallProvider {
|
|||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Twilio-specific types
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
interface TwilioCallResponse {
|
||||
sid: string;
|
||||
status: string;
|
||||
|
|
|
|||
|
|
@ -26,9 +26,7 @@ import { resolveCallAgentId } from "./resolve-call-agent-id.js";
|
|||
import { resolveVoiceResponseModel } from "./response-model.js";
|
||||
|
||||
type VoiceResponseParams = {
|
||||
/** Voice call config */
|
||||
voiceConfig: VoiceCallConfig;
|
||||
/** Core OpenClaw config */
|
||||
coreConfig: OpenClawConfig;
|
||||
/** Injected host agent runtime */
|
||||
agentRuntime: OpenClawPluginApi["runtime"]["agent"];
|
||||
|
|
@ -44,7 +42,6 @@ type VoiceResponseParams = {
|
|||
agentId?: string;
|
||||
/** Audible call transcript, used only for bounded first-turn opening context. */
|
||||
transcript: Array<{ speaker: "user" | "bot"; text: string }>;
|
||||
/** Latest user message */
|
||||
userMessage: string;
|
||||
/** Delivers completed reply blocks while post-turn work is still running. */
|
||||
onEarlyText?: (text: string) => Promise<boolean>;
|
||||
|
|
@ -338,7 +335,6 @@ export async function generateVoiceResponse(
|
|||
});
|
||||
const toolsAllow = resolveVoiceAgentToolsAllow(cfg, agentId);
|
||||
|
||||
// Resolve paths
|
||||
const storePath = agentRuntime.session.resolveStorePath(cfg.session?.store, { agentId });
|
||||
try {
|
||||
return await agentRuntime.session.runWithWorkAdmission(
|
||||
|
|
@ -347,17 +343,14 @@ export async function generateVoiceResponse(
|
|||
const agentDir = agentRuntime.resolveAgentDir(cfg, agentId);
|
||||
const workspaceDir = agentRuntime.resolveAgentWorkspaceDir(cfg, agentId);
|
||||
|
||||
// Ensure workspace exists
|
||||
await agentRuntime.ensureAgentWorkspace({ dir: workspaceDir });
|
||||
|
||||
// Load or create session entry
|
||||
const now = Date.now();
|
||||
const existingSessionEntry = agentRuntime.session.getSessionEntry({
|
||||
storePath,
|
||||
sessionKey: resolvedSessionKey,
|
||||
});
|
||||
|
||||
// Resolve model from config
|
||||
const { provider, model } = resolveVoiceResponseModel({ voiceConfig, agentRuntime });
|
||||
const configuredModel = resolveDefaultModelForAgent({ cfg, agentId });
|
||||
|
||||
|
|
@ -417,10 +410,8 @@ export async function generateVoiceResponse(
|
|||
? resolvePersistedSessionRuntimeId(sessionEntry)
|
||||
: undefined;
|
||||
|
||||
// Resolve thinking level
|
||||
const thinkLevel = agentRuntime.resolveThinkingDefault({ cfg, provider, model });
|
||||
|
||||
// Resolve agent identity for personalized prompt
|
||||
const identity = agentRuntime.resolveAgentIdentity(cfg, agentId);
|
||||
const agentName = identity?.name?.trim() || "assistant";
|
||||
|
||||
|
|
@ -435,7 +426,6 @@ export async function generateVoiceResponse(
|
|||
].join("\n\n");
|
||||
const prompt = buildVoiceTurnPrompt({ transcript, userMessage });
|
||||
|
||||
// Resolve timeout
|
||||
const timeoutMs =
|
||||
voiceConfig.responseTimeoutMs ?? agentRuntime.resolveAgentTimeoutMs({ cfg });
|
||||
const runId = `voice:${callId}:${Date.now()}`;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,6 @@ function listenForChildStreamErrors(
|
|||
}
|
||||
|
||||
interface TunnelConfig {
|
||||
/** Tunnel provider: ngrok, tailscale-serve, or tailscale-funnel */
|
||||
provider: "ngrok" | "tailscale-serve" | "tailscale-funnel" | "none";
|
||||
/** Local port to tunnel */
|
||||
port: number;
|
||||
|
|
@ -106,7 +105,6 @@ async function startNgrokTunnel(config: {
|
|||
authToken?: string;
|
||||
domain?: string;
|
||||
}): Promise<TunnelResult> {
|
||||
// Build ngrok command args
|
||||
const args = ["http", String(config.port), "--log", "stdout", "--log-format", "json"];
|
||||
|
||||
// Add custom domain if provided (paid ngrok feature)
|
||||
|
|
@ -161,17 +159,14 @@ async function startNgrokTunnel(config: {
|
|||
publicUrl = log.url;
|
||||
}
|
||||
|
||||
// Also check for the URL field directly
|
||||
if (log.addr && log.url && !publicUrl) {
|
||||
publicUrl = log.url;
|
||||
}
|
||||
|
||||
// Check for ready state
|
||||
if (publicUrl && !startupSettled) {
|
||||
startupSettled = true;
|
||||
clearTimeout(timeout);
|
||||
|
||||
// Add path to the public URL
|
||||
const fullUrl = publicUrl + config.path;
|
||||
|
||||
console.log(`[voice-call] ngrok tunnel active: ${fullUrl}`);
|
||||
|
|
|
|||
|
|
@ -12,9 +12,6 @@ export function escapeXml(text: string): string {
|
|||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Map of OpenAI voice names to similar Twilio Polly voices.
|
||||
*/
|
||||
const OPENAI_TO_POLLY_MAP = new Map<string, string>([
|
||||
["alloy", "Polly.Joanna"], // neutral, warm
|
||||
["echo", "Polly.Matthew"], // male, warm
|
||||
|
|
@ -24,9 +21,6 @@ const OPENAI_TO_POLLY_MAP = new Map<string, string>([
|
|||
["shimmer", "Polly.Kimberly"], // female, clear
|
||||
]);
|
||||
|
||||
/**
|
||||
* Default Polly voice when no mapping is found.
|
||||
*/
|
||||
const DEFAULT_POLLY_VOICE = "Polly.Joanna";
|
||||
|
||||
/**
|
||||
|
|
@ -41,7 +35,6 @@ export function mapVoiceToPolly(voice: string | undefined): string {
|
|||
return DEFAULT_POLLY_VOICE;
|
||||
}
|
||||
|
||||
// Already a Polly/Google voice - pass through
|
||||
if (voice.startsWith("Polly.") || voice.startsWith("Google.")) {
|
||||
return voice;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -191,12 +191,10 @@ export class VoiceCallWebhookServer {
|
|||
private stopStaleCallReaper: (() => void) | null = null;
|
||||
private readonly webhookInFlightLimiter = createWebhookInFlightLimiter();
|
||||
|
||||
/** Media stream handler for bidirectional audio (when streaming enabled) */
|
||||
private mediaStreamHandler: MediaStreamHandler | null = null;
|
||||
private readonly streamDisconnectGrace: StreamDisconnectGrace;
|
||||
// Revoke pending transcript replies before persistence has created a response guard.
|
||||
private readonly streamSpeechGenerations = new WeakMap<CallRecord, symbol>();
|
||||
/** Realtime voice handler for duplex provider bridges. */
|
||||
private realtimeHandler: RealtimeCallHandler | null = null;
|
||||
private replayResponses = new Map<string, CachedWebhookResponse>();
|
||||
private replayResponseCacheCalls = 0;
|
||||
|
|
@ -348,9 +346,6 @@ export class VoiceCallWebhookServer {
|
|||
return generation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize media streaming with the selected realtime transcription provider.
|
||||
*/
|
||||
private async initializeMediaStreaming(): Promise<void> {
|
||||
const streaming = this.config.streaming;
|
||||
const pluginConfig = this.fullConfig ?? this.coreConfig ?? undefined;
|
||||
|
|
@ -438,7 +433,6 @@ export class VoiceCallWebhookServer {
|
|||
|
||||
const generation = this.interruptStreamReply(providerCallId, streamSid);
|
||||
|
||||
// Create a speech event and process it through the manager
|
||||
const event: NormalizedEvent = {
|
||||
id: `stream-transcript-${Date.now()}`,
|
||||
type: "call.speech",
|
||||
|
|
@ -519,7 +513,6 @@ export class VoiceCallWebhookServer {
|
|||
}
|
||||
|
||||
/**
|
||||
* Start the webhook server.
|
||||
* Idempotent: returns immediately if the server is already listening.
|
||||
*/
|
||||
async start(): Promise<string> {
|
||||
|
|
@ -554,7 +547,6 @@ export class VoiceCallWebhookServer {
|
|||
});
|
||||
});
|
||||
|
||||
// Handle WebSocket upgrades for realtime voice and media streams.
|
||||
if (this.realtimeHandler || this.mediaStreamHandler) {
|
||||
this.server.on("upgrade", (request, socket, head) => {
|
||||
if (this.realtimeHandler && this.isRealtimeWebSocketUpgrade(request)) {
|
||||
|
|
@ -592,7 +584,6 @@ export class VoiceCallWebhookServer {
|
|||
}
|
||||
resolve(url);
|
||||
|
||||
// Start the stale call reaper if configured
|
||||
this.stopStaleCallReaper = startStaleCallReaper({
|
||||
manager: this.manager,
|
||||
staleCallReaperSeconds: this.config.staleCallReaperSeconds,
|
||||
|
|
@ -1077,7 +1068,6 @@ export class VoiceCallWebhookServer {
|
|||
private async handleInboundResponse(callId: string, userMessage: string): Promise<void> {
|
||||
this.logger.info(`Auto-responding to inbound call ${callId} chars=${userMessage.length}`);
|
||||
|
||||
// Get the persisted call context for routing and response delivery.
|
||||
const call = this.manager.getCall(callId);
|
||||
if (!call) {
|
||||
this.logger.warn(`Call ${callId} not found for auto-response`);
|
||||
|
|
|
|||
|
|
@ -131,9 +131,6 @@ function resolveZaloApiUrl(apiUrl?: string): string {
|
|||
return parsed.href.replace(/\/+$/u, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the Zalo Bot API
|
||||
*/
|
||||
export async function callZaloApi<T = unknown>(
|
||||
method: string,
|
||||
token: string,
|
||||
|
|
@ -183,9 +180,6 @@ export async function callZaloApi<T = unknown>(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate bot token and get bot info
|
||||
*/
|
||||
export async function getMe(
|
||||
token: string,
|
||||
timeoutMs?: number,
|
||||
|
|
@ -194,9 +188,6 @@ export async function getMe(
|
|||
return callZaloApi<ZaloBotInfo>("getMe", token, undefined, { timeoutMs, fetch: fetcher });
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a text message
|
||||
*/
|
||||
export async function sendMessage(
|
||||
token: string,
|
||||
params: ZaloSendMessageParams,
|
||||
|
|
@ -209,9 +200,6 @@ export async function sendMessage(
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a photo message
|
||||
*/
|
||||
export async function sendPhoto(
|
||||
token: string,
|
||||
params: ZaloSendPhotoParams,
|
||||
|
|
@ -268,7 +256,6 @@ export async function sendChatAction(
|
|||
}
|
||||
|
||||
/**
|
||||
* Get updates using long polling (dev/testing only)
|
||||
* Note: Zalo returns a single update per call, not an array like Telegram
|
||||
*/
|
||||
export async function getUpdates(
|
||||
|
|
@ -282,9 +269,6 @@ export async function getUpdates(
|
|||
return callZaloApi<ZaloUpdate>("getUpdates", token, body, { timeoutMs, fetch: fetcher });
|
||||
}
|
||||
|
||||
/**
|
||||
* Set webhook URL for receiving updates
|
||||
*/
|
||||
export async function setWebhook(
|
||||
token: string,
|
||||
params: ZaloSetWebhookParams,
|
||||
|
|
@ -293,9 +277,6 @@ export async function setWebhook(
|
|||
return callZaloApi<ZaloWebhookInfo>("setWebhook", token, params, { fetch: fetcher });
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete webhook configuration
|
||||
*/
|
||||
export async function deleteWebhook(
|
||||
token: string,
|
||||
fetcher?: ZaloFetch,
|
||||
|
|
@ -307,9 +288,6 @@ export async function deleteWebhook(
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current webhook info
|
||||
*/
|
||||
export async function getWebhookInfo(
|
||||
token: string,
|
||||
fetcher?: ZaloFetch,
|
||||
|
|
|
|||
|
|
@ -116,9 +116,6 @@ function parsePositiveIntegerArg(raw: string | undefined, label: string): number
|
|||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses docs MDX check arguments.
|
||||
*/
|
||||
export function parseArgs(argv: string[]) {
|
||||
const roots: string[] = [];
|
||||
let jsonOut = "";
|
||||
|
|
|
|||
|
|
@ -17,14 +17,8 @@ const policy = {
|
|||
} satisfies ExtensionWildcardReexportPolicy;
|
||||
const scanner = createExtensionWildcardReexportScanner(policy);
|
||||
|
||||
/**
|
||||
* Finds local wildcard re-export lines in a barrel source string.
|
||||
*/
|
||||
export const findLocalWildcardReexports = scanner.findLines;
|
||||
|
||||
/**
|
||||
* Runs the extension wildcard re-export guard.
|
||||
*/
|
||||
export const main = scanner.main;
|
||||
|
||||
await scanner.exitIfMain(import.meta.url);
|
||||
|
|
|
|||
|
|
@ -20,9 +20,6 @@ const enforcedFiles = new Set([
|
|||
"src/gateway/server-node-events.ts",
|
||||
]);
|
||||
|
||||
/**
|
||||
* Finds legacy `agentCommand(...)` call lines in ingress-owned source.
|
||||
*/
|
||||
function findLegacyAgentCommandCallLines(
|
||||
_content: string,
|
||||
_fileName: string,
|
||||
|
|
@ -34,9 +31,6 @@ function findLegacyAgentCommandCallLines(
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the ingress owner-context guard.
|
||||
*/
|
||||
async function main() {
|
||||
await runCallsiteGuard({
|
||||
importMetaUrl: import.meta.url,
|
||||
|
|
|
|||
|
|
@ -10,9 +10,6 @@ import {
|
|||
unwrapExpression,
|
||||
} from "./lib/ts-guard-utils.mts";
|
||||
|
||||
/**
|
||||
* Source roots scanned for unsafe messaging tmpdir usage.
|
||||
*/
|
||||
export const messagingTmpdirGuardSourceRoots = [
|
||||
"src/channels",
|
||||
"src/infra/outbound",
|
||||
|
|
@ -55,9 +52,6 @@ function collectOsTmpdirImports(sourceFile: ts.SourceFile) {
|
|||
return { osNamespaceOrDefault, namedTmpdir };
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds `os.tmpdir()` or imported `tmpdir()` call lines in source.
|
||||
*/
|
||||
export function findMessagingTmpdirCallLines(
|
||||
_content: string,
|
||||
_fileName: string,
|
||||
|
|
@ -78,9 +72,6 @@ export function findMessagingTmpdirCallLines(
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the messaging tmpdir guard.
|
||||
*/
|
||||
export async function main() {
|
||||
await runCallsiteGuard({
|
||||
importMetaUrl: import.meta.url,
|
||||
|
|
|
|||
|
|
@ -75,18 +75,12 @@ function isRawFetchCall(expression: ts.Expression) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds raw `fetch(...)` and `globalThis.fetch(...)` call lines.
|
||||
*/
|
||||
function findRawFetchCallLines(_content: string, _fileName: string, sourceFile: ts.SourceFile) {
|
||||
return collectCallExpressionLines(sourceFile, (node) =>
|
||||
isRawFetchCall(node.expression) ? node.expression : null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the raw channel/plugin fetch guard.
|
||||
*/
|
||||
async function main() {
|
||||
await runCallsiteGuard({
|
||||
importMetaUrl: import.meta.url,
|
||||
|
|
|
|||
|
|
@ -121,9 +121,6 @@ function collectPatchFileViolations(cwd: string, violations: PackagePatchViolati
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects disallowed package patch declarations and patch files.
|
||||
*/
|
||||
export function collectPackagePatchViolations(cwd = process.cwd()) {
|
||||
const violations: PackagePatchViolation[] = [];
|
||||
collectWorkspacePatchViolations(cwd, violations);
|
||||
|
|
@ -133,9 +130,6 @@ export function collectPackagePatchViolations(cwd = process.cwd()) {
|
|||
return violations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the package patch guard.
|
||||
*/
|
||||
export async function main() {
|
||||
const violations = collectPackagePatchViolations();
|
||||
if (violations.length === 0) {
|
||||
|
|
|
|||
|
|
@ -109,9 +109,6 @@ const formatInventoryHuman = (inventory: PluginExtensionInventoryEntry[]) =>
|
|||
inventory,
|
||||
);
|
||||
|
||||
/**
|
||||
* Runs the plugin-extension import boundary check.
|
||||
*/
|
||||
async function runPluginExtensionImportBoundaryCheck(): Promise<0 | 1> {
|
||||
const actual = await collectPluginExtensionImportBoundaryInventory();
|
||||
|
||||
|
|
|
|||
|
|
@ -18,14 +18,8 @@ const policy = {
|
|||
} satisfies ExtensionWildcardReexportPolicy;
|
||||
const scanner = createExtensionWildcardReexportScanner(policy);
|
||||
|
||||
/**
|
||||
* Finds wildcard plugin SDK re-export lines in an extension API barrel.
|
||||
*/
|
||||
export const findPluginSdkWildcardReexports = scanner.findLines;
|
||||
|
||||
/**
|
||||
* Runs the plugin SDK wildcard re-export guard.
|
||||
*/
|
||||
export const main = scanner.main;
|
||||
|
||||
await scanner.exitIfMain(import.meta.url);
|
||||
|
|
|
|||
|
|
@ -34,9 +34,6 @@ function getCalleeName(expression: ts.Expression): string | null {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds request body reads that occur before webhook auth validation.
|
||||
*/
|
||||
function findBlockedWebhookBodyReadLines(
|
||||
_content: string,
|
||||
_fileName: string,
|
||||
|
|
@ -48,9 +45,6 @@ function findBlockedWebhookBodyReadLines(
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the webhook auth/body-order guard.
|
||||
*/
|
||||
async function main() {
|
||||
await runCallsiteGuard({
|
||||
importMetaUrl: import.meta.url,
|
||||
|
|
|
|||
|
|
@ -42,9 +42,6 @@ export const PREFLIGHT_CHECKS: CheckCommand[] = [
|
|||
{ name: "package patch guard", args: ["deps:patches:check"] },
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns command usage text for the aggregate check runner.
|
||||
*/
|
||||
export function usage() {
|
||||
return [
|
||||
"Usage: node --import tsx scripts/check.mts [--base <ref>] [--timed] [--include-architecture] [--include-test-types]",
|
||||
|
|
@ -60,9 +57,6 @@ export function usage() {
|
|||
].join("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses aggregate check runner arguments.
|
||||
*/
|
||||
function parseCheckArgs(argv: string[]) {
|
||||
return parseFlagArgs(
|
||||
argv,
|
||||
|
|
@ -84,9 +78,6 @@ function parseCheckArgs(argv: string[]) {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs selected repository check lanes.
|
||||
*/
|
||||
export async function main(argv = process.argv.slice(2)) {
|
||||
let args;
|
||||
try {
|
||||
|
|
@ -197,9 +188,6 @@ async function runSerial(commands: CheckCommand[]) {
|
|||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs one managed check command and returns timing/status details.
|
||||
*/
|
||||
export async function runCommand(
|
||||
command: CheckCommand,
|
||||
runManagedCommandImpl: RunManagedCheck = runManagedCommand,
|
||||
|
|
|
|||
|
|
@ -23,16 +23,10 @@ type StartupBuildParams = Partial<{
|
|||
timeoutMs: number;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Resolves the CLI startup build timeout from environment.
|
||||
*/
|
||||
export function resolveCliStartupBuildTimeoutMs(env: NodeJS.ProcessEnv = process.env) {
|
||||
return readPositiveEnvInt("OPENCLAW_CLI_STARTUP_BUILD_TIMEOUT_MS", env, DEFAULT_BUILD_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports whether required CLI startup build outputs exist.
|
||||
*/
|
||||
export function hasCliStartupBuild(params: StartupBuildParams = {}) {
|
||||
const rootDir = params.rootDir ?? repoRoot;
|
||||
const exists = params.existsSync ?? existsSync;
|
||||
|
|
@ -40,9 +34,6 @@ export function hasCliStartupBuild(params: StartupBuildParams = {}) {
|
|||
return hasEntry && exists(path.join(rootDir, startupMetadataPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds CLI startup assets when required outputs are missing.
|
||||
*/
|
||||
export function ensureCliStartupBuild(params: StartupBuildParams = {}) {
|
||||
const rootDir = params.rootDir ?? repoRoot;
|
||||
if (hasCliStartupBuild({ rootDir, existsSync: params.existsSync })) {
|
||||
|
|
|
|||
|
|
@ -28,9 +28,6 @@ type ExtensionMemoryBuildParams = {
|
|||
timeoutMs?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves the extension memory build timeout from environment.
|
||||
*/
|
||||
function resolveExtensionMemoryBuildTimeoutMs(env: NodeJS.ProcessEnv = process.env) {
|
||||
return readPositiveEnvInt(
|
||||
"OPENCLAW_EXTENSION_MEMORY_BUILD_TIMEOUT_MS",
|
||||
|
|
@ -103,9 +100,6 @@ export function hasBuiltExtensionMemoryEntries(params: ExtensionMemoryBuildParam
|
|||
return requiredExtensionIds.every((id) => builtIds.has(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds memory extension entries when required outputs are missing.
|
||||
*/
|
||||
export function ensureExtensionMemoryBuild(params: ExtensionMemoryBuildParams = {}) {
|
||||
const rootDir = params.rootDir ?? repoRoot;
|
||||
if (hasBuiltExtensionMemoryEntries(params)) {
|
||||
|
|
|
|||
|
|
@ -30,9 +30,6 @@ type CollectFileViolationsParams<Violation extends object> = {
|
|||
sourceRoots: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts repo-relative source roots into absolute paths.
|
||||
*/
|
||||
export function resolveSourceRoots(repoRoot: string, relativeRoots: string[]) {
|
||||
return relativeRoots.map((root) => path.join(repoRoot, ...root.split("/").filter(Boolean)));
|
||||
}
|
||||
|
|
@ -41,9 +38,6 @@ export function isTestLikeTypeScriptFile(filePath: string, extraTestSuffixes: st
|
|||
return [...baseTestSuffixes, ...extraTestSuffixes].some((suffix) => filePath.endsWith(suffix));
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively collects TypeScript files under a file or directory target.
|
||||
*/
|
||||
export async function collectTypeScriptFiles(
|
||||
targetPath: string,
|
||||
options: CollectTypeScriptFilesOptions = {},
|
||||
|
|
@ -128,9 +122,6 @@ export async function collectTypeScriptFilesFromRoots(
|
|||
).flat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a guard's violation scanner across collected TypeScript source files.
|
||||
*/
|
||||
export async function collectFileViolations<Violation extends object>(
|
||||
params: CollectFileViolationsParams<Violation>,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -75,9 +75,6 @@ export function resolveSpawnCommand(
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the signal-forwarding force-kill grace period.
|
||||
*/
|
||||
export function resolveForceKillDelayMs(env: NodeJS.ProcessEnv = process.env) {
|
||||
const raw = env.OPENCLAW_RUN_WITH_ENV_FORCE_KILL_MS;
|
||||
const text = raw?.trim();
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { createSessionManagerRuntimeRegistry } from "./session-manager-runtime-r
|
|||
|
||||
export type CompactionSafeguardCancellation = { reason: string; error?: unknown };
|
||||
|
||||
/** Runtime knobs consumed by the compaction safeguard extension. */
|
||||
type CompactionSafeguardRuntimeValue = {
|
||||
maxHistoryShare?: number;
|
||||
contextWindowTokens?: number;
|
||||
|
|
|
|||
|
|
@ -286,10 +286,6 @@ type ToolFailure = {
|
|||
meta?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve model credentials. Returns auth details on success or a cancel reason on failure.
|
||||
* Extracted to keep the main handler readable when model/auth is conditional.
|
||||
*/
|
||||
async function resolveModelAuth(
|
||||
ctx: ExtensionContext,
|
||||
model: NonNullable<ExtensionContext["model"]>,
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ type ProviderSetDiscoveryParams = {
|
|||
allowKeychainPrompt?: false;
|
||||
};
|
||||
|
||||
/** Disables external CLI auth discovery. */
|
||||
function externalCliDiscoveryNone(params?: { config?: OpenClawConfig }): ExternalCliAuthDiscovery {
|
||||
return {
|
||||
mode: "none",
|
||||
|
|
|
|||
|
|
@ -17,9 +17,6 @@ function calculateBackoffMs(consecutiveNoOutputPolls: number): number {
|
|||
|
||||
/**
|
||||
* Record a command poll and return suggested retry delay.
|
||||
* @param state Session state to track polling in
|
||||
* @param commandId Unique identifier for the command being polled
|
||||
* @param hasNewOutput Whether this poll returned new output
|
||||
* @returns Suggested delay in milliseconds before next poll
|
||||
*/
|
||||
export function recordCommandPoll(
|
||||
|
|
@ -45,9 +42,6 @@ export function recordCommandPoll(
|
|||
return calculateBackoffMs(newCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset poll count for a command (e.g., when command completes).
|
||||
*/
|
||||
export function resetCommandPollCount(state: SessionState, commandId: string): void {
|
||||
state.commandPollCounts?.delete(commandId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { patchSessionEntryCore } from "../../config/sessions/session-accessor.js
|
|||
import { buildSessionCreationStamp } from "../../config/sessions/session-entry-provenance.js";
|
||||
import { mergeSessionSnapshotChanges } from "../../config/sessions/session-snapshot-merge.js";
|
||||
import type { SessionEntry } from "../../config/sessions/types.js";
|
||||
/** Parameters for merging and persisting a session entry update. */
|
||||
type PersistSessionEntryParams = {
|
||||
agentId: string;
|
||||
sessionStore: Record<string, SessionEntry>;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ const EMBEDDED_AGENT_EXECUTION_PHASE_LABELS = {
|
|||
|
||||
export type EmbeddedAgentExecutionPhase = keyof typeof EMBEDDED_AGENT_EXECUTION_PHASE_LABELS;
|
||||
|
||||
/** Converts an internal phase id into the compact label used in status output. */
|
||||
export function formatEmbeddedAgentExecutionPhase(
|
||||
phase?: EmbeddedAgentExecutionPhase,
|
||||
): string | undefined {
|
||||
|
|
|
|||
|
|
@ -132,7 +132,6 @@ export function resolveProviderModelInput(params: {
|
|||
return normalizedInput.length > 0 ? normalizedInput : ["text"];
|
||||
}
|
||||
|
||||
/** Builds runtime model records from inline provider config. */
|
||||
export function buildInlineProviderModels(
|
||||
providers: Record<string, InlineProviderConfig>,
|
||||
options: { providerMetadataOwners?: PluginMetadataSnapshotOwnerMaps } = {},
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import type { ProviderRuntimeModel } from "../../plugins/provider-runtime-model.
|
|||
import { DEFAULT_CONTEXT_TOKENS } from "../defaults.js";
|
||||
import { resolveProviderModelInput } from "./model.inline-provider.js";
|
||||
|
||||
/** Converts a normalized catalog row into the provider runtime model shape. */
|
||||
export function modelFromStaticCatalogRow(row: NormalizedModelCatalogRow): ProviderRuntimeModel {
|
||||
return {
|
||||
id: row.id,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import {
|
|||
type StageTimingSummary,
|
||||
} from "../../../shared/stage-timing.js";
|
||||
|
||||
/** Canonical stage names for dispatch-time embedded attempt diagnostics. */
|
||||
export const EMBEDDED_RUN_ATTEMPT_DISPATCH_STAGE = {
|
||||
workspace: "attempt-workspace",
|
||||
prompt: "attempt-prompt",
|
||||
|
|
@ -66,7 +65,6 @@ export function createEmbeddedRunStageSummaryEmitter(options: {
|
|||
};
|
||||
}
|
||||
|
||||
/** Formats stage timing into compact log text for startup/attempt diagnostics. */
|
||||
export function formatEmbeddedRunStageSummary(prefix: string, summary: StageTimingSummary): string {
|
||||
const stages = formatStageTimings(summary.stages);
|
||||
return `${prefix} pid=${process.pid} threadId=${threadId} isMainThread=${isMainThread} totalMs=${summary.totalMs} stages=${stages}`;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ type ProviderContextMaterializer = (input: {
|
|||
signal?: AbortSignal;
|
||||
}) => Promise<ProviderContext>;
|
||||
|
||||
/** Wraps a stream function with a conditional message-list transform. */
|
||||
export function wrapStreamFnWithMessageTransform(
|
||||
streamFn: StreamFn,
|
||||
transform: MessageTransform,
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ export class MidTurnPrecheckSignal extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
/** Narrows unknown errors to the mid-turn overflow signal used by attempt cleanup. */
|
||||
export function isMidTurnPrecheckSignal(error: unknown): error is MidTurnPrecheckSignal {
|
||||
return error instanceof MidTurnPrecheckSignal;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ export function resolveEmbeddedFullAccessState(params: {
|
|||
return { available: true };
|
||||
}
|
||||
|
||||
/** Resolves the effective exec policy for sandbox-info reporting. */
|
||||
export async function resolveEmbeddedSandboxInfoExecPolicy(
|
||||
params: {
|
||||
config?: OpenClawConfig;
|
||||
|
|
@ -93,7 +92,6 @@ export async function resolveEmbeddedSandboxInfoExecPolicy(
|
|||
);
|
||||
}
|
||||
|
||||
/** Builds the serializable sandbox metadata attached to embedded agent run results. */
|
||||
export function buildEmbeddedSandboxInfo(
|
||||
sandbox?: Awaited<ReturnType<typeof resolveSandboxContext>>,
|
||||
execElevated?: ExecElevatedDefaults,
|
||||
|
|
|
|||
|
|
@ -29,9 +29,6 @@ export function collectAllowedToolNames(params: {
|
|||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the exact tool names registered with the embedded agent for this session.
|
||||
*/
|
||||
export function collectRegisteredToolNames(tools: Array<{ name?: string }>): Set<string> {
|
||||
const names = new Set<string>();
|
||||
for (const tool of tools) {
|
||||
|
|
|
|||
|
|
@ -1106,7 +1106,6 @@ export function resolveAllowedModelRefFromAliasIndex(
|
|||
return { ref: resolved.ref, key: status.key };
|
||||
}
|
||||
|
||||
/** True when config contains provider model rows that should seed catalogs. */
|
||||
function hasConfiguredProviderModelRows(cfg: OpenClawConfig): boolean {
|
||||
const providers = cfg.models?.providers;
|
||||
if (!providers || typeof providers !== "object") {
|
||||
|
|
|
|||
|
|
@ -86,7 +86,6 @@ export function renderDiff(diffText: string): string {
|
|||
}
|
||||
|
||||
if (parsed.prefix === "-") {
|
||||
// Collect consecutive removed lines
|
||||
const removedLines: { lineNum: string; content: string }[] = [];
|
||||
while (i < lines.length) {
|
||||
const currentLine = lines.at(i);
|
||||
|
|
@ -98,7 +97,6 @@ export function renderDiff(diffText: string): string {
|
|||
i++;
|
||||
}
|
||||
|
||||
// Collect consecutive added lines
|
||||
const addedLines: { lineNum: string; content: string }[] = [];
|
||||
while (i < lines.length) {
|
||||
const currentLine = lines.at(i);
|
||||
|
|
@ -138,11 +136,9 @@ export function renderDiff(diffText: string): string {
|
|||
}
|
||||
}
|
||||
} else if (parsed.prefix === "+") {
|
||||
// Standalone added line
|
||||
result.push(theme.fg("toolDiffAdded", `+${parsed.lineNum} ${replaceTabs(parsed.content)}`));
|
||||
i++;
|
||||
} else {
|
||||
// Context line
|
||||
result.push(theme.fg("toolDiffContext", ` ${parsed.lineNum} ${replaceTabs(parsed.content)}`));
|
||||
i++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ const ThemeJsonSchema = Type.Object({
|
|||
name: Type.String(),
|
||||
vars: Type.Optional(Type.Record(Type.String(), ColorValueSchema)),
|
||||
colors: Type.Object({
|
||||
// Core UI (10 colors)
|
||||
accent: ColorValueSchema,
|
||||
border: ColorValueSchema,
|
||||
borderAccent: ColorValueSchema,
|
||||
|
|
@ -35,7 +34,6 @@ const ThemeJsonSchema = Type.Object({
|
|||
dim: ColorValueSchema,
|
||||
text: ColorValueSchema,
|
||||
thinkingText: ColorValueSchema,
|
||||
// Backgrounds & Content Text (11 colors)
|
||||
selectedBg: ColorValueSchema,
|
||||
userMessageBg: ColorValueSchema,
|
||||
userMessageText: ColorValueSchema,
|
||||
|
|
@ -47,7 +45,6 @@ const ThemeJsonSchema = Type.Object({
|
|||
toolErrorBg: ColorValueSchema,
|
||||
toolTitle: ColorValueSchema,
|
||||
toolOutput: ColorValueSchema,
|
||||
// Markdown (10 colors)
|
||||
mdHeading: ColorValueSchema,
|
||||
mdLink: ColorValueSchema,
|
||||
mdLinkUrl: ColorValueSchema,
|
||||
|
|
@ -58,11 +55,9 @@ const ThemeJsonSchema = Type.Object({
|
|||
mdQuoteBorder: ColorValueSchema,
|
||||
mdHr: ColorValueSchema,
|
||||
mdListBullet: ColorValueSchema,
|
||||
// Tool Diffs (3 colors)
|
||||
toolDiffAdded: ColorValueSchema,
|
||||
toolDiffRemoved: ColorValueSchema,
|
||||
toolDiffContext: ColorValueSchema,
|
||||
// Syntax Highlighting (9 colors)
|
||||
syntaxComment: ColorValueSchema,
|
||||
syntaxKeyword: ColorValueSchema,
|
||||
syntaxFunction: ColorValueSchema,
|
||||
|
|
@ -72,14 +67,12 @@ const ThemeJsonSchema = Type.Object({
|
|||
syntaxType: ColorValueSchema,
|
||||
syntaxOperator: ColorValueSchema,
|
||||
syntaxPunctuation: ColorValueSchema,
|
||||
// Thinking Level Borders (6 colors)
|
||||
thinkingOff: ColorValueSchema,
|
||||
thinkingMinimal: ColorValueSchema,
|
||||
thinkingLow: ColorValueSchema,
|
||||
thinkingMedium: ColorValueSchema,
|
||||
thinkingHigh: ColorValueSchema,
|
||||
thinkingXhigh: ColorValueSchema,
|
||||
// Bash Mode (1 color)
|
||||
bashMode: ColorValueSchema,
|
||||
}),
|
||||
export: Type.Optional(
|
||||
|
|
@ -156,7 +149,6 @@ function colorDistance(
|
|||
}
|
||||
|
||||
function rgbTo256(r: number, g: number, b: number): number {
|
||||
// Find closest color in the 6x6x6 cube
|
||||
const rIdx = findClosestPaletteIndex(r, CUBE_VALUES);
|
||||
const gIdx = findClosestPaletteIndex(g, CUBE_VALUES);
|
||||
const bIdx = findClosestPaletteIndex(b, CUBE_VALUES);
|
||||
|
|
@ -169,7 +161,6 @@ function rgbTo256(r: number, g: number, b: number): number {
|
|||
const cubeIndex = 16 + 36 * rIdx + 6 * gIdx + bIdx;
|
||||
const cubeDist = colorDistance(r, g, b, cubeR, cubeG, cubeB);
|
||||
|
||||
// Find closest grayscale
|
||||
const gray = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
|
||||
const grayIdx = findClosestPaletteIndex(gray, GRAY_VALUES);
|
||||
const grayValue = GRAY_VALUES[grayIdx];
|
||||
|
|
|
|||
|
|
@ -98,7 +98,6 @@ export abstract class AgentSessionTree extends AgentSessionExecution {
|
|||
fromExtension = true;
|
||||
}
|
||||
|
||||
// Allow extensions to override instructions and label
|
||||
if (result?.customInstructions !== undefined) {
|
||||
customInstructions = result.customInstructions;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,13 +43,11 @@ const OpenRouterRoutingSchema = Type.Object({
|
|||
preferred_max_latency: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema])),
|
||||
});
|
||||
|
||||
// Schema for Vercel AI Gateway routing preferences
|
||||
const VercelGatewayRoutingSchema = Type.Object({
|
||||
only: Type.Optional(Type.Array(Type.String())),
|
||||
order: Type.Optional(Type.Array(Type.String())),
|
||||
});
|
||||
|
||||
// Schema for thinking level support and provider-specific values
|
||||
const ThinkingLevelMapValueSchema = Type.Union([Type.String(), Type.Null()]);
|
||||
const ThinkingLevelMapSchema = Type.Object({
|
||||
off: Type.Optional(ThinkingLevelMapValueSchema),
|
||||
|
|
|
|||
|
|
@ -266,7 +266,6 @@ export async function resolveModelScope(
|
|||
const scopedModels: ScopedModel[] = [];
|
||||
|
||||
for (const pattern of patterns) {
|
||||
// Check if pattern contains glob characters
|
||||
if (pattern.includes("*") || pattern.includes("?") || pattern.includes("[")) {
|
||||
// Extract optional thinking level suffix (e.g., "provider/*:high")
|
||||
const suffix = splitModelPatternSuffix(pattern);
|
||||
|
|
@ -312,7 +311,6 @@ export async function resolveModelScope(
|
|||
continue;
|
||||
}
|
||||
|
||||
// Avoid duplicates
|
||||
if (!scopedModels.some((sm) => modelsAreEqual(sm.model, model))) {
|
||||
scopedModels.push({ model, thinkingLevel });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,8 +125,6 @@ interface CreateAgentSessionResult {
|
|||
modelFallbackMessage?: string;
|
||||
}
|
||||
|
||||
// Helper Functions
|
||||
|
||||
function createSessionPrepareNextTurnWithContext(
|
||||
getAgent: () => Agent,
|
||||
): NonNullable<AgentOptions["prepareNextTurnWithContext"]> {
|
||||
|
|
@ -273,7 +271,6 @@ async function createAgentSessionImpl(
|
|||
}
|
||||
let resourceLoader = options.resourceLoader;
|
||||
|
||||
// Use provided or create AuthStorage and ModelRegistry
|
||||
const config = options.authStorage && options.modelRegistry ? undefined : install.config;
|
||||
const authStorage = options.authStorage ?? AuthStorage.forAgent(agentDir, config);
|
||||
const modelRegistry =
|
||||
|
|
@ -303,7 +300,6 @@ async function createAgentSessionImpl(
|
|||
modelRegistry.refresh();
|
||||
}
|
||||
|
||||
// Check if session has existing data to restore
|
||||
const existingSession = await sessionManager[sessionManagerReadInitialContext]();
|
||||
assertInitialSessionCurrent();
|
||||
const hasExistingSession = existingSession.messages.length > 0;
|
||||
|
|
@ -314,7 +310,6 @@ async function createAgentSessionImpl(
|
|||
let model = options.model;
|
||||
let modelFallbackMessage: string | undefined;
|
||||
|
||||
// If session has data, try to restore model from it
|
||||
if (!model && hasExistingSession && existingSession.model) {
|
||||
const restoredModel = modelRegistry.find(
|
||||
existingSession.model.provider,
|
||||
|
|
@ -379,7 +374,6 @@ async function createAgentSessionImpl(
|
|||
settingsManager.getDefaultThinkingLevel() ??
|
||||
modelThinkingDefault;
|
||||
|
||||
// Clamp to model capabilities
|
||||
if (!model) {
|
||||
thinkingLevel = "off";
|
||||
} else {
|
||||
|
|
@ -405,7 +399,6 @@ async function createAgentSessionImpl(
|
|||
if (!settingsManager.getBlockImages()) {
|
||||
return converted;
|
||||
}
|
||||
// Filter out ImageContent from all messages, replacing with text placeholder
|
||||
return converted.map((msg) => {
|
||||
if (msg.role === "user" || msg.role === "toolResult") {
|
||||
const content = msg.content;
|
||||
|
|
@ -420,7 +413,6 @@ async function createAgentSessionImpl(
|
|||
)
|
||||
.filter((c, i, arr) => {
|
||||
const previous = arr.at(i - 1);
|
||||
// Dedupe consecutive "Image reading is disabled." texts
|
||||
return !(
|
||||
c.type === "text" &&
|
||||
c.text === "Image reading is disabled." &&
|
||||
|
|
|
|||
|
|
@ -180,7 +180,6 @@ export function createFindToolDefinition(
|
|||
const effectiveLimit = normalizePositiveLimit(limit, DEFAULT_LIMIT);
|
||||
// One extra candidate distinguishes an exact-size result from a truncated one.
|
||||
const observationLimit = effectiveLimit + 1;
|
||||
// If custom operations provide glob(), use that instead of fd.
|
||||
if (customOps?.glob) {
|
||||
if (!(await customOps.exists(searchPath))) {
|
||||
settle(() => reject(new Error(`Path not found: ${searchPath}`)));
|
||||
|
|
@ -222,7 +221,6 @@ export function createFindToolDefinition(
|
|||
return;
|
||||
}
|
||||
|
||||
// Default implementation uses fd.
|
||||
const fdPath = await ensureTool("fd", true);
|
||||
if (signal?.aborted) {
|
||||
settle(() => reject(new Error("Operation aborted")));
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ type SubagentAnnounceSteerOutcome =
|
|||
| { status: "steered"; deliveredAt?: number; enqueuedAt?: number }
|
||||
| { status: "none" | "dropped" | "source_owner_changed" };
|
||||
|
||||
/** Result of trying to deliver a subagent announcement. */
|
||||
export type SubagentAnnounceDeliveryResult = {
|
||||
delivered: boolean;
|
||||
path: SubagentDeliveryPath;
|
||||
|
|
@ -73,7 +72,6 @@ export function sourceOwnerChangedResult(): SubagentAnnounceDeliveryResult {
|
|||
};
|
||||
}
|
||||
|
||||
/** Converts a steer outcome into the shared delivery result shape. */
|
||||
function mapSteerOutcomeToDeliveryResult(
|
||||
outcome: SubagentAnnounceSteerOutcome,
|
||||
): SubagentAnnounceDeliveryResult {
|
||||
|
|
|
|||
|
|
@ -305,7 +305,6 @@ export async function safeRemoveAttachmentsDir(
|
|||
}
|
||||
}
|
||||
|
||||
/** Resolves the completed subagent archive delay from config. */
|
||||
function resolveArchiveAfterMs(cfg?: OpenClawConfig) {
|
||||
const config = cfg ?? getRuntimeConfig();
|
||||
const minutes =
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ function shouldPreserveForMaintenance(entry: SubagentRunMaintenanceRecord): bool
|
|||
);
|
||||
}
|
||||
|
||||
/** Lists child session keys protected from session-store maintenance pruning. */
|
||||
function protectedSubagentSessionKeys(runs: Iterable<SubagentRunMaintenanceRecord>): string[] {
|
||||
const keys = new Set<string>();
|
||||
for (const entry of runs) {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ const TERMINAL_QUESTION_ERROR_REASONS = new Set([
|
|||
"QUESTION_NOT_FOUND",
|
||||
]);
|
||||
|
||||
/** Reads the Gateway's structured failure from a question RPC rejection. */
|
||||
export function readQuestionRejection(
|
||||
error: unknown,
|
||||
): { code: unknown; reason?: string } | undefined {
|
||||
|
|
@ -49,7 +48,6 @@ export function isTerminalQuestionResolveError(error: unknown): boolean {
|
|||
return reason !== undefined && TERMINAL_QUESTION_ERROR_REASONS.has(reason);
|
||||
}
|
||||
|
||||
/** Waits for one question's terminal state, validating the Gateway's payload. */
|
||||
export async function awaitGatewayQuestionAnswer(params: {
|
||||
gatewayCall: GatewayQuestionCall;
|
||||
questionId: string;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
import { Type } from "typebox";
|
||||
import { optionalPositiveIntegerSchema } from "../schema/typebox.js";
|
||||
|
||||
/** Returns optional gateway URL/token/timeout schema properties for tool params. */
|
||||
export function gatewayCallOptionSchemaProperties() {
|
||||
return {
|
||||
gatewayUrl: Type.Optional(Type.String()),
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ const HIDDEN_STYLE_PATTERNS = (
|
|||
return [new RegExp(`(?:^|;)\\s*${escapedProp}\\s*:\\s*([^;]+)`, "i"), valuePattern] as const;
|
||||
});
|
||||
|
||||
// Class names associated with visually hidden content
|
||||
const HIDDEN_CLASS_NAMES = new Set([
|
||||
"sr-only",
|
||||
"visually-hidden",
|
||||
|
|
@ -78,7 +77,6 @@ function isStyleHidden(style: string): boolean {
|
|||
}
|
||||
}
|
||||
|
||||
// width:0 + height:0 + overflow:hidden
|
||||
const width = style.match(/(?:^|;)\s*width\s*:\s*([^;]+)/i);
|
||||
const height = style.match(/(?:^|;)\s*height\s*:\s*([^;]+)/i);
|
||||
const overflow = style.match(/(?:^|;)\s*overflow\s*:\s*([^;]+)/i);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
import { parse } from "yaml";
|
||||
import { extractFrontmatterBlock } from "../../../packages/markdown-core/src/frontmatter.js";
|
||||
|
||||
/** Parsed frontmatter metadata plus the remaining document body. */
|
||||
type ParsedFrontmatter<T extends Record<string, unknown>> = {
|
||||
frontmatter: T;
|
||||
body: string;
|
||||
|
|
|
|||
|
|
@ -58,12 +58,10 @@ function loadHighlightJsRuntime(): HighlightJs {
|
|||
return setHighlightJsRuntime(createRequire(import.meta.url)("highlight.js"));
|
||||
}
|
||||
|
||||
/** Formatter applied to highlighted text segments. */
|
||||
type HighlightFormatter = (text: string) => string;
|
||||
/** Mapping from highlight.js scope names to text formatters. */
|
||||
type HighlightTheme = Partial<Record<string, HighlightFormatter>>;
|
||||
|
||||
/** Options used when highlighting code and rendering themed text. */
|
||||
interface HighlightOptions {
|
||||
language?: string;
|
||||
ignoreIllegals?: boolean;
|
||||
|
|
|
|||
|
|
@ -262,7 +262,6 @@ function resolveCommandsAllowFromList(
|
|||
return null; // Not configured, fall back to channel allowFrom
|
||||
}
|
||||
|
||||
// Check provider-specific list first, then fall back to global "*"
|
||||
const providerKey = params.providerId ?? "";
|
||||
const providerList = commandsAllowFrom[providerKey];
|
||||
const globalList = commandsAllowFrom["*"];
|
||||
|
|
|
|||
|
|
@ -178,11 +178,8 @@ export function stripHeartbeatToken(
|
|||
// (e.g., <b>HEARTBEAT_OK</b> or **HEARTBEAT_OK**) still strips.
|
||||
const stripMarkup = (text: string) =>
|
||||
text
|
||||
// Drop HTML tags.
|
||||
.replace(/<[^>]*>/g, " ")
|
||||
// Decode common nbsp variant.
|
||||
.replace(/ /gi, " ")
|
||||
// Remove markdown-ish wrappers at the edges.
|
||||
.replace(/^[*`~_]+/, "")
|
||||
.replace(/[*`~_]+$/, "");
|
||||
|
||||
|
|
|
|||
|
|
@ -555,7 +555,6 @@ export async function chooseDispatchRoute(state: PrepareDispatchOperationReadySt
|
|||
};
|
||||
};
|
||||
|
||||
// Run before_dispatch hook — let plugins inspect or handle before model dispatch.
|
||||
if (
|
||||
state.allowInboundHandlers &&
|
||||
!admittedSessionSettingsRestrictRuntime(params.replyOptions?.admittedSessionSettings) &&
|
||||
|
|
|
|||
|
|
@ -467,7 +467,6 @@ export async function gatherDispatchRequest(
|
|||
});
|
||||
}));
|
||||
const hookRunner = getGlobalHookRunner();
|
||||
// Extract message context for hooks (plugin and internal)
|
||||
const timestamp =
|
||||
typeof ctx.Timestamp === "number" && Number.isFinite(ctx.Timestamp) ? ctx.Timestamp : undefined;
|
||||
const messageIdForHook =
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ export async function prepareDispatchExecution(state: ChooseDispatchRouteReadySt
|
|||
markInboundDedupeReplayUnsafe();
|
||||
turnLedger.sendQueued("tool", replyPayload);
|
||||
};
|
||||
// Track accumulated block text for TTS generation after streaming completes.
|
||||
// When block streaming succeeds, there's no final reply, so we need to generate
|
||||
// TTS audio separately from the accumulated block content.
|
||||
const progressState = {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
/** Normalizes real inbound newline characters while preserving literal escape text. */
|
||||
export function normalizeInboundTextNewlines(input: string): string {
|
||||
// Normalize actual newline characters (CR+LF and CR to LF).
|
||||
// Do NOT replace literal backslash-n sequences (\\n) as they may be part of
|
||||
// Windows paths like C:\Work\nxxx\README.md or user-intended escape sequences.
|
||||
return input.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
|
||||
|
|
|
|||
|
|
@ -166,7 +166,6 @@ export function normalizeReplyPayloadOutcome(
|
|||
text = enrichedPayload.text;
|
||||
}
|
||||
|
||||
// Resolve template variables in responsePrefix if context is provided
|
||||
const effectivePrefix = opts.responsePrefixContext
|
||||
? resolveResponsePrefixTemplate(opts.responsePrefix, opts.responsePrefixContext)
|
||||
: opts.responsePrefix;
|
||||
|
|
|
|||
|
|
@ -572,7 +572,6 @@ export function createReplyDispatcher(
|
|||
deliveryOutcomeTracker.tracked = true;
|
||||
}
|
||||
|
||||
// Determine if we should add human-like delay (only for block replies after the first).
|
||||
const shouldDelay = kind === "block" && sentFirstBlock;
|
||||
if (kind === "block") {
|
||||
sentFirstBlock = true;
|
||||
|
|
|
|||
|
|
@ -68,7 +68,6 @@ function buildRegisteredChannelPluginLookup(): RegisteredChannelPluginLookup {
|
|||
return registeredChannelPluginLookup;
|
||||
}
|
||||
|
||||
/** Lists active channel plugin registrations from the current registry snapshot. */
|
||||
export function listRegisteredChannelPluginEntries(): ActivePluginChannelRegistration[] {
|
||||
return buildRegisteredChannelPluginLookup().entries;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ function encodeCompletionProfile(content: string, encoding: CompletionProfileEnc
|
|||
return encoding === "utf16be" ? buffer.swap16() : buffer;
|
||||
}
|
||||
|
||||
/** Narrows an arbitrary shell label to a completion shell supported by installer logic. */
|
||||
export function isCompletionShell(value: string): value is CompletionShell {
|
||||
return COMPLETION_SHELLS.includes(value as CompletionShell);
|
||||
}
|
||||
|
|
@ -102,7 +101,6 @@ export function resolveCompletionCachePath(shell: CompletionShell, binName: stri
|
|||
);
|
||||
}
|
||||
|
||||
/** Check if the completion cache file exists for the given shell. */
|
||||
export async function completionCacheExists(
|
||||
shell: CompletionShell,
|
||||
binName = "openclaw",
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ const parseFacing = (value: string): CameraFacing => {
|
|||
throw new Error(`invalid facing: ${value} (expected front|back)`);
|
||||
};
|
||||
|
||||
/** Register node camera list/snap/clip commands. */
|
||||
export function registerNodesCameraCommands(nodes: Command) {
|
||||
const camera = nodes.command("camera").description("Capture camera media from a paired node");
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ function parseNodeInvokeParams(value = "{}"): unknown {
|
|||
}
|
||||
}
|
||||
|
||||
/** Register direct node command invocation. */
|
||||
export function registerNodesInvokeCommands(nodes: Command) {
|
||||
nodesCallOpts(
|
||||
nodes
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import {
|
|||
} from "./rpc.js";
|
||||
import type { NodesRpcOpts } from "./types.js";
|
||||
|
||||
/** Register node location lookup commands. */
|
||||
export function registerNodesLocationCommands(nodes: Command) {
|
||||
const location = nodes.command("location").description("Fetch location from a paired node");
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import {
|
|||
} from "./rpc.js";
|
||||
import type { NodesRpcOpts } from "./types.js";
|
||||
|
||||
/** Register node notification command. */
|
||||
export function registerNodesNotifyCommand(nodes: Command) {
|
||||
nodesCallOpts(
|
||||
nodes
|
||||
|
|
|
|||
|
|
@ -122,7 +122,6 @@ function rethrowUnknownNodePairRequestId(
|
|||
throw error;
|
||||
}
|
||||
|
||||
/** Register node pairing management commands. */
|
||||
export function registerNodesPairingCommands(nodes: Command) {
|
||||
nodesCallOpts(
|
||||
nodes
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import {
|
|||
} from "./rpc.js";
|
||||
import type { NodesRpcOpts } from "./types.js";
|
||||
|
||||
/** Register node screen recording commands. */
|
||||
export function registerNodesScreenCommands(nodes: Command) {
|
||||
const screen = nodes
|
||||
.command("screen")
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ const requiredOptions = {
|
|||
startTime: ["--start-time <iso>", "Event start time"],
|
||||
} as const;
|
||||
|
||||
/** Register Discord admin and moderation message subcommands. */
|
||||
export function registerMessageDiscordAdminCommands(message: Command, helpers: MessageCliHelpers) {
|
||||
function register(
|
||||
parent: Command,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import type { Command } from "commander";
|
|||
import { collectOption } from "../helpers.js";
|
||||
import type { MessageCliHelpers } from "./helpers.js";
|
||||
|
||||
/** Register the channel permissions inspection command. */
|
||||
export function registerMessagePermissionsCommand(message: Command, helpers: MessageCliHelpers) {
|
||||
helpers
|
||||
.withMessageBase(
|
||||
|
|
@ -13,7 +12,6 @@ export function registerMessagePermissionsCommand(message: Command, helpers: Mes
|
|||
.action((opts) => helpers.runMessageAction("permissions", opts));
|
||||
}
|
||||
|
||||
/** Register the channel message search command and repeatable filters. */
|
||||
export function registerMessageSearchCommand(message: Command, helpers: MessageCliHelpers) {
|
||||
helpers
|
||||
.withMessageBase(message.command("search").description("Search messages"))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { Option, type Command } from "commander";
|
||||
import type { MessageCliHelpers } from "./helpers.js";
|
||||
|
||||
/** Register message read, edit, and delete commands. */
|
||||
export function registerMessageReadEditDeleteCommands(
|
||||
message: Command,
|
||||
helpers: MessageCliHelpers,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ function resolveThreadCreateRequest(opts: Record<string, unknown>) {
|
|||
};
|
||||
}
|
||||
|
||||
/** Register thread create/list/reply commands. */
|
||||
export function registerMessageThreadCommands(message: Command, helpers: MessageCliHelpers) {
|
||||
const thread = message.command("thread").description("Thread actions");
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { runCommandWithRuntime } from "../cli-utils.js";
|
|||
import { formatDocsHelp } from "../help-format.js";
|
||||
import { collectOption } from "./helpers.js";
|
||||
|
||||
/** Register the interactive `configure` command and section filter flag. */
|
||||
export function registerConfigureCommand(program: Command): void {
|
||||
program
|
||||
.command("configure")
|
||||
|
|
|
|||
|
|
@ -102,7 +102,6 @@ export function formatAuthChoiceChoicesForCli(params?: {
|
|||
return uniqueStrings(values).join("|");
|
||||
}
|
||||
|
||||
/** Build flat auth-choice options from core choices plus provider setup flows. */
|
||||
function buildAuthChoiceOptions(params: {
|
||||
assistantVisibleOnly?: boolean;
|
||||
detectedProviderIds?: ReadonlySet<string>;
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ function formatAccountLabel(params: { accountId: string; name?: string }) {
|
|||
return base;
|
||||
}
|
||||
|
||||
/** Format a channel/account label with optional display styles for terminal output. */
|
||||
export function formatChannelAccountLabel(params: {
|
||||
channel: ChatChannel;
|
||||
accountId: string;
|
||||
|
|
@ -104,7 +103,6 @@ export function appendEnabledConfiguredLinkedBits(
|
|||
}
|
||||
}
|
||||
|
||||
/** Append account mode metadata when present. */
|
||||
export function appendModeBit(bits: string[], account: Record<string, unknown>) {
|
||||
if (typeof account.mode === "string" && account.mode.length > 0) {
|
||||
bits.push(`mode:${account.mode}`);
|
||||
|
|
@ -129,14 +127,12 @@ export function appendTokenSourceBits(bits: string[], account: Record<string, un
|
|||
appendSourceBit("signing", "signingSecretSource", "signingSecretStatus");
|
||||
}
|
||||
|
||||
/** Append account base URL metadata when present. */
|
||||
export function appendBaseUrlBit(bits: string[], account: Record<string, unknown>) {
|
||||
if (typeof account.baseUrl === "string" && account.baseUrl) {
|
||||
bits.push(`url:${account.baseUrl}`);
|
||||
}
|
||||
}
|
||||
|
||||
/** Build a complete human-readable channel account status line. */
|
||||
export function buildChannelAccountLine(
|
||||
provider: ChatChannel,
|
||||
account: Record<string, unknown>,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue