UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

340 lines (339 loc) 14.9 kB
import { n as isTruthyEnvValue } from "./env-M3R40TOb.js"; import { w as projectModelThinkingCompat } from "./model-selection-shared-BlLyx1r2.js"; import { Et as formatNoModelsAvailableMessage, Mt as sanitizeCompactionReplayMessages, at as findInitialModel, dt as ModelRegistry, ft as getModelRegistryRuntime, n as AgentSession, nt as SettingsManager, t as DefaultResourceLoader, ut as DEFAULT_THINKING_LEVEL, xt as AuthStorage } from "./sessions-BdNAJTEP.js"; import { t as Agent } from "./agent-core-fAQEcq-h.js"; import { h as setInternalBeforeToolBatch } from "./internal-hooks-CgPsiqhr.js"; import { o as bindStreamLlmRuntime } from "./stream-Ci9pOvq7.js"; import { u as convertToLlm } from "./session-CcAchsL_.js"; import "./session-manager-codec-DIXSw5vP.js"; import { _t as createSessionEntryWithTranscript } from "./session-accessor-YsytfDtG.js"; import { i as getAgentDir } from "./config-Wv6IuErc.js"; import { t as SessionManager } from "./session-manager-DO0bDxav.js"; import { s as resolveThinkingDefaultForModel } from "./thinking-DterdXhr.js"; import { join } from "node:path"; import { randomUUID } from "node:crypto"; import { clampThinkingLevel } from "@openclaw/ai/internal/runtime"; //#region src/agents/sessions/telemetry.ts /** Resolves whether install telemetry is enabled from env override or settings. */ function isInstallTelemetryEnabled(settingsManager, telemetryEnv = process.env.OPENCLAW_TELEMETRY) { return telemetryEnv !== void 0 ? isTruthyEnvValue(telemetryEnv) : settingsManager.getEnableInstallTelemetry(); } //#endregion //#region src/agents/sessions/sdk.ts /** * Agent session SDK factory. * * Selects models, wires built-in/custom tools, loads resources, and creates AgentSession instances. */ function getDefaultAgentDir() { return getAgentDir(); } function createSessionPrepareNextTurnWithContext(getAgent) { let activeRunMessages; let effectiveModel; let effectiveThinkingLevel; let lastSessionModel; let lastSessionThinkingLevel; let lastSessionPrompt; let lastSessionTools = []; const sameTools = (left, right) => left.length === right.length && left.every((tool, index) => tool === right[index]); return async (turn, signal) => { const agent = getAgent(); const firstTurnInRun = activeRunMessages !== turn.newMessages; if (firstTurnInRun) { activeRunMessages = turn.newMessages; effectiveModel = agent.state.model; effectiveThinkingLevel = agent.state.thinkingLevel; } const previousSnapshot = await agent.prepareNextTurn?.(signal); const sessionPrompt = agent.state.systemPrompt; const sessionTools = agent.state.tools; const sessionModelChanged = firstTurnInRun || agent.state.model !== lastSessionModel; const sessionThinkingChanged = firstTurnInRun || agent.state.thinkingLevel !== lastSessionThinkingLevel; const sessionPromptChanged = firstTurnInRun || sessionPrompt !== lastSessionPrompt; const sessionToolsChanged = firstTurnInRun || !sameTools(sessionTools, lastSessionTools); effectiveModel = previousSnapshot?.model ?? (sessionModelChanged ? agent.state.model : effectiveModel); effectiveThinkingLevel = previousSnapshot?.thinkingLevel ?? (sessionThinkingChanged ? agent.state.thinkingLevel : effectiveThinkingLevel); lastSessionModel = agent.state.model; lastSessionThinkingLevel = agent.state.thinkingLevel; lastSessionPrompt = sessionPrompt; lastSessionTools = sessionTools.slice(); const nextContext = previousSnapshot?.context ? { ...previousSnapshot.context } : { ...turn.context, systemPrompt: sessionPromptChanged ? sessionPrompt : turn.context.systemPrompt, tools: sessionToolsChanged ? sessionTools.slice() : turn.context.tools?.slice() }; return { ...previousSnapshot, context: nextContext, model: effectiveModel, thinkingLevel: effectiveThinkingLevel }; }; } function getAttributionHeaders(model, settingsManager) { if (!isInstallTelemetryEnabled(settingsManager)) return; const baseUrl = model.baseUrl ?? ""; if (model.provider === "openrouter" || baseUrl.includes("openrouter.ai")) return { "HTTP-Referer": "https://openclaw.ai", "X-OpenRouter-Title": "OpenClaw", "X-OpenRouter-Categories": "cli-agent" }; if (model.provider === "cloudflare-workers-ai" || model.provider === "cloudflare-ai-gateway" || baseUrl.includes("api.cloudflare.com") || baseUrl.includes("gateway.ai.cloudflare.com")) return { "User-Agent": "openclaw" }; } /** * Create an AgentSession with the specified options. * * @example * ```typescript * // Minimal - uses defaults * const { session } = await createAgentSession(); * * // With explicit model from the configured registry * const model = ModelRegistry.create(AuthStorage.load()).find('anthropic', 'claude-opus-4-5'); * const { session } = await createAgentSession({ * model, * thinkingLevel: 'high', * }); * * // Continue previous session * const { session, modelFallbackMessage } = await createAgentSession({ * continueSession: true, * }); * * // Full control * const loader = new DefaultResourceLoader({ * cwd: process.cwd(), * agentDir: getAgentDir(), * settingsManager: SettingsManager.create(), * }); * await loader.reload(); * const { session } = await createAgentSession({ * model: myModel, * tools: ["read", "bash"], * resourceLoader: loader, * sessionManager: SessionManager.inMemory(), * }); * ``` */ async function createAgentSession(options = {}) { return await createAgentSessionImpl(options); } /** Internal factory for temporary embedded sessions that do not own durable provider resources. */ async function createAgentSessionForEmbeddedRunner(options, internalOptions) { return await createAgentSessionImpl(options, internalOptions, false); } async function createAgentSessionImpl(options, internalOptions = {}, cleanupProviderSessionResourcesOnDispose = true) { const cwd = options.cwd ?? options.sessionManager?.getCwd() ?? process.cwd(); const agentDir = options.agentDir ?? getDefaultAgentDir(); let resourceLoader = options.resourceLoader; const modelsPath = options.agentDir ? join(agentDir, "models.json") : void 0; const authStorage = options.authStorage ?? AuthStorage.forAgent(agentDir); const modelRegistry = options.modelRegistry ?? ModelRegistry.create(authStorage, modelsPath); const settingsManager = options.settingsManager ?? SettingsManager.create(cwd, agentDir); const sessionManager = options.sessionManager ?? await createDefaultSdkSessionManager(cwd, agentDir); if (!resourceLoader) { resourceLoader = new DefaultResourceLoader({ cwd, agentDir, settingsManager }); await resourceLoader.reload(); modelRegistry.refresh(); } const existingSession = sessionManager.buildSessionContext(); const hasExistingSession = existingSession.messages.length > 0; const hasThinkingEntry = sessionManager.getBranch().some((entry) => entry.type === "thinking_level_change"); let model = options.model; let modelFallbackMessage; if (!model && hasExistingSession && existingSession.model) { const restoredModel = modelRegistry.find(existingSession.model.provider, existingSession.model.modelId); if (restoredModel && modelRegistry.hasConfiguredAuth(restoredModel)) model = restoredModel; if (!model) modelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`; } if (!model) { model = (await findInitialModel({ scopedModels: [], isContinuing: hasExistingSession, defaultProvider: settingsManager.getDefaultProvider(), defaultModelId: settingsManager.getDefaultModel(), defaultThinkingLevel: settingsManager.getDefaultThinkingLevel(), modelRegistry })).model; if (!model) modelFallbackMessage = formatNoModelsAvailableMessage(); else if (modelFallbackMessage) modelFallbackMessage += `. Using ${model.provider}/${model.id}`; } let thinkingLevel = options.thinkingLevel; const modelThinkingProvider = model?.api === "ollama" ? "ollama" : model?.provider; const modelThinkingCompat = model ? projectModelThinkingCompat(model.compat) : void 0; const modelThinkingDefault = (model && modelThinkingProvider ? resolveThinkingDefaultForModel({ provider: modelThinkingProvider, model: model.id, catalog: [{ provider: modelThinkingProvider, id: model.id, api: model.api, reasoning: model.reasoning, ...model.params ? { params: model.params } : {}, ...modelThinkingCompat ? { compat: modelThinkingCompat } : {} }] }) : void 0) === "off" ? "off" : DEFAULT_THINKING_LEVEL; if (thinkingLevel === void 0 && hasExistingSession) thinkingLevel = hasThinkingEntry ? existingSession.thinkingLevel : settingsManager.getDefaultThinkingLevel() ?? modelThinkingDefault; if (thinkingLevel === void 0) thinkingLevel = settingsManager.getDefaultThinkingLevel() ?? modelThinkingDefault; if (!model) thinkingLevel = "off"; else thinkingLevel = clampThinkingLevel(model, thinkingLevel); const defaultActiveToolNames = [ "read", "bash", "edit", "write" ]; const customToolNames = options.customTools?.map((tool) => tool.name) ?? []; const allowedToolNames = options.tools ?? (options.noTools === "all" ? [] : void 0); const disableBuiltInTools = !options.tools && options.noTools === "builtin"; const initialActiveToolNames = options.tools ? [...options.tools] : options.noTools === "all" ? [] : options.noTools === "builtin" ? customToolNames : defaultActiveToolNames; const convertToLlmWithBlockImages = (messages) => { const converted = convertToLlm(messages); if (!settingsManager.getBlockImages()) return converted; return converted.map((msg) => { if (msg.role === "user" || msg.role === "toolResult") { const content = msg.content; if (Array.isArray(content)) { if (content.some((c) => c.type === "image")) { const filteredContent = content.map((c) => c.type === "image" ? { type: "text", text: "Image reading is disabled." } : c).filter((c, i, arr) => { const previous = arr.at(i - 1); return !(c.type === "text" && c.text === "Image reading is disabled." && i > 0 && previous?.type === "text" && previous.text === "Image reading is disabled."); }); return Object.assign({}, msg, { content: filteredContent }); } } } return msg; }); }; const extensionRunnerRef = {}; const runWithSessionWriteSettlement = async (run) => options.withSessionWriteSettlement ? await options.withSessionWriteSettlement(run) : await run(); const modelRegistryRuntime = getModelRegistryRuntime(modelRegistry); const agent = new Agent({ initialState: { systemPrompt: "", model, thinkingLevel, tools: [] }, convertToLlm: convertToLlmWithBlockImages, streamFn: async (modelResult, context, optionsLocal) => { const auth = await modelRegistry.getApiKeyAndHeaders(modelResult); if (!auth.ok) throw new Error(auth.error); const providerRetrySettings = settingsManager.getProviderRetrySettings(); const attributionHeaders = getAttributionHeaders(modelResult, settingsManager); return modelRegistryRuntime.llmRuntime.streamSimple(modelResult, context, { ...optionsLocal, apiKey: auth.apiKey, timeoutMs: optionsLocal?.timeoutMs ?? providerRetrySettings.timeoutMs, maxRetryDelayMs: optionsLocal?.maxRetryDelayMs ?? providerRetrySettings.maxRetryDelayMs, headers: attributionHeaders || auth.headers || optionsLocal?.headers ? { ...attributionHeaders, ...auth.headers, ...optionsLocal?.headers } : void 0 }); }, onPayload: async (payload, modelValue) => { const runner = extensionRunnerRef.current; if (!runner?.hasHandlers("before_provider_request")) return payload; return await runWithSessionWriteSettlement(async () => await runner.emitBeforeProviderRequest(payload)); }, onResponse: async (response, modelLocal) => { const runner = extensionRunnerRef.current; if (!runner?.hasHandlers("after_provider_response")) return; await runWithSessionWriteSettlement(async () => await runner.emit({ type: "after_provider_response", status: response.status, headers: response.headers })); }, sessionId: sessionManager.getSessionId(), transformContext: async (messages) => { const runner = extensionRunnerRef.current; if (!runner) return messages; return runner.emitContext(messages); }, resolveDeferredTool: options.resolveDeferredTool, prepareNextTurnWithContext: createSessionPrepareNextTurnWithContext(() => agent), steeringMode: settingsManager.getSteeringMode(), followUpMode: settingsManager.getFollowUpMode(), transport: settingsManager.getTransport(), thinkingBudgets: settingsManager.getThinkingBudgets(), maxRetryDelayMs: settingsManager.getProviderRetrySettings().maxRetryDelayMs }); setInternalBeforeToolBatch(agent, internalOptions.beforeToolBatch); if (agent.streamFn) bindStreamLlmRuntime(agent.streamFn, modelRegistryRuntime.llmRuntime); if (hasExistingSession) { agent.state.messages = sanitizeCompactionReplayMessages(existingSession.messages); if (!hasThinkingEntry) sessionManager.appendThinkingLevelChange(thinkingLevel); } else { if (model) sessionManager.appendModelChange(model.provider, model.id); sessionManager.appendThinkingLevelChange(thinkingLevel); } return { session: new AgentSession({ agent, sessionManager, settingsManager, cwd, scopedModels: options.scopedModels, resourceLoader, customTools: options.customTools, modelRegistry, initialActiveToolNames, allowedToolNames, disableBuiltInTools, extensionRunnerRef, sessionStartEvent: options.sessionStartEvent, withSessionWriteSettlement: options.withSessionWriteSettlement, contextOverflowRecoveryOwner: internalOptions.contextOverflowRecoveryOwner, cleanupProviderSessionResourcesOnDispose }), extensionsResult: resourceLoader.getExtensions(), modelFallbackMessage }; } async function createDefaultSdkSessionManager(cwd, agentDir) { const sessionId = randomUUID(); const target = { agentId: "main", sessionId, sessionKey: `agent:main:sdk:${sessionId}`, storePath: join(agentDir, "openclaw-agent.sqlite") }; const created = await createSessionEntryWithTranscript(target, () => ({ ok: true, entry: { sessionId, updatedAt: Date.now() } }), { cwd }); if (!created.ok) throw new Error(`Failed to initialize SDK session transcript: ${created.error}`); return SessionManager.open(target, cwd); } //#endregion //#region src/agents/embedded-agent-runner/resource-loader.ts /** Embedded sessions consume prepared resources, never ambient local discovery. */ function createEmbeddedAgentResourceLoader(options) { return new DefaultResourceLoader({ ...options, noExtensions: true, noSkills: true, noPromptTemplates: true, noThemes: true, noContextFiles: true, systemPrompt: "", appendSystemPrompt: [] }); } //#endregion export { createAgentSession as n, createAgentSessionForEmbeddedRunner as r, createEmbeddedAgentResourceLoader as t };