From a82217a5f3fc3ed7caa65141d4079c7a1f99480f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 17 Jan 2026 03:40:49 +0000 Subject: [PATCH] chore: format + regenerate protocol --- .../ClawdbotProtocol/GatewayModels.swift | 6 +++- src/agents/schema/typebox.ts | 4 ++- src/agents/subagent-announce.ts | 5 +-- .../reply/inbound-sender-meta.test.ts | 9 +++-- src/auto-reply/reply/inbound-sender-meta.ts | 5 +-- .../reply/session.sender-meta.test.ts | 1 - src/channels/plugins/discord.ts | 3 +- src/cli/program/message/helpers.ts | 10 ++---- src/cli/program/message/register.broadcast.ts | 5 +-- src/config/schema.ts | 3 +- src/gateway/server-methods/agent.ts | 3 +- .../outbound/message-action-runner.test.ts | 5 ++- src/infra/outbound/message-action-runner.ts | 5 ++- src/infra/outbound/outbound-policy.ts | 5 +-- src/infra/outbound/target-resolver.ts | 13 +++---- src/telegram/bot-message-context.ts | 16 ++++----- ...patterns-match-without-botusername.test.ts | 36 +++++++++---------- src/utils/delivery-context.ts | 3 +- ...asts-sequentially-configured-order.test.ts | 7 +++- src/web/auto-reply/monitor/process-message.ts | 9 +++-- 20 files changed, 79 insertions(+), 74 deletions(-) diff --git a/apps/macos/Sources/ClawdbotProtocol/GatewayModels.swift b/apps/macos/Sources/ClawdbotProtocol/GatewayModels.swift index 5e8ac5e2c..3ca98cbbb 100644 --- a/apps/macos/Sources/ClawdbotProtocol/GatewayModels.swift +++ b/apps/macos/Sources/ClawdbotProtocol/GatewayModels.swift @@ -357,7 +357,7 @@ public struct SendParams: Codable, Sendable { gifplayback: Bool?, channel: String?, accountid: String?, - sessionkey: String? = nil, + sessionkey: String?, idempotencykey: String ) { self.to = to @@ -431,6 +431,7 @@ public struct AgentParams: Codable, Sendable { public let deliver: Bool? public let attachments: [AnyCodable]? public let channel: String? + public let accountid: String? public let timeout: Int? public let lane: String? public let extrasystemprompt: String? @@ -447,6 +448,7 @@ public struct AgentParams: Codable, Sendable { deliver: Bool?, attachments: [AnyCodable]?, channel: String?, + accountid: String?, timeout: Int?, lane: String?, extrasystemprompt: String?, @@ -462,6 +464,7 @@ public struct AgentParams: Codable, Sendable { self.deliver = deliver self.attachments = attachments self.channel = channel + self.accountid = accountid self.timeout = timeout self.lane = lane self.extrasystemprompt = extrasystemprompt @@ -478,6 +481,7 @@ public struct AgentParams: Codable, Sendable { case deliver case attachments case channel + case accountid = "accountId" case timeout case lane case extrasystemprompt = "extraSystemPrompt" diff --git a/src/agents/schema/typebox.ts b/src/agents/schema/typebox.ts index 428ed73d1..ac6c28fe0 100644 --- a/src/agents/schema/typebox.ts +++ b/src/agents/schema/typebox.ts @@ -37,5 +37,7 @@ export function channelTargetSchema(options?: { description?: string }) { } export function channelTargetsSchema(options?: { description?: string }) { - return Type.Array(channelTargetSchema({ description: options?.description ?? CHANNEL_TARGETS_DESCRIPTION })); + return Type.Array( + channelTargetSchema({ description: options?.description ?? CHANNEL_TARGETS_DESCRIPTION }), + ); } diff --git a/src/agents/subagent-announce.ts b/src/agents/subagent-announce.ts index 0c3453d40..d4b302f00 100644 --- a/src/agents/subagent-announce.ts +++ b/src/agents/subagent-announce.ts @@ -16,10 +16,7 @@ import { } from "../auto-reply/reply/queue.js"; import { callGateway } from "../gateway/call.js"; import { defaultRuntime } from "../runtime.js"; -import { - type DeliveryContext, - normalizeDeliveryContext, -} from "../utils/delivery-context.js"; +import { type DeliveryContext, normalizeDeliveryContext } from "../utils/delivery-context.js"; import { isEmbeddedPiRunActive, queueEmbeddedPiMessage } from "./pi-embedded.js"; import { readLatestAssistantReply } from "./tools/agent-step.js"; diff --git a/src/auto-reply/reply/inbound-sender-meta.test.ts b/src/auto-reply/reply/inbound-sender-meta.test.ts index 9bc5839cb..fd083c134 100644 --- a/src/auto-reply/reply/inbound-sender-meta.test.ts +++ b/src/auto-reply/reply/inbound-sender-meta.test.ts @@ -11,7 +11,9 @@ describe("formatInboundBodyWithSenderMeta", () => { it("appends a sender meta line for non-direct messages", () => { const ctx: MsgContext = { ChatType: "group", SenderName: "Alice", SenderId: "A1" }; - expect(formatInboundBodyWithSenderMeta({ ctx, body: "[X] hi" })).toBe("[X] hi\n[from: Alice (A1)]"); + expect(formatInboundBodyWithSenderMeta({ ctx, body: "[X] hi" })).toBe( + "[X] hi\n[from: Alice (A1)]", + ); }); it("prefers SenderE164 in the label when present", () => { @@ -21,7 +23,9 @@ describe("formatInboundBodyWithSenderMeta", () => { SenderId: "bob@s.whatsapp.net", SenderE164: "+222", }; - expect(formatInboundBodyWithSenderMeta({ ctx, body: "[X] hi" })).toBe("[X] hi\n[from: Bob (+222)]"); + expect(formatInboundBodyWithSenderMeta({ ctx, body: "[X] hi" })).toBe( + "[X] hi\n[from: Bob (+222)]", + ); }); it("preserves escaped newline style when body uses literal \\\\n", () => { @@ -38,4 +42,3 @@ describe("formatInboundBodyWithSenderMeta", () => { ); }); }); - diff --git a/src/auto-reply/reply/inbound-sender-meta.ts b/src/auto-reply/reply/inbound-sender-meta.ts index 24e102c4d..5d4ff3f20 100644 --- a/src/auto-reply/reply/inbound-sender-meta.ts +++ b/src/auto-reply/reply/inbound-sender-meta.ts @@ -1,9 +1,6 @@ import type { MsgContext } from "../templating.js"; -export function formatInboundBodyWithSenderMeta(params: { - body: string; - ctx: MsgContext; -}): string { +export function formatInboundBodyWithSenderMeta(params: { body: string; ctx: MsgContext }): string { const body = params.body; if (!body.trim()) return body; const chatType = params.ctx.ChatType?.trim().toLowerCase(); diff --git a/src/auto-reply/reply/session.sender-meta.test.ts b/src/auto-reply/reply/session.sender-meta.test.ts index 47691f966..455cfbb11 100644 --- a/src/auto-reply/reply/session.sender-meta.test.ts +++ b/src/auto-reply/reply/session.sender-meta.test.ts @@ -49,4 +49,3 @@ describe("initSessionState sender meta", () => { expect(result.sessionCtx.BodyStripped).toBe("[WhatsApp +1] ping"); }); }); - diff --git a/src/channels/plugins/discord.ts b/src/channels/plugins/discord.ts index 9b87d5da9..1b87a5a1f 100644 --- a/src/channels/plugins/discord.ts +++ b/src/channels/plugins/discord.ts @@ -234,8 +234,7 @@ export const discordPlugin: ChannelPlugin = { } } const filtered = q ? rows.filter((row) => row.name?.toLowerCase().includes(q)) : rows; - const limited = - typeof limit === "number" && limit > 0 ? filtered.slice(0, limit) : filtered; + const limited = typeof limit === "number" && limit > 0 ? filtered.slice(0, limit) : filtered; return limited; }, }, diff --git a/src/cli/program/message/helpers.ts b/src/cli/program/message/helpers.ts index c5249159d..249110300 100644 --- a/src/cli/program/message/helpers.ts +++ b/src/cli/program/message/helpers.ts @@ -25,15 +25,9 @@ export function createMessageCliHelpers( .option("--verbose", "Verbose logging", false); const withMessageTarget = (command: Command) => - command.option( - "-t, --to ", - CHANNEL_TARGET_DESCRIPTION, - ); + command.option("-t, --to ", CHANNEL_TARGET_DESCRIPTION); const withRequiredMessageTarget = (command: Command) => - command.requiredOption( - "-t, --to ", - CHANNEL_TARGET_DESCRIPTION, - ); + command.requiredOption("-t, --to ", CHANNEL_TARGET_DESCRIPTION); const runMessageAction = async (action: string, opts: Record) => { setVerbose(Boolean(opts.verbose)); diff --git a/src/cli/program/message/register.broadcast.ts b/src/cli/program/message/register.broadcast.ts index f4fde0660..bfdd1ebee 100644 --- a/src/cli/program/message/register.broadcast.ts +++ b/src/cli/program/message/register.broadcast.ts @@ -7,10 +7,7 @@ export function registerMessageBroadcastCommand(message: Command, helpers: Messa .withMessageBase( message.command("broadcast").description("Broadcast a message to multiple targets"), ) - .requiredOption( - "--targets ", - CHANNEL_TARGETS_DESCRIPTION, - ) + .requiredOption("--targets ", CHANNEL_TARGETS_DESCRIPTION) .option("--message ", "Message to send") .option("--media ", "Media URL") .action(async (options: Record) => { diff --git a/src/config/schema.ts b/src/config/schema.ts index bffe39f49..f3ea4211b 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -275,8 +275,7 @@ const FIELD_HELP: Record = { 'Text prefix for cross-context markers (supports "{channel}").', "tools.message.crossContext.marker.suffix": 'Text suffix for cross-context markers (supports "{channel}").', - "tools.message.broadcast.enabled": - "Enable broadcast action (default: true).", + "tools.message.broadcast.enabled": "Enable broadcast action (default: true).", "tools.web.search.enabled": "Enable the web_search tool (requires Brave API key).", "tools.web.search.provider": 'Search provider (only "brave" supported today).', "tools.web.search.apiKey": "Brave Search API key (fallback: BRAVE_API_KEY env var).", diff --git a/src/gateway/server-methods/agent.ts b/src/gateway/server-methods/agent.ts index 5f6068f83..280fb5ec0 100644 --- a/src/gateway/server-methods/agent.ts +++ b/src/gateway/server-methods/agent.ts @@ -202,8 +202,7 @@ export const agentHandlers: GatewayRequestHandlers = { const lastChannel = sessionEntry?.lastChannel; const lastTo = typeof sessionEntry?.lastTo === "string" ? sessionEntry.lastTo.trim() : ""; const resolvedAccountId = - normalizeAccountId(request.accountId) ?? - normalizeAccountId(sessionEntry?.lastAccountId); + normalizeAccountId(request.accountId) ?? normalizeAccountId(sessionEntry?.lastAccountId); const wantsDelivery = request.deliver === true; diff --git a/src/infra/outbound/message-action-runner.test.ts b/src/infra/outbound/message-action-runner.test.ts index cee3f9404..e175d0928 100644 --- a/src/infra/outbound/message-action-runner.test.ts +++ b/src/infra/outbound/message-action-runner.test.ts @@ -157,7 +157,10 @@ describe("runMessageAction context isolation", () => { to: "imessage:+15551230000", message: "hi", }, - toolContext: { currentChannelId: "imessage:+15551234567", currentChannelProvider: "imessage" }, + toolContext: { + currentChannelId: "imessage:+15551234567", + currentChannelProvider: "imessage", + }, dryRun: true, }); diff --git a/src/infra/outbound/message-action-runner.ts b/src/infra/outbound/message-action-runner.ts index c15860dd1..c49ac3790 100644 --- a/src/infra/outbound/message-action-runner.ts +++ b/src/infra/outbound/message-action-runner.ts @@ -13,7 +13,10 @@ import type { } from "../../channels/plugins/types.js"; import type { ClawdbotConfig } from "../../config/config.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 type { OutboundSendDeps } from "./deliver.js"; import type { MessagePollResult, MessageSendResult } from "./message.js"; import { sendMessage, sendPoll } from "./message.js"; diff --git a/src/infra/outbound/outbound-policy.ts b/src/infra/outbound/outbound-policy.ts index 07b8982a9..f99e0826a 100644 --- a/src/infra/outbound/outbound-policy.ts +++ b/src/infra/outbound/outbound-policy.ts @@ -77,7 +77,8 @@ export function enforceCrossContextPolicy(params: { if (params.cfg.tools?.message?.allowCrossContextSend) return; const currentProvider = params.toolContext?.currentChannelProvider; - const allowWithinProvider = params.cfg.tools?.message?.crossContext?.allowWithinProvider !== false; + const allowWithinProvider = + params.cfg.tools?.message?.crossContext?.allowWithinProvider !== false; const allowAcrossProviders = params.cfg.tools?.message?.crossContext?.allowAcrossProviders === true; @@ -132,7 +133,7 @@ export async function buildCrossContextDecoration(params: { const adapter = getChannelMessageAdapter(params.channel); const embeds = adapter.supportsEmbeds - ? adapter.buildCrossContextEmbeds?.(originLabel) ?? undefined + ? (adapter.buildCrossContextEmbeds?.(originLabel) ?? undefined) : undefined; return { prefix, suffix, embeds }; diff --git a/src/infra/outbound/target-resolver.ts b/src/infra/outbound/target-resolver.ts index 11e6a4cd7..8d9ade55e 100644 --- a/src/infra/outbound/target-resolver.ts +++ b/src/infra/outbound/target-resolver.ts @@ -31,7 +31,10 @@ function normalizeQuery(value: string): string { } function stripTargetPrefixes(value: string): string { - return value.replace(/^(channel|group|user):/i, "").replace(/^[@#]/, "").trim(); + return value + .replace(/^(channel|group|user):/i, "") + .replace(/^[@#]/, "") + .trim(); } function preserveTargetCase(channel: ChannelId, raw: string, normalized: string): string { @@ -132,7 +135,7 @@ async function listDirectoryEntries(params: { const runtime = params.runtime ?? defaultRuntime; const useLive = params.source === "live"; if (params.kind === "user") { - const fn = useLive ? directory.listPeersLive ?? directory.listPeers : directory.listPeers; + const fn = useLive ? (directory.listPeersLive ?? directory.listPeers) : directory.listPeers; if (!fn) return []; return await fn({ cfg: params.cfg, @@ -142,7 +145,7 @@ async function listDirectoryEntries(params: { runtime, }); } - const fn = useLive ? directory.listGroupsLive ?? directory.listGroups : directory.listGroups; + const fn = useLive ? (directory.listGroupsLive ?? directory.listGroups) : directory.listGroups; if (!fn) return []; return await fn({ cfg: params.cfg, @@ -254,9 +257,7 @@ export async function resolveMessagingTarget(params: { if (match.kind === "ambiguous") { return { ok: false, - error: new Error( - `Ambiguous target "${raw}". Provide a unique name or an explicit id.`, - ), + error: new Error(`Ambiguous target "${raw}". Provide a unique name or an explicit id.`), candidates: match.entries, }; } diff --git a/src/telegram/bot-message-context.ts b/src/telegram/bot-message-context.ts index 8769d4799..5baf1948b 100644 --- a/src/telegram/bot-message-context.ts +++ b/src/telegram/bot-message-context.ts @@ -323,15 +323,13 @@ export const buildTelegramMessageContext = async ({ replyTarget.id ? ` id:${replyTarget.id}` : "" }]\n${replyTarget.body}\n[/Replying]` : ""; - const groupLabel = isGroup ? buildGroupLabel(msg, chatId, resolvedThreadId) : undefined; - const body = formatAgentEnvelope({ - channel: "Telegram", - from: isGroup - ? (groupLabel ?? `group:${chatId}`) - : buildSenderLabel(msg, senderId || chatId), - timestamp: msg.date ? msg.date * 1000 : undefined, - body: `${bodyText}${replySuffix}`, - }); + const groupLabel = isGroup ? buildGroupLabel(msg, chatId, resolvedThreadId) : undefined; + const body = formatAgentEnvelope({ + channel: "Telegram", + from: isGroup ? (groupLabel ?? `group:${chatId}`) : buildSenderLabel(msg, senderId || chatId), + timestamp: msg.date ? msg.date * 1000 : undefined, + body: `${bodyText}${replySuffix}`, + }); let combinedBody = body; if (isGroup && historyKey && historyLimit > 0) { combinedBody = buildPendingHistoryContextFromMap({ diff --git a/src/telegram/bot.create-telegram-bot.accepts-group-messages-mentionpatterns-match-without-botusername.test.ts b/src/telegram/bot.create-telegram-bot.accepts-group-messages-mentionpatterns-match-without-botusername.test.ts index 92524b3f7..83297495c 100644 --- a/src/telegram/bot.create-telegram-bot.accepts-group-messages-mentionpatterns-match-without-botusername.test.ts +++ b/src/telegram/bot.create-telegram-bot.accepts-group-messages-mentionpatterns-match-without-botusername.test.ts @@ -171,17 +171,17 @@ describe("createTelegramBot", () => { getFile: async () => ({ download: async () => new Uint8Array() }), }); - expect(replySpy).toHaveBeenCalledTimes(1); - const payload = replySpy.mock.calls[0][0]; - expect(payload.WasMentioned).toBe(true); - expect(payload.SenderName).toBe("Ada"); - expect(payload.SenderId).toBe("9"); - expect(payload.Body).toMatch(/^\[Telegram Test Group id:7 2025-01-09T00:00Z\]/); - }); - it("keeps group envelope headers stable (sender identity is separate)", async () => { - onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType; - replySpy.mockReset(); + expect(replySpy).toHaveBeenCalledTimes(1); + const payload = replySpy.mock.calls[0][0]; + expect(payload.WasMentioned).toBe(true); + expect(payload.SenderName).toBe("Ada"); + expect(payload.SenderId).toBe("9"); + expect(payload.Body).toMatch(/^\[Telegram Test Group id:7 2025-01-09T00:00Z\]/); + }); + it("keeps group envelope headers stable (sender identity is separate)", async () => { + onSpy.mockReset(); + const replySpy = replyModule.__replySpy as unknown as ReturnType; + replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -212,13 +212,13 @@ describe("createTelegramBot", () => { getFile: async () => ({ download: async () => new Uint8Array() }), }); - expect(replySpy).toHaveBeenCalledTimes(1); - const payload = replySpy.mock.calls[0][0]; - expect(payload.SenderName).toBe("Ada Lovelace"); - expect(payload.SenderId).toBe("99"); - expect(payload.SenderUsername).toBe("ada"); - expect(payload.Body).toMatch(/^\[Telegram Ops id:42 2025-01-09T00:00Z\]/); - }); + expect(replySpy).toHaveBeenCalledTimes(1); + const payload = replySpy.mock.calls[0][0]; + expect(payload.SenderName).toBe("Ada Lovelace"); + expect(payload.SenderId).toBe("99"); + expect(payload.SenderUsername).toBe("ada"); + expect(payload.Body).toMatch(/^\[Telegram Ops id:42 2025-01-09T00:00Z\]/); + }); it("reacts to mention-gated group messages when ackReaction is enabled", async () => { onSpy.mockReset(); setMessageReactionSpy.mockReset(); diff --git a/src/utils/delivery-context.ts b/src/utils/delivery-context.ts index a3a1e3956..e0a4d4d6f 100644 --- a/src/utils/delivery-context.ts +++ b/src/utils/delivery-context.ts @@ -8,8 +8,7 @@ export type DeliveryContext = { export function normalizeDeliveryContext(context?: DeliveryContext): DeliveryContext | undefined { if (!context) return undefined; - const channel = - typeof context.channel === "string" ? context.channel.trim() : undefined; + const channel = typeof context.channel === "string" ? context.channel.trim() : undefined; const to = typeof context.to === "string" ? context.to.trim() : undefined; const accountId = normalizeAccountId(context.accountId); if (!channel && !to && !accountId) return undefined; diff --git a/src/web/auto-reply.broadcast-groups.broadcasts-sequentially-configured-order.test.ts b/src/web/auto-reply.broadcast-groups.broadcasts-sequentially-configured-order.test.ts index 2c6707853..3f84fc2e3 100644 --- a/src/web/auto-reply.broadcast-groups.broadcasts-sequentially-configured-order.test.ts +++ b/src/web/auto-reply.broadcast-groups.broadcasts-sequentially-configured-order.test.ts @@ -216,7 +216,12 @@ describe("broadcast groups", () => { expect(resolver).toHaveBeenCalledTimes(2); for (const call of resolver.mock.calls.slice(0, 2)) { - const payload = call[0] as { Body: string; SenderName?: string; SenderE164?: string; SenderId?: string }; + const payload = call[0] as { + Body: string; + SenderName?: string; + SenderE164?: string; + SenderId?: string; + }; expect(payload.Body).toContain("Chat messages since your last reply"); expect(payload.Body).toContain("Alice (+111): hello group"); expect(payload.Body).toContain("[message_id: g1]"); diff --git a/src/web/auto-reply/monitor/process-message.ts b/src/web/auto-reply/monitor/process-message.ts index c5524e825..b1c8d393f 100644 --- a/src/web/auto-reply/monitor/process-message.ts +++ b/src/web/auto-reply/monitor/process-message.ts @@ -9,7 +9,10 @@ import { } from "../../../auto-reply/reply/response-prefix-template.js"; import { resolveTextChunkLimit } from "../../../auto-reply/chunk.js"; import { formatAgentEnvelope } from "../../../auto-reply/envelope.js"; -import { buildHistoryContextFromEntries, type HistoryEntry } from "../../../auto-reply/reply/history.js"; +import { + buildHistoryContextFromEntries, + type HistoryEntry, +} from "../../../auto-reply/reply/history.js"; import { dispatchReplyWithBufferedBlockDispatcher } from "../../../auto-reply/reply/provider-dispatcher.js"; import type { getReplyFromConfig } from "../../../auto-reply/reply.js"; import type { ReplyPayload } from "../../../auto-reply/types.js"; @@ -88,7 +91,9 @@ export async function processMessage(params: { currentMessage: combinedBody, excludeLast: false, formatEntry: (entry) => { - const bodyWithId = entry.messageId ? `${entry.body}\n[message_id: ${entry.messageId}]` : entry.body; + const bodyWithId = entry.messageId + ? `${entry.body}\n[message_id: ${entry.messageId}]` + : entry.body; return formatAgentEnvelope({ channel: "WhatsApp", from: conversationId,