mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
* feat(bluebubbles): auto-strip markdown from outbound messages (#7402) * fix(security): add timeout to webhook body reading (#6762) Adds 30-second timeout to readBody() in voice-call, bluebubbles, and nostr webhook handlers. Prevents Slow-Loris DoS (CWE-400, CVSS 7.5). Merged with existing maxBytes protection in voice-call. * fix(security): unify Error objects and lint fixes in webhook timeouts (#6762) * fix: prevent plugins from auto-enabling without user consent (#3961) Changes default plugin enabled state from true to false in enablePluginEntry(). Preserves existing enabled:true values. Fixes #3932. * fix: apply hierarchical mediaMaxMb config to all channels (#8749) Generalizes resolveAttachmentMaxBytes() to use account → channel → global config resolution for all channels, not just BlueBubbles. Fixes #7847. * fix(bluebubbles): sanitize attachment filenames against header injection (#10333) Strip ", \r, \n, and \\ from filenames after path.basename() to prevent multipart Content-Disposition header injection (CWE-93, CVSS 5.4). Also adds sanitization to setGroupIconBlueBubbles which had zero filename sanitization. * fix(lint): exclude extensions/ from Oxlint preflight check (#9313) Extensions use PluginRuntime|null patterns that trigger no-redundant-type-constituents because PluginRuntime resolves to any. Excluding extensions/ from Oxlint unblocks user upgrades. Re-applies the approach from closed PR #10087. * fix(bluebubbles): add tempGuid to createNewChatWithMessage payload (#7745) Non-Private-API mode (AppleScript) requires tempGuid in send payloads. The main sendMessageBlueBubbles already had it, but createNewChatWithMessage was missing it, causing 400 errors for new chat creation without Private API. * fix: send stop-typing signal when run ends with NO_REPLY (#8785) Adds onCleanup callback to the typing controller that fires when the controller is cleaned up while typing was active (e.g., after NO_REPLY). Channels using createTypingCallbacks automatically get stop-typing on cleanup. This prevents the typing indicator from lingering in group chats when the agent decides not to reply. * fix(telegram): deduplicate skill commands in multi-agent setup (#5717) Two fixes: 1. Skip duplicate workspace dirs when listing skill commands across agents. Multiple agents sharing the same workspace would produce duplicate commands with _2, _3 suffixes. 2. Clear stale commands via deleteMyCommands before registering new ones. Commands from deleted skills now get cleaned up on restart. * fix: add size limits to unbounded in-memory caches (#4948) Adds max-size caps with oldest-entry eviction to prevent OOM in long-running deployments: - BlueBubbles serverInfoCache: 64 entries (already has TTL) - Google Chat authCache: 32 entries - Matrix directRoomCache: 1024 entries - Discord presenceCache: 5000 entries per account * fix: address review concerns (#11093) - Chain deleteMyCommands → setMyCommands to prevent race condition (#5717) - Rename enablePluginEntry to registerPluginEntry (now sets enabled: false) - Add Slow-Loris timeout test for readJsonBody (#6023)
60 lines
2.4 KiB
TypeScript
60 lines
2.4 KiB
TypeScript
import type { ImageContent } from "@mariozechner/pi-ai";
|
|
import type { TypingController } from "./reply/typing.js";
|
|
|
|
export type BlockReplyContext = {
|
|
abortSignal?: AbortSignal;
|
|
timeoutMs?: number;
|
|
};
|
|
|
|
/** Context passed to onModelSelected callback with actual model used. */
|
|
export type ModelSelectedContext = {
|
|
provider: string;
|
|
model: string;
|
|
thinkLevel: string | undefined;
|
|
};
|
|
|
|
export type GetReplyOptions = {
|
|
/** Override run id for agent events (defaults to random UUID). */
|
|
runId?: string;
|
|
/** Abort signal for the underlying agent run. */
|
|
abortSignal?: AbortSignal;
|
|
/** Optional inbound images (used for webchat attachments). */
|
|
images?: ImageContent[];
|
|
/** Notifies when an agent run actually starts (useful for webchat command handling). */
|
|
onAgentRunStart?: (runId: string) => void;
|
|
onReplyStart?: () => Promise<void> | void;
|
|
/** Called when the typing controller cleans up (e.g., run ended with NO_REPLY). */
|
|
onTypingCleanup?: () => void;
|
|
onTypingController?: (typing: TypingController) => void;
|
|
isHeartbeat?: boolean;
|
|
onPartialReply?: (payload: ReplyPayload) => Promise<void> | void;
|
|
onReasoningStream?: (payload: ReplyPayload) => Promise<void> | void;
|
|
onBlockReply?: (payload: ReplyPayload, context?: BlockReplyContext) => Promise<void> | void;
|
|
onToolResult?: (payload: ReplyPayload) => Promise<void> | void;
|
|
/** Called when the actual model is selected (including after fallback).
|
|
* Use this to get model/provider/thinkLevel for responsePrefix template interpolation. */
|
|
onModelSelected?: (ctx: ModelSelectedContext) => void;
|
|
disableBlockStreaming?: boolean;
|
|
/** Timeout for block reply delivery (ms). */
|
|
blockReplyTimeoutMs?: number;
|
|
/** If provided, only load these skills for this session (empty = no skills). */
|
|
skillFilter?: string[];
|
|
/** Mutable ref to track if a reply was sent (for Slack "first" threading mode). */
|
|
hasRepliedRef?: { value: boolean };
|
|
};
|
|
|
|
export type ReplyPayload = {
|
|
text?: string;
|
|
mediaUrl?: string;
|
|
mediaUrls?: string[];
|
|
replyToId?: string;
|
|
replyToTag?: boolean;
|
|
/** True when [[reply_to_current]] was present but not yet mapped to a message id. */
|
|
replyToCurrent?: boolean;
|
|
/** Send audio as voice message (bubble) instead of audio file. Defaults to false. */
|
|
audioAsVoice?: boolean;
|
|
isError?: boolean;
|
|
/** Channel-specific payload data (per-channel envelope). */
|
|
channelData?: Record<string, unknown>;
|
|
};
|