style: oxfmt format

This commit is contained in:
Peter Steinberger
2026-01-17 05:48:34 +00:00
parent 8b42902cee
commit e59d8c5436
47 changed files with 152 additions and 165 deletions

View File

@@ -97,7 +97,8 @@ const execSchema = Type.Object({
), ),
pty: Type.Optional( pty: Type.Optional(
Type.Boolean({ Type.Boolean({
description: "Run in a pseudo-terminal (PTY) when available (TTY-required CLIs, coding agents)", description:
"Run in a pseudo-terminal (PTY) when available (TTY-required CLIs, coding agents)",
}), }),
), ),
elevated: Type.Optional( elevated: Type.Optional(

View File

@@ -68,9 +68,7 @@ export function buildEmbeddedRunPayloads(params: {
if (errorText) replyItems.push({ text: errorText, isError: true }); if (errorText) replyItems.push({ text: errorText, isError: true });
const inlineToolResults = const inlineToolResults =
params.inlineToolResultsAllowed && params.inlineToolResultsAllowed && params.verboseLevel !== "off" && params.toolMetas.length > 0;
params.verboseLevel !== "off" &&
params.toolMetas.length > 0;
if (inlineToolResults) { if (inlineToolResults) {
for (const { toolName, meta } of params.toolMetas) { for (const { toolName, meta } of params.toolMetas) {
const agg = formatToolAggregate(toolName, meta ? [meta] : []); const agg = formatToolAggregate(toolName, meta ? [meta] : []);

View File

@@ -9,9 +9,7 @@ import {
resolveStorePath, resolveStorePath,
} from "../config/sessions.js"; } from "../config/sessions.js";
import { normalizeMainKey } from "../routing/session-key.js"; import { normalizeMainKey } from "../routing/session-key.js";
import { import { resolveQueueSettings } from "../auto-reply/reply/queue.js";
resolveQueueSettings,
} from "../auto-reply/reply/queue.js";
import { callGateway } from "../gateway/call.js"; import { callGateway } from "../gateway/call.js";
import { defaultRuntime } from "../runtime.js"; import { defaultRuntime } from "../runtime.js";
import { import {

View File

@@ -54,9 +54,7 @@ export function loadSubagentRegistryFromDisk(): Map<string, SubagentRunRecord> {
? typed.announceCompletedAt ? typed.announceCompletedAt
: undefined; : undefined;
const cleanupCompletedAt = const cleanupCompletedAt =
typeof typed.cleanupCompletedAt === "number" typeof typed.cleanupCompletedAt === "number" ? typed.cleanupCompletedAt : legacyCompletedAt;
? typed.cleanupCompletedAt
: legacyCompletedAt;
const cleanupHandled = const cleanupHandled =
typeof typed.cleanupHandled === "boolean" typeof typed.cleanupHandled === "boolean"
? typed.cleanupHandled ? typed.cleanupHandled

View File

@@ -1,10 +1,7 @@
import { loadConfig } from "../config/config.js"; import { loadConfig } from "../config/config.js";
import { callGateway } from "../gateway/call.js"; import { callGateway } from "../gateway/call.js";
import { onAgentEvent } from "../infra/agent-events.js"; import { onAgentEvent } from "../infra/agent-events.js";
import { import { type DeliveryContext, normalizeDeliveryContext } from "../utils/delivery-context.js";
type DeliveryContext,
normalizeDeliveryContext,
} from "../utils/delivery-context.js";
import { runSubagentAnnounceFlow, type SubagentRunOutcome } from "./subagent-announce.js"; import { runSubagentAnnounceFlow, type SubagentRunOutcome } from "./subagent-announce.js";
import { import {
loadSubagentRegistryFromDisk, loadSubagentRegistryFromDisk,

View File

@@ -26,9 +26,7 @@ const AllMessageActions = CHANNEL_MESSAGE_ACTION_NAMES;
function buildRoutingSchema() { function buildRoutingSchema() {
return { return {
channel: Type.Optional(Type.String()), channel: Type.Optional(Type.String()),
target: Type.Optional( target: Type.Optional(channelTargetSchema({ description: "Target channel/user id or name." })),
channelTargetSchema({ description: "Target channel/user id or name." }),
),
targets: Type.Optional(channelTargetsSchema()), targets: Type.Optional(channelTargetsSchema()),
accountId: Type.Optional(Type.String()), accountId: Type.Optional(Type.String()),
dryRun: Type.Optional(Type.Boolean()), dryRun: Type.Optional(Type.Boolean()),
@@ -188,7 +186,6 @@ function buildMessageToolSchemaProps(options: { includeButtons: boolean }) {
}; };
} }
function buildMessageToolSchemaFromActions( function buildMessageToolSchemaFromActions(
actions: readonly string[], actions: readonly string[],
options: { includeButtons: boolean }, options: { includeButtons: boolean },

View File

@@ -44,8 +44,6 @@ describe("formatInboundBodyWithSenderMeta", () => {
it("does not append when the body already includes a sender prefix", () => { it("does not append when the body already includes a sender prefix", () => {
const ctx: MsgContext = { ChatType: "group", SenderName: "Alice", SenderId: "A1" }; const ctx: MsgContext = { ChatType: "group", SenderName: "Alice", SenderId: "A1" };
expect(formatInboundBodyWithSenderMeta({ ctx, body: "Alice (A1): hi" })).toBe( expect(formatInboundBodyWithSenderMeta({ ctx, body: "Alice (A1): hi" })).toBe("Alice (A1): hi");
"Alice (A1): hi",
);
}); });
}); });

View File

@@ -16,4 +16,3 @@ describe("normalizeInboundTextNewlines", () => {
expect(normalizeInboundTextNewlines("a\\nb")).toBe("a\nb"); expect(normalizeInboundTextNewlines("a\\nb")).toBe("a\nb");
}); });
}); });

View File

@@ -129,7 +129,8 @@ export async function initSessionState(params: {
const groupResolution = resolveGroupSessionKey(sessionCtxForState) ?? undefined; const groupResolution = resolveGroupSessionKey(sessionCtxForState) ?? undefined;
const normalizedChatType = normalizeChatType(ctx.ChatType); const normalizedChatType = normalizeChatType(ctx.ChatType);
const isGroup = normalizedChatType != null && normalizedChatType !== "direct" ? true : Boolean(groupResolution); const isGroup =
normalizedChatType != null && normalizedChatType !== "direct" ? true : Boolean(groupResolution);
// Prefer CommandBody/RawBody (clean message) for command detection; fall back // Prefer CommandBody/RawBody (clean message) for command detection; fall back
// to Body which may contain structural context (history, sender labels). // to Body which may contain structural context (history, sender labels).
const commandSource = ctx.BodyForCommands ?? ctx.CommandBody ?? ctx.RawBody ?? ctx.Body ?? ""; const commandSource = ctx.BodyForCommands ?? ctx.CommandBody ?? ctx.RawBody ?? ctx.Body ?? "";

View File

@@ -20,13 +20,20 @@ describe("resolveConversationLabel", () => {
}); });
it("does not append ids for #rooms/channels", () => { it("does not append ids for #rooms/channels", () => {
const ctx: MsgContext = { ChatType: "channel", GroupSubject: "#general", From: "slack:channel:C123" }; const ctx: MsgContext = {
ChatType: "channel",
GroupSubject: "#general",
From: "slack:channel:C123",
};
expect(resolveConversationLabel(ctx)).toBe("#general"); expect(resolveConversationLabel(ctx)).toBe("#general");
}); });
it("appends ids for WhatsApp-like group ids when a subject exists", () => { it("appends ids for WhatsApp-like group ids when a subject exists", () => {
const ctx: MsgContext = { ChatType: "group", GroupSubject: "Family", From: "whatsapp:group:123@g.us" }; const ctx: MsgContext = {
ChatType: "group",
GroupSubject: "Family",
From: "whatsapp:group:123@g.us",
};
expect(resolveConversationLabel(ctx)).toBe("Family id:123@g.us"); expect(resolveConversationLabel(ctx)).toBe("Family id:123@g.us");
}); });
}); });

View File

@@ -43,4 +43,3 @@ export function resolveConversationLabel(ctx: MsgContext): string | undefined {
if (base.startsWith("#") || base.startsWith("@")) return base; if (base.startsWith("#") || base.startsWith("@")) return base;
return `${base} id:${id}`; return `${base} id:${id}`;
} }

View File

@@ -53,10 +53,7 @@ export const whatsappOutbound: ChannelOutboundAdapter = {
} }
return { return {
ok: false, ok: false,
error: missingTargetError( error: missingTargetError("WhatsApp", "<E.164|group JID> or channels.whatsapp.allowFrom[0]"),
"WhatsApp",
"<E.164|group JID> or channels.whatsapp.allowFrom[0]",
),
}; };
}, },
sendText: async ({ to, text, accountId, deps, gifPlayback }) => { sendText: async ({ to, text, accountId, deps, gifPlayback }) => {

View File

@@ -202,10 +202,7 @@ export const signalPlugin: ChannelPlugin<ResolvedSignalAccount> = {
if (!trimmed) { if (!trimmed) {
return { return {
ok: false, ok: false,
error: missingTargetError( error: missingTargetError("Signal", "<E.164|group:ID|signal:group:ID|signal:+E.164>"),
"Signal",
"<E.164|group:ID|signal:group:ID|signal:+E.164>",
),
}; };
} }
return { ok: true, to: trimmed }; return { ok: true, to: trimmed };

View File

@@ -342,7 +342,10 @@ export const whatsappPlugin: ChannelPlugin<ResolvedWhatsAppAccount> = {
} }
return { return {
ok: false, ok: false,
error: missingTargetError("WhatsApp", "<E.164|group JID> or channels.whatsapp.allowFrom[0]"), error: missingTargetError(
"WhatsApp",
"<E.164|group JID> or channels.whatsapp.allowFrom[0]",
),
}; };
}, },
sendText: async ({ to, text, accountId, deps, gifPlayback }) => { sendText: async ({ to, text, accountId, deps, gifPlayback }) => {

View File

@@ -11,7 +11,9 @@ describe("validateSenderIdentity", () => {
it("requires some sender identity for non-direct chats", () => { it("requires some sender identity for non-direct chats", () => {
const ctx: MsgContext = { ChatType: "group" }; const ctx: MsgContext = { ChatType: "group" };
expect(validateSenderIdentity(ctx)).toContain("missing sender identity (SenderId/SenderName/SenderUsername/SenderE164)"); expect(validateSenderIdentity(ctx)).toContain(
"missing sender identity (SenderId/SenderName/SenderUsername/SenderE164)",
);
}); });
it("validates SenderE164 and SenderUsername shape", () => { it("validates SenderE164 and SenderUsername shape", () => {
@@ -27,4 +29,3 @@ describe("validateSenderIdentity", () => {
]); ]);
}); });
}); });

View File

@@ -25,8 +25,10 @@ export function validateSenderIdentity(ctx: MsgContext): string[] {
} }
if (senderUsername) { if (senderUsername) {
if (senderUsername.includes("@")) issues.push(`SenderUsername should not include "@": ${senderUsername}`); if (senderUsername.includes("@"))
if (/\s/.test(senderUsername)) issues.push(`SenderUsername should not include whitespace: ${senderUsername}`); issues.push(`SenderUsername should not include "@": ${senderUsername}`);
if (/\s/.test(senderUsername))
issues.push(`SenderUsername should not include whitespace: ${senderUsername}`);
} }
if (ctx.SenderId != null && !senderId) { if (ctx.SenderId != null && !senderId) {
@@ -35,4 +37,3 @@ export function validateSenderIdentity(ctx: MsgContext): string[] {
return issues; return issues;
} }

View File

@@ -20,4 +20,3 @@ export function createOutboundSendDeps(deps: CliDeps): OutboundSendDeps {
sendIMessage: deps.sendMessageIMessage, sendIMessage: deps.sendMessageIMessage,
}; };
} }

View File

@@ -40,9 +40,7 @@ export function registerMessageDiscordAdminCommands(message: Command, helpers: M
const channel = message.command("channel").description("Channel actions"); const channel = message.command("channel").description("Channel actions");
helpers helpers
.withMessageBase( .withMessageBase(
helpers.withRequiredMessageTarget( helpers.withRequiredMessageTarget(channel.command("info").description("Fetch channel info")),
channel.command("info").description("Fetch channel info"),
),
) )
.action(async (opts) => { .action(async (opts) => {
await helpers.runMessageAction("channel-info", opts); await helpers.runMessageAction("channel-info", opts);

View File

@@ -21,7 +21,9 @@ export function registerMessagePinCommands(message: Command, helpers: MessageCli
}), }),
helpers helpers
.withMessageBase( .withMessageBase(
helpers.withRequiredMessageTarget(message.command("pins").description("List pinned messages")), helpers.withRequiredMessageTarget(
message.command("pins").description("List pinned messages"),
),
) )
.option("--limit <n>", "Result limit") .option("--limit <n>", "Result limit")
.action(async (opts) => { .action(async (opts) => {

View File

@@ -7,7 +7,9 @@ export function registerMessageReadEditDeleteCommands(
) { ) {
helpers helpers
.withMessageBase( .withMessageBase(
helpers.withRequiredMessageTarget(message.command("read").description("Read recent messages")), helpers.withRequiredMessageTarget(
message.command("read").description("Read recent messages"),
),
) )
.option("--limit <n>", "Result limit") .option("--limit <n>", "Result limit")
.option("--before <id>", "Read/search before id") .option("--before <id>", "Read/search before id")

View File

@@ -34,9 +34,7 @@ function resolveDeliveryAccountId(params: {
const sessionOrigin = deliveryContextFromSession(params.sessionEntry); const sessionOrigin = deliveryContextFromSession(params.sessionEntry);
return ( return (
normalizeAccountId(params.opts.accountId) ?? normalizeAccountId(params.opts.accountId) ??
(params.targetMode === "implicit" (params.targetMode === "implicit" ? normalizeAccountId(sessionOrigin?.accountId) : undefined)
? normalizeAccountId(sessionOrigin?.accountId)
: undefined)
); );
} }

View File

@@ -332,9 +332,7 @@ export const LEGACY_CONFIG_MIGRATIONS_PART_2: LegacyConfigMigration[] = [
const tools = ensureRecord(raw, "tools"); const tools = ensureRecord(raw, "tools");
const media = ensureRecord(tools, "media"); const media = ensureRecord(tools, "media");
const mediaAudio = ensureRecord(media, "audio"); const mediaAudio = ensureRecord(media, "audio");
const models = Array.isArray(mediaAudio.models) const models = Array.isArray(mediaAudio.models) ? (mediaAudio.models as unknown[]) : [];
? (mediaAudio.models as unknown[])
: [];
if (models.length === 0) { if (models.length === 0) {
mediaAudio.enabled = true; mediaAudio.enabled = true;
mediaAudio.models = [mapped]; mediaAudio.models = [mapped];
@@ -355,9 +353,7 @@ export const LEGACY_CONFIG_MIGRATIONS_PART_2: LegacyConfigMigration[] = [
const tools = ensureRecord(raw, "tools"); const tools = ensureRecord(raw, "tools");
const media = ensureRecord(tools, "media"); const media = ensureRecord(tools, "media");
const mediaAudio = ensureRecord(media, "audio"); const mediaAudio = ensureRecord(media, "audio");
const models = Array.isArray(mediaAudio.models) const models = Array.isArray(mediaAudio.models) ? (mediaAudio.models as unknown[]) : [];
? (mediaAudio.models as unknown[])
: [];
if (models.length === 0) { if (models.length === 0) {
mediaAudio.enabled = true; mediaAudio.enabled = true;
mediaAudio.models = [mapped]; mediaAudio.models = [mapped];

View File

@@ -274,7 +274,9 @@ export const MediaUnderstandingAttachmentsSchema = z
.object({ .object({
mode: z.union([z.literal("first"), z.literal("all")]).optional(), mode: z.union([z.literal("first"), z.literal("all")]).optional(),
maxAttachments: z.number().int().positive().optional(), maxAttachments: z.number().int().positive().optional(),
prefer: z.union([z.literal("first"), z.literal("last"), z.literal("path"), z.literal("url")]).optional(), prefer: z
.union([z.literal("first"), z.literal("last"), z.literal("path"), z.literal("url")])
.optional(),
}) })
.optional(); .optional();

View File

@@ -8,7 +8,10 @@ import {
} from "../../config/sessions.js"; } from "../../config/sessions.js";
import { resolveMessageChannelSelection } from "../../infra/outbound/channel-selection.js"; import { resolveMessageChannelSelection } from "../../infra/outbound/channel-selection.js";
import type { OutboundChannel } from "../../infra/outbound/targets.js"; import type { OutboundChannel } from "../../infra/outbound/targets.js";
import { resolveOutboundTarget, resolveSessionDeliveryTarget } from "../../infra/outbound/targets.js"; import {
resolveOutboundTarget,
resolveSessionDeliveryTarget,
} from "../../infra/outbound/targets.js";
export async function resolveDeliveryTarget( export async function resolveDeliveryTarget(
cfg: ClawdbotConfig, cfg: ClawdbotConfig,

View File

@@ -87,4 +87,3 @@ describe("discord processDiscordMessage inbound contract", () => {
expectInboundContextContract(capturedCtx!); expectInboundContextContract(capturedCtx!);
}); });
}); });

View File

@@ -8,10 +8,7 @@ import {
extractShortModelName, extractShortModelName,
type ResponsePrefixContext, type ResponsePrefixContext,
} from "../../auto-reply/reply/response-prefix-template.js"; } from "../../auto-reply/reply/response-prefix-template.js";
import { import { formatInboundEnvelope, formatThreadStarterEnvelope } from "../../auto-reply/envelope.js";
formatInboundEnvelope,
formatThreadStarterEnvelope,
} from "../../auto-reply/envelope.js";
import { dispatchReplyFromConfig } from "../../auto-reply/reply/dispatch-from-config.js"; import { dispatchReplyFromConfig } from "../../auto-reply/reply/dispatch-from-config.js";
import { import {
buildPendingHistoryContextFromMap, buildPendingHistoryContextFromMap,
@@ -126,7 +123,7 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext)
const senderLabel = const senderLabel =
senderDisplay && senderTag && senderDisplay !== senderTag senderDisplay && senderTag && senderDisplay !== senderTag
? `${senderDisplay} (${senderTag})` ? `${senderDisplay} (${senderTag})`
: senderDisplay ?? senderTag ?? author.id; : (senderDisplay ?? senderTag ?? author.id);
const groupRoom = isGuildMessage && displayChannelSlug ? `#${displayChannelSlug}` : undefined; const groupRoom = isGuildMessage && displayChannelSlug ? `#${displayChannelSlug}` : undefined;
const groupSubject = isDirectMessage ? undefined : groupRoom; const groupSubject = isDirectMessage ? undefined : groupRoom;
const channelDescription = channelInfo?.topic?.trim(); const channelDescription = channelInfo?.topic?.trim();

View File

@@ -13,7 +13,10 @@ import type {
} from "../../channels/plugins/types.js"; } from "../../channels/plugins/types.js";
import type { ClawdbotConfig } from "../../config/config.js"; import type { ClawdbotConfig } from "../../config/config.js";
import type { GatewayClientMode, GatewayClientName } from "../../utils/message-channel.js"; import type { GatewayClientMode, GatewayClientName } from "../../utils/message-channel.js";
import { listConfiguredMessageChannels, resolveMessageChannelSelection } from "./channel-selection.js"; import {
listConfiguredMessageChannels,
resolveMessageChannelSelection,
} from "./channel-selection.js";
import { applyTargetToParams } from "./channel-target.js"; import { applyTargetToParams } from "./channel-target.js";
import type { OutboundSendDeps } from "./deliver.js"; import type { OutboundSendDeps } from "./deliver.js";
import type { MessagePollResult, MessageSendResult } from "./message.js"; import type { MessagePollResult, MessageSendResult } from "./message.js";
@@ -483,10 +486,7 @@ async function handlePollAction(ctx: ResolvedActionContext): Promise<MessageActi
async function handlePluginAction(ctx: ResolvedActionContext): Promise<MessageActionRunResult> { async function handlePluginAction(ctx: ResolvedActionContext): Promise<MessageActionRunResult> {
const { cfg, params, channel, accountId, dryRun, gateway, input } = ctx; const { cfg, params, channel, accountId, dryRun, gateway, input } = ctx;
const action = input.action as Exclude< const action = input.action as Exclude<ChannelMessageActionName, "send" | "poll" | "broadcast">;
ChannelMessageActionName,
"send" | "poll" | "broadcast"
>;
if (dryRun) { if (dryRun) {
return { return {
kind: "action", kind: "action",

View File

@@ -1,9 +1,6 @@
import type { AgentToolResult } from "@mariozechner/pi-agent-core"; import type { AgentToolResult } from "@mariozechner/pi-agent-core";
import { dispatchChannelMessageAction } from "../../channels/plugins/message-actions.js"; import { dispatchChannelMessageAction } from "../../channels/plugins/message-actions.js";
import type { import type { ChannelId, ChannelThreadingToolContext } from "../../channels/plugins/types.js";
ChannelId,
ChannelThreadingToolContext,
} from "../../channels/plugins/types.js";
import type { ClawdbotConfig } from "../../config/config.js"; import type { ClawdbotConfig } from "../../config/config.js";
import type { GatewayClientMode, GatewayClientName } from "../../utils/message-channel.js"; import type { GatewayClientMode, GatewayClientName } from "../../utils/message-channel.js";
import type { OutboundSendDeps } from "./deliver.js"; import type { OutboundSendDeps } from "./deliver.js";

View File

@@ -340,9 +340,7 @@ describe("applyMediaUnderstanding", () => {
expect(result.appliedAudio).toBe(true); expect(result.appliedAudio).toBe(true);
expect(ctx.Transcript).toBe("Audio 1:\nnote-a.ogg\n\nAudio 2:\nnote-b.ogg"); expect(ctx.Transcript).toBe("Audio 1:\nnote-a.ogg\n\nAudio 2:\nnote-b.ogg");
expect(ctx.Body).toBe( expect(ctx.Body).toBe(
["[Audio 1/2]\nTranscript:\nnote-a.ogg", "[Audio 2/2]\nTranscript:\nnote-b.ogg"].join( ["[Audio 1/2]\nTranscript:\nnote-a.ogg", "[Audio 2/2]\nTranscript:\nnote-b.ogg"].join("\n\n"),
"\n\n",
),
); );
}); });
}); });

View File

@@ -9,11 +9,7 @@ import type {
MediaUnderstandingConfig, MediaUnderstandingConfig,
MediaUnderstandingModelConfig, MediaUnderstandingModelConfig,
} from "../config/types.tools.js"; } from "../config/types.tools.js";
import { import { MediaAttachmentCache, normalizeAttachments, selectAttachments } from "./attachments.js";
MediaAttachmentCache,
normalizeAttachments,
selectAttachments,
} from "./attachments.js";
import { import {
CLI_OUTPUT_MAX_BUFFER, CLI_OUTPUT_MAX_BUFFER,
DEFAULT_AUDIO_MODELS, DEFAULT_AUDIO_MODELS,
@@ -85,7 +81,9 @@ async function runProviderEntry(params: {
const maxBytes = resolveMaxBytes({ capability, entry, cfg, config: params.config }); const maxBytes = resolveMaxBytes({ capability, entry, cfg, config: params.config });
const maxChars = resolveMaxChars({ capability, entry, cfg, config: params.config }); const maxChars = resolveMaxChars({ capability, entry, cfg, config: params.config });
const timeoutMs = resolveTimeoutMs( const timeoutMs = resolveTimeoutMs(
entry.timeoutSeconds ?? params.config?.timeoutSeconds ?? cfg.tools?.media?.[capability]?.timeoutSeconds, entry.timeoutSeconds ??
params.config?.timeoutSeconds ??
cfg.tools?.media?.[capability]?.timeoutSeconds,
DEFAULT_TIMEOUT_SECONDS[capability], DEFAULT_TIMEOUT_SECONDS[capability],
); );
const prompt = resolvePrompt( const prompt = resolvePrompt(
@@ -250,7 +248,9 @@ async function runCliEntry(params: {
const maxBytes = resolveMaxBytes({ capability, entry, cfg, config: params.config }); const maxBytes = resolveMaxBytes({ capability, entry, cfg, config: params.config });
const maxChars = resolveMaxChars({ capability, entry, cfg, config: params.config }); const maxChars = resolveMaxChars({ capability, entry, cfg, config: params.config });
const timeoutMs = resolveTimeoutMs( const timeoutMs = resolveTimeoutMs(
entry.timeoutSeconds ?? params.config?.timeoutSeconds ?? cfg.tools?.media?.[capability]?.timeoutSeconds, entry.timeoutSeconds ??
params.config?.timeoutSeconds ??
cfg.tools?.media?.[capability]?.timeoutSeconds,
DEFAULT_TIMEOUT_SECONDS[capability], DEFAULT_TIMEOUT_SECONDS[capability],
); );
const prompt = resolvePrompt( const prompt = resolvePrompt(

View File

@@ -317,10 +317,7 @@ export class MediaAttachmentCache {
timeoutMs: params.timeoutMs, timeoutMs: params.timeoutMs,
}); });
const extension = path.extname(bufferResult.fileName || "") || ""; const extension = path.extname(bufferResult.fileName || "") || "";
const tmpPath = path.join( const tmpPath = path.join(os.tmpdir(), `clawdbot-media-${crypto.randomUUID()}${extension}`);
os.tmpdir(),
`clawdbot-media-${crypto.randomUUID()}${extension}`,
);
await fs.writeFile(tmpPath, bufferResult.buffer); await fs.writeFile(tmpPath, bufferResult.buffer);
entry.tempPath = tmpPath; entry.tempPath = tmpPath;
entry.tempCleanup = async () => { entry.tempCleanup = async () => {
@@ -348,8 +345,9 @@ export class MediaAttachmentCache {
} }
return existing; return existing;
} }
const attachment = const attachment = this.attachments.find((item) => item.index === attachmentIndex) ?? {
this.attachments.find((item) => item.index === attachmentIndex) ?? { index: attachmentIndex }; index: attachmentIndex,
};
const entry: AttachmentCacheEntry = { const entry: AttachmentCacheEntry = {
attachment, attachment,
resolvedPath: this.resolveLocalPath(attachment), resolvedPath: this.resolveLocalPath(attachment),

View File

@@ -10,8 +10,6 @@ export class MediaUnderstandingSkipError extends Error {
} }
} }
export function isMediaUnderstandingSkipError( export function isMediaUnderstandingSkipError(err: unknown): err is MediaUnderstandingSkipError {
err: unknown,
): err is MediaUnderstandingSkipError {
return err instanceof MediaUnderstandingSkipError; return err instanceof MediaUnderstandingSkipError;
} }

View File

@@ -88,7 +88,5 @@ export function formatMediaUnderstandingBody(params: {
export function formatAudioTranscripts(outputs: MediaUnderstandingOutput[]): string { export function formatAudioTranscripts(outputs: MediaUnderstandingOutput[]): string {
if (outputs.length === 1) return outputs[0].text; if (outputs.length === 1) return outputs[0].text;
return outputs return outputs.map((output, index) => `Audio ${index + 1}:\n${output.text}`).join("\n\n");
.map((output, index) => `Audio ${index + 1}:\n${output.text}`)
.join("\n\n");
} }

View File

@@ -383,13 +383,19 @@ export async function collectPluginsTrustFindings(params: {
if (!allowConfigured) { if (!allowConfigured) {
const hasString = (value: unknown) => typeof value === "string" && value.trim().length > 0; const hasString = (value: unknown) => typeof value === "string" && value.trim().length > 0;
const hasAccountStringKey = (account: unknown, key: string) => const hasAccountStringKey = (account: unknown, key: string) =>
Boolean(account && typeof account === "object" && hasString((account as Record<string, unknown>)[key])); Boolean(
account &&
typeof account === "object" &&
hasString((account as Record<string, unknown>)[key]),
);
const discordConfigured = const discordConfigured =
hasString(params.cfg.channels?.discord?.token) || hasString(params.cfg.channels?.discord?.token) ||
Boolean( Boolean(
params.cfg.channels?.discord?.accounts && params.cfg.channels?.discord?.accounts &&
Object.values(params.cfg.channels.discord.accounts).some((a) => hasAccountStringKey(a, "token")), Object.values(params.cfg.channels.discord.accounts).some((a) =>
hasAccountStringKey(a, "token"),
),
) || ) ||
hasString(process.env.DISCORD_BOT_TOKEN); hasString(process.env.DISCORD_BOT_TOKEN);

View File

@@ -505,10 +505,8 @@ async function collectChannelSecurityFindings(params: {
if (!allowTextCommands) continue; if (!allowTextCommands) continue;
const telegramCfg = const telegramCfg =
(account as { config?: Record<string, unknown> } | null)?.config ?? ({} as Record< (account as { config?: Record<string, unknown> } | null)?.config ??
string, ({} as Record<string, unknown>);
unknown
>);
const groupPolicy = (telegramCfg.groupPolicy as string | undefined) ?? "allowlist"; const groupPolicy = (telegramCfg.groupPolicy as string | undefined) ?? "allowlist";
const groups = telegramCfg.groups as Record<string, unknown> | undefined; const groups = telegramCfg.groups as Record<string, unknown> | undefined;
const groupsConfigured = Boolean(groups) && Object.keys(groups ?? {}).length > 0; const groupsConfigured = Boolean(groups) && Object.keys(groups ?? {}).length > 0;
@@ -518,7 +516,9 @@ async function collectChannelSecurityFindings(params: {
const storeAllowFrom = await readChannelAllowFromStore("telegram").catch(() => []); const storeAllowFrom = await readChannelAllowFromStore("telegram").catch(() => []);
const storeHasWildcard = storeAllowFrom.some((v) => String(v).trim() === "*"); const storeHasWildcard = storeAllowFrom.some((v) => String(v).trim() === "*");
const groupAllowFrom = Array.isArray(telegramCfg.groupAllowFrom) ? telegramCfg.groupAllowFrom : []; const groupAllowFrom = Array.isArray(telegramCfg.groupAllowFrom)
? telegramCfg.groupAllowFrom
: [];
const groupAllowFromHasWildcard = groupAllowFrom.some((v) => String(v).trim() === "*"); const groupAllowFromHasWildcard = groupAllowFrom.some((v) => String(v).trim() === "*");
const anyGroupOverride = Boolean( const anyGroupOverride = Boolean(
groups && groups &&

View File

@@ -1,6 +1,5 @@
import { beforeEach, describe, expect, it, vi } from "vitest"; import { beforeEach, describe, expect, it, vi } from "vitest";
const dispatchMock = vi.fn(); const dispatchMock = vi.fn();
const readAllowFromMock = vi.fn(); const readAllowFromMock = vi.fn();
@@ -9,7 +8,6 @@ vi.mock("../pairing/pairing-store.js", () => ({
upsertChannelPairingRequest: vi.fn(), upsertChannelPairingRequest: vi.fn(),
})); }));
describe("signal event handler sender prefix", () => { describe("signal event handler sender prefix", () => {
beforeEach(() => { beforeEach(() => {
dispatchMock.mockReset().mockImplementation(async ({ dispatcher, ctx }) => { dispatchMock.mockReset().mockImplementation(async ({ dispatcher, ctx }) => {

View File

@@ -49,7 +49,7 @@ describe("slack prepareSlackMessage inbound contract", () => {
mediaMaxBytes: 1024, mediaMaxBytes: 1024,
removeAckAfterReply: false, removeAckAfterReply: false,
}); });
slackCtx.resolveUserName = async () => ({ name: "Alice" } as any); slackCtx.resolveUserName = async () => ({ name: "Alice" }) as any;
const account: ResolvedSlackAccount = { const account: ResolvedSlackAccount = {
accountId: "default", accountId: "default",
@@ -78,4 +78,3 @@ describe("slack prepareSlackMessage inbound contract", () => {
expectInboundContextContract(prepared!.ctxPayload as any); expectInboundContextContract(prepared!.ctxPayload as any);
}); });
}); });

View File

@@ -240,7 +240,12 @@ export async function prepareSlackMessage(params: {
: false; : false;
const commandAuthorized = ownerAuthorized || channelCommandAuthorized; const commandAuthorized = ownerAuthorized || channelCommandAuthorized;
if (allowTextCommands && isRoomish && hasControlCommand(message.text ?? "", cfg) && !commandAuthorized) { if (
allowTextCommands &&
isRoomish &&
hasControlCommand(message.text ?? "", cfg) &&
!commandAuthorized
) {
logVerbose(`Blocked slack control command from unauthorized sender ${senderId}`); logVerbose(`Blocked slack control command from unauthorized sender ${senderId}`);
return null; return null;
} }

View File

@@ -251,7 +251,9 @@ export const registerTelegramNativeCommands = ({
const groupSystemPrompt = const groupSystemPrompt =
systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : undefined; systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : undefined;
const conversationLabel = isGroup const conversationLabel = isGroup
? (msg.chat.title ? `${msg.chat.title} id:${chatId}` : `group:${chatId}`) ? msg.chat.title
? `${msg.chat.title} id:${chatId}`
: `group:${chatId}`
: (buildSenderName(msg) ?? String(senderId || chatId)); : (buildSenderName(msg) ?? String(senderId || chatId));
const ctxPayload = finalizeInboundContext({ const ctxPayload = finalizeInboundContext({
Body: prompt, Body: prompt,

View File

@@ -38,13 +38,11 @@ describe("delivery context helpers", () => {
}); });
it("builds stable keys only when channel and to are present", () => { it("builds stable keys only when channel and to are present", () => {
expect(deliveryContextKey({ channel: "whatsapp", to: "+1555" })).toBe( expect(deliveryContextKey({ channel: "whatsapp", to: "+1555" })).toBe("whatsapp|+1555|");
"whatsapp|+1555|",
);
expect(deliveryContextKey({ channel: "whatsapp" })).toBeUndefined(); expect(deliveryContextKey({ channel: "whatsapp" })).toBeUndefined();
expect( expect(deliveryContextKey({ channel: "whatsapp", to: "+1555", accountId: "acct-1" })).toBe(
deliveryContextKey({ channel: "whatsapp", to: "+1555", accountId: "acct-1" }), "whatsapp|+1555|acct-1",
).toBe("whatsapp|+1555|acct-1"); );
}); });
it("derives delivery context from a session entry", () => { it("derives delivery context from a session entry", () => {

View File

@@ -31,7 +31,11 @@ describe("web processMessage inbound contract", () => {
groupSubject: "Test Group", groupSubject: "Test Group",
groupParticipants: [], groupParticipants: [],
} as any, } as any,
route: { agentId: "main", accountId: "default", sessionKey: "agent:main:whatsapp:group:123" } as any, route: {
agentId: "main",
accountId: "default",
sessionKey: "agent:main:whatsapp:group:123",
} as any,
groupHistoryKey: "123@g.us", groupHistoryKey: "123@g.us",
groupHistories: new Map(), groupHistories: new Map(),
groupMemberNames: new Map(), groupMemberNames: new Map(),

View File

@@ -18,4 +18,3 @@ export function expectInboundContextContract(ctx: MsgContext) {
expect(label).toBeTruthy(); expect(label).toBeTruthy();
} }
} }

View File

@@ -160,4 +160,3 @@ describe("inbound context contract (providers + extensions)", () => {
}); });
} }
}); });