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-BADdmgZf.mjs"; import { d as resolveAgentWorkspaceDir } from "./agent-scope-config-CDYpzA0r.mjs"; import { c as resolveSessionFilePathOptions, s as resolveSessionFilePathCore } from "./paths-dpReqUI8.mjs"; import { r as resolveCollapsedSessionAuthPinSource } from "./auth-profile-override-provenance-Jztnei-C.mjs"; import "./agent-scope-CG17uXFn.mjs"; import { n as withPluginRuntimeGenerationScope } from "./generation-scope-tH9UXyLm.mjs"; import { i as isOpenAIProvider } from "./openai-routing-CI5rYN6I.mjs"; import { t as applyPreparedRuntimeAuthToModel } from "./provider-request-config-By9R9pyt.mjs"; import { d as prepareSystemAgentRunAdmission } from "./admitted-run-context-QJnFIxUZ.mjs"; import { A as requireApiKey } from "./model-auth-provider-config-C6nCoXKi.mjs"; import { i as ensureAuthProfileStoreWithoutExternalProfiles, n as ensureAuthProfileStore } from "./store-runtime-DZk7O5wt.mjs"; import { a as reconcileAuthProfileQuotaBlocks } from "./usage-DoyB4QE2.mjs"; import { a as resolveAgentHarnessOwnerPluginId } from "./registry-x-yKA-v_.mjs"; import { i as unwrapSecretSentinelsForProviderEgress } from "./provider-secret-egress-LltAZ95s.mjs"; import { _ as prepareProviderRuntimeAuth } from "./provider-runtime-D-Xv0Y4z.mjs"; import { n as getModelRegistryRuntime } from "./model-registry-Cfl88QiZ.mjs"; import { n as resolveAvailableAgentHarnessPolicy, o as resolveAgentHarnessPreparedAuthSupport, s as resolveAgentHarnessPreparedRouteSupport } from "./availability-B_rOizs1.mjs"; import { i as isCliRuntimeAliasForProvider, s as resolveCliRuntimeExecutionProvider } from "./model-runtime-aliases-t0kOML62.mjs"; import { i as resolveSessionRuntimeOverrideForProvider } from "./session-runtime-compat-zlHVO23h.mjs"; import { C as preparedModelRuntimeConfigsMatch, i as acquirePublishedPreparedModelRuntime } from "./prepared-model-runtime-BbAS5OtQ.mjs"; import { r as applySecretRefHeaderSentinels } from "./model-auth-CU3YlZuV.mjs"; import { r as resolveImageSanitizationLimits } from "./image-sanitization-DhkMOJXD.mjs"; import { n as sanitizeImageBlocks } from "./tool-images-DBPr26rh.mjs"; import { n as parseSqliteSessionFileMarker } from "./legacy-sqlite-marker-COPKCuIN.mjs"; import { l as scanSessionTranscriptTree } from "./transcript-tree-CaYJo8Xs.mjs"; import { a as loadSessionEntry } from "./session-accessor.sqlite-entry-Dn_MsDKD.mjs"; import { t as listSessionEntriesCore } from "./session-accessor.entry-BpOUC9jw.mjs"; import { d as loadTranscriptEvents } from "./session-accessor-BDmTPdtx.mjs"; import { s as isModelSelectionLocked } from "./model-overrides-uZFvcSRc.mjs"; import { a as hasBillableUsage, d as toDiagnosticUsage, u as normalizeUsage } from "./usage-XmAH1cfG.mjs"; import { a as migrateSessionEntries, t as buildSessionContext } from "./session-manager-codec-Dl5j6GJ-.mjs"; import { n as resolveAgentTimeoutMs } from "./timeout-Q2p-0uCf.mjs"; import { t as diagnosticLogger } from "./diagnostic-runtime-Clvsorku.mjs"; import "./diagnostic-B-cJgPjo.mjs"; import { s as getActiveEmbeddedRunSnapshot } from "./runs-D3elciTh.mjs"; import { h as recordReplyUsageState, m as buildReplyUsageState } from "./deliver-prepare-eSoKByIL.mjs"; import "./sessions-DQtCC-5j.mjs"; import { t as resolvePreferredSessionKeyForSessionIdMatches } from "./session-id-resolution--80nkk68.mjs"; import { n as resolveSessionModelRef } from "./session-model-ref-3-J97GZs.mjs"; import { t as runWithAsyncWorkResources } from "./async-work-resources-Dpudqxo5.mjs"; import { n as resolveModelAsync } from "./model-DASaQ-x0.mjs"; import { t as materializePreparedRuntimeModel } from "./materialize-model-CyJxJBW5.mjs"; import { r as prepareAgentRuntimeAuth } from "./prepare-auth-C8Bztjj8.mjs"; import { t as protectPreparedProviderRuntimeAuth } from "./provider-runtime-auth-protection-Bk_n7IRK.mjs"; import { a as resolvePreparedRuntimeAuthAttempts, o as resolvePreparedRuntimeModelAuth, s as scopeAuthProfileStoreToPreparedPlan } from "./simple-completion-runtime-BYHNOuzH.mjs"; import { a as resolvePluginHarnessPolicyToolsAllow } from "./execution-environment-BRwQLGad.mjs"; import { t as createAgentHarnessHostCapabilities } from "./host-capability-DKXWakUW.mjs"; import { n as resolveSandboxContext } from "./context-CUY6yznM.mjs"; import "./session-manager-CJB28fah.mjs"; import { t as streamWithPayloadPatch } from "./stream-payload-utils-BoUixBWV.mjs"; import { a as resolveEmbeddedAgentStream } from "./attempt.model-diagnostic-events-Bz4GFzsv.mjs"; import { t as registerProviderStreamForModel } from "./provider-stream-CQjNOiOR.mjs"; import { t as EmbeddedBlockChunker } from "./embedded-agent-block-chunker-Ct-avwXe.mjs"; import { i as selectAgentHarnessForPreparedModelProviders, r as selectAgentHarness } from "./selection-lyiH3_Xg.mjs"; import { t as ensureSelectedAgentHarnessPlugin } from "./runtime-plugin-CUsUwqcE.mjs"; import { a as resolveSessionPlacementSandbox } from "./session-placement-admission-D3FdTA2Z.mjs"; import { t as resolveExternalCliAuthOverlayScopeFromSelection } from "./external-cli-auth-selection-DoxobKHk.mjs"; import { n as resolveSessionAuthSelection } from "./session-override-Dww-KmFM.mjs"; import { n as buildBtwQuestionPrompt, r as buildBtwSystemPrompt, t as buildBtwCliPrompt } from "./btw-prompts-BvS7REMb.mjs"; import { t as executePreparedCliRun } from "./execute.runtime-z4Gtl6lu.mjs"; import { t as prepareCliRunContext } from "./prepare.runtime-CmNHLoXI.mjs"; import { randomUUID } from "node:crypto"; //#region src/agents/btw-transcript.ts /** * Reads prior session transcript context for `/btw` side-question handoffs. */ /** 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.ts /** * Runs `/btw` side questions against the active conversation without resuming * or continuing the main task. */ function collectTextContent(content) { return content.filter((part) => part.type === "text").map((part) => part.text).join(""); } function resolveBtwAuthProfileStore(params) { if (isOpenAIProvider(params.provider)) return { store: ensureAuthProfileStore(params.agentDir, { profileId: params.authProfileId, externalCliProviderIds: ["openai"], allowKeychainPrompt: false }), ignoreAutoPreferredProfile: false }; const userPinnedAuthProfileId = params.authProfileIdSource === "user" ? params.authProfileId : void 0; let externalCliAuthScope = resolveExternalCliAuthOverlayScopeFromSelection({ provider: params.provider, cfg: params.cfg, agentId: params.agentId, modelId: params.modelId, workspaceDir: params.workspaceDir, userPinnedAuthProfileId }); let store; if (externalCliAuthScope.providerIds) store = ensureAuthProfileStore(params.agentDir, { profileId: params.authProfileId, externalCliProviderIds: externalCliAuthScope.providerIds, allowKeychainPrompt: false }); else { store = ensureAuthProfileStoreWithoutExternalProfiles(params.agentDir, { profileId: params.authProfileId, allowKeychainPrompt: false }); externalCliAuthScope = resolveExternalCliAuthOverlayScopeFromSelection({ provider: params.provider, cfg: params.cfg, agentId: params.agentId, modelId: params.modelId, workspaceDir: params.workspaceDir, store, userPinnedAuthProfileId }); if (externalCliAuthScope.providerIds) store = ensureAuthProfileStore(params.agentDir, { profileId: params.authProfileId, externalCliProviderIds: externalCliAuthScope.providerIds, allowKeychainPrompt: false }); } return { store, ignoreAutoPreferredProfile: externalCliAuthScope.ignoreAutoPreferredProfile }; } function normalizeBtwContentBlocks(content) { if (Array.isArray(content)) return content; if (content && typeof content === "object") return [content]; } 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; const sanitizedMessage = role === "user" ? await sanitizeBtwUserMessage({ message, imageLimits: params.imageLimits }) : 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 authProfileId = authSelection?.profileId; const authProfileIdSource = authSelection?.source; const authProfileStoreSelection = resolveBtwAuthProfileStore({ cfg, provider: runtimeProvider, modelId: runtimeModelId, agentId: params.agentId, agentDir, workspaceDir, authProfileId, authProfileIdSource }); const effectiveAuthProfileId = authProfileStoreSelection.ignoreAutoPreferredProfile && authProfileIdSource !== "user" ? void 0 : authProfileId; const authParams = { provider: runtimeProvider, modelId: runtimeModelId, modelApi: model.api, modelBaseUrl: model.baseUrl, config: cfg, agentId: params.agentId, agentDir, env: process.env, workspaceDir, authProfileStore: authProfileStoreSelection.store, sessionAuthProfileId: effectiveAuthProfileId, sessionAuthProfileSource: authProfileIdSource, harnessId: params.harnessId, harnessRuntime: params.harnessId, harnessAuthBootstrap: params.harnessAuthBootstrap }; await reconcileAuthProfileQuotaBlocks(authParams); const runtimeAuthPreparation = prepareAgentRuntimeAuth(authParams); 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: authProfileStoreSelection.store, runtimeAuthPreparation, authStorage, modelRegistry }; } 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. */ async function runBtwSideQuestion(paramsInput) { let params = { ...paramsInput, authorityRunId: paramsInput.authorityRunId ?? `btw-${randomUUID()}` }; 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."); 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); let runtimeSelection; const resolveRuntimeSelection = async () => { if (!runtimeSelection) runtimeSelection = await resolveRuntimeModel({ abortSignal: params.opts?.abortSignal, provider: params.provider, model: params.model, agentId: sessionAgentId, sessionEntry: params.sessionEntry, sessionStore: params.sessionStore, sessionKey: params.sessionKey, storePath: params.storePath, isNewSession: params.isNewSession, harnessId: harness.id, harnessAuthBootstrap: harness.authBootstrap, preparedModelRuntime }); return runtimeSelection; }; const recordBtwUsage = (runtimeModel, usage) => { if (!hasBillableUsage(usage)) return; const usageState = buildReplyUsageState({ config: params.cfg, agentDir: params.agentDir, agentId: sessionAgentId, sessionId, provider: runtimeModel.provider, model: runtimeModel.id, chatType: params.chatType, usage }); recordReplyUsageState(params.opts?.runId, usageState); if (isDiagnosticsEnabled(params.cfg)) emitTrustedDiagnosticEvent({ type: "model.usage", sessionKey: params.sessionKey, sessionId, channel: params.messageChannel, agentId: sessionAgentId, provider: runtimeModel.provider, model: runtimeModel.id, usage: toDiagnosticUsage(usage), costUsd: usageState.turnUsd }); }; let preparedOpenClawFallback; const runHarnessSideQuestion = async (selectedHarness, runtime, routeFinalized = false) => { 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 authProfileStoreSelection = selectedHarness.id === harness.id ? void 0 : resolveBtwAuthProfileStore({ cfg: params.cfg, provider: runtime.model.provider, modelId: runtime.model.id, agentId: sessionAgentId, agentDir: params.agentDir, workspaceDir, authProfileId: runtime.authProfileId, authProfileIdSource: runtime.authProfileIdSource }); let runtimeAuthPreparation = runtime.runtimeAuthPreparation; if (authProfileStoreSelection) { const authParams = { provider: runtime.model.provider, modelId: runtime.model.id, modelApi: runtime.model.api, modelBaseUrl: runtime.model.baseUrl, config: params.cfg, agentId: sessionAgentId, agentDir: params.agentDir, env: process.env, workspaceDir, authProfileStore: authProfileStoreSelection.store, sessionAuthProfileId: authProfileStoreSelection.ignoreAutoPreferredProfile && runtime.authProfileIdSource !== "user" ? void 0 : runtime.authProfileId, sessionAuthProfileSource: runtime.authProfileIdSource, harnessId: selectedHarness.id, harnessRuntime: selectedHarness.id, harnessAuthBootstrap: selectedHarness.authBootstrap }; await reconcileAuthProfileQuotaBlocks(authParams); runtimeAuthPreparation = prepareAgentRuntimeAuth(authParams); } const selectedAuthProfileStore = authProfileStoreSelection?.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 sandbox = await resolveSessionPlacementSandbox({ agentId: sessionAgentId, config: params.cfg, sessionId, sessionKey: params.sessionKey, workspaceDir }) ?? 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 { result = await selectedHarness.runSideQuestion(sideParams); } finally { host.close(); } recordBtwUsage(runtimeModel, result.usage); return { kind: "handled", payload: { text: result.text } }; } finally { preparedRunAdmission.close(); } }; 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 = collectTextContent(finalEvent.error.content); throw new Error(message || finalEvent.error.errorMessage || "BTW failed."); } const finalMessage = finalEvent?.type === "done" ? finalEvent.message : void 0; if (finalMessage && !sawTextEvent) answerText = collectTextContent(finalMessage.content); 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 };