UNPKG

@gguf/claw

Version:

WhatsApp gateway CLI (Baileys web) with Pi RPC agent

702 lines (693 loc) 28.8 kB
import { $ as DEFAULT_CHAT_CHANNEL, p as defaultRuntime } from "./entry.js"; import { C as buildAllowedModelSet, D as isCliProvider, L as resolveThinkingDefault, N as resolveConfiguredModelRef, O as modelKey, _ as ensureAuthProfileStore, _t as DEFAULT_PROVIDER, gt as DEFAULT_MODEL, ht as DEFAULT_CONTEXT_TOKENS } from "./auth-profiles-CYBuGiBb.js"; import { t as formatCliCommand } from "./command-format-ayFsmwwz.js"; import { c as normalizeAgentId, l as normalizeMainKey, u as resolveAgentIdFromSessionKey } from "./session-key-CZkcvAtx.js"; import { a as resolveAgentModelPrimary, i as resolveAgentModelFallbacksOverride, o as resolveAgentSkillsFilter, r as resolveAgentDir, s as resolveAgentWorkspaceDir, t as listAgentIds, x as ensureAgentWorkspace } from "./agent-scope-C9VjJXEK.js"; import { i as loadConfig } from "./config-CKLedg5Y.js"; import { a as isInternalMessageChannel, d as resolveMessageChannel, i as isGatewayMessageChannel, l as normalizeMessageChannel, n as isDeliverableMessageChannel, t as INTERNAL_MESSAGE_CHANNEL } from "./message-channel-BlgPSDAh.js"; import { r as normalizeChannelId, t as getChannelPlugin } from "./plugins-BUPpq5aS.js"; import { Bt as emitAgentEvent, Dt as runEmbeddedPiAgent, Gt as AGENT_LANE_NESTED, H as getCliSessionId, K as resolveSendPolicy, Kt as loadModelCatalog, Rt as resolveAgentTimeoutMs, Tn as runWithModelFallback, U as setCliSessionId, Ut as registerAgentRunContext, W as runCliAgent, bn as resolveOutboundTarget, ct as clearSessionAuthProfileOverride, dn as lookupContextTokens, lt as applyVerboseOverride, ot as getSkillsSnapshotVersion, q as getRemoteSkillEligibility, qt as applyModelOverrideToSessionEntry, xn as resolveSessionDeliveryTarget, zt as clearAgentRunContext } from "./loader-_Pj-TZS2.js"; import { n as resolveSessionFilePath, o as resolveStorePath } from "./paths-CTg8F3AE.js"; import { B as normalizeThinkLevel, F as formatXHighModelHint, H as normalizeVerboseLevel, P as formatThinkingLevels, W as supportsXHighThinking } from "./pi-embedded-helpers-DF8SAHU-.js"; import { a as normalizeOutboundPayloadsForJson, i as normalizeOutboundPayloads, r as formatOutboundPayloadLog, t as deliverOutboundPayloads } from "./deliver-Cau4HL7W.js"; import { A as resolveSessionResetType, D as evaluateSessionFreshness, E as resolveSessionKey, F as resolveExplicitAgentSessionKey, O as resolveChannelResetConfig, T as normalizeAccountId, d as loadSessionStore, g as updateSessionStore, k as resolveSessionResetPolicy } from "./sandbox-DuqLKN5J.js"; import { r as buildWorkspaceSkillSnapshot } from "./skills-CmU0Q92f.js"; import { l as hasNonzeroUsage } from "./session-cost-usage-BTXosU1k.js"; import { t as createDefaultDeps } from "./deps-ytXmI88x.js"; import crypto from "node:crypto"; //#region src/cli/outbound-send-deps.ts function createOutboundSendDeps(deps) { return { sendWhatsApp: deps.sendMessageWhatsApp, sendTelegram: deps.sendMessageTelegram, sendDiscord: deps.sendMessageDiscord, sendSlack: deps.sendMessageSlack, sendSignal: deps.sendMessageSignal, sendIMessage: deps.sendMessageIMessage }; } //#endregion //#region src/infra/outbound/agent-delivery.ts function resolveAgentDeliveryPlan(params) { const requestedRaw = typeof params.requestedChannel === "string" ? params.requestedChannel.trim() : ""; const requestedChannel = (requestedRaw ? normalizeMessageChannel(requestedRaw) : void 0) || "last"; const explicitTo = typeof params.explicitTo === "string" && params.explicitTo.trim() ? params.explicitTo.trim() : void 0; const baseDelivery = resolveSessionDeliveryTarget({ entry: params.sessionEntry, requestedChannel: requestedChannel === INTERNAL_MESSAGE_CHANNEL ? "last" : requestedChannel, explicitTo, explicitThreadId: params.explicitThreadId }); const resolvedChannel = (() => { if (requestedChannel === INTERNAL_MESSAGE_CHANNEL) return INTERNAL_MESSAGE_CHANNEL; if (requestedChannel === "last") { if (baseDelivery.channel && baseDelivery.channel !== INTERNAL_MESSAGE_CHANNEL) return baseDelivery.channel; return params.wantsDelivery ? DEFAULT_CHAT_CHANNEL : INTERNAL_MESSAGE_CHANNEL; } if (isGatewayMessageChannel(requestedChannel)) return requestedChannel; if (baseDelivery.channel && baseDelivery.channel !== INTERNAL_MESSAGE_CHANNEL) return baseDelivery.channel; return params.wantsDelivery ? DEFAULT_CHAT_CHANNEL : INTERNAL_MESSAGE_CHANNEL; })(); const deliveryTargetMode = explicitTo ? "explicit" : isDeliverableMessageChannel(resolvedChannel) ? "implicit" : void 0; const resolvedAccountId = normalizeAccountId(params.accountId) ?? (deliveryTargetMode === "implicit" ? baseDelivery.accountId : void 0); let resolvedTo = explicitTo; if (!resolvedTo && isDeliverableMessageChannel(resolvedChannel) && resolvedChannel === baseDelivery.lastChannel) resolvedTo = baseDelivery.lastTo; return { baseDelivery, resolvedChannel, resolvedTo, resolvedAccountId, resolvedThreadId: baseDelivery.threadId, deliveryTargetMode }; } function resolveAgentOutboundTarget(params) { const targetMode = params.targetMode ?? params.plan.deliveryTargetMode ?? (params.plan.resolvedTo ? "explicit" : "implicit"); if (!isDeliverableMessageChannel(params.plan.resolvedChannel)) return { resolvedTarget: null, resolvedTo: params.plan.resolvedTo, targetMode }; if (params.validateExplicitTarget !== true && params.plan.resolvedTo) return { resolvedTarget: null, resolvedTo: params.plan.resolvedTo, targetMode }; const resolvedTarget = resolveOutboundTarget({ channel: params.plan.resolvedChannel, to: params.plan.resolvedTo, cfg: params.cfg, accountId: params.plan.resolvedAccountId, mode: targetMode }); return { resolvedTarget, resolvedTo: resolvedTarget.ok ? resolvedTarget.to : params.plan.resolvedTo, targetMode }; } //#endregion //#region src/infra/outbound/envelope.ts const isOutboundPayloadJson = (payload) => "mediaUrl" in payload; function buildOutboundResultEnvelope(params) { const hasPayloads = params.payloads !== void 0; const payloads = params.payloads === void 0 ? void 0 : params.payloads.length === 0 ? [] : isOutboundPayloadJson(params.payloads[0]) ? params.payloads : normalizeOutboundPayloadsForJson(params.payloads); if (params.flattenDelivery !== false && params.delivery && !params.meta && !hasPayloads) return params.delivery; return { ...hasPayloads ? { payloads } : {}, ...params.meta ? { meta: params.meta } : {}, ...params.delivery ? { delivery: params.delivery } : {} }; } //#endregion //#region src/commands/agent/delivery.ts const NESTED_LOG_PREFIX = "[agent:nested]"; function formatNestedLogPrefix(opts) { const parts = [NESTED_LOG_PREFIX]; const session = opts.sessionKey ?? opts.sessionId; if (session) parts.push(`session=${session}`); if (opts.runId) parts.push(`run=${opts.runId}`); const channel = opts.messageChannel ?? opts.channel; if (channel) parts.push(`channel=${channel}`); if (opts.to) parts.push(`to=${opts.to}`); if (opts.accountId) parts.push(`account=${opts.accountId}`); return parts.join(" "); } function logNestedOutput(runtime, opts, output) { const prefix = formatNestedLogPrefix(opts); for (const line of output.split(/\r?\n/)) { if (!line) continue; runtime.log(`${prefix} ${line}`); } } async function deliverAgentCommandResult(params) { const { cfg, deps, runtime, opts, sessionEntry, payloads, result } = params; const deliver = opts.deliver === true; const bestEffortDeliver = opts.bestEffortDeliver === true; const deliveryPlan = resolveAgentDeliveryPlan({ sessionEntry, requestedChannel: opts.replyChannel ?? opts.channel, explicitTo: opts.replyTo ?? opts.to, explicitThreadId: opts.threadId, accountId: opts.replyAccountId ?? opts.accountId, wantsDelivery: deliver }); const deliveryChannel = deliveryPlan.resolvedChannel; const deliveryPlugin = !isInternalMessageChannel(deliveryChannel) ? getChannelPlugin(normalizeChannelId(deliveryChannel) ?? deliveryChannel) : void 0; const isDeliveryChannelKnown = isInternalMessageChannel(deliveryChannel) || Boolean(deliveryPlugin); const targetMode = opts.deliveryTargetMode ?? deliveryPlan.deliveryTargetMode ?? (opts.to ? "explicit" : "implicit"); const resolvedAccountId = deliveryPlan.resolvedAccountId; const resolved = deliver && isDeliveryChannelKnown && deliveryChannel ? resolveAgentOutboundTarget({ cfg, plan: deliveryPlan, targetMode, validateExplicitTarget: true }) : { resolvedTarget: null, resolvedTo: deliveryPlan.resolvedTo, targetMode }; const resolvedTarget = resolved.resolvedTarget; const deliveryTarget = resolved.resolvedTo; const resolvedThreadId = deliveryPlan.resolvedThreadId ?? opts.threadId; const resolvedReplyToId = deliveryChannel === "slack" && resolvedThreadId != null ? String(resolvedThreadId) : void 0; const resolvedThreadTarget = deliveryChannel === "slack" ? void 0 : resolvedThreadId; const logDeliveryError = (err) => { const message = `Delivery failed (${deliveryChannel}${deliveryTarget ? ` to ${deliveryTarget}` : ""}): ${String(err)}`; runtime.error?.(message); if (!runtime.error) runtime.log(message); }; if (deliver) { if (!isDeliveryChannelKnown) { const err = /* @__PURE__ */ new Error(`Unknown channel: ${deliveryChannel}`); if (!bestEffortDeliver) throw err; logDeliveryError(err); } else if (resolvedTarget && !resolvedTarget.ok) { if (!bestEffortDeliver) throw resolvedTarget.error; logDeliveryError(resolvedTarget.error); } } const normalizedPayloads = normalizeOutboundPayloadsForJson(payloads ?? []); if (opts.json) { runtime.log(JSON.stringify(buildOutboundResultEnvelope({ payloads: normalizedPayloads, meta: result.meta }), null, 2)); if (!deliver) return { payloads: normalizedPayloads, meta: result.meta }; } if (!payloads || payloads.length === 0) { runtime.log("No reply from agent."); return { payloads: [], meta: result.meta }; } const deliveryPayloads = normalizeOutboundPayloads(payloads); const logPayload = (payload) => { if (opts.json) return; const output = formatOutboundPayloadLog(payload); if (!output) return; if (opts.lane === AGENT_LANE_NESTED) { logNestedOutput(runtime, opts, output); return; } runtime.log(output); }; if (!deliver) for (const payload of deliveryPayloads) logPayload(payload); if (deliver && deliveryChannel && !isInternalMessageChannel(deliveryChannel)) { if (deliveryTarget) await deliverOutboundPayloads({ cfg, channel: deliveryChannel, to: deliveryTarget, accountId: resolvedAccountId, payloads: deliveryPayloads, replyToId: resolvedReplyToId ?? null, threadId: resolvedThreadTarget ?? null, bestEffort: bestEffortDeliver, onError: (err) => logDeliveryError(err), onPayload: logPayload, deps: createOutboundSendDeps(deps) }); } return { payloads: normalizedPayloads, meta: result.meta }; } //#endregion //#region src/commands/agent/run-context.ts function resolveAgentRunContext(opts) { const merged = opts.runContext ? { ...opts.runContext } : {}; const normalizedChannel = resolveMessageChannel(merged.messageChannel ?? opts.messageChannel, opts.replyChannel ?? opts.channel); if (normalizedChannel) merged.messageChannel = normalizedChannel; const normalizedAccountId = normalizeAccountId(merged.accountId ?? opts.accountId); if (normalizedAccountId) merged.accountId = normalizedAccountId; const groupId = (merged.groupId ?? opts.groupId)?.toString().trim(); if (groupId) merged.groupId = groupId; const groupChannel = (merged.groupChannel ?? opts.groupChannel)?.toString().trim(); if (groupChannel) merged.groupChannel = groupChannel; const groupSpace = (merged.groupSpace ?? opts.groupSpace)?.toString().trim(); if (groupSpace) merged.groupSpace = groupSpace; if (merged.currentThreadTs == null && opts.threadId != null && opts.threadId !== "" && opts.threadId !== null) merged.currentThreadTs = String(opts.threadId); if (!merged.currentChannelId && opts.to) { const trimmedTo = opts.to.trim(); if (trimmedTo) merged.currentChannelId = trimmedTo; } return merged; } //#endregion //#region src/commands/agent/session-store.ts async function updateSessionStoreAfterAgentRun(params) { const { cfg, sessionId, sessionKey, storePath, sessionStore, defaultProvider, defaultModel, fallbackProvider, fallbackModel, result } = params; const usage = result.meta.agentMeta?.usage; const modelUsed = result.meta.agentMeta?.model ?? fallbackModel ?? defaultModel; const providerUsed = result.meta.agentMeta?.provider ?? fallbackProvider ?? defaultProvider; const contextTokens = params.contextTokensOverride ?? lookupContextTokens(modelUsed) ?? DEFAULT_CONTEXT_TOKENS; const next = { ...sessionStore[sessionKey] ?? { sessionId, updatedAt: Date.now() }, sessionId, updatedAt: Date.now(), modelProvider: providerUsed, model: modelUsed, contextTokens }; if (isCliProvider(providerUsed, cfg)) { const cliSessionId = result.meta.agentMeta?.sessionId?.trim(); if (cliSessionId) setCliSessionId(next, providerUsed, cliSessionId); } next.abortedLastRun = result.meta.aborted ?? false; if (hasNonzeroUsage(usage)) { const input = usage.input ?? 0; const output = usage.output ?? 0; const promptTokens = input + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0); next.inputTokens = input; next.outputTokens = output; next.totalTokens = promptTokens > 0 ? promptTokens : usage.total ?? input; } sessionStore[sessionKey] = next; await updateSessionStore(storePath, (store) => { store[sessionKey] = next; }); } //#endregion //#region src/commands/agent/session.ts function resolveSessionKeyForRequest(opts) { const sessionCfg = opts.cfg.session; const scope = sessionCfg?.scope ?? "per-sender"; const mainKey = normalizeMainKey(sessionCfg?.mainKey); const explicitSessionKey = opts.sessionKey?.trim() || resolveExplicitAgentSessionKey({ cfg: opts.cfg, agentId: opts.agentId }); const storeAgentId = resolveAgentIdFromSessionKey(explicitSessionKey); const storePath = resolveStorePath(sessionCfg?.store, { agentId: storeAgentId }); const sessionStore = loadSessionStore(storePath); const ctx = opts.to?.trim() ? { From: opts.to } : void 0; let sessionKey = explicitSessionKey ?? (ctx ? resolveSessionKey(scope, ctx, mainKey) : void 0); if (!explicitSessionKey && opts.sessionId && (!sessionKey || sessionStore[sessionKey]?.sessionId !== opts.sessionId)) { const foundKey = Object.keys(sessionStore).find((key) => sessionStore[key]?.sessionId === opts.sessionId); if (foundKey) sessionKey = foundKey; } return { sessionKey, sessionStore, storePath }; } function resolveSession(opts) { const sessionCfg = opts.cfg.session; const { sessionKey, sessionStore, storePath } = resolveSessionKeyForRequest({ cfg: opts.cfg, to: opts.to, sessionId: opts.sessionId, sessionKey: opts.sessionKey, agentId: opts.agentId }); const now = Date.now(); const sessionEntry = sessionKey ? sessionStore[sessionKey] : void 0; const resetPolicy = resolveSessionResetPolicy({ sessionCfg, resetType: resolveSessionResetType({ sessionKey }), resetOverride: resolveChannelResetConfig({ sessionCfg, channel: sessionEntry?.lastChannel ?? sessionEntry?.channel }) }); const fresh = sessionEntry ? evaluateSessionFreshness({ updatedAt: sessionEntry.updatedAt, now, policy: resetPolicy }).fresh : false; return { sessionId: opts.sessionId?.trim() || (fresh ? sessionEntry?.sessionId : void 0) || crypto.randomUUID(), sessionKey, sessionEntry, sessionStore, storePath, isNewSession: !fresh && !opts.sessionId, persistedThinking: fresh && sessionEntry?.thinkingLevel ? normalizeThinkLevel(sessionEntry.thinkingLevel) : void 0, persistedVerbose: fresh && sessionEntry?.verboseLevel ? normalizeVerboseLevel(sessionEntry.verboseLevel) : void 0 }; } //#endregion //#region src/commands/agent.ts async function agentCommand(opts, runtime = defaultRuntime, deps = createDefaultDeps()) { const body = (opts.message ?? "").trim(); if (!body) throw new Error("Message (--message) is required"); if (!opts.to && !opts.sessionId && !opts.sessionKey && !opts.agentId) throw new Error("Pass --to <E.164>, --session-id, or --agent to choose a session"); const cfg = loadConfig(); const agentIdOverrideRaw = opts.agentId?.trim(); const agentIdOverride = agentIdOverrideRaw ? normalizeAgentId(agentIdOverrideRaw) : void 0; if (agentIdOverride) { if (!listAgentIds(cfg).includes(agentIdOverride)) throw new Error(`Unknown agent id "${agentIdOverrideRaw}". Use "${formatCliCommand("openclaw agents list")}" to see configured agents.`); } if (agentIdOverride && opts.sessionKey) { const sessionAgentId = resolveAgentIdFromSessionKey(opts.sessionKey); if (sessionAgentId !== agentIdOverride) throw new Error(`Agent id "${agentIdOverrideRaw}" does not match session key agent "${sessionAgentId}".`); } const agentCfg = cfg.agents?.defaults; const sessionAgentId = agentIdOverride ?? resolveAgentIdFromSessionKey(opts.sessionKey?.trim()); const workspaceDirRaw = resolveAgentWorkspaceDir(cfg, sessionAgentId); const agentDir = resolveAgentDir(cfg, sessionAgentId); const workspaceDir = (await ensureAgentWorkspace({ dir: workspaceDirRaw, ensureBootstrapFiles: !agentCfg?.skipBootstrap })).dir; const configuredModel = resolveConfiguredModelRef({ cfg, defaultProvider: DEFAULT_PROVIDER, defaultModel: DEFAULT_MODEL }); const thinkingLevelsHint = formatThinkingLevels(configuredModel.provider, configuredModel.model); const thinkOverride = normalizeThinkLevel(opts.thinking); const thinkOnce = normalizeThinkLevel(opts.thinkingOnce); if (opts.thinking && !thinkOverride) throw new Error(`Invalid thinking level. Use one of: ${thinkingLevelsHint}.`); if (opts.thinkingOnce && !thinkOnce) throw new Error(`Invalid one-shot thinking level. Use one of: ${thinkingLevelsHint}.`); const verboseOverride = normalizeVerboseLevel(opts.verbose); if (opts.verbose && !verboseOverride) throw new Error("Invalid verbose level. Use \"on\", \"full\", or \"off\"."); const timeoutSecondsRaw = opts.timeout !== void 0 ? Number.parseInt(String(opts.timeout), 10) : void 0; if (timeoutSecondsRaw !== void 0 && (Number.isNaN(timeoutSecondsRaw) || timeoutSecondsRaw <= 0)) throw new Error("--timeout must be a positive integer (seconds)"); const timeoutMs = resolveAgentTimeoutMs({ cfg, overrideSeconds: timeoutSecondsRaw }); const { sessionId, sessionKey, sessionEntry: resolvedSessionEntry, sessionStore, storePath, isNewSession, persistedThinking, persistedVerbose } = resolveSession({ cfg, to: opts.to, sessionId: opts.sessionId, sessionKey: opts.sessionKey, agentId: agentIdOverride }); let sessionEntry = resolvedSessionEntry; const runId = opts.runId?.trim() || sessionId; try { if (opts.deliver === true) { if (resolveSendPolicy({ cfg, entry: sessionEntry, sessionKey, channel: sessionEntry?.channel, chatType: sessionEntry?.chatType }) === "deny") throw new Error("send blocked by session policy"); } let resolvedThinkLevel = thinkOnce ?? thinkOverride ?? persistedThinking ?? agentCfg?.thinkingDefault; const resolvedVerboseLevel = verboseOverride ?? persistedVerbose ?? agentCfg?.verboseDefault; if (sessionKey) registerAgentRunContext(runId, { sessionKey, verboseLevel: resolvedVerboseLevel }); const needsSkillsSnapshot = isNewSession || !sessionEntry?.skillsSnapshot; const skillsSnapshotVersion = getSkillsSnapshotVersion(workspaceDir); const skillFilter = resolveAgentSkillsFilter(cfg, sessionAgentId); const skillsSnapshot = needsSkillsSnapshot ? buildWorkspaceSkillSnapshot(workspaceDir, { config: cfg, eligibility: { remote: getRemoteSkillEligibility() }, snapshotVersion: skillsSnapshotVersion, skillFilter }) : sessionEntry?.skillsSnapshot; if (skillsSnapshot && sessionStore && sessionKey && needsSkillsSnapshot) { const next = { ...sessionEntry ?? { sessionId, updatedAt: Date.now() }, sessionId, updatedAt: Date.now(), skillsSnapshot }; sessionStore[sessionKey] = next; await updateSessionStore(storePath, (store) => { store[sessionKey] = next; }); sessionEntry = next; } if (sessionStore && sessionKey) { const next = { ...sessionStore[sessionKey] ?? sessionEntry ?? { sessionId, updatedAt: Date.now() }, sessionId, updatedAt: Date.now() }; if (thinkOverride) if (thinkOverride === "off") delete next.thinkingLevel; else next.thinkingLevel = thinkOverride; applyVerboseOverride(next, verboseOverride); sessionStore[sessionKey] = next; await updateSessionStore(storePath, (store) => { store[sessionKey] = next; }); } const agentModelPrimary = resolveAgentModelPrimary(cfg, sessionAgentId); const { provider: defaultProvider, model: defaultModel } = resolveConfiguredModelRef({ cfg: agentModelPrimary ? { ...cfg, agents: { ...cfg.agents, defaults: { ...cfg.agents?.defaults, model: { ...typeof cfg.agents?.defaults?.model === "object" ? cfg.agents.defaults.model : void 0, primary: agentModelPrimary } } } } : cfg, defaultProvider: DEFAULT_PROVIDER, defaultModel: DEFAULT_MODEL }); let provider = defaultProvider; let model = defaultModel; const hasAllowlist = agentCfg?.models && Object.keys(agentCfg.models).length > 0; const hasStoredOverride = Boolean(sessionEntry?.modelOverride || sessionEntry?.providerOverride); const needsModelCatalog = hasAllowlist || hasStoredOverride; let allowedModelKeys = /* @__PURE__ */ new Set(); let allowedModelCatalog = []; let modelCatalog = null; if (needsModelCatalog) { modelCatalog = await loadModelCatalog({ config: cfg }); const allowed = buildAllowedModelSet({ cfg, catalog: modelCatalog, defaultProvider, defaultModel }); allowedModelKeys = allowed.allowedKeys; allowedModelCatalog = allowed.allowedCatalog; } if (sessionEntry && sessionStore && sessionKey && hasStoredOverride) { const entry = sessionEntry; const overrideProvider = sessionEntry.providerOverride?.trim() || defaultProvider; const overrideModel = sessionEntry.modelOverride?.trim(); if (overrideModel) { const key = modelKey(overrideProvider, overrideModel); if (!isCliProvider(overrideProvider, cfg) && allowedModelKeys.size > 0 && !allowedModelKeys.has(key)) { const { updated } = applyModelOverrideToSessionEntry({ entry, selection: { provider: defaultProvider, model: defaultModel, isDefault: true } }); if (updated) { sessionStore[sessionKey] = entry; await updateSessionStore(storePath, (store) => { store[sessionKey] = entry; }); } } } } const storedProviderOverride = sessionEntry?.providerOverride?.trim(); const storedModelOverride = sessionEntry?.modelOverride?.trim(); if (storedModelOverride) { const candidateProvider = storedProviderOverride || defaultProvider; const key = modelKey(candidateProvider, storedModelOverride); if (isCliProvider(candidateProvider, cfg) || allowedModelKeys.size === 0 || allowedModelKeys.has(key)) { provider = candidateProvider; model = storedModelOverride; } } if (sessionEntry) { const authProfileId = sessionEntry.authProfileOverride; if (authProfileId) { const entry = sessionEntry; const profile = ensureAuthProfileStore().profiles[authProfileId]; if (!profile || profile.provider !== provider) { if (sessionStore && sessionKey) await clearSessionAuthProfileOverride({ sessionEntry: entry, sessionStore, sessionKey, storePath }); } } } if (!resolvedThinkLevel) { let catalogForThinking = modelCatalog ?? allowedModelCatalog; if (!catalogForThinking || catalogForThinking.length === 0) { modelCatalog = await loadModelCatalog({ config: cfg }); catalogForThinking = modelCatalog; } resolvedThinkLevel = resolveThinkingDefault({ cfg, provider, model, catalog: catalogForThinking }); } if (resolvedThinkLevel === "xhigh" && !supportsXHighThinking(provider, model)) { if (Boolean(thinkOnce || thinkOverride)) throw new Error(`Thinking level "xhigh" is only supported for ${formatXHighModelHint()}.`); resolvedThinkLevel = "high"; if (sessionEntry && sessionStore && sessionKey && sessionEntry.thinkingLevel === "xhigh") { const entry = sessionEntry; entry.thinkingLevel = "high"; entry.updatedAt = Date.now(); sessionStore[sessionKey] = entry; await updateSessionStore(storePath, (store) => { store[sessionKey] = entry; }); } } const sessionFile = resolveSessionFilePath(sessionId, sessionEntry, { agentId: sessionAgentId }); const startedAt = Date.now(); let lifecycleEnded = false; let result; let fallbackProvider = provider; let fallbackModel = model; try { const runContext = resolveAgentRunContext(opts); const messageChannel = resolveMessageChannel(runContext.messageChannel, opts.replyChannel ?? opts.channel); const spawnedBy = opts.spawnedBy ?? sessionEntry?.spawnedBy; const fallbackResult = await runWithModelFallback({ cfg, provider, model, agentDir, fallbacksOverride: resolveAgentModelFallbacksOverride(cfg, sessionAgentId), run: (providerOverride, modelOverride) => { if (isCliProvider(providerOverride, cfg)) { const cliSessionId = getCliSessionId(sessionEntry, providerOverride); return runCliAgent({ sessionId, sessionKey, sessionFile, workspaceDir, config: cfg, prompt: body, provider: providerOverride, model: modelOverride, thinkLevel: resolvedThinkLevel, timeoutMs, runId, extraSystemPrompt: opts.extraSystemPrompt, cliSessionId, images: opts.images, streamParams: opts.streamParams }); } const authProfileId = providerOverride === provider ? sessionEntry?.authProfileOverride : void 0; return runEmbeddedPiAgent({ sessionId, sessionKey, messageChannel, agentAccountId: runContext.accountId, messageTo: opts.replyTo ?? opts.to, messageThreadId: opts.threadId, groupId: runContext.groupId, groupChannel: runContext.groupChannel, groupSpace: runContext.groupSpace, spawnedBy, currentChannelId: runContext.currentChannelId, currentThreadTs: runContext.currentThreadTs, replyToMode: runContext.replyToMode, hasRepliedRef: runContext.hasRepliedRef, senderIsOwner: true, sessionFile, workspaceDir, config: cfg, skillsSnapshot, prompt: body, images: opts.images, clientTools: opts.clientTools, provider: providerOverride, model: modelOverride, authProfileId, authProfileIdSource: authProfileId ? sessionEntry?.authProfileOverrideSource : void 0, thinkLevel: resolvedThinkLevel, verboseLevel: resolvedVerboseLevel, timeoutMs, runId, lane: opts.lane, abortSignal: opts.abortSignal, extraSystemPrompt: opts.extraSystemPrompt, streamParams: opts.streamParams, agentDir, onAgentEvent: (evt) => { if (evt.stream === "lifecycle" && typeof evt.data?.phase === "string" && (evt.data.phase === "end" || evt.data.phase === "error")) lifecycleEnded = true; } }); } }); result = fallbackResult.result; fallbackProvider = fallbackResult.provider; fallbackModel = fallbackResult.model; if (!lifecycleEnded) emitAgentEvent({ runId, stream: "lifecycle", data: { phase: "end", startedAt, endedAt: Date.now(), aborted: result.meta.aborted ?? false } }); } catch (err) { if (!lifecycleEnded) emitAgentEvent({ runId, stream: "lifecycle", data: { phase: "error", startedAt, endedAt: Date.now(), error: String(err) } }); throw err; } if (sessionStore && sessionKey) await updateSessionStoreAfterAgentRun({ cfg, contextTokensOverride: agentCfg?.contextTokens, sessionId, sessionKey, storePath, sessionStore, defaultProvider: provider, defaultModel: model, fallbackProvider, fallbackModel, result }); const payloads = result.payloads ?? []; return await deliverAgentCommandResult({ cfg, deps, runtime, opts, sessionEntry, result, payloads }); } finally { clearAgentRunContext(runId); } } //#endregion export { createOutboundSendDeps as a, resolveAgentOutboundTarget as i, resolveSessionKeyForRequest as n, resolveAgentDeliveryPlan as r, agentCommand as t };