UNPKG

@gguf/claw

Version:

Multi-channel AI gateway with extensible messaging integrations

833 lines (824 loc) 28.4 kB
import { _ as isSubagentSessionKey, g as isCronSessionKey, l as normalizeAgentId, n as DEFAULT_AGENT_ID, v as parseAgentSessionKey } from "./session-key-9yEYlIQe.js"; import { $ as getLogger, B as danger, J as warn, K as shouldLogVerbose, O as pathExists, V as info, W as logVerboseConsole, j as resolveUserPath } from "./registry-C8pj8ctW.js"; import { s as resolveStateDir, u as resolveRequiredHomeDir } from "./paths-DJmOcr7Q.js"; import { i as defaultRuntime, t as createSubsystemLogger } from "./subsystem-BfLMZ8kW.js"; import path from "node:path"; import fs from "node:fs"; import os from "node:os"; import fs$1 from "node:fs/promises"; import { promisify } from "node:util"; import { execFile, spawn } from "node:child_process"; import { fileURLToPath } from "node:url"; //#region src/logger.ts const subsystemPrefixRe = /^([a-z][a-z0-9-]{1,20}):\s+(.*)$/i; function splitSubsystem(message) { const match = message.match(subsystemPrefixRe); if (!match) return null; const [, subsystem, rest] = match; return { subsystem, rest }; } function logInfo(message, runtime = defaultRuntime) { const parsed = runtime === defaultRuntime ? splitSubsystem(message) : null; if (parsed) { createSubsystemLogger(parsed.subsystem).info(parsed.rest); return; } runtime.log(info(message)); getLogger().info(message); } function logWarn(message, runtime = defaultRuntime) { const parsed = runtime === defaultRuntime ? splitSubsystem(message) : null; if (parsed) { createSubsystemLogger(parsed.subsystem).warn(parsed.rest); return; } runtime.log(warn(message)); getLogger().warn(message); } function logError(message, runtime = defaultRuntime) { const parsed = runtime === defaultRuntime ? splitSubsystem(message) : null; if (parsed) { createSubsystemLogger(parsed.subsystem).error(parsed.rest); return; } runtime.error(danger(message)); getLogger().error(message); } function logDebug(message) { getLogger().debug(message); logVerboseConsole(message); } //#endregion //#region src/process/spawn-utils.ts const DEFAULT_RETRY_CODES = ["EBADF"]; function resolveCommandStdio(params) { return [ params.hasInput ? "pipe" : params.preferInherit ? "inherit" : "pipe", "pipe", "pipe" ]; } function shouldRetry(err, codes) { const code = err && typeof err === "object" && "code" in err ? String(err.code) : ""; return code.length > 0 && codes.includes(code); } async function spawnAndWaitForSpawn(spawnImpl, argv, options) { const child = spawnImpl(argv[0], argv.slice(1), options); return await new Promise((resolve, reject) => { let settled = false; const cleanup = () => { child.removeListener("error", onError); child.removeListener("spawn", onSpawn); }; const finishResolve = () => { if (settled) return; settled = true; cleanup(); resolve(child); }; const onError = (err) => { if (settled) return; settled = true; cleanup(); reject(err); }; const onSpawn = () => { finishResolve(); }; child.once("error", onError); child.once("spawn", onSpawn); process.nextTick(() => { if (typeof child.pid === "number") finishResolve(); }); }); } async function spawnWithFallback(params) { const spawnImpl = params.spawnImpl ?? spawn; const retryCodes = params.retryCodes ?? DEFAULT_RETRY_CODES; const baseOptions = { ...params.options }; const fallbacks = params.fallbacks ?? []; const attempts = [{ options: baseOptions }, ...fallbacks.map((fallback) => ({ label: fallback.label, options: { ...baseOptions, ...fallback.options } }))]; let lastError; for (let index = 0; index < attempts.length; index += 1) { const attempt = attempts[index]; try { return { child: await spawnAndWaitForSpawn(spawnImpl, params.argv, attempt.options), usedFallback: index > 0, fallbackLabel: attempt.label }; } catch (err) { lastError = err; const nextFallback = fallbacks[index]; if (!nextFallback || !shouldRetry(err, retryCodes)) throw err; params.onFallback?.(err, nextFallback); } } throw lastError; } //#endregion //#region src/process/exec.ts const execFileAsync = promisify(execFile); /** * Resolves a command for Windows compatibility. * On Windows, non-.exe commands (like npm, pnpm) require their .cmd extension. */ function resolveCommand(command) { if (process.platform !== "win32") return command; const basename = path.basename(command).toLowerCase(); if (path.extname(basename)) return command; if ([ "npm", "pnpm", "yarn", "npx" ].includes(basename)) return `${command}.cmd`; return command; } function shouldSpawnWithShell(params) { return false; } async function runExec(command, args, opts = 1e4) { const options = typeof opts === "number" ? { timeout: opts, encoding: "utf8" } : { timeout: opts.timeoutMs, maxBuffer: opts.maxBuffer, encoding: "utf8" }; try { const { stdout, stderr } = await execFileAsync(resolveCommand(command), args, options); if (shouldLogVerbose()) { if (stdout.trim()) logDebug(stdout.trim()); if (stderr.trim()) logError(stderr.trim()); } return { stdout, stderr }; } catch (err) { if (shouldLogVerbose()) logError(danger(`Command failed: ${command} ${args.join(" ")}`)); throw err; } } async function runCommandWithTimeout(argv, optionsOrTimeout) { const options = typeof optionsOrTimeout === "number" ? { timeoutMs: optionsOrTimeout } : optionsOrTimeout; const { timeoutMs, cwd, input, env, noOutputTimeoutMs } = options; const { windowsVerbatimArguments } = options; const hasInput = input !== void 0; const shouldSuppressNpmFund = (() => { const cmd = path.basename(argv[0] ?? ""); if (cmd === "npm" || cmd === "npm.cmd" || cmd === "npm.exe") return true; if (cmd === "node" || cmd === "node.exe") return (argv[1] ?? "").includes("npm-cli.js"); return false; })(); const mergedEnv = env ? { ...process.env, ...env } : { ...process.env }; const resolvedEnv = Object.fromEntries(Object.entries(mergedEnv).filter(([, value]) => value !== void 0).map(([key, value]) => [key, String(value)])); if (shouldSuppressNpmFund) { if (resolvedEnv.NPM_CONFIG_FUND == null) resolvedEnv.NPM_CONFIG_FUND = "false"; if (resolvedEnv.npm_config_fund == null) resolvedEnv.npm_config_fund = "false"; } const stdio = resolveCommandStdio({ hasInput, preferInherit: true }); const resolvedCommand = resolveCommand(argv[0] ?? ""); const child = spawn(resolvedCommand, argv.slice(1), { stdio, cwd, env: resolvedEnv, windowsVerbatimArguments, ...shouldSpawnWithShell({ resolvedCommand, platform: process.platform }) ? { shell: true } : {} }); return await new Promise((resolve, reject) => { let stdout = ""; let stderr = ""; let settled = false; let timedOut = false; let noOutputTimedOut = false; let noOutputTimer = null; const shouldTrackOutputTimeout = typeof noOutputTimeoutMs === "number" && Number.isFinite(noOutputTimeoutMs) && noOutputTimeoutMs > 0; const clearNoOutputTimer = () => { if (!noOutputTimer) return; clearTimeout(noOutputTimer); noOutputTimer = null; }; const armNoOutputTimer = () => { if (!shouldTrackOutputTimeout || settled) return; clearNoOutputTimer(); noOutputTimer = setTimeout(() => { if (settled) return; noOutputTimedOut = true; if (typeof child.kill === "function") child.kill("SIGKILL"); }, Math.floor(noOutputTimeoutMs)); }; const timer = setTimeout(() => { timedOut = true; if (typeof child.kill === "function") child.kill("SIGKILL"); }, timeoutMs); armNoOutputTimer(); if (hasInput && child.stdin) { child.stdin.write(input ?? ""); child.stdin.end(); } child.stdout?.on("data", (d) => { stdout += d.toString(); armNoOutputTimer(); }); child.stderr?.on("data", (d) => { stderr += d.toString(); armNoOutputTimer(); }); child.on("error", (err) => { if (settled) return; settled = true; clearTimeout(timer); clearNoOutputTimer(); reject(err); }); child.on("close", (code, signal) => { if (settled) return; settled = true; clearTimeout(timer); clearNoOutputTimer(); const termination = noOutputTimedOut ? "no-output-timeout" : timedOut ? "timeout" : signal != null ? "signal" : "exit"; resolve({ pid: child.pid ?? void 0, stdout, stderr, code, signal, killed: child.killed, termination, noOutputTimedOut }); }); }); } //#endregion //#region src/agents/skills/filter.ts function normalizeSkillFilter(skillFilter) { if (skillFilter === void 0) return; return skillFilter.map((entry) => String(entry).trim()).filter(Boolean); } //#endregion //#region src/infra/openclaw-root.ts const CORE_PACKAGE_NAMES = new Set(["openclaw"]); async function readPackageName(dir) { try { const raw = await fs$1.readFile(path.join(dir, "package.json"), "utf-8"); const parsed = JSON.parse(raw); return typeof parsed.name === "string" ? parsed.name : null; } catch { return null; } } function readPackageNameSync(dir) { try { const raw = fs.readFileSync(path.join(dir, "package.json"), "utf-8"); const parsed = JSON.parse(raw); return typeof parsed.name === "string" ? parsed.name : null; } catch { return null; } } async function findPackageRoot(startDir, maxDepth = 12) { for (const current of iterAncestorDirs(startDir, maxDepth)) { const name = await readPackageName(current); if (name && CORE_PACKAGE_NAMES.has(name)) return current; } return null; } function findPackageRootSync(startDir, maxDepth = 12) { for (const current of iterAncestorDirs(startDir, maxDepth)) { const name = readPackageNameSync(current); if (name && CORE_PACKAGE_NAMES.has(name)) return current; } return null; } function* iterAncestorDirs(startDir, maxDepth) { let current = path.resolve(startDir); for (let i = 0; i < maxDepth; i += 1) { yield current; const parent = path.dirname(current); if (parent === current) break; current = parent; } } function candidateDirsFromArgv1(argv1) { const normalized = path.resolve(argv1); const candidates = [path.dirname(normalized)]; try { const resolved = fs.realpathSync(normalized); if (resolved !== normalized) candidates.push(path.dirname(resolved)); } catch {} const parts = normalized.split(path.sep); const binIndex = parts.lastIndexOf(".bin"); if (binIndex > 0 && parts[binIndex - 1] === "node_modules") { const binName = path.basename(normalized); const nodeModulesDir = parts.slice(0, binIndex).join(path.sep); candidates.push(path.join(nodeModulesDir, binName)); } return candidates; } async function resolveOpenClawPackageRoot(opts) { for (const candidate of buildCandidates(opts)) { const found = await findPackageRoot(candidate); if (found) return found; } return null; } function resolveOpenClawPackageRootSync(opts) { for (const candidate of buildCandidates(opts)) { const found = findPackageRootSync(candidate); if (found) return found; } return null; } function buildCandidates(opts) { const candidates = []; if (opts.moduleUrl) candidates.push(path.dirname(fileURLToPath(opts.moduleUrl))); if (opts.argv1) candidates.push(...candidateDirsFromArgv1(opts.argv1)); if (opts.cwd) candidates.push(opts.cwd); return candidates; } //#endregion //#region src/agents/workspace-templates.ts const FALLBACK_TEMPLATE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../docs/reference/templates"); let cachedTemplateDir; let resolvingTemplateDir; async function resolveWorkspaceTemplateDir(opts) { if (cachedTemplateDir) return cachedTemplateDir; if (resolvingTemplateDir) return resolvingTemplateDir; resolvingTemplateDir = (async () => { const moduleUrl = opts?.moduleUrl ?? import.meta.url; const argv1 = opts?.argv1 ?? process.argv[1]; const cwd = opts?.cwd ?? process.cwd(); const packageRoot = await resolveOpenClawPackageRoot({ moduleUrl, argv1, cwd }); const candidates = [ packageRoot ? path.join(packageRoot, "docs", "reference", "templates") : null, cwd ? path.resolve(cwd, "docs", "reference", "templates") : null, FALLBACK_TEMPLATE_DIR ].filter(Boolean); for (const candidate of candidates) if (await pathExists(candidate)) { cachedTemplateDir = candidate; return candidate; } cachedTemplateDir = candidates[0] ?? FALLBACK_TEMPLATE_DIR; return cachedTemplateDir; })(); try { return await resolvingTemplateDir; } finally { resolvingTemplateDir = void 0; } } //#endregion //#region src/agents/workspace.ts function resolveDefaultAgentWorkspaceDir(env = process.env, homedir = os.homedir) { const home = resolveRequiredHomeDir(env, homedir); const profile = env.OPENCLAW_PROFILE?.trim(); if (profile && profile.toLowerCase() !== "default") return path.join(home, ".openclaw", `workspace-${profile}`); return path.join(home, ".openclaw", "workspace"); } const DEFAULT_AGENT_WORKSPACE_DIR = resolveDefaultAgentWorkspaceDir(); const DEFAULT_AGENTS_FILENAME = "AGENTS.md"; const DEFAULT_SOUL_FILENAME = "SOUL.md"; const DEFAULT_TOOLS_FILENAME = "TOOLS.md"; const DEFAULT_IDENTITY_FILENAME = "IDENTITY.md"; const DEFAULT_USER_FILENAME = "USER.md"; const DEFAULT_HEARTBEAT_FILENAME = "HEARTBEAT.md"; const DEFAULT_BOOTSTRAP_FILENAME = "BOOTSTRAP.md"; const DEFAULT_MEMORY_FILENAME = "MEMORY.md"; const DEFAULT_MEMORY_ALT_FILENAME = "memory.md"; const WORKSPACE_STATE_DIRNAME = ".openclaw"; const WORKSPACE_STATE_FILENAME = "workspace-state.json"; const WORKSPACE_STATE_VERSION = 1; const workspaceTemplateCache = /* @__PURE__ */ new Map(); let gitAvailabilityPromise = null; const workspaceFileCache = /* @__PURE__ */ new Map(); /** * Read file with caching based on mtime. Returns cached content if file * hasn't changed, otherwise reads from disk and updates cache. */ async function readFileWithCache(filePath) { try { const mtimeMs = (await fs$1.stat(filePath)).mtimeMs; const cached = workspaceFileCache.get(filePath); if (cached && cached.mtimeMs === mtimeMs) return cached.content; const content = await fs$1.readFile(filePath, "utf-8"); workspaceFileCache.set(filePath, { content, mtimeMs }); return content; } catch (error) { workspaceFileCache.delete(filePath); throw error; } } function stripFrontMatter(content) { if (!content.startsWith("---")) return content; const endIndex = content.indexOf("\n---", 3); if (endIndex === -1) return content; const start = endIndex + 4; let trimmed = content.slice(start); trimmed = trimmed.replace(/^\s+/, ""); return trimmed; } async function loadTemplate(name) { const cached = workspaceTemplateCache.get(name); if (cached) return cached; const pending = (async () => { const templateDir = await resolveWorkspaceTemplateDir(); const templatePath = path.join(templateDir, name); try { return stripFrontMatter(await fs$1.readFile(templatePath, "utf-8")); } catch { throw new Error(`Missing workspace template: ${name} (${templatePath}). Ensure docs/reference/templates are packaged.`); } })(); workspaceTemplateCache.set(name, pending); try { return await pending; } catch (error) { workspaceTemplateCache.delete(name); throw error; } } async function writeFileIfMissing(filePath, content) { try { await fs$1.writeFile(filePath, content, { encoding: "utf-8", flag: "wx" }); return true; } catch (err) { if (err.code !== "EEXIST") throw err; return false; } } async function fileExists(filePath) { try { await fs$1.access(filePath); return true; } catch { return false; } } function resolveWorkspaceStatePath(dir) { return path.join(dir, WORKSPACE_STATE_DIRNAME, WORKSPACE_STATE_FILENAME); } function parseWorkspaceOnboardingState(raw) { try { const parsed = JSON.parse(raw); if (!parsed || typeof parsed !== "object") return null; return { version: WORKSPACE_STATE_VERSION, bootstrapSeededAt: typeof parsed.bootstrapSeededAt === "string" ? parsed.bootstrapSeededAt : void 0, onboardingCompletedAt: typeof parsed.onboardingCompletedAt === "string" ? parsed.onboardingCompletedAt : void 0 }; } catch { return null; } } async function readWorkspaceOnboardingState(statePath) { try { return parseWorkspaceOnboardingState(await fs$1.readFile(statePath, "utf-8")) ?? { version: WORKSPACE_STATE_VERSION }; } catch (err) { if (err.code !== "ENOENT") throw err; return { version: WORKSPACE_STATE_VERSION }; } } async function writeWorkspaceOnboardingState(statePath, state) { await fs$1.mkdir(path.dirname(statePath), { recursive: true }); const payload = `${JSON.stringify(state, null, 2)}\n`; const tmpPath = `${statePath}.tmp-${process.pid}-${Date.now().toString(36)}`; try { await fs$1.writeFile(tmpPath, payload, { encoding: "utf-8" }); await fs$1.rename(tmpPath, statePath); } catch (err) { await fs$1.unlink(tmpPath).catch(() => {}); throw err; } } async function hasGitRepo(dir) { try { await fs$1.stat(path.join(dir, ".git")); return true; } catch { return false; } } async function isGitAvailable() { if (gitAvailabilityPromise) return gitAvailabilityPromise; gitAvailabilityPromise = (async () => { try { return (await runCommandWithTimeout(["git", "--version"], { timeoutMs: 2e3 })).code === 0; } catch { return false; } })(); return gitAvailabilityPromise; } async function ensureGitRepo(dir, isBrandNewWorkspace) { if (!isBrandNewWorkspace) return; if (await hasGitRepo(dir)) return; if (!await isGitAvailable()) return; try { await runCommandWithTimeout(["git", "init"], { cwd: dir, timeoutMs: 1e4 }); } catch {} } async function ensureAgentWorkspace(params) { const dir = resolveUserPath(params?.dir?.trim() ? params.dir.trim() : DEFAULT_AGENT_WORKSPACE_DIR); await fs$1.mkdir(dir, { recursive: true }); if (!params?.ensureBootstrapFiles) return { dir }; const agentsPath = path.join(dir, DEFAULT_AGENTS_FILENAME); const soulPath = path.join(dir, DEFAULT_SOUL_FILENAME); const toolsPath = path.join(dir, DEFAULT_TOOLS_FILENAME); const identityPath = path.join(dir, DEFAULT_IDENTITY_FILENAME); const userPath = path.join(dir, DEFAULT_USER_FILENAME); const heartbeatPath = path.join(dir, DEFAULT_HEARTBEAT_FILENAME); const bootstrapPath = path.join(dir, DEFAULT_BOOTSTRAP_FILENAME); const statePath = resolveWorkspaceStatePath(dir); const isBrandNewWorkspace = await (async () => { const paths = [ agentsPath, soulPath, toolsPath, identityPath, userPath, heartbeatPath ]; return (await Promise.all(paths.map(async (p) => { try { await fs$1.access(p); return true; } catch { return false; } }))).every((v) => !v); })(); const agentsTemplate = await loadTemplate(DEFAULT_AGENTS_FILENAME); const soulTemplate = await loadTemplate(DEFAULT_SOUL_FILENAME); const toolsTemplate = await loadTemplate(DEFAULT_TOOLS_FILENAME); const identityTemplate = await loadTemplate(DEFAULT_IDENTITY_FILENAME); const userTemplate = await loadTemplate(DEFAULT_USER_FILENAME); const heartbeatTemplate = await loadTemplate(DEFAULT_HEARTBEAT_FILENAME); await writeFileIfMissing(agentsPath, agentsTemplate); await writeFileIfMissing(soulPath, soulTemplate); await writeFileIfMissing(toolsPath, toolsTemplate); await writeFileIfMissing(identityPath, identityTemplate); await writeFileIfMissing(userPath, userTemplate); await writeFileIfMissing(heartbeatPath, heartbeatTemplate); let state = await readWorkspaceOnboardingState(statePath); let stateDirty = false; const markState = (next) => { state = { ...state, ...next }; stateDirty = true; }; const nowIso = () => (/* @__PURE__ */ new Date()).toISOString(); let bootstrapExists = await fileExists(bootstrapPath); if (!state.bootstrapSeededAt && bootstrapExists) markState({ bootstrapSeededAt: nowIso() }); if (!state.onboardingCompletedAt && state.bootstrapSeededAt && !bootstrapExists) markState({ onboardingCompletedAt: nowIso() }); if (!state.bootstrapSeededAt && !state.onboardingCompletedAt && !bootstrapExists) { const [identityContent, userContent] = await Promise.all([fs$1.readFile(identityPath, "utf-8"), fs$1.readFile(userPath, "utf-8")]); if (identityContent !== identityTemplate || userContent !== userTemplate) markState({ onboardingCompletedAt: nowIso() }); else { if (!await writeFileIfMissing(bootstrapPath, await loadTemplate(DEFAULT_BOOTSTRAP_FILENAME))) bootstrapExists = await fileExists(bootstrapPath); else bootstrapExists = true; if (bootstrapExists && !state.bootstrapSeededAt) markState({ bootstrapSeededAt: nowIso() }); } } if (stateDirty) await writeWorkspaceOnboardingState(statePath, state); await ensureGitRepo(dir, isBrandNewWorkspace); return { dir, agentsPath, soulPath, toolsPath, identityPath, userPath, heartbeatPath, bootstrapPath }; } async function resolveMemoryBootstrapEntries(resolvedDir) { const candidates = [DEFAULT_MEMORY_FILENAME, DEFAULT_MEMORY_ALT_FILENAME]; const entries = []; for (const name of candidates) { const filePath = path.join(resolvedDir, name); try { await fs$1.access(filePath); entries.push({ name, filePath }); } catch {} } if (entries.length <= 1) return entries; const seen = /* @__PURE__ */ new Set(); const deduped = []; for (const entry of entries) { let key = entry.filePath; try { key = await fs$1.realpath(entry.filePath); } catch {} if (seen.has(key)) continue; seen.add(key); deduped.push(entry); } return deduped; } async function loadWorkspaceBootstrapFiles(dir) { const resolvedDir = resolveUserPath(dir); const entries = [ { name: DEFAULT_AGENTS_FILENAME, filePath: path.join(resolvedDir, DEFAULT_AGENTS_FILENAME) }, { name: DEFAULT_SOUL_FILENAME, filePath: path.join(resolvedDir, DEFAULT_SOUL_FILENAME) }, { name: DEFAULT_TOOLS_FILENAME, filePath: path.join(resolvedDir, DEFAULT_TOOLS_FILENAME) }, { name: DEFAULT_IDENTITY_FILENAME, filePath: path.join(resolvedDir, DEFAULT_IDENTITY_FILENAME) }, { name: DEFAULT_USER_FILENAME, filePath: path.join(resolvedDir, DEFAULT_USER_FILENAME) }, { name: DEFAULT_HEARTBEAT_FILENAME, filePath: path.join(resolvedDir, DEFAULT_HEARTBEAT_FILENAME) }, { name: DEFAULT_BOOTSTRAP_FILENAME, filePath: path.join(resolvedDir, DEFAULT_BOOTSTRAP_FILENAME) } ]; entries.push(...await resolveMemoryBootstrapEntries(resolvedDir)); const result = []; for (const entry of entries) try { const content = await readFileWithCache(entry.filePath); result.push({ name: entry.name, path: entry.filePath, content, missing: false }); } catch { result.push({ name: entry.name, path: entry.filePath, missing: true }); } return result; } const MINIMAL_BOOTSTRAP_ALLOWLIST = new Set([DEFAULT_AGENTS_FILENAME, DEFAULT_TOOLS_FILENAME]); function filterBootstrapFilesForSession(files, sessionKey) { if (!sessionKey || !isSubagentSessionKey(sessionKey) && !isCronSessionKey(sessionKey)) return files; return files.filter((file) => MINIMAL_BOOTSTRAP_ALLOWLIST.has(file.name)); } //#endregion //#region src/agents/agent-scope.ts let defaultAgentWarned = false; function listAgentEntries(cfg) { const list = cfg.agents?.list; if (!Array.isArray(list)) return []; return list.filter((entry) => Boolean(entry && typeof entry === "object")); } function listAgentIds(cfg) { const agents = listAgentEntries(cfg); if (agents.length === 0) return [DEFAULT_AGENT_ID]; const seen = /* @__PURE__ */ new Set(); const ids = []; for (const entry of agents) { const id = normalizeAgentId(entry?.id); if (seen.has(id)) continue; seen.add(id); ids.push(id); } return ids.length > 0 ? ids : [DEFAULT_AGENT_ID]; } function resolveDefaultAgentId(cfg) { const agents = listAgentEntries(cfg); if (agents.length === 0) return DEFAULT_AGENT_ID; const defaults = agents.filter((agent) => agent?.default); if (defaults.length > 1 && !defaultAgentWarned) { defaultAgentWarned = true; console.warn("Multiple agents marked default=true; using the first entry as default."); } const chosen = (defaults[0] ?? agents[0])?.id?.trim(); return normalizeAgentId(chosen || DEFAULT_AGENT_ID); } function resolveSessionAgentIds(params) { const defaultAgentId = resolveDefaultAgentId(params.config ?? {}); const sessionKey = params.sessionKey?.trim(); const normalizedSessionKey = sessionKey ? sessionKey.toLowerCase() : void 0; const parsed = normalizedSessionKey ? parseAgentSessionKey(normalizedSessionKey) : null; return { defaultAgentId, sessionAgentId: parsed?.agentId ? normalizeAgentId(parsed.agentId) : defaultAgentId }; } function resolveSessionAgentId(params) { return resolveSessionAgentIds(params).sessionAgentId; } function resolveAgentEntry(cfg, agentId) { const id = normalizeAgentId(agentId); return listAgentEntries(cfg).find((entry) => normalizeAgentId(entry.id) === id); } function resolveAgentConfig(cfg, agentId) { const entry = resolveAgentEntry(cfg, normalizeAgentId(agentId)); if (!entry) return; return { name: typeof entry.name === "string" ? entry.name : void 0, workspace: typeof entry.workspace === "string" ? entry.workspace : void 0, agentDir: typeof entry.agentDir === "string" ? entry.agentDir : void 0, model: typeof entry.model === "string" || entry.model && typeof entry.model === "object" ? entry.model : void 0, skills: Array.isArray(entry.skills) ? entry.skills : void 0, memorySearch: entry.memorySearch, humanDelay: entry.humanDelay, heartbeat: entry.heartbeat, identity: entry.identity, groupChat: entry.groupChat, subagents: typeof entry.subagents === "object" && entry.subagents ? entry.subagents : void 0, sandbox: entry.sandbox, tools: entry.tools }; } function resolveAgentSkillsFilter(cfg, agentId) { return normalizeSkillFilter(resolveAgentConfig(cfg, agentId)?.skills); } function resolveAgentModelPrimary(cfg, agentId) { const raw = resolveAgentConfig(cfg, agentId)?.model; if (!raw) return; if (typeof raw === "string") return raw.trim() || void 0; return raw.primary?.trim() || void 0; } function resolveAgentModelFallbacksOverride(cfg, agentId) { const raw = resolveAgentConfig(cfg, agentId)?.model; if (!raw || typeof raw === "string") return; if (!Object.hasOwn(raw, "fallbacks")) return; return Array.isArray(raw.fallbacks) ? raw.fallbacks : void 0; } function resolveEffectiveModelFallbacks(params) { const agentFallbacksOverride = resolveAgentModelFallbacksOverride(params.cfg, params.agentId); if (!params.hasSessionModelOverride) return agentFallbacksOverride; const defaultFallbacks = typeof params.cfg.agents?.defaults?.model === "object" ? params.cfg.agents.defaults.model.fallbacks ?? [] : []; return agentFallbacksOverride ?? defaultFallbacks; } function resolveAgentWorkspaceDir(cfg, agentId) { const id = normalizeAgentId(agentId); const configured = resolveAgentConfig(cfg, id)?.workspace?.trim(); if (configured) return resolveUserPath(configured); if (id === resolveDefaultAgentId(cfg)) { const fallback = cfg.agents?.defaults?.workspace?.trim(); if (fallback) return resolveUserPath(fallback); return resolveDefaultAgentWorkspaceDir(process.env); } const stateDir = resolveStateDir(process.env); return path.join(stateDir, `workspace-${id}`); } function resolveAgentDir(cfg, agentId) { const id = normalizeAgentId(agentId); const configured = resolveAgentConfig(cfg, id)?.agentDir?.trim(); if (configured) return resolveUserPath(configured); const root = resolveStateDir(process.env); return path.join(root, "agents", id, "agent"); } //#endregion export { logError as A, resolveOpenClawPackageRoot as C, runExec as D, runCommandWithTimeout as E, logWarn as M, spawnWithFallback as O, loadWorkspaceBootstrapFiles as S, normalizeSkillFilter as T, DEFAULT_SOUL_FILENAME as _, resolveAgentModelPrimary as a, ensureAgentWorkspace as b, resolveDefaultAgentId as c, resolveSessionAgentIds as d, DEFAULT_AGENTS_FILENAME as f, DEFAULT_IDENTITY_FILENAME as g, DEFAULT_HEARTBEAT_FILENAME as h, resolveAgentModelFallbacksOverride as i, logInfo as j, logDebug as k, resolveEffectiveModelFallbacks as l, DEFAULT_BOOTSTRAP_FILENAME as m, resolveAgentConfig as n, resolveAgentSkillsFilter as o, DEFAULT_AGENT_WORKSPACE_DIR as p, resolveAgentDir as r, resolveAgentWorkspaceDir as s, listAgentIds as t, resolveSessionAgentId as u, DEFAULT_TOOLS_FILENAME as v, resolveOpenClawPackageRootSync as w, filterBootstrapFilesForSession as x, DEFAULT_USER_FILENAME as y };