openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
340 lines (339 loc) • 14.9 kB
JavaScript
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 };