UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

372 lines (371 loc) โ€ข 10.6 kB
import { c as normalizeOptionalLowercaseString } from "./string-coerce-1VuiLaaR.mjs"; import "./identity-DMes0XZ3.mjs"; import { i as shouldAckReaction } from "./ack-reactions-CBXgZ9bf.mjs"; import "./logging-C9LTVJNE.mjs"; //#region src/channels/status-reaction-tool-emojis.ts const TOOL_REACTION_EMOJIS = /* @__PURE__ */ new Map([ ["bash", "๐Ÿ› ๏ธ"], ["computer", "๐Ÿ–ฑ๏ธ"], ["mobile_ui", "๐Ÿ“ฑ"], ["screen", "๐Ÿ–ฅ๏ธ"], ["theme", "๐ŸŽจ"], ["terminal", "โŒจ๏ธ"], ["portal", "๐ŸŒ"], ["process", "๐Ÿงฐ"], ["gateway_process", "๐Ÿงฐ"], ["read", "๐Ÿ“–"], ["write", "โœ๏ธ"], ["edit", "๐Ÿ“"], ["personal_instructions", "๐Ÿ“"], ["presence", "๐Ÿงฉ"], ["attach", "๐Ÿ“Ž"], ["api", "๐ŸŒ"], ["browser", "๐ŸŒ"], ["canvas", "๐Ÿ–ผ๏ธ"], ["dashboard", "๐Ÿ“‹"], ["nodes", "๐Ÿ“ฑ"], ["cron", "โฐ"], ["get_goal", "๐ŸŽฏ"], ["create_goal", "๐ŸŽฏ"], ["update_goal", "๐ŸŽฏ"], ["progress_card", "๐Ÿ—บ๏ธ"], ["ask_user", "โ“"], ["secrets", "๐Ÿ”‘"], ["suggest_task", "โœจ"], ["dismiss_task", "๐Ÿ—‘๏ธ"], ["skill_workshop", "๐Ÿงฐ"], ["openclaw", "๐Ÿฆ€"], ["gateway", "๐Ÿ”Œ"], ["plugins", "๐Ÿงฉ"], ["exec", "๐Ÿ› ๏ธ"], ["tool_call", "๐Ÿงฐ"], ["tool_call_update", "๐Ÿงฐ"], ["session_status", "๐Ÿ“Š"], ["github_publish", "๐Ÿ”€"], ["github_identity_status", "๐Ÿ”"], ["sessions", "๐Ÿ—‚๏ธ"], ["sessions_list", "๐Ÿ—‚๏ธ"], ["conversations_list", "๐Ÿ’ฌ"], ["conversations_send", "๐Ÿ“จ"], ["conversations_turn", "โ†”๏ธ"], ["sessions_send", "๐Ÿ“จ"], ["sessions_history", "๐Ÿงพ"], ["sessions_search", "๐Ÿ”Ž"], ["transcripts", "๐ŸŽ™๏ธ"], ["sessions_spawn", "๐Ÿง‘โ€๐Ÿ”ง"], ["agents_wait", "โณ"], ["structured_output", "๐Ÿงพ"], ["subagents", "๐Ÿค–"], ["agents_list", "๐Ÿงญ"], ["memory_search", "๐Ÿง "], ["memory_get", "๐Ÿ““"], ["skills_search", "๐Ÿ”"], ["skills_read", "๐Ÿ“–"], ["web_search", "๐Ÿ”Ž"], ["web_fetch", "๐Ÿ“„"], ["code_execution", "๐Ÿงฎ"], ["decision_evaluate", "โš–๏ธ"], ["message", "โœ‰๏ธ"], ["apply_patch", "๐Ÿฉน"], ["image", "๐Ÿ–ผ๏ธ"], ["view_image", "๐Ÿ–ผ๏ธ"], ["image_generate", "๐ŸŽจ"], ["music_generate", "๐ŸŽต"], ["video_generate", "๐ŸŽฌ"], ["pdf", "๐Ÿ“‘"], ["sessions_yield", "โธ๏ธ"], ["tts", "๐Ÿ”Š"] ]); //#endregion //#region src/channels/status-reactions.ts const DEFAULT_EMOJIS = { queued: "๐Ÿ‘€", thinking: "๐Ÿง ", tool: "๐Ÿ› ๏ธ", coding: "๐Ÿ’ป", web: "๐ŸŒ", deploy: "๐Ÿ›ซ", build: "๐Ÿ—๏ธ", concierge: "๐Ÿ’", done: "โœ…", error: "โŒ", stallSoft: "โณ", stallHard: "โš ๏ธ", compacting: "๐Ÿ—œ๏ธ" }; const DEFAULT_TIMING = { debounceMs: 700, stallSoftMs: 1e4, stallHardMs: 3e4, doneHoldMs: 1500, errorHoldMs: 2500 }; const CODING_TOOL_TOKENS = [ "exec", "process", "read", "write", "edit", "session_status", "bash" ]; const WEB_TOOL_TOKENS = [ "web_search", "web-search", "web_fetch", "web-fetch", "browser" ]; const DEPLOY_TOOL_TOKENS = [ "fastlane", "deploy", "upload", "testflight", "ship", "release", "publish", "distribute" ]; const BUILD_TOOL_TOKENS = [ "build", "compile", "xcode", "swift", "gradle", "cargo", "make", "cmake", "webpack", "vite", "tsc", "lint" ]; const CONCIERGE_TOOL_TOKENS = [ "navigate", "click", "fill", "screenshot", "scroll", "page", "form", "puppeteer", "playwright", "selenium", "chromedp" ]; function resolveToolEmoji(toolName, emojis, emojiOverrides) { const normalized = normalizeOptionalLowercaseString(toolName) ?? ""; if (!normalized) return emojis.tool; const category = DEPLOY_TOOL_TOKENS.some((token) => normalized.includes(token)) ? "deploy" : BUILD_TOOL_TOKENS.some((token) => normalized.includes(token)) ? "build" : CONCIERGE_TOOL_TOKENS.some((token) => normalized.includes(token)) ? "concierge" : WEB_TOOL_TOKENS.some((token) => normalized.includes(token)) ? "web" : CODING_TOOL_TOKENS.some((token) => normalized.includes(token)) ? "coding" : "tool"; if (emojiOverrides?.[category] !== void 0) return emojis[category]; return TOOL_REACTION_EMOJIS.get(normalized) ?? emojis[category]; } /** Defer reaction removal until cleanup to avoid flicker without atomic replacement. */ function createStatusReactionController(params) { const { enabled, adapter, initialEmoji, onError } = params; const showActivity = params.presentation !== "acknowledgement"; const emojis = { ...DEFAULT_EMOJIS, queued: params.emojis?.queued ?? initialEmoji, ...params.emojis }; const timing = { ...DEFAULT_TIMING, ...params.timing }; let currentEmoji = ""; let pendingEmoji = ""; let debounceTimer = null; let stallTimers = []; let terminalHold = null; let terminalHoldGeneration = 0; let finished = false; let chainPromise = Promise.resolve(); const activeEmojis = /* @__PURE__ */ new Set(); function enqueue(fn) { chainPromise = chainPromise.then(fn, fn); return chainPromise; } function clearActivityTimers() { clearDebounceTimer(); clearStallTimers(); } function clearStallTimers() { for (const timer of stallTimers) clearTimeout(timer); stallTimers = []; } function cancelTerminalHold() { terminalHoldGeneration += 1; const hold = terminalHold; if (!hold) return; terminalHold = null; clearTimeout(hold.timer); hold.resolve(); } function waitForTerminalHold(holdMs, generation) { if (holdMs <= 0 || generation !== terminalHoldGeneration) return Promise.resolve(); return new Promise((resolve) => { terminalHold = { timer: setTimeout(() => { terminalHold = null; resolve(); }, holdMs), resolve }; }); } function clearDebounceTimer() { if (debounceTimer) { clearTimeout(debounceTimer); debounceTimer = null; } } function resetStallTimers() { if (!showActivity) return; clearStallTimers(); stallTimers = ["stallSoft", "stallHard"].map((phase) => setTimeout(() => { scheduleEmoji(emojis[phase], { immediate: true, skipStallReset: true }); }, timing[`${phase}Ms`])); } async function removeActiveEmojis(options = {}) { if (!adapter.removeReaction) return; for (const emoji of Array.from(activeEmojis)) { if (emoji === options.keepEmoji) continue; try { await adapter.removeReaction(emoji); } catch (err) { onError?.(err); } finally { activeEmojis.delete(emoji); } } } async function applyEmoji(newEmoji) { if (!enabled) return; try { if (!adapter.removeReaction || !activeEmojis.has(newEmoji)) await adapter.setReaction(newEmoji); activeEmojis.add(newEmoji); currentEmoji = newEmoji; } catch (err) { onError?.(err); } } function scheduleEmoji(requestedEmoji, options = {}) { if (!enabled || finished) return; const emoji = showActivity ? requestedEmoji : initialEmoji; if (emoji === currentEmoji || emoji === pendingEmoji) { if (!options.skipStallReset) resetStallTimers(); return; } pendingEmoji = emoji; clearDebounceTimer(); const applyPendingEmoji = async () => { await applyEmoji(emoji); pendingEmoji = ""; }; if (options.immediate) enqueue(applyPendingEmoji); else debounceTimer = setTimeout(() => { debounceTimer = null; enqueue(applyPendingEmoji); }, timing.debounceMs); if (!options.skipStallReset) resetStallTimers(); } function finishWithEmoji(emoji, holdMs) { if (!enabled) return Promise.resolve(); finished = true; clearActivityTimers(); const holdGeneration = terminalHoldGeneration; return enqueue(async () => { await applyEmoji(emoji); await removeActiveEmojis({ keepEmoji: emoji }); pendingEmoji = ""; await waitForTerminalHold(holdMs, holdGeneration); }); } async function clear() { if (!enabled) return; clearActivityTimers(); cancelTerminalHold(); finished = true; await enqueue(async () => { if (adapter.clearReaction) try { await adapter.clearReaction(); } catch (err) { onError?.(err); } finally { activeEmojis.clear(); } else if (adapter.removeReaction) await removeActiveEmojis(); currentEmoji = ""; pendingEmoji = ""; }); } async function restoreInitial() { if (!enabled) return; const alreadyInitial = currentEmoji === initialEmoji; const pendingBeforeClear = pendingEmoji; const hadDebouncedPending = debounceTimer !== null; const hasExtraActiveEmoji = Array.from(activeEmojis).some((emoji) => emoji !== initialEmoji); clearActivityTimers(); if (!finished && alreadyInitial && (!pendingBeforeClear || hadDebouncedPending) && !hasExtraActiveEmoji) { pendingEmoji = ""; return; } if (!finished && pendingBeforeClear === initialEmoji && !hadDebouncedPending) { await chainPromise; return; } await enqueue(async () => { await applyEmoji(initialEmoji); await removeActiveEmojis({ keepEmoji: initialEmoji }); pendingEmoji = ""; }); } return { setQueued: () => scheduleEmoji(emojis.queued, { immediate: true }), setThinking: () => scheduleEmoji(emojis.thinking), setTool: (toolName) => scheduleEmoji(resolveToolEmoji(toolName, emojis, params.emojis)), setCompacting: () => scheduleEmoji(emojis.compacting), cancelPending() { clearDebounceTimer(); pendingEmoji = ""; }, setDone: () => showActivity ? finishWithEmoji(emojis.done, timing.doneHoldMs) : finishWithEmoji(initialEmoji, 0), setError: () => finishWithEmoji(emojis.error, timing.errorHoldMs), clear, restoreInitial }; } //#endregion //#region src/plugin-sdk/channel-feedback.ts /** * Public SDK subpath for channel feedback reactions, status reactions, and logging helpers. */ /** * @deprecated Load-only bridge: the published WhatsApp channel package * (2026.7.2-beta.7 and earlier) imports this at module top level, so removing * it makes the installed plugin fail to load after a core upgrade. Behavior * preserved verbatim from the pre-#121257 owner. Remove once managed releases * have replaced the old npm latest/extended-stable packages and their upgrade * window has closed. */ function shouldAckReactionForWhatsApp(params) { if (!params.emoji) return false; if (params.isDirect) return params.directEnabled; if (!params.isGroup || params.groupMode === "never") return false; if (params.groupMode === "always") return true; return shouldAckReaction({ scope: "group-mentions", isDirect: false, isGroup: true, isMentionableGroup: true, canDetectMention: true, effectiveWasMentioned: params.wasMentioned, shouldBypassMention: params.groupActivated }); } //#endregion export { DEFAULT_EMOJIS as a, WEB_TOOL_TOKENS as c, CONCIERGE_TOOL_TOKENS as i, createStatusReactionController as l, BUILD_TOOL_TOKENS as n, DEFAULT_TIMING as o, CODING_TOOL_TOKENS as r, DEPLOY_TOOL_TOKENS as s, shouldAckReactionForWhatsApp as t, resolveToolEmoji as u };