UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

279 lines (278 loc) • 11.7 kB
import { l as normalizeOptionalString } from "./string-coerce-6kR-86XC.mjs"; import { a as resolveAgentDir, d as resolveAgentWorkspaceDir } from "./agent-scope-config-BU4qc9bA.mjs"; import { A as parseAgentSessionKey } from "./session-key-BC4m_Ly5.mjs"; import { n as parseSqliteSessionFileMarker, t as formatSqliteSessionFileMarker } from "./legacy-sqlite-marker-COPKCuIN.mjs"; import "./string-coerce-runtime-B1b8UNzb.mjs"; import "./routing-D93XBoA5.mjs"; import { c as readSessionTranscriptEvents } from "./session-transcript-runtime-BP9tuXFg.mjs"; import { c as patchSessionEntry, t as cleanupSessionLifecycleArtifacts } from "./session-store-runtime-B4-uDJLY.mjs"; import "./agent-runtime-BRyPGGZC.mjs"; import { s as ACTIVE_MEMORY_RECALL_LANE, t as ACTIVE_MEMORY_CLEANUP_RETRY_DELAYS_MS } from "./types-DgEaE0BK.mjs"; import { c as resolvePersistentTranscriptBaseDir, l as resolveSafeTranscriptDir, r as isMissingRegisteredMemoryToolsError } from "./config-D1P2Clvx.mjs"; import { r as buildRecallPrompt } from "./prompt-COnUktx-.mjs"; import { m as toSingleLineErrorMessage } from "./recall-state-CRzzGoCJ.mjs"; import { a as resolveRecallRunChannelContext } from "./session-D9QlhmX-.mjs"; import { t as readMergedActiveMemoryTranscriptState } from "./transcript-watch-MBrSC4dW.mjs"; import { i as readMemoryToolResultEvidence, o as readPartialAssistantTextFromSources, t as attachPartialTimeoutData } from "./transcript-result-LCMLHxyT.mjs"; import path from "node:path"; import fs from "node:fs/promises"; import crypto from "node:crypto"; import { setTimeout } from "node:timers/promises"; //#region extensions/active-memory/recall-run.ts async function persistActiveMemoryTranscriptArtifact(params) { const events = /* @__PURE__ */ new Set(); for (const source of params.sources) { let sourceEvents; try { sourceEvents = await readSessionTranscriptEvents(source); } catch { continue; } for (const event of sourceEvents) events.add(JSON.stringify(event)); } if (events.size === 0) return; await fs.mkdir(path.dirname(params.sessionFile), { recursive: true, mode: 448 }); await fs.writeFile(params.sessionFile, `${[...events].join("\n")}\n`, { encoding: "utf8", mode: 384 }); } async function cleanupActiveMemoryRecallSession(params) { const sessionKeySegmentPrefix = parseAgentSessionKey(params.sessionKey)?.rest ?? params.sessionKey; let lastError; for (const delayMs of ACTIVE_MEMORY_CLEANUP_RETRY_DELAYS_MS) { if (delayMs > 0) await setTimeout(delayMs); try { const result = await cleanupSessionLifecycleArtifacts({ agentId: params.agentId, archiveRemovedEntryTranscripts: false, orphanTranscriptMinAgeMs: 0, sessionKeySegmentPrefix, storePath: params.storePath, transcriptContentMarker: `"runId":"${params.sessionId}"` }); if (result.removedEntries !== 1) throw new Error(`active-memory recall cleanup removed ${String(result.removedEntries)} sessions`); return; } catch (error) { lastError = error; } } throw lastError instanceof Error ? lastError : /* @__PURE__ */ new Error(`active-memory recall cleanup failed: ${String(lastError)}`); } async function runRecallSubagent(params) { const workspaceDir = resolveAgentWorkspaceDir(params.runtimeConfig, params.agentId); const agentDir = resolveAgentDir(params.runtimeConfig, params.agentId); const modelRef = params.modelRef; if (!modelRef) return { rawReply: "NONE" }; const subagentSessionId = `active-memory-${Date.now().toString(36)}-${crypto.randomUUID().slice(0, 8)}`; const parentSessionKey = params.parentSessionKey; const subagentScope = parentSessionKey ?? params.sessionId ?? crypto.randomUUID(); const subagentSuffix = `active-memory:${crypto.createHash("sha1").update(`${subagentScope}:${params.query}:${subagentSessionId}`).digest("hex").slice(0, 12)}`; const subagentSessionKey = parentSessionKey ? `${parentSessionKey}:${subagentSuffix}` : `agent:${params.agentId}:${subagentSuffix}`; const persistedDir = params.config.persistTranscripts ? resolveSafeTranscriptDir(resolvePersistentTranscriptBaseDir(params.api, params.agentId), params.config.transcriptDir) : void 0; const artifactSessionFile = persistedDir !== void 0 ? path.join(persistedDir, `${subagentSessionId}.jsonl`) : void 0; const storePath = params.storePath; const runtimeSessionFile = formatSqliteSessionFileMarker({ agentId: params.agentId, sessionId: subagentSessionId, storePath }); const runtimeSource = { agentId: params.agentId, sessionId: subagentSessionId, sessionKey: subagentSessionKey, storePath }; const transcriptSources = [runtimeSource]; let harnessHasUsableMemoryResult = false; let harnessHasUnavailableMemorySearchResult = false; let transcriptArtifactPersisted = false; let runtimeSessionCreated = false; let resultStatus; const cleanupRecallResources = async () => { try { if (runtimeSessionCreated) { if (artifactSessionFile && !transcriptArtifactPersisted) await persistActiveMemoryTranscriptArtifact({ sources: transcriptSources, sessionFile: artifactSessionFile }).catch((error) => { const message = toSingleLineErrorMessage(error); params.api.logger.debug?.(`active-memory: failed to persist recall transcript ${artifactSessionFile}: ${message}`); }); await cleanupActiveMemoryRecallSession({ agentId: params.agentId, sessionId: subagentSessionId, sessionKey: subagentSessionKey, storePath }).catch((error) => { const message = toSingleLineErrorMessage(error); params.api.logger.warn?.(`active-memory: failed to clean up recall session ${subagentSessionKey}: ${message}`); throw error; }); } } catch (error) { attachPartialTimeoutData(error, { cleanupFailed: true }); throw error; } }; try { const runtimeEntry = { pluginOwnerId: params.api.id, sessionId: subagentSessionId, sessionFile: runtimeSessionFile, updatedAt: Date.now() }; if ((await patchSessionEntry({ agentId: params.agentId, fallbackEntry: runtimeEntry, replaceEntry: true, sessionKey: subagentSessionKey, skipMaintenance: true, storePath, update: (_entry, context) => context.existingEntry ? null : runtimeEntry }))?.sessionId !== subagentSessionId) throw new Error(`active-memory recall session already exists: ${subagentSessionKey}`); runtimeSessionCreated = true; params.onTranscriptSources?.(transcriptSources); if (persistedDir) { await fs.mkdir(persistedDir, { recursive: true, mode: 448 }); await fs.chmod(persistedDir, 448).catch(() => void 0); } const prompt = buildRecallPrompt({ config: params.config, query: params.query, searchQuery: params.searchQuery }); const { messageChannel, messageProvider } = resolveRecallRunChannelContext({ api: params.api, agentId: params.agentId, sessionKey: parentSessionKey, sessionId: params.sessionId, messageProvider: params.messageProvider, channelId: params.channelId }); const embeddedTimeoutMs = params.config.timeoutMs + params.config.setupGraceTimeoutMs; const result = await params.api.runtime.agent.runEmbeddedAgent({ sessionId: subagentSessionId, sessionKey: subagentSessionKey, agentId: params.agentId, sessionTarget: { agentId: params.agentId, sessionId: subagentSessionId, sessionKey: subagentSessionKey, storePath }, messageChannel, messageProvider, sessionFile: runtimeSessionFile, workspaceDir, agentDir, config: params.runtimeConfig, prompt, provider: modelRef.provider, model: modelRef.model, lane: ACTIVE_MEMORY_RECALL_LANE, timeoutMs: embeddedTimeoutMs, runId: subagentSessionId, trigger: "manual", conversationRecall: params.conversationRecall, toolsAllow: [...params.config.toolsAllow], disableMessageTool: true, allowGatewaySubagentBinding: true, bootstrapContextMode: "lightweight", verboseLevel: "off", thinkLevel: params.config.thinking, fastMode: params.fastMode, reasoningLevel: "off", silentExpected: true, authProfileFailurePolicy: "local", cliBackendDispatch: "subscription-auth", cleanupBundleMcpOnRunEnd: true, abortSignal: params.abortSignal, onAgentToolResult: (event) => { const evidence = readMemoryToolResultEvidence({ ...event, toolsAllow: params.config.toolsAllow }); harnessHasUsableMemoryResult ||= evidence.hasUsableMemoryResult; harnessHasUnavailableMemorySearchResult ||= evidence.hasUnavailableMemorySearchResult; } }).finally(params.onEmbeddedRunSettled); resultStatus = result.meta.error ? "failed" : void 0; const agentMeta = result.meta.agentMeta; const activeSessionFile = normalizeOptionalString(agentMeta?.sessionFile); const activeSessionId = parseSqliteSessionFileMarker(activeSessionFile)?.sessionId ?? (activeSessionFile === subagentSessionKey ? normalizeOptionalString(agentMeta?.sessionId) : void 0); if (activeSessionId && activeSessionId !== subagentSessionId) transcriptSources.push({ ...runtimeSource, sessionId: activeSessionId }); params.onTranscriptSources?.(transcriptSources); if (params.abortSignal?.aborted) { const reason = params.abortSignal.reason; if (reason instanceof Error) throw reason; const abortErr = reason !== void 0 ? new Error("Operation aborted", { cause: reason }) : /* @__PURE__ */ new Error("Operation aborted"); abortErr.name = "AbortError"; throw abortErr; } const rawReply = (result.payloads ?? []).filter((payload) => payload.isError !== true).map((payload) => payload.text?.trim() ?? "").filter(Boolean).join("\n").trim(); if (artifactSessionFile) { await persistActiveMemoryTranscriptArtifact({ sources: transcriptSources, sessionFile: artifactSessionFile }); transcriptArtifactPersisted = true; } const transcriptState = await readMergedActiveMemoryTranscriptState({ sources: transcriptSources, toolsAllow: params.config.toolsAllow }); return { rawReply: rawReply || "NONE", resultStatus, transcriptPath: artifactSessionFile, searchDebug: transcriptState.searchDebug, hasUsableMemoryResult: transcriptState.hasUsableMemoryResult || harnessHasUsableMemoryResult, hasUnavailableMemorySearchResult: transcriptState.hasUnavailableMemorySearchResult || harnessHasUnavailableMemorySearchResult }; } catch (error) { if (params.abortSignal?.aborted) { const partialReply = await readPartialAssistantTextFromSources(transcriptSources); const transcriptState = await readMergedActiveMemoryTranscriptState({ sources: transcriptSources, toolsAllow: params.config.toolsAllow }); attachPartialTimeoutData(error, { rawReply: partialReply ?? void 0, resultStatus, searchDebug: transcriptState.searchDebug, hasUnavailableMemorySearchResult: transcriptState.hasUnavailableMemorySearchResult || harnessHasUnavailableMemorySearchResult, hasUsableMemoryResult: transcriptState.hasUsableMemoryResult || harnessHasUsableMemoryResult }); } if (!params.abortSignal?.aborted && isMissingRegisteredMemoryToolsError(error, params.config.toolsAllow)) { params.api.logger.debug?.(`active-memory: no configured memory tools available; skipping sub-agent`); return { rawReply: "NONE", resultStatus: "unavailable" }; } if (!params.abortSignal?.aborted) { const message = toSingleLineErrorMessage(error); params.api.logger.warn?.(`active-memory: memory sub-agent failed, skipping recall: ${message}`); return { rawReply: "NONE", resultStatus: "failed" }; } throw error; } finally { await cleanupRecallResources(); } } //#endregion export { runRecallSubagent as t };