UNPKG

@gguf/claw

Version:

Multi-channel AI gateway with extensible messaging integrations

453 lines (451 loc) 16.7 kB
import "./paths-B4BZAPZh.js"; import "./utils-CP9YLh6M.js"; import "./thinking-EAliFiVK.js"; import { E as resolveGatewaySessionStoreTarget, Lt as resolveOutboundTarget, T as pruneLegacyStoreKeys, ft as requestHeartbeatNow, jt as agentCommand, pr as enqueueSystemEvent, w as loadSessionEntry, zt as createOutboundSendDeps } from "./reply-BylFHBd4.js"; import { u as normalizeMainKey } from "./session-key-CZ6OwgSB.js"; import "./registry-B-j4DRfe.js"; import { f as defaultRuntime } from "./subsystem-BCQGGxdd.js"; import "./exec-DYqRzFbo.js"; import { d as resolveSessionAgentId } from "./agent-scope-BnZW9Gh2.js"; import "./model-selection-CqaTAlhy.js"; import "./github-copilot-token-D2zp6kMZ.js"; import "./boolean-BsqeuxE6.js"; import "./env-VriqyjXT.js"; import "./message-channel-Bena1Tzd.js"; import "./send-CsyONLnQ.js"; import { i as loadConfig } from "./config-PQiujvsf.js"; import "./manifest-registry-4k4vkhPS.js"; import "./runner-U04aiHHC.js"; import "./image-DxEpBZim.js"; import "./models-config-B459BnCS.js"; import "./pi-model-discovery-4uUnLc3n.js"; import "./pi-embedded-helpers-BdscQhyS.js"; import "./sandbox-UAzvS0V6.js"; import "./image-ops-CI1VknD1.js"; import "./common-a25M2Kvi.js"; import "./chrome-Dd5zBIFu.js"; import "./tailscale-C3JsBEiY.js"; import "./auth-DZSWPd8D.js"; import "./server-context-DAWsUNUs.js"; import "./frontmatter-6HZ0_y2V.js"; import "./skills-CLmzWt48.js"; import "./routes-DIQa_0pt.js"; import "./redact-B7Mjvk0c.js"; import "./errors-kfGqPQ4b.js"; import "./fs-safe-_OpKPuZA.js"; import "./paths-D1WZUYry.js"; import "./ssrf-6f5m2MMA.js"; import "./store-DSGYY-H9.js"; import "./ports-DwDxX6cS.js"; import "./trash-DLzIf__E.js"; import { u as updateSessionStore } from "./sessions-Z1BZU3xh.js"; import "./dock-7F-MiPtF.js"; import "./normalize-Ci8UyR3Z.js"; import "./accounts-Bj1dJ-fd.js"; import "./accounts-DOl1Wkxo.js"; import "./accounts-t6R5_PzC.js"; import "./bindings-Dja1z-h8.js"; import "./logging-xYH6GmRT.js"; import "./send-uu2Jc-Js.js"; import { r as normalizeChannelId } from "./plugins-CKbXkuXd.js"; import "./paths-C2NfoGZE.js"; import "./tool-display-Bx4M6uNT.js"; import "./fetch-guard-C1sYsWgl.js"; import "./api-key-rotation-CpGefdGL.js"; import "./local-roots-DiDvXIIo.js"; import "./sqlite-JcMMx8Z5.js"; import "./model-catalog-CIixTnGt.js"; import "./tokens-D4lZk7-h.js"; import "./with-timeout-DijiQjw6.js"; import { t as deliverOutboundPayloads } from "./deliver-DgvS3uCz.js"; import "./diagnostic-UUwku4RV.js"; import "./diagnostic-session-state-ByqoIyGn.js"; import "./send-DbwyyQAv.js"; import "./model-VbWjwqaW.js"; import "./reply-prefix-DAK7-zK4.js"; import "./memory-cli-DxmtLhh_.js"; import "./manager-C0teWocQ.js"; import "./retry-rUEdE6zT.js"; import "./chunk-CMylpCPi.js"; import "./markdown-tables-B9VGUdDc.js"; import "./ir-24qCLTKH.js"; import "./render-CXDO_kgw.js"; import "./commands-registry-bb_U-3FM.js"; import "./client-BdSkEtCd.js"; import "./call-BCz_8mqq.js"; import "./pairing-token-B-_eiWlR.js"; import "./channel-activity-DynIQUB_.js"; import "./fetch-Bu5zBCce.js"; import "./tables-C70h_80U.js"; import "./send-CAfFUUkm.js"; import "./pairing-store-T5DpOfoL.js"; import "./proxy-BiOWrea2.js"; import "./links-BVCMOGeE.js"; import "./cli-utils-CX1oQ81G.js"; import "./help-format-dbk2xW0E.js"; import "./progress-CaVMHLaE.js"; import "./resolve-route-2TU_aaLs.js"; import "./replies-NhrZJ3yf.js"; import "./skill-commands-N8Sho46-.js"; import "./workspace-dirs-CE3CxabZ.js"; import "./pi-tools.policy-DTy1DnPK.js"; import "./outbound-attachment-CntoLTI_.js"; import "./delivery-queue-CxDDEWQg.js"; import "./session-cost-usage-BlUDaylg.js"; import "./send-4LoRaltQ.js"; import "./onboard-helpers-DbjfBSMX.js"; import "./prompt-style-VqCNjBi8.js"; import "./pairing-labels-DaESq9ML.js"; import "./exec-approvals-q0C8VDMT.js"; import "./nodes-screen-5fMfTT2n.js"; import "./control-service-BoO2TGOx.js"; import "./stagger-B6VQyn1F.js"; import "./channel-selection-DuDSoVSC.js"; import { c as parseMessageWithAttachments, l as formatForLog, r as registerApnsToken, s as normalizeRpcAttachmentsToChatAttachments } from "./push-apns-DAgh5PMd.js"; import { randomUUID } from "node:crypto"; //#region src/gateway/server-node-events.ts const MAX_EXEC_EVENT_OUTPUT_CHARS = 180; const VOICE_TRANSCRIPT_DEDUPE_WINDOW_MS = 1500; const MAX_RECENT_VOICE_TRANSCRIPTS = 200; const recentVoiceTranscripts = /* @__PURE__ */ new Map(); function normalizeNonEmptyString(value) { if (typeof value !== "string") return null; const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : null; } function normalizeFiniteInteger(value) { return typeof value === "number" && Number.isFinite(value) ? Math.trunc(value) : null; } function resolveVoiceTranscriptFingerprint(obj, text) { const eventId = normalizeNonEmptyString(obj.eventId) ?? normalizeNonEmptyString(obj.providerEventId) ?? normalizeNonEmptyString(obj.transcriptId); if (eventId) return `event:${eventId}`; const callId = normalizeNonEmptyString(obj.providerCallId) ?? normalizeNonEmptyString(obj.callId); const sequence = normalizeFiniteInteger(obj.sequence) ?? normalizeFiniteInteger(obj.seq); if (callId && sequence !== null) return `call-seq:${callId}:${sequence}`; const eventTimestamp = normalizeFiniteInteger(obj.timestamp) ?? normalizeFiniteInteger(obj.ts) ?? normalizeFiniteInteger(obj.eventTimestamp); if (callId && eventTimestamp !== null) return `call-ts:${callId}:${eventTimestamp}`; if (eventTimestamp !== null) return `timestamp:${eventTimestamp}|text:${text}`; return `text:${text}`; } function shouldDropDuplicateVoiceTranscript(params) { const previous = recentVoiceTranscripts.get(params.sessionKey); if (previous && previous.fingerprint === params.fingerprint && params.now - previous.ts <= VOICE_TRANSCRIPT_DEDUPE_WINDOW_MS) return true; recentVoiceTranscripts.set(params.sessionKey, { fingerprint: params.fingerprint, ts: params.now }); if (recentVoiceTranscripts.size > MAX_RECENT_VOICE_TRANSCRIPTS) { const cutoff = params.now - VOICE_TRANSCRIPT_DEDUPE_WINDOW_MS * 2; for (const [key, value] of recentVoiceTranscripts) { if (value.ts < cutoff) recentVoiceTranscripts.delete(key); if (recentVoiceTranscripts.size <= MAX_RECENT_VOICE_TRANSCRIPTS) break; } while (recentVoiceTranscripts.size > MAX_RECENT_VOICE_TRANSCRIPTS) { const oldestKey = recentVoiceTranscripts.keys().next().value; if (oldestKey === void 0) break; recentVoiceTranscripts.delete(oldestKey); } } return false; } function compactExecEventOutput(raw) { const normalized = raw.replace(/\s+/g, " ").trim(); if (!normalized) return ""; if (normalized.length <= MAX_EXEC_EVENT_OUTPUT_CHARS) return normalized; const safe = Math.max(1, MAX_EXEC_EVENT_OUTPUT_CHARS - 1); return `${normalized.slice(0, safe)}…`; } async function touchSessionStore(params) { const { storePath } = params; if (!storePath) return; await updateSessionStore(storePath, (store) => { const target = resolveGatewaySessionStoreTarget({ cfg: params.cfg, key: params.sessionKey, store }); pruneLegacyStoreKeys({ store, canonicalKey: target.canonicalKey, candidates: target.storeKeys }); store[params.canonicalKey] = { sessionId: params.sessionId, updatedAt: params.now, thinkingLevel: params.entry?.thinkingLevel, verboseLevel: params.entry?.verboseLevel, reasoningLevel: params.entry?.reasoningLevel, systemSent: params.entry?.systemSent, sendPolicy: params.entry?.sendPolicy, lastChannel: params.entry?.lastChannel, lastTo: params.entry?.lastTo }; }); } function queueSessionStoreTouch(params) { touchSessionStore({ cfg: params.cfg, sessionKey: params.sessionKey, storePath: params.storePath, canonicalKey: params.canonicalKey, entry: params.entry, sessionId: params.sessionId, now: params.now }).catch((err) => { params.ctx.logGateway.warn("voice session-store update failed: " + formatForLog(err)); }); } function parseSessionKeyFromPayloadJSON(payloadJSON) { let payload; try { payload = JSON.parse(payloadJSON); } catch { return null; } if (typeof payload !== "object" || payload === null) return null; const obj = payload; const sessionKey = typeof obj.sessionKey === "string" ? obj.sessionKey.trim() : ""; return sessionKey.length > 0 ? sessionKey : null; } async function sendReceiptAck(params) { const resolved = resolveOutboundTarget({ channel: params.channel, to: params.to, cfg: params.cfg, mode: "explicit" }); if (!resolved.ok) throw new Error(String(resolved.error)); const agentId = resolveSessionAgentId({ sessionKey: params.sessionKey, config: params.cfg }); await deliverOutboundPayloads({ cfg: params.cfg, channel: params.channel, to: resolved.to, payloads: [{ text: params.text }], agentId, bestEffort: true, deps: createOutboundSendDeps(params.deps) }); } const handleNodeEvent = async (ctx, nodeId, evt) => { switch (evt.event) { case "voice.transcript": { if (!evt.payloadJSON) return; let payload; try { payload = JSON.parse(evt.payloadJSON); } catch { return; } const obj = typeof payload === "object" && payload !== null ? payload : {}; const text = typeof obj.text === "string" ? obj.text.trim() : ""; if (!text) return; if (text.length > 2e4) return; const sessionKeyRaw = typeof obj.sessionKey === "string" ? obj.sessionKey.trim() : ""; const cfg = loadConfig(); const rawMainKey = normalizeMainKey(cfg.session?.mainKey); const sessionKey = sessionKeyRaw.length > 0 ? sessionKeyRaw : rawMainKey; const { storePath, entry, canonicalKey } = loadSessionEntry(sessionKey); const now = Date.now(); if (shouldDropDuplicateVoiceTranscript({ sessionKey: canonicalKey, fingerprint: resolveVoiceTranscriptFingerprint(obj, text), now })) return; const sessionId = entry?.sessionId ?? randomUUID(); queueSessionStoreTouch({ ctx, cfg, sessionKey, storePath, canonicalKey, entry, sessionId, now }); ctx.addChatRun(sessionId, { sessionKey: canonicalKey, clientRunId: `voice-${randomUUID()}` }); agentCommand({ message: text, sessionId, sessionKey: canonicalKey, thinking: "low", deliver: false, messageChannel: "node", inputProvenance: { kind: "external_user", sourceChannel: "voice", sourceTool: "gateway.voice.transcript" } }, defaultRuntime, ctx.deps).catch((err) => { ctx.logGateway.warn(`agent failed node=${nodeId}: ${formatForLog(err)}`); }); return; } case "agent.request": { if (!evt.payloadJSON) return; let link = null; try { link = JSON.parse(evt.payloadJSON); } catch { return; } let message = (link?.message ?? "").trim(); const normalizedAttachments = normalizeRpcAttachmentsToChatAttachments(link?.attachments ?? void 0); let images = []; if (normalizedAttachments.length > 0) try { const parsed = await parseMessageWithAttachments(message, normalizedAttachments, { maxBytes: 5e6, log: ctx.logGateway }); message = parsed.message.trim(); images = parsed.images; } catch { return; } if (!message) return; if (message.length > 2e4) return; let channel = normalizeChannelId(typeof link?.channel === "string" ? link.channel.trim() : "") ?? void 0; let to = typeof link?.to === "string" && link.to.trim() ? link.to.trim() : void 0; const deliverRequested = Boolean(link?.deliver); const wantsReceipt = Boolean(link?.receipt); const receiptText = (typeof link?.receiptText === "string" ? link.receiptText.trim() : "") || "Just received your iOS share + request, working on it."; const sessionKeyRaw = (link?.sessionKey ?? "").trim(); const sessionKey = sessionKeyRaw.length > 0 ? sessionKeyRaw : `node-${nodeId}`; const cfg = loadConfig(); const { storePath, entry, canonicalKey } = loadSessionEntry(sessionKey); const now = Date.now(); const sessionId = entry?.sessionId ?? randomUUID(); await touchSessionStore({ cfg, sessionKey, storePath, canonicalKey, entry, sessionId, now }); if (deliverRequested && (!channel || !to)) { const entryChannel = typeof entry?.lastChannel === "string" ? normalizeChannelId(entry.lastChannel) : void 0; const entryTo = typeof entry?.lastTo === "string" ? entry.lastTo.trim() : ""; if (!channel && entryChannel) channel = entryChannel; if (!to && entryTo) to = entryTo; } const deliver = deliverRequested && Boolean(channel && to); const deliveryChannel = deliver ? channel : void 0; const deliveryTo = deliver ? to : void 0; if (deliverRequested && !deliver) ctx.logGateway.warn(`agent delivery disabled node=${nodeId}: missing session delivery route (channel=${channel ?? "-"} to=${to ?? "-"})`); if (wantsReceipt && deliveryChannel && deliveryTo) sendReceiptAck({ cfg, deps: ctx.deps, sessionKey: canonicalKey, channel: deliveryChannel, to: deliveryTo, text: receiptText }).catch((err) => { ctx.logGateway.warn(`agent receipt failed node=${nodeId}: ${formatForLog(err)}`); }); else if (wantsReceipt) ctx.logGateway.warn(`agent receipt skipped node=${nodeId}: missing delivery route (channel=${deliveryChannel ?? "-"} to=${deliveryTo ?? "-"})`); agentCommand({ message, images, sessionId, sessionKey: canonicalKey, thinking: link?.thinking ?? void 0, deliver, to: deliveryTo, channel: deliveryChannel, timeout: typeof link?.timeoutSeconds === "number" ? link.timeoutSeconds.toString() : void 0, messageChannel: "node" }, defaultRuntime, ctx.deps).catch((err) => { ctx.logGateway.warn(`agent failed node=${nodeId}: ${formatForLog(err)}`); }); return; } case "chat.subscribe": { if (!evt.payloadJSON) return; const sessionKey = parseSessionKeyFromPayloadJSON(evt.payloadJSON); if (!sessionKey) return; ctx.nodeSubscribe(nodeId, sessionKey); return; } case "chat.unsubscribe": { if (!evt.payloadJSON) return; const sessionKey = parseSessionKeyFromPayloadJSON(evt.payloadJSON); if (!sessionKey) return; ctx.nodeUnsubscribe(nodeId, sessionKey); return; } case "exec.started": case "exec.finished": case "exec.denied": { if (!evt.payloadJSON) return; let payload; try { payload = JSON.parse(evt.payloadJSON); } catch { return; } const obj = typeof payload === "object" && payload !== null ? payload : {}; const sessionKey = typeof obj.sessionKey === "string" ? obj.sessionKey.trim() : `node-${nodeId}`; if (!sessionKey) return; const runId = typeof obj.runId === "string" ? obj.runId.trim() : ""; const command = typeof obj.command === "string" ? obj.command.trim() : ""; const exitCode = typeof obj.exitCode === "number" && Number.isFinite(obj.exitCode) ? obj.exitCode : void 0; const timedOut = obj.timedOut === true; const output = typeof obj.output === "string" ? obj.output.trim() : ""; const reason = typeof obj.reason === "string" ? obj.reason.trim() : ""; let text = ""; if (evt.event === "exec.started") { text = `Exec started (node=${nodeId}${runId ? ` id=${runId}` : ""})`; if (command) text += `: ${command}`; } else if (evt.event === "exec.finished") { const exitLabel = timedOut ? "timeout" : `code ${exitCode ?? "?"}`; const compactOutput = compactExecEventOutput(output); if (!(timedOut || exitCode !== 0 || compactOutput.length > 0)) return; text = `Exec finished (node=${nodeId}${runId ? ` id=${runId}` : ""}, ${exitLabel})`; if (compactOutput) text += `\n${compactOutput}`; } else { text = `Exec denied (node=${nodeId}${runId ? ` id=${runId}` : ""}${reason ? `, ${reason}` : ""})`; if (command) text += `: ${command}`; } enqueueSystemEvent(text, { sessionKey, contextKey: runId ? `exec:${runId}` : "exec" }); requestHeartbeatNow({ reason: "exec-event" }); return; } case "push.apns.register": { if (!evt.payloadJSON) return; let payload; try { payload = JSON.parse(evt.payloadJSON); } catch { return; } const obj = typeof payload === "object" && payload !== null ? payload : {}; const token = typeof obj.token === "string" ? obj.token : ""; const topic = typeof obj.topic === "string" ? obj.topic : ""; const environment = obj.environment; try { await registerApnsToken({ nodeId, token, topic, environment }); } catch (err) { ctx.logGateway.warn(`push apns register failed node=${nodeId}: ${formatForLog(err)}`); } return; } default: return; } }; //#endregion export { handleNodeEvent };