UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

1,042 lines (1,041 loc) • 43.1 kB
import { o as normalizeLowercaseStringOrEmpty } from "./string-coerce-1VuiLaaR.mjs"; import { p as isDiagnosticsEnabled, s as emitTrustedDiagnosticEvent } from "./diagnostic-events-DT4ejwoa.mjs"; import { o as resolveSessionFilePathCore, s as resolveSessionFilePathOptions } from "./paths-Bonddxfk.mjs"; import { d as resolveAgentWorkspaceDir } from "./agent-scope-config-BEk6-ThS.mjs"; import { t as _usingCtx } from "./usingCtx-BhiZp_rS.mjs"; import { r as resolveCollapsedSessionAuthPinSource } from "./auth-profile-override-provenance-Jztnei-C.mjs"; import "./agent-scope-bJOokm5r.mjs"; import { n as withPluginRuntimeGenerationScope } from "./generation-scope-DnORwJwz.mjs"; import { i as isOpenAIProvider } from "./openai-routing-DNTQ-lG2.mjs"; import { t as applyPreparedRuntimeAuthToModel } from "./provider-request-config-zrhxha-3.mjs"; import { p as prepareSystemAgentRunAdmission } from "./admitted-run-context-D0yzk2io.mjs"; import { A as requireApiKey } from "./model-auth-provider-config-Cwa51-db.mjs"; import { i as ensureAuthProfileStoreWithoutExternalProfiles, n as ensureAuthProfileStore } from "./store-runtime-DPTEKEq5.mjs"; import { a as reconcileAuthProfileQuotaBlocks } from "./usage-CJIu7a1i.mjs"; import { a as resolveAgentHarnessOwnerPluginId } from "./registry-DLDDp3Ju.mjs"; import { F as prepareProviderRuntimeAuth } from "./provider-runtime-DyOfn8ff.mjs"; import { i as unwrapSecretSentinelsForProviderEgress } from "./provider-secret-egress-CRmcEjuL.mjs"; import { n as getModelRegistryRuntime } from "./model-registry-Gn0C-qAI.mjs"; import { a as resolveAgentHarnessPreparedRouteSupport, i as resolveAgentHarnessPreparedAuthSupport } from "./support-Dz8wOBK3.mjs"; import { n as resolveAvailableAgentHarnessPolicy } from "./availability-LCoXDLq_.mjs"; import { c as resolveCliRuntimeExecutionProvider, i as isCliRuntimeAliasForProvider } from "./model-runtime-aliases-B9nh73GM.mjs"; import { i as resolveSessionRuntimeOverrideForProvider } from "./session-runtime-compat-ro9WhJ4w.mjs"; import { i as acquirePublishedPreparedModelRuntime, w as preparedModelRuntimeConfigsMatch } from "./prepared-model-runtime-l04jZgyc.mjs"; import { r as applySecretRefHeaderSentinels } from "./model-auth-CGgrsVoA.mjs"; import { t as collectTextContentBlocks } from "./content-blocks-DDtxq3Ff.mjs"; import { r as resolveImageSanitizationLimits } from "./image-sanitization-DhkMOJXD.mjs"; import { n as sanitizeImageBlocks } from "./tool-images-Bl1ujVGo.mjs"; import { _ as parseSqliteSessionFileMarker } from "./session-accessor.sqlite-scope-BkgwxatT.mjs"; import { l as scanSessionTranscriptTree } from "./transcript-tree-CaYJo8Xs.mjs"; import { a as hasBillableUsage, d as toDiagnosticUsage, u as normalizeUsage } from "./usage-B5bs3hAI.mjs"; import { a as isModelSelectionLocked } from "./model-overrides-DqarTyJt.mjs"; import { t as loadTranscriptEvents } from "./session-transcript-events-IicO6BRV.mjs"; import { a as migrateSessionEntries, t as buildSessionContext } from "./session-manager-codec-NRzy0b5d.mjs"; import { o as loadSessionEntry } from "./session-accessor.sqlite-entry-DLa7_9ui.mjs"; import "./session-accessor-C3ljJ974.mjs"; import { t as listSessionEntriesCore } from "./session-accessor.entry-xtqpYxWo.mjs"; import { n as resolveAgentTimeoutMs } from "./timeout-BBs3o51w.mjs"; import { t as diagnosticLogger } from "./diagnostic-runtime-Dl4K1Cyg.mjs"; import "./diagnostic-jtgHaFVw.mjs"; import { s as getActiveEmbeddedRunSnapshot } from "./runs-Qhulcs4s.mjs"; import { f as buildReplyUsageState, p as recordReplyUsageState } from "./deliver-prepare-BBdlVLgv.mjs"; import "./sessions-CRUJeu5H.mjs"; import { t as resolvePreferredSessionKeyForSessionIdMatches } from "./session-id-resolution-BnecGj-H.mjs"; import { r as resolveSessionModelRef } from "./session-model-ref-CS-wdkCn.mjs"; import { t as runWithAsyncWorkResources } from "./async-work-resources-BvYhIOBb.mjs"; import { n as resolveModelAsync } from "./model-Co5AQ0pq.mjs"; import { t as materializePreparedRuntimeModel } from "./materialize-model-bSlhBdBN.mjs"; import { r as prepareAgentRuntimeAuth } from "./prepare-auth-B7499kxH.mjs"; import { t as protectPreparedProviderRuntimeAuth } from "./provider-runtime-auth-protection-ZTqTlJ4N.mjs"; import { a as resolvePreparedRuntimeAuthAttempts, o as resolvePreparedRuntimeModelAuth, s as scopeAuthProfileStoreToPreparedPlan } from "./simple-completion-runtime-C71S2ucI.mjs"; import { a as resolvePluginHarnessPolicyToolsAllow } from "./execution-environment-6hDKMkOF.mjs"; import { t as createAgentHarnessHostCapabilities } from "./host-capability-DhglHzwG.mjs"; import { n as resolveSandboxContext } from "./context-BwDVZiBb.mjs"; import { r as prepareSessionPlacementSandbox } from "./session-placement-admission-BDxHsbjT.mjs"; import "./session-manager-BpvVpTVC.mjs"; import { t as streamWithPayloadPatch } from "./stream-payload-utils-BoUixBWV.mjs"; import { o as resolveEmbeddedAgentStream } from "./session-boundary-prompt-cache-key-SDHAWKdr.mjs"; import { t as registerProviderStreamForModel } from "./provider-stream-BMdyPkWO.mjs"; import { t as EmbeddedBlockChunker } from "./embedded-agent-block-chunker-CxsctPo2.mjs"; import { i as selectAgentHarnessForPreparedModelProviders, r as selectAgentHarness } from "./selection-BmmsVs_p.mjs"; import { t as ensureSelectedAgentHarnessPlugin } from "./runtime-plugin-h9eBG_Gs.mjs"; import { t as resolveExternalCliAuthOverlayScopeFromSelection } from "./external-cli-auth-selection-C1ZVPG_s.mjs"; import { n as resolveSessionAuthSelection } from "./session-override-ERbkZ0dT.mjs"; import { n as buildBtwQuestionPrompt, r as buildBtwSystemPrompt, t as buildBtwCliPrompt } from "./btw-prompts-BvS7REMb.mjs"; import { t as executePreparedCliRun } from "./execute.runtime-DJQsoxvu.mjs"; import { t as prepareCliRunContext } from "./prepare.runtime-Bo3vSGw_.mjs"; import { randomUUID } from "node:crypto"; //#region src/agents/btw-transcript.ts /** * Reads prior session transcript context for `/btw` side-question handoffs. */ function normalizeBtwContentBlocks(content) { if (Array.isArray(content)) return content; if (content && typeof content === "object") return [content]; } /** Resolves the persisted transcript file for a BTW session handoff. */ function resolveBtwSessionTranscriptPath(params) { try { const agentId = params.sessionKey?.split(":")[1]; const pathOpts = resolveSessionFilePathOptions({ agentId, storePath: params.storePath }); return resolveSessionFilePathCore(params.sessionId, params.sessionEntry, pathOpts); } catch (error) { diagnosticLogger.debug(`resolveSessionTranscriptPath failed: sessionId=${params.sessionId} err=${String(error)}`); return; } } function readSessionEntryId(entry) { const id = entry.id; return typeof id === "string" && id.trim().length > 0 ? id : void 0; } function buildSessionBranchEntries(tree, leafId) { if (leafId === null) return []; if (!leafId) return; const branch = []; const seen = /* @__PURE__ */ new Set(); let currentId = leafId; while (currentId) { if (seen.has(currentId)) return; seen.add(currentId); const node = tree.byId.get(currentId); if (!node) return; if (node.entry.type !== "leaf") branch.push(node.entry.parentId === node.parentId ? node.entry : { ...node.entry, parentId: node.parentId }); currentId = node.parentId ?? void 0; } return branch.toReversed(); } function isTrailingUserMessage(entry) { return entry?.type === "message" && entry.message?.role === "user"; } /** * Reads prior messages for BTW continuation. * * When a transcript has fork links, this returns the selected snapshot branch * instead of the full file so a resumed agent does not inherit sibling-branch * messages. */ async function readBtwTranscriptMessages(params) { try { const marker = parseSqliteSessionFileMarker(params.sessionFile); const completeTarget = Boolean(params.agentId?.trim() && params.sessionId.trim() && params.sessionKey?.trim() && params.storePath?.trim()); const agentId = completeTarget ? params.agentId : params.agentId ?? marker?.agentId; const sessionId = completeTarget ? params.sessionId : marker?.sessionId ?? params.sessionId; const storePath = completeTarget ? params.storePath : params.storePath ?? marker?.storePath; const markerMatches = marker && !completeTarget ? listSessionEntriesCore({ agentId: marker.agentId, storePath: marker.storePath }).filter(({ entry }) => entry.sessionId === marker.sessionId) : []; const suppliedEntry = marker && params.sessionKey && !completeTarget ? loadSessionEntry({ agentId: marker.agentId, sessionKey: params.sessionKey, storePath: marker.storePath }) : void 0; if (marker && !completeTarget && params.sessionKey && (suppliedEntry && suppliedEntry.sessionId !== marker.sessionId || !suppliedEntry && markerMatches.length > 0)) return []; const sessionKey = completeTarget ? params.sessionKey : marker ? suppliedEntry?.sessionId === marker.sessionId ? params.sessionKey : resolvePreferredSessionKeyForSessionIdMatches(markerMatches.map(({ sessionKey: mappedKey, entry }) => [mappedKey, entry]), marker.sessionId) ?? (markerMatches.length === 0 ? params.sessionKey : void 0) : params.sessionKey; if (!sessionKey || !storePath) return []; const entries = await loadTranscriptEvents({ agentId, sessionId, sessionKey, storePath }); migrateSessionEntries(entries); const sessionEntries = entries.filter((entry) => entry.type !== "session"); const tree = scanSessionTranscriptTree(sessionEntries); if (!tree.hasLeafUpdate) return buildSessionContext(sessionEntries).messages; const hasSnapshotLeaf = params.snapshotLeafId !== void 0; let branchEntries = hasSnapshotLeaf ? buildSessionBranchEntries(tree, params.snapshotLeafId) : void 0; if (hasSnapshotLeaf && branchEntries === void 0) diagnosticLogger.debug(`btw snapshot leaf unavailable: sessionId=${sessionId} leaf=${params.snapshotLeafId}`); branchEntries ??= buildSessionBranchEntries(tree, tree.leafId); if (!hasSnapshotLeaf && isTrailingUserMessage(branchEntries?.at(-1))) { const trailingId = readSessionEntryId(branchEntries.at(-1)); const parentId = trailingId ? tree.byId.get(trailingId)?.parentId : null; branchEntries = parentId ? buildSessionBranchEntries(tree, parentId) ?? [] : []; } const sessionContext = buildSessionContext(branchEntries ?? sessionEntries); return Array.isArray(sessionContext.messages) ? sessionContext.messages : []; } catch { return []; } } //#endregion //#region src/agents/btw-runtime.ts /** Runtime preparation for /btw side questions. */ async function prepareBtwRuntimeAuth(params) { const { model } = params; const storeOptions = { profileId: params.authProfileId, allowKeychainPrompt: false }; const loadStore = (externalCliProviderIds) => externalCliProviderIds ? ensureAuthProfileStore(params.agentDir, { ...storeOptions, externalCliProviderIds }) : ensureAuthProfileStoreWithoutExternalProfiles(params.agentDir, storeOptions); let store; let ignoreAutoPreferredProfile = false; if (isOpenAIProvider(model.provider)) store = loadStore(["openai"]); else { const selection = { provider: model.provider, cfg: params.cfg, agentId: params.agentId, modelId: model.id, workspaceDir: params.workspaceDir, userPinnedAuthProfileId: params.authProfileIdSource === "user" ? params.authProfileId : void 0 }; let externalCliAuthScope = resolveExternalCliAuthOverlayScopeFromSelection(selection); store = loadStore(externalCliAuthScope.providerIds); if (!externalCliAuthScope.providerIds) { externalCliAuthScope = resolveExternalCliAuthOverlayScopeFromSelection({ ...selection, store }); if (externalCliAuthScope.providerIds) store = loadStore(externalCliAuthScope.providerIds); } ignoreAutoPreferredProfile = externalCliAuthScope.ignoreAutoPreferredProfile; } const authParams = { provider: model.provider, modelId: model.id, modelApi: model.api, modelBaseUrl: model.baseUrl, config: params.cfg, agentId: params.agentId, agentDir: params.agentDir, env: process.env, workspaceDir: params.workspaceDir, authProfileStore: store, sessionAuthProfileId: ignoreAutoPreferredProfile && params.authProfileIdSource !== "user" ? void 0 : params.authProfileId, sessionAuthProfileSource: params.authProfileIdSource, harnessId: params.harnessId, harnessRuntime: params.harnessId, harnessAuthBootstrap: params.harnessAuthBootstrap }; await reconcileAuthProfileQuotaBlocks(authParams); return { store, preparation: prepareAgentRuntimeAuth(authParams) }; } function isBtwTextBlock(block) { if (!block || typeof block !== "object") return false; const record = block; return normalizeLowercaseStringOrEmpty(record.type) === "text" && typeof record.text === "string"; } function isBtwImageBlock(block) { if (!block || typeof block !== "object") return false; const record = block; return normalizeLowercaseStringOrEmpty(record.type) === "image" && typeof record.data === "string" && typeof record.mimeType === "string"; } async function sanitizeBtwUserMessage(params) { if (typeof params.message.content === "string") return params.message; const blocks = normalizeBtwContentBlocks(params.message.content); if (!blocks) return; const content = []; for (const block of blocks) { if (isBtwTextBlock(block)) { content.push({ type: "text", text: block.text }); continue; } if (!isBtwImageBlock(block)) continue; const { images } = await sanitizeImageBlocks([block], "btw:context", params.imageLimits); const image = images[0]; if (image) content.push(image); } if (content.length === 0) return; return { ...params.message, content }; } function sanitizeBtwAssistantMessage(message) { const rawContent = message.content; if (typeof rawContent === "string") { const trimmed = rawContent.trim(); return trimmed.length > 0 ? { ...message, content: [{ type: "text", text: trimmed }] } : void 0; } const blocks = normalizeBtwContentBlocks(rawContent); if (!blocks) return; const content = blocks.flatMap((block) => isBtwTextBlock(block) ? [{ type: "text", text: block.text }] : []); if (content.length === 0) return; return { ...message, content }; } async function toSimpleContextMessages(params) { const contextMessages = []; for (const message of params.messages) { if (!message || typeof message !== "object") continue; const role = message.role; if (role !== "user" && role !== "assistant") continue; let sanitizedMessage; if (role === "user") sanitizedMessage = await sanitizeBtwUserMessage({ message, imageLimits: params.imageLimits }); else sanitizedMessage = sanitizeBtwAssistantMessage(message); if (sanitizedMessage) contextMessages.push(sanitizedMessage); } return contextMessages; } async function materializeBtwRuntimeModel(params) { const { agentDir, config: cfg, workspaceDir } = params.preparedModelRuntime; return await materializePreparedRuntimeModel({ plan: params.plan, provider: params.provider, modelId: params.modelId, config: cfg, workspaceDir, metadataSnapshot: params.preparedModelRuntime.metadataSnapshot, model: params.model, ...params.forceResolve !== void 0 ? { forceResolve: params.forceResolve } : {}, resolveModel: ({ config, authProfileId, authProfileMode }) => resolveModelAsync(params.provider, params.modelId, agentDir, config, { abortSignal: params.abortSignal, modelIdSource: "selected", authStorage: params.authStorage, modelRegistry: params.modelRegistry, skipAgentDiscovery: true, allowBundledStaticCatalogFallback: true, preparedModelRuntime: params.preparedModelRuntime, workspaceDir, authProfileId, authProfileMode }) }) ?? params.model; } async function resolveBtwPreparedRuntimeAuth(params) { const { agentDir, config: cfg, workspaceDir } = params.preparedModelRuntime; return resolvePreparedRuntimeAuthAttempts({ attempts: params.preparation.attempts, store: params.authProfileStore, modelId: params.modelId, model: params.model, materializeModel: ({ plan, model, forceResolve }) => materializeBtwRuntimeModel({ ...params, plan, model, forceResolve }), resolveAuth: async ({ attempt, model }) => await resolvePreparedRuntimeModelAuth({ plan: attempt.plan, model, cfg, store: params.authProfileStore, agentDir, workspaceDir, ...attempt.allowAuthProfileFallback !== void 0 ? { allowAuthProfileFallback: attempt.allowAuthProfileFallback } : {}, secretSentinels: true }), errorMessage: "BTW prepared auth attempts could not be resolved." }); } async function resolveRuntimeModel(params) { const preparedModelRuntime = params.preparedModelRuntime; const { config: cfg, agentDir, workspaceDir } = preparedModelRuntime; const { authStorage, modelRegistry } = preparedModelRuntime.createStores(); const resolution = await resolveModelAsync(params.provider, params.model, agentDir, cfg, { abortSignal: params.abortSignal, authStorage, modelRegistry, preparedModelRuntime, workspaceDir, skipAgentDiscovery: true, allowBundledStaticCatalogFallback: true, preferBundledStaticCatalogTransport: Boolean(params.harnessId && params.harnessId !== "openclaw") }); let model = resolution.model; if (!model) throw new Error(resolution.error ?? `Unknown model: ${params.provider}/${params.model}`); const runtimeProvider = model.provider; const runtimeModelId = model.id; const authSelection = await resolveSessionAuthSelection({ cfg, provider: runtimeProvider, modelId: runtimeModelId, agentId: params.agentId, harnessRuntime: params.harnessId, agentDir, sessionEntry: params.sessionEntry, sessionStore: params.sessionStore, sessionKey: params.sessionKey, storePath: params.storePath, isNewSession: params.isNewSession }); const { store: authProfileStore, preparation: runtimeAuthPreparation } = await prepareBtwRuntimeAuth({ cfg, model, agentId: params.agentId, agentDir, workspaceDir, authProfileId: authSelection?.profileId, authProfileIdSource: authSelection?.source, harnessId: params.harnessId, harnessAuthBootstrap: params.harnessAuthBootstrap }); model = await materializeBtwRuntimeModel({ abortSignal: params.abortSignal, provider: runtimeProvider, modelId: runtimeModelId, preparedModelRuntime, authStorage, modelRegistry, plan: runtimeAuthPreparation.plan, model }); return { model, authProfileId: runtimeAuthPreparation.plan.forwardedAuthProfileId, authProfileIdSource: runtimeAuthPreparation.plan.forwardedAuthProfileSource, authProfileStore, runtimeAuthPreparation, authStorage, modelRegistry }; } function resolveBtwTranscript(params) { const sessionId = params.sessionEntry.sessionId?.trim(); if (!sessionId) throw new Error("No active session context."); const sessionFile = resolveBtwSessionTranscriptPath({ sessionId, sessionEntry: params.sessionEntry, sessionKey: params.sessionKey, storePath: params.storePath }); if (!sessionFile) throw new Error("No active session transcript."); return { sessionId, sessionFile }; } function createBtwRuntimeSelectionResolver(params) { let selection; return async () => { selection ??= await resolveRuntimeModel({ abortSignal: params.request.opts?.abortSignal, provider: params.request.provider, model: params.request.model, agentId: params.sessionAgentId, sessionEntry: params.request.sessionEntry, sessionStore: params.request.sessionStore, sessionKey: params.request.sessionKey, storePath: params.request.storePath, isNewSession: params.request.isNewSession, harnessId: params.harness.id, harnessAuthBootstrap: params.harness.authBootstrap, preparedModelRuntime: params.preparedModelRuntime }); return selection; }; } function createBtwUsageRecorder(params) { return (runtimeModel, usage) => { if (!hasBillableUsage(usage)) return; const usageState = buildReplyUsageState({ config: params.request.cfg, agentDir: params.request.agentDir, agentId: params.sessionAgentId, sessionId: params.sessionId, provider: runtimeModel.provider, model: runtimeModel.id, chatType: params.request.chatType, usage }); recordReplyUsageState(params.request.opts?.runId, usageState); if (isDiagnosticsEnabled(params.request.cfg)) emitTrustedDiagnosticEvent({ type: "model.usage", sessionKey: params.request.sessionKey, sessionId: params.sessionId, channel: params.request.messageChannel, agentId: params.sessionAgentId, provider: runtimeModel.provider, model: runtimeModel.id, usage: toDiagnosticUsage(usage), costUsd: usageState.turnUsd }); }; } async function runCliBtwSideQuestion(params) { const timeoutMs = resolveAgentTimeoutMs({ cfg: params.cfg, overrideSeconds: params.opts?.timeoutOverrideSeconds, overrideMs: params.opts?.timeoutOverrideMs }); const runId = params.authorityRunId; const preparedRunAdmission = prepareSystemAgentRunAdmission(params.cfg, runId, params.sessionAgentId, "btw.side-question"); let prepared; try { prepared = await prepareCliRunContext({ preparedRunAdmission, sessionId: params.sessionId, sessionKey: params.sessionKey, sessionEntry: params.sessionEntry, agentId: params.sessionAgentId, trigger: "user", sessionFile: params.sessionFile, workspaceDir: params.workspaceDir, config: params.cfg, prompt: buildBtwCliPrompt({ messages: params.messages, question: params.question, imageCount: params.imageCount, inFlightPrompt: params.inFlightPrompt }), extraSystemPrompt: buildBtwSystemPrompt(), executionMode: "side-question", provider: params.cliProvider, model: params.model, thinkLevel: params.resolvedThinkLevel, disableTools: true, timeoutMs, runTimeoutOverrideMs: timeoutMs, runId, authProfileId: params.authProfileId, abortSignal: params.opts?.abortSignal, messageChannel: params.messageChannel, messageProvider: params.messageProvider, currentChannelId: params.currentChannelId }); const text = (await executePreparedCliRun(prepared)).text.trim(); if (!text) throw new Error(`/btw side question via ${params.cliProvider} produced no answer.`); return { text }; } finally { try { await prepared?.preparedBackend.cleanup?.(); } finally { preparedRunAdmission.close(); } } } /** The visible answer may finish before cooperating provider and cleanup work settles. */ async function withBtwPreparedRuntime(input, run) { return await runWithAsyncWorkResources(async (onAcquired, captureWorkContext) => { const lease = await acquirePublishedPreparedModelRuntime(input); onAcquired({ release: () => lease[Symbol.asyncDispose]() }); return withPluginRuntimeGenerationScope(lease.snapshot, () => { captureWorkContext(); return run(lease.snapshot); }); }); } /** Answers a side question using sanitized session context and no tool execution. */ //#endregion //#region src/agents/btw.ts async function runBtwSideQuestion(paramsInput) { let params = { ...paramsInput, authorityRunId: paramsInput.authorityRunId ?? `btw-${randomUUID()}` }; const { sessionId, sessionFile } = resolveBtwTranscript(params); const requestedWorkspaceDir = resolveAgentWorkspaceDir(params.cfg, params.agentId); return await withBtwPreparedRuntime({ config: params.cfg, agentId: params.agentId, agentDir: params.agentDir, workspaceDir: requestedWorkspaceDir, ...params.allowGatewaySubagentBinding ? { allowGatewaySubagentBinding: true } : {} }, async (preparedModelRuntime) => { const sessionAgentId = preparedModelRuntime.agentId ?? params.agentId; const workspaceDir = preparedModelRuntime.workspaceDir ?? resolveAgentWorkspaceDir(preparedModelRuntime.config, sessionAgentId); const preparedModelRef = preparedModelRuntimeConfigsMatch(preparedModelRuntime.config, params.cfg) ? { provider: params.provider, model: params.model } : resolveSessionModelRef(preparedModelRuntime.config, params.sessionEntry, sessionAgentId); params = { ...params, cfg: preparedModelRuntime.config, agentDir: preparedModelRuntime.agentDir, provider: preparedModelRef.provider, model: preparedModelRef.model }; const preparedHarnesses = /* @__PURE__ */ new Map(); const prepareHarness = async (provider, modelId, modelProvider) => { const agentHarnessId = isModelSelectionLocked(params.sessionEntry) ? params.sessionEntry.agentHarnessId : void 0; const agentHarnessRuntimeOverride = agentHarnessId ? void 0 : resolveSessionRuntimeOverrideForProvider({ provider, entry: params.sessionEntry, cfg: params.cfg }); const key = [ `${provider}/${modelId}/${agentHarnessId ?? agentHarnessRuntimeOverride ?? "configured"}`, modelProvider?.api ?? "", modelProvider?.baseUrl ?? "", modelProvider?.requestTransportOverrides ?? "", modelProvider?.runtimePolicy?.compatibleIds.join(",") ?? "", modelProvider?.preparedAuth?.source ?? "", modelProvider?.preparedAuth?.mode ?? "", modelProvider?.preparedAuth?.requirement ?? "" ].join("\0"); const cached = preparedHarnesses.get(key); if (cached) return cached; await ensureSelectedAgentHarnessPlugin({ provider, modelId, config: params.cfg, agentId: sessionAgentId, sessionKey: params.sessionKey, workspaceDir, ...agentHarnessId ? { agentHarnessId } : {}, ...agentHarnessRuntimeOverride ? { agentHarnessRuntimeOverride } : {}, pluginRegistry: preparedModelRuntime.pluginRegistry }); const selectionParams = { provider, modelId, config: params.cfg, agentId: sessionAgentId, sessionKey: params.sessionKey, ...agentHarnessId ? { agentHarnessId } : {}, ...agentHarnessRuntimeOverride ? { agentHarnessRuntimeOverride } : {} }; const harness = modelProvider ? selectAgentHarnessForPreparedModelProviders({ ...selectionParams, modelProviders: [modelProvider] }) : selectAgentHarness(selectionParams); preparedHarnesses.set(key, harness); return harness; }; const harness = await prepareHarness(params.provider, params.model); const resolveRuntimeSelection = createBtwRuntimeSelectionResolver({ request: params, sessionAgentId, harness, preparedModelRuntime }); const recordBtwUsage = createBtwUsageRecorder({ request: params, sessionAgentId, sessionId }); let preparedOpenClawFallback; const runHarnessSideQuestion = async (selectedHarness, runtime, routeFinalized = false) => { try { var _usingCtx$1 = _usingCtx(); const toolsAllow = resolvePluginHarnessPolicyToolsAllow({ config: params.cfg, sessionId, sessionKey: params.sessionKey, sandboxSessionKey: params.sandboxSessionKey, agentId: sessionAgentId, provider: runtime.model.provider, modelId: runtime.model.id, messageProvider: params.messageProvider, messageChannel: params.messageChannel, spawnedBy: params.spawnedBy, groupId: params.groupId, groupChannel: params.groupChannel, groupSpace: params.groupSpace, agentAccountId: params.agentAccountId, senderId: params.senderId, senderName: params.senderName, senderUsername: params.senderUsername, senderE164: params.senderE164 }); const authPreparation = selectedHarness.id === harness.id ? void 0 : await prepareBtwRuntimeAuth({ cfg: params.cfg, model: runtime.model, agentId: sessionAgentId, agentDir: params.agentDir, workspaceDir, authProfileId: runtime.authProfileId, authProfileIdSource: runtime.authProfileIdSource, harnessId: selectedHarness.id, harnessAuthBootstrap: selectedHarness.authBootstrap }); const runtimeAuthPreparation = authPreparation?.preparation ?? runtime.runtimeAuthPreparation; const selectedAuthProfileStore = authPreparation?.store ?? runtime.authProfileStore; const implicitHarnessAuthPlan = selectedHarness.authBootstrap === "harness" && runtimeAuthPreparation.attempts.length === 1 && runtimeAuthPreparation.attempts[0]?.kind === "implicit" && runtimeAuthPreparation.attempts[0].plan.harnessAuthProvider ? runtimeAuthPreparation.attempts[0].plan : void 0; const resolvedAttempt = implicitHarnessAuthPlan ? { plan: implicitHarnessAuthPlan, model: runtime.model } : await resolveBtwPreparedRuntimeAuth({ abortSignal: params.opts?.abortSignal, preparation: runtimeAuthPreparation, model: runtime.model, provider: runtime.model.provider, modelId: runtime.model.id, preparedModelRuntime, authStorage: runtime.authStorage, modelRegistry: runtime.modelRegistry, authProfileStore: selectedAuthProfileStore }); const runtimeAuthPlan = resolvedAttempt.plan; const runtimeModel = resolvedAttempt.model; const finalizedHarness = await prepareHarness(runtimeModel.provider, runtimeModel.id, { api: runtimeModel.api, baseUrl: runtimeModel.baseUrl, ...resolveAgentHarnessPreparedRouteSupport(runtimeAuthPlan), preparedAuth: resolveAgentHarnessPreparedAuthSupport({ plan: runtimeAuthPlan }) }); if (finalizedHarness.id !== selectedHarness.id) { if (routeFinalized) throw new Error("Agent harness selection changed after route materialization."); return runHarnessSideQuestion(finalizedHarness, { ...runtime, model: runtimeModel, runtimeAuthPreparation, authProfileStore: selectedAuthProfileStore }, true); } if (!selectedHarness.runSideQuestion) { if (selectedHarness.id !== "openclaw" || !("auth" in resolvedAttempt)) throw new Error(`Selected agent harness "${selectedHarness.id}" does not support /btw side questions.`); return { kind: "openclaw", harness: selectedHarness, runtime: { ...runtime, model: runtimeModel, authProfileId: runtimeAuthPlan.forwardedAuthProfileId, authProfileIdSource: runtimeAuthPlan.forwardedAuthProfileSource, authProfileStore: selectedAuthProfileStore, runtimeAuthPreparation }, resolvedAttempt }; } const resolvedApiKey = runtimeAuthPlan.modelRoute?.authRequirement === "api-key" && "auth" in resolvedAttempt ? resolvedAttempt.auth.apiKey?.trim() : void 0; const sideRunId = params.authorityRunId; const placement = _usingCtx$1.u(await prepareSessionPlacementSandbox({ agentId: sessionAgentId, config: params.cfg, sessionId, sessionKey: params.sessionKey, workspaceDir })); placement.assertCurrent(); const sandbox = placement.sandbox ?? await resolveSandboxContext({ config: params.cfg, agentId: !params.sandboxSessionKey || params.sandboxSessionKey === params.sessionKey ? sessionAgentId : void 0, sessionKey: params.sandboxSessionKey ?? params.sessionKey ?? sessionId, workspaceDir }); const preparedRunAdmission = prepareSystemAgentRunAdmission(params.cfg, sideRunId, sessionAgentId, "btw.side-question"); const admittedRunContext = await preparedRunAdmission.admit("plugin-harness"); try { const { model: _sideModel, authorityRunId: _authorityRunId, ...hostAttempt } = params; const host = createAgentHarnessHostCapabilities({ attempt: { ...hostAttempt, admittedRunContext, config: params.cfg, agentId: sessionAgentId, sessionId, sessionKey: params.sessionKey, sandbox, workspaceDir, runId: sideRunId, currentMessagingTarget: params.messageTo, currentThreadTs: params.messageThreadId === void 0 ? void 0 : String(params.messageThreadId) }, pluginId: resolveAgentHarnessOwnerPluginId(selectedHarness), nativeModelPolicySupport: selectedHarness.nativeModelPolicySupport }); const sideParams = { ...hostAttempt, hostCapabilities: host.capabilities, sandbox, provider: runtimeModel.provider, model: runtimeModel.id, runtimeModel, preparedModelRuntime, preparedRuntimeAuth: { plan: runtimeAuthPlan, authProfileStore: scopeAuthProfileStoreToPreparedPlan(selectedAuthProfileStore, runtimeAuthPlan), authStorage: runtime.authStorage, modelRegistry: runtime.modelRegistry, ...resolvedApiKey ? { resolvedApiKey: unwrapSecretSentinelsForProviderEgress(resolvedApiKey, "BTW harness handoff") } : {} }, sessionId, sessionFile, agentId: sessionAgentId, workspaceDir, ...toolsAllow ? { toolsAllow } : {}, authProfileId: runtimeAuthPlan.modelRoute?.authRequirement === "api-key" ? void 0 : runtimeAuthPlan.forwardedAuthProfileId, opts: { ...params.opts, runId: sideRunId }, authProfileIdSource: runtimeAuthPlan.modelRoute?.authRequirement === "api-key" ? void 0 : runtimeAuthPlan.forwardedAuthProfileSource }; let result; try { placement.assertCurrent(); result = await selectedHarness.runSideQuestion(sideParams); } finally { host.close(); } recordBtwUsage(runtimeModel, result.usage); return { kind: "handled", payload: { text: result.text } }; } finally { preparedRunAdmission.close(); } } catch (_) { _usingCtx$1.e = _; } finally { _usingCtx$1.d(); } }; if (harness.runSideQuestion) { const dispatch = await runHarnessSideQuestion(harness, await resolveRuntimeSelection()); if (dispatch.kind === "handled") return dispatch.payload; preparedOpenClawFallback = dispatch; } if (harness.id === "codex" && !harness.runSideQuestion) throw new Error(`Selected agent harness "${harness.id}" does not support /btw side questions.`); const activeRunSnapshot = getActiveEmbeddedRunSnapshot(sessionId); const imageLimits = resolveImageSanitizationLimits(params.cfg); let messages = []; const inFlightPrompt = activeRunSnapshot?.inFlightPrompt; if (Array.isArray(activeRunSnapshot?.messages) && activeRunSnapshot.messages.length > 0) messages = await toSimpleContextMessages({ messages: activeRunSnapshot.messages, imageLimits }); if (messages.length === 0) messages = await toSimpleContextMessages({ messages: await readBtwTranscriptMessages({ agentId: sessionAgentId, sessionFile, sessionId, sessionKey: params.sessionKey, storePath: params.storePath, snapshotLeafId: activeRunSnapshot?.transcriptLeafId }), imageLimits }); params.opts?.abortSignal?.throwIfAborted(); if (messages.length === 0 && !inFlightPrompt?.trim()) throw new Error("No active session context."); const fallbackRuntime = resolveAvailableAgentHarnessPolicy({ provider: params.provider, modelId: params.model, config: params.cfg, agentId: sessionAgentId, sessionKey: params.sessionKey }).runtime.trim(); const sessionAuthProfileId = params.sessionEntry.authProfileOverride?.trim() || void 0; const sessionAuthProfileSource = sessionAuthProfileId ? resolveCollapsedSessionAuthPinSource(params.sessionEntry) : void 0; const cliProviderFromSessionAuth = sessionAuthProfileId ? resolveCliRuntimeExecutionProvider({ provider: params.provider, cfg: params.cfg, agentId: sessionAgentId, modelId: params.model, authProfileId: sessionAuthProfileId })?.trim() : void 0; const cliProviderFromAuthOrder = !sessionAuthProfileId || sessionAuthProfileSource === "auto" ? resolveCliRuntimeExecutionProvider({ provider: params.provider, cfg: params.cfg, agentId: sessionAgentId, modelId: params.model })?.trim() : void 0; const cliProvider = cliProviderFromSessionAuth ?? cliProviderFromAuthOrder ?? (isCliRuntimeAliasForProvider({ runtime: fallbackRuntime, provider: params.provider, cfg: params.cfg }) ? fallbackRuntime : void 0); if (cliProvider) return runCliBtwSideQuestion({ cfg: params.cfg, model: params.model, question: params.question, imageCount: params.images?.length ?? 0, sessionId, sessionFile, sessionEntry: params.sessionEntry, sessionKey: params.sessionKey, sessionAgentId, workspaceDir, cliProvider, authProfileId: cliProviderFromSessionAuth ? sessionAuthProfileId : void 0, resolvedThinkLevel: params.resolvedThinkLevel, messages, inFlightPrompt, opts: params.opts, authorityRunId: params.authorityRunId, messageChannel: params.messageChannel, messageProvider: params.messageProvider, currentChannelId: params.currentChannelId }); const initialOpenClawFallback = preparedOpenClawFallback; const runtimeSelectionForHarness = initialOpenClawFallback?.runtime ?? await resolveRuntimeSelection(); const runtimeHarness = initialOpenClawFallback?.harness ?? await prepareHarness(runtimeSelectionForHarness.model.provider, runtimeSelectionForHarness.model.id); if (runtimeHarness.runSideQuestion) { const dispatch = await runHarnessSideQuestion(runtimeHarness, runtimeSelectionForHarness); if (dispatch.kind === "handled") return dispatch.payload; preparedOpenClawFallback = dispatch; } if (runtimeHarness.id === "codex" && !runtimeHarness.runSideQuestion) throw new Error(`Selected agent harness "${runtimeHarness.id}" does not support /btw side questions.`); const finalizedOpenClawFallback = preparedOpenClawFallback; const { authStorage, model, modelRegistry, authProfileStore, runtimeAuthPreparation } = finalizedOpenClawFallback?.runtime ?? runtimeSelectionForHarness; const resolvedAttempt = finalizedOpenClawFallback?.resolvedAttempt ?? await resolveBtwPreparedRuntimeAuth({ abortSignal: params.opts?.abortSignal, preparation: runtimeAuthPreparation, model, provider: model.provider, modelId: model.id, preparedModelRuntime, authStorage, modelRegistry, authProfileStore }); const apiKeyInfo = resolvedAttempt.auth; const resolvedAuthProfileId = resolvedAttempt.plan.forwardedAuthProfileId; let runtimeModel = resolvedAttempt.model; let apiKey = apiKeyInfo.mode === "aws-sdk" && !apiKeyInfo.apiKey ? void 0 : requireApiKey(apiKeyInfo, runtimeModel.provider); if (apiKey) { const preparedAuth = protectPreparedProviderRuntimeAuth({ provider: runtimeModel.provider, preparedAuth: await prepareProviderRuntimeAuth({ provider: runtimeModel.provider, config: params.cfg, workspaceDir, env: process.env, context: { config: params.cfg, agentDir: params.agentDir, workspaceDir, env: process.env, provider: runtimeModel.provider, modelId: runtimeModel.id, model: runtimeModel, apiKey: unwrapSecretSentinelsForProviderEgress(apiKey, "provider runtime auth exchange"), authMode: apiKeyInfo.mode, profileId: resolvedAuthProfileId } }) }); runtimeModel = applyPreparedRuntimeAuthToModel(runtimeModel, preparedAuth); if (preparedAuth?.apiKey) apiKey = preparedAuth.apiKey; } runtimeModel = applySecretRefHeaderSentinels(runtimeModel, params.cfg); const modelRegistryRuntime = getModelRegistryRuntime(modelRegistry); const providerStreamFn = registerProviderStreamForModel({ model: runtimeModel, cfg: params.cfg, agentDir: params.agentDir, workspaceDir, env: process.env, wrapProviderStream: true, auth: { mode: apiKeyInfo.mode, authFlow: apiKeyInfo.authFlow }, apiRegistry: modelRegistryRuntime.apiRegistry }); const { streamFn } = resolveEmbeddedAgentStream({ llmRuntime: modelRegistryRuntime.llmRuntime, currentStreamFn: modelRegistryRuntime.llmRuntime.streamSimple, providerStreamFn, sessionId, signal: params.opts?.abortSignal, model: runtimeModel, resolvedApiKey: apiKey, authProfileId: resolvedAuthProfileId }); const chunker = params.opts?.onBlockReply && params.blockReplyChunking ? new EmbeddedBlockChunker(params.blockReplyChunking) : void 0; let emittedBlocks = 0; let blockEmitChain = Promise.resolve(); let answerText = ""; let reasoningText = ""; let assistantStarted = false; let sawTextEvent = false; const flushAtTextEnd = params.resolvedBlockStreamingBreak === "text_end"; const emitBlockChunk = async (text) => { if (!text.trim() || !params.opts?.onBlockReply) return; emittedBlocks += 1; blockEmitChain = blockEmitChain.then(async () => { await params.opts?.onBlockReply?.({ text, btw: { question: params.question } }); }); await blockEmitChain; }; const { images } = await sanitizeImageBlocks(params.images ?? [], "btw:question", imageLimits); const stream = await streamWithPayloadPatch(streamFn, runtimeModel, { systemPrompt: buildBtwSystemPrompt(), messages: [...messages, { role: "user", content: [{ type: "text", text: buildBtwQuestionPrompt(params.question, inFlightPrompt) }, ...images], timestamp: Date.now() }] }, { apiKey, reasoning: void 0, signal: params.opts?.abortSignal }, (payloadObj) => { if (Array.isArray(payloadObj.tools) && payloadObj.tools.length === 0) delete payloadObj.tools; }); let finalEvent; for await (const event of stream) { finalEvent = event.type === "done" || event.type === "error" ? event : finalEvent; if (!assistantStarted && (event.type === "text_start" || event.type === "start")) { assistantStarted = true; await params.opts?.onAssistantMessageStart?.(); } if (event.type === "text_delta") { sawTextEvent = true; answerText += event.delta; chunker?.append(event.delta); if (chunker && flushAtTextEnd) chunker.drain({ force: false, emit: (chunk) => void emitBlockChunk(chunk) }); continue; } if (event.type === "text_end" && chunker && flushAtTextEnd) { chunker.drain({ force: true, emit: (chunk) => void emitBlockChunk(chunk) }); continue; } if (event.type === "thinking_delta") { if (params.resolvedReasoningLevel !== "off") { reasoningText += event.delta; await params.opts?.onReasoningStream?.({ text: reasoningText, isReasoning: true }); } continue; } if (event.type === "thinking_end" && params.resolvedReasoningLevel !== "off") await params.opts?.onReasoningEnd?.(); } if (chunker && params.resolvedBlockStreamingBreak !== "text_end" && chunker.hasBuffered()) chunker.drain({ force: true, emit: (chunk) => void emitBlockChunk(chunk) }); await blockEmitChain; if (finalEvent?.type === "error") { const message = collectTextContentBlocks(finalEvent.error.content).join(""); throw new Error(message || finalEvent.error.errorMessage || "BTW failed."); } const finalMessage = finalEvent?.type === "done" ? finalEvent.message : void 0; if (finalMessage && !sawTextEvent) answerText = collectTextContentBlocks(finalMessage.content).join(""); const answer = answerText.trim(); if (!answer) throw new Error("No BTW response generated."); recordBtwUsage(runtimeModel, normalizeUsage(finalMessage?.usage)); if (emittedBlocks > 0) return; return { text: answer }; }); } //#endregion export { runBtwSideQuestion as t };