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