UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

271 lines (270 loc) 14.3 kB
import { N as resolveOptionalIntegerOption, w as parseStrictPositiveInteger } from "./number-coercion-CLj0HTDM.js"; import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.js"; import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js"; import { D as freezeDiagnosticTraceContext } from "./diagnostic-events-Cwe92uV3.js"; import { o as isSilentReplyText } from "./tokens-DbeAFqg7.js"; import { d as normalizeAgentRunAttemptTerminal } from "./agent-run-terminal-outcome-CigeY75d.js"; import { d as resolveFinalAssistantVisibleText } from "./helpers-CkZ0XN7O.js"; import { n as mergeForcedEmbeddedAttemptToolsAllow } from "./attempt-tool-construction-plan-CRC43c_k.js"; import { n as buildAgentHookContextIdentityFields, t as buildAgentHookContextChannelFields } from "./hook-agent-context-kN_gMcDh.js"; //#region src/agents/embedded-agent-runner/run/agent-end-context.ts function buildEmbeddedForegroundPromptContext(run, agentDir) { const callerOrigin = run.cronCreatorAuthorityCapability?.callerOrigin; const sessionKey = run.sessionKey?.trim() || run.sessionId; const sandboxSessionKey = run.sandboxSessionKey?.trim() || sessionKey; return { agentId: run.agentId, agentDir, workspaceDir: run.workspaceDir, cwd: run.cwd, sandboxSessionKey, sandboxAgentId: run.sandboxAgentId ?? (sandboxSessionKey === sessionKey ? run.agentId : void 0), promptCacheKey: run.promptCacheKey, reasoningLevel: run.reasoningLevel, messageChannel: run.messageChannel, messageProvider: run.messageProvider, clientCaps: run.clientCaps, toolBindings: run.toolBindings, chatType: run.chatType, agentAccountId: run.agentAccountId, trigger: run.trigger, messageTo: run.messageTo, messageThreadId: run.messageThreadId, conversationToolPolicy: run.conversationToolPolicy, groupId: run.groupId, groupChannel: run.groupChannel, groupSpace: run.groupSpace, memberRoleIds: run.memberRoleIds, messageActionTurnCapability: run.messageActionTurnCapability, spawnedBy: run.spawnedBy, isCanonicalWorkspace: run.isCanonicalWorkspace, senderId: run.senderId, senderName: run.senderName, senderUsername: run.senderUsername, senderE164: run.senderE164, senderIsOwner: run.senderIsOwner, approvalReviewerDeviceId: run.approvalReviewerDeviceId, currentChannelId: run.currentChannelId, chatId: run.chatId, channelContext: run.channelContext, currentMessagingTarget: run.currentMessagingTarget, currentThreadTs: run.currentThreadTs, currentMessageId: run.currentMessageId, currentInboundAudio: run.currentInboundAudio, replyToMode: run.replyToMode, requireExplicitMessageTarget: run.requireExplicitMessageTarget, disableMessageTool: run.disableMessageTool, githubPublicationAvailable: run.githubPublicationAvailable, conversationRecall: run.conversationRecall, toolOverrides: run.toolOverrides, skillsSnapshot: run.skillsSnapshot, currentInboundEventKind: run.currentInboundEventKind, clientTools: run.clientTools, disableTools: run.disableTools, contextWindow: run.contextWindow, promptMode: run.promptMode, forceMessageTool: run.forceMessageTool, enableHeartbeatTool: run.enableHeartbeatTool, forceHeartbeatTool: run.forceHeartbeatTool, allowGatewaySubagentBinding: run.allowGatewaySubagentBinding, extraSystemPrompt: run.extraSystemPrompt, sourceReplyDeliveryMode: run.sourceReplyDeliveryMode, taskSuggestionDeliveryMode: run.taskSuggestionDeliveryMode, silentReplyPromptMode: run.silentReplyPromptMode, ownerNumbers: run.ownerNumbers, toolsAllow: run.toolsAllow, runtimePluginToolGrant: run.runtimePluginToolGrant, inputProvenance: run.inputProvenance, scheduledToolPolicy: run.scheduledToolPolicy, modelThinkingCapability: run.modelThinkingCapability, modelFallbacksOverride: run.modelFallbacksOverride, ...callerOrigin && callerOrigin.kind !== "unknown" ? { cronCreatorCallerOrigin: callerOrigin } : {} }; } function buildEmbeddedAgentEndContext(params) { const run = params.run; return { runId: run.runId, trace: params.trace, agentId: params.agentId, sessionKey: run.sessionKey, sessionId: run.sessionId, workspaceDir: run.workspaceDir, modelProviderId: run.provider, modelId: run.modelId, modelContextWindowTokens: run.contextTokenBudget ?? run.model.contextWindow, foregroundPromptContext: buildEmbeddedForegroundPromptContext({ ...run, agentId: params.agentId }, params.agentDir), authProfileId: run.authProfileId, skillWorkshopAvailable: params.skillWorkshopAvailable, compacted: params.compacted, trigger: run.trigger, ...run.config ? { config: run.config } : {}, ...buildAgentHookContextChannelFields(run), ...buildAgentHookContextIdentityFields({ trigger: run.trigger, senderId: run.senderId, chatId: run.chatId, channelContext: run.channelContext }) }; } //#endregion //#region src/agents/run-cleanup-timeout.ts /** * Agent cleanup timeout guard. * * Bounds cleanup steps so run completion cannot hang forever while preserving late-failure diagnostics. */ const AGENT_CLEANUP_STEP_TIMEOUT_MS = 1e4; const AGENT_CLEANUP_STEP_TIMEOUT_ENV = "OPENCLAW_AGENT_CLEANUP_TIMEOUT_MS"; const TRAJECTORY_FLUSH_TIMEOUT_ENV = "OPENCLAW_TRAJECTORY_FLUSH_TIMEOUT_MS"; const CLEANUP_TIMEOUT_DETAILS_MAX_CHARS = 512; const CLEANUP_TIMEOUT_DETAILS_TRUNCATED_SUFFIX = "...[truncated]"; function parseTimeoutEnvValue(value) { const trimmed = value?.trim(); if (!trimmed) return; return parseStrictPositiveInteger(trimmed); } function resolveCleanupTimeoutDetails(getTimeoutDetails) { try { const timeoutDetails = getTimeoutDetails?.()?.trim(); return timeoutDetails ? ` details=${truncateCleanupTimeoutDetails(timeoutDetails)}` : ""; } catch (error) { return ` detailsError=${truncateCleanupTimeoutDetails(formatErrorMessage(error))}`; } } function truncateCleanupTimeoutDetails(value) { if (value.length <= CLEANUP_TIMEOUT_DETAILS_MAX_CHARS) return value; return `${truncateUtf16Safe(value, Math.max(0, 498))}${CLEANUP_TIMEOUT_DETAILS_TRUNCATED_SUFFIX}`; } function resolveAgentCleanupStepTimeoutMs(params) { const explicitTimeoutMs = resolveOptionalIntegerOption(params.timeoutMs, { min: 1 }); if (explicitTimeoutMs !== void 0) return explicitTimeoutMs; const env = params.env ?? process.env; if (params.step === "openclaw-trajectory-flush") { const trajectoryTimeoutMs = parseTimeoutEnvValue(env[TRAJECTORY_FLUSH_TIMEOUT_ENV]); if (trajectoryTimeoutMs !== void 0) return trajectoryTimeoutMs; } return parseTimeoutEnvValue(env[AGENT_CLEANUP_STEP_TIMEOUT_ENV]) ?? AGENT_CLEANUP_STEP_TIMEOUT_MS; } /** Run one cleanup step with timeout logging and late-rejection handling. */ async function runAgentCleanupStep(params) { const timeoutMs = resolveAgentCleanupStepTimeoutMs({ step: params.step, timeoutMs: params.timeoutMs, env: params.env }); let timeoutHandle; let timedOut = false; const cleanupPromise = Promise.resolve().then(params.cleanup); const observedCleanupPromise = cleanupPromise.catch((error) => { if (!timedOut) params.log.warn(`agent cleanup failed: runId=${params.runId} sessionId=${params.sessionId} step=${params.step} error=${formatErrorMessage(error)}`); }); const timeoutPromise = new Promise((resolve) => { timeoutHandle = setTimeout(() => { timedOut = true; resolve("timeout"); }, timeoutMs); timeoutHandle.unref?.(); }); const result = await Promise.race([observedCleanupPromise.then(() => "done"), timeoutPromise]); if (timeoutHandle) clearTimeout(timeoutHandle); if (result === "timeout") { const details = resolveCleanupTimeoutDetails(params.getTimeoutDetails); params.log.warn(`agent cleanup timed out: runId=${params.runId} sessionId=${params.sessionId} step=${params.step} timeoutMs=${timeoutMs}${details}`); cleanupPromise.catch((error) => { params.log.warn(`agent cleanup rejected after timeout: runId=${params.runId} sessionId=${params.sessionId} step=${params.step} error=${formatErrorMessage(error)}`); }); } } //#endregion //#region src/agents/embedded-agent-runner/run/attempt-tool-run-context.ts /** * Builds the shared tool-run context for embedded and plugin harness attempts. */ function buildEmbeddedAttemptToolRunContext(params) { const { currentInboundAudio, replyOperation } = params; const runtimeToolAllowlist = mergeForcedEmbeddedAttemptToolsAllow(params.toolsAllow, { forceMessageTool: params.forceMessageTool, forceToolNames: params.swarmCollector && params.swarmOutputSchema ? ["structured_output"] : void 0 }); return { requesterThinkingLevel: params.thinkLevel, trigger: params.trigger, jobId: params.jobId, memoryFlushWritePath: params.memoryFlushWritePath, swarmCollector: params.swarmCollector, swarmOutputSchema: params.swarmOutputSchema, currentInboundAudio, hasCurrentInboundAudio: () => currentInboundAudio === true || replyOperation?.acceptedSteeredInboundAudio === true, ...runtimeToolAllowlist ? { runtimeToolAllowlist } : {}, ...params.conversationToolPolicy ? { conversationToolPolicy: params.conversationToolPolicy } : {}, ...params.trace ? { trace: freezeDiagnosticTraceContext(params.trace) } : {} }; } //#endregion //#region src/agents/harness/settled-turn-finalization-outcome.ts /** A normally stopped finalizer exhausted its visible answer without failing or using tools. */ var EmptySettledTurnFinalizationError = class extends Error { constructor(result) { super("Settled-turn finalization completed without a visible answer"); this.result = result; this.name = "EmptySettledTurnFinalizationError"; } }; //#endregion //#region src/agents/harness/settled-turn-finalization-result.ts const ALLOWED_SETTLED_FINALIZATION_RESULT_KEYS = /* @__PURE__ */ new Set([ "assistant", "usage", "assistantTranscriptOwned", "assistantTranscriptIdempotencyKey", "assistantMessageIndex", "diagnosticTrace" ]); function assistantContainsToolCall(assistant) { return assistant.content.some((block) => block !== null && typeof block === "object" && block.type === "toolCall"); } /** * Validates the deliberately narrow finalizer result before core turns it into * a terminal reply. Capability and delivery fields cannot cross this contract. */ function assertSettledTurnFinalizationResult(result) { const unknownKey = Object.keys(result).find((key) => !ALLOWED_SETTLED_FINALIZATION_RESULT_KEYS.has(key)); if (unknownKey) throw new Error(`Settled-turn finalization returned unsupported result field: ${unknownKey}`); if (!result.assistant || result.assistant.role !== "assistant") throw new Error("Settled-turn finalization did not return an assistant message"); if (result.assistant.stopReason === "toolUse" || assistantContainsToolCall(result.assistant)) throw new Error("Settled-turn finalization returned a tool call"); if (result.assistant.stopReason !== "stop") throw new Error(`Settled-turn finalization returned unsuccessful stop reason: ${result.assistant.stopReason}`); if (result.assistantMessageIndex !== void 0 && (!Number.isSafeInteger(result.assistantMessageIndex) || result.assistantMessageIndex < 0)) throw new Error("Settled-turn finalization returned an invalid assistant message index"); resolveSettledTurnFinalizationText(result); return result; } function resolveSettledTurnFinalizationText(result) { const text = resolveFinalAssistantVisibleText(result.assistant); if (!text || isSilentReplyText(text)) throw new EmptySettledTurnFinalizationError(result); return text; } /** * Projects a harness-owned full attempt engine into the narrow finalization * contract, rejecting canonical failure or capability evidence first. */ function projectSettledTurnFinalizationAttemptResult(result) { if (("terminal" in result ? result.terminal : normalizeAgentRunAttemptTerminal(result)).kind !== "ok" || (result.compactionCount ?? 0) > 0 || result.promptTimeoutOutcome || result.preflightRecovery || result.beforeAgentFinalizeRevisionReason || result.codexAppServerFailure || result.cloudCodeAssistFormatError) throw new Error("Settled-turn finalization attempt did not complete successfully"); if (result.toolMetas.length > 0 || result.itemLifecycle.startedCount > 0 || result.itemLifecycle.completedCount > 0 || result.itemLifecycle.activeCount > 0 || result.replayMetadata.hadPotentialSideEffects || !result.replayMetadata.replaySafe || result.currentAttemptReplayMetadata?.hadPotentialSideEffects || result.currentAttemptReplayMetadata && !result.currentAttemptReplayMetadata.replaySafe || (result.clientToolCalls?.length ?? 0) > 0 || (result.acceptedSessionSpawns?.length ?? 0) > 0 || result.didSendViaMessagingTool || result.didDeliverSourceReplyViaMessageTool || result.didSendDeterministicApprovalPrompt || result.messagingToolSentTexts.length > 0 || result.messagingToolSentMediaUrls.length > 0 || result.messagingToolSentTargets.length > 0 || (result.messagingToolSourceReplyPayloads?.length ?? 0) > 0 || result.heartbeatToolResponse || (result.toolMediaUrls?.length ?? 0) > 0 || (result.hostOwnedToolMediaUrls?.length ?? 0) > 0 || result.toolAudioAsVoice || result.toolTrustedLocalMedia || result.hasToolMediaBlockReply || result.lastToolError || (result.successfulCronAdds ?? 0) > 0 || result.yieldDetected) throw new Error("Settled-turn finalization attempt reported capability activity"); const assistant = result.currentAttemptCompletedAssistant; if (!assistant) throw new Error("Settled-turn finalization attempt returned no completed assistant message"); return assertSettledTurnFinalizationResult({ assistant, ...result.attemptUsage ? { usage: result.attemptUsage } : {}, ...result.assistantTranscriptOwned ? { assistantTranscriptOwned: true, ...result.assistantTranscriptIdempotencyKey ? { assistantTranscriptIdempotencyKey: result.assistantTranscriptIdempotencyKey } : {} } : result.lastAssistantTextMessageIndex !== void 0 ? { assistantMessageIndex: result.lastAssistantTextMessageIndex } : {}, ...result.diagnosticTrace ? { diagnosticTrace: result.diagnosticTrace } : {} }); } //#endregion export { buildEmbeddedAttemptToolRunContext as a, buildEmbeddedForegroundPromptContext as c, EmptySettledTurnFinalizationError as i, projectSettledTurnFinalizationAttemptResult as n, runAgentCleanupStep as o, resolveSettledTurnFinalizationText as r, buildEmbeddedAgentEndContext as s, assertSettledTurnFinalizationResult as t };