openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
162 lines (161 loc) • 7.55 kB
JavaScript
import { D as resolveExpiresAtMsFromDurationMs, E as resolveDateTimestampMs, a as addTimerTimeoutGraceMs, b as parseFiniteNumber, m as clampTimerTimeoutMs, o as asDateTimestampMs } from "./number-coercion-CLj0HTDM.js";
import { t as asBoolean } from "./boolean-DmBL0YJK.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { $ as normalizeAgentRunTerminalReplySnapshot, rt as normalizeAgentRunTerminalReceipt } from "./openclaw-state-db-BRTnL-D8.js";
import { o as callGateway } from "./call-BWR5pPgw.js";
import { t as hasRetryableConnectionErrorCode } from "./retryable-network-errors-D2gJmBqw.js";
import { o as isOpenClawMessageToolMirrorAssistantMessage, s as isTranscriptOnlyOpenClawAssistantMessage } from "./transcript-only-openclaw-assistant-CVgy4bjA.js";
import { a as buildAgentRunTerminalOutcomeFromWaitResult, m as normalizeBlockedLivenessWaitStatus } from "./agent-run-terminal-outcome-CigeY75d.js";
import { v as normalizeAgentRunTimeoutPhase } from "./run-termination-jabvTOp5.js";
import { n as stripToolMessages, t as extractStoredAssistantText } from "./chat-history-text-C2UOMgRe.js";
//#region src/agents/run-wait.ts
/**
* Gateway-backed agent run wait helpers.
* Normalizes run wait responses, reads the latest assistant reply, and drains
* pending run sets for tools that need synchronous completion semantics.
*/
function resolveRunWaitTimeoutMs(value) {
return clampTimerTimeoutMs(parseFiniteNumber(value) ?? 1) ?? 1;
}
function resolveRunWaitDeadlineAtMs(params) {
if (params.deadlineAtMs !== void 0) return asDateTimestampMs(params.deadlineAtMs) ?? resolveDateTimestampMs(Date.now());
return resolveExpiresAtMsFromDurationMs(resolveRunWaitTimeoutMs(params.timeoutMs)) ?? resolveDateTimestampMs(Date.now());
}
function normalizeAgentWaitResult(status, runId, wait) {
const receipt = normalizeAgentRunTerminalReceipt(wait?.terminalReceipt);
const stopReason = typeof wait?.stopReason === "string" ? wait.stopReason : void 0;
const normalized = normalizeTerminalOutcomeForWait(buildAgentRunTerminalOutcomeFromWaitResult({
...wait,
status
}), status, wait?.livenessState);
return {
status: normalized.status,
error: normalized.error,
startedAt: typeof wait?.startedAt === "number" ? wait.startedAt : void 0,
endedAt: typeof wait?.endedAt === "number" ? wait.endedAt : void 0,
stopReason,
livenessState: typeof wait?.livenessState === "string" ? wait.livenessState : void 0,
yielded: wait?.yielded === true ? true : void 0,
pendingError: wait?.pendingError === true ? true : void 0,
timeoutPhase: normalizeAgentRunTimeoutPhase(wait?.timeoutPhase),
providerStarted: asBoolean(wait?.providerStarted),
terminalReply: normalizeAgentRunTerminalReplySnapshot(wait?.terminalReply),
sourceReplyDelivered: receipt?.runId === runId && receipt.sourceReplyDelivered === true ? true : void 0
};
}
function normalizeTerminalOutcomeForWait(outcome, fallbackStatus, livenessState) {
if (outcome?.reason === "hard_timeout") return {
status: outcome.status,
error: outcome.error
};
return normalizeBlockedLivenessWaitStatus({
status: outcome?.status ?? fallbackStatus,
livenessState,
error: outcome?.error
});
}
const RECOVERABLE_AGENT_WAIT_ERROR_PATTERNS = [
/gateway closed \(1006/i,
/transport close/i,
/connection loss/i,
/connection closed/i,
/gateway not connected/i,
/no active .* listener/i,
/socket hang up/i
];
/** Return true for transient gateway/transport failures that callers may retry. */
function isRecoverableAgentWaitError(error) {
const message = error?.trim();
if (!message) return false;
if (message.includes("gateway timeout") || message.includes("gateway request timeout")) return false;
return hasRetryableConnectionErrorCode(message) || RECOVERABLE_AGENT_WAIT_ERROR_PATTERNS.some((pattern) => pattern.test(message));
}
function normalizePendingRunIds(runIds) {
const seen = /* @__PURE__ */ new Set();
for (const runId of runIds) {
const normalized = runId.trim();
if (!normalized || seen.has(normalized)) continue;
seen.add(normalized);
}
return [...seen];
}
function isAssistantReplyTranscriptArtifact(message) {
return isTranscriptOnlyOpenClawAssistantMessage(message) || isOpenClawMessageToolMirrorAssistantMessage(message) || isInterSessionInputMessage(message);
}
function isInterSessionInputMessage(message) {
if (!message || typeof message !== "object" || Array.isArray(message)) return false;
const provenance = message.provenance;
return Boolean(provenance) && typeof provenance === "object" && !Array.isArray(provenance) && provenance.kind === "inter_session";
}
/** Read the latest model-authored assistant text from session history. */
async function readLatestAssistantReply(params) {
const history = await (params.callGateway ?? callGateway)({
method: "chat.history",
params: {
sessionKey: params.sessionKey,
...params.agentId ? { agentId: params.agentId } : {},
limit: params.limit ?? 50
}
});
const messages = stripToolMessages(Array.isArray(history?.messages) ? history.messages : []);
for (let i = messages.length - 1; i >= 0; i -= 1) {
const message = messages[i];
if (isAssistantReplyTranscriptArtifact(message)) continue;
const text = extractStoredAssistantText(message);
if (text?.trim()) return text;
}
}
/** Wait for one agent run through the gateway and normalize timeout/error states. */
async function waitForAgentRun(params) {
const timeoutMs = resolveRunWaitTimeoutMs(params.timeoutMs);
try {
const wait = await (params.callGateway ?? callGateway)({
method: "agent.wait",
params: {
runId: params.runId,
timeoutMs
},
timeoutMs: addTimerTimeoutGraceMs(timeoutMs, 2e3)
});
if (wait?.status === "timeout") return normalizeAgentWaitResult("timeout", params.runId, wait);
if (wait?.status === "pending") return normalizeAgentWaitResult("pending", params.runId, wait);
if (wait?.status === "error") return normalizeAgentWaitResult("error", params.runId, wait);
return normalizeAgentWaitResult("ok", params.runId, wait);
} catch (err) {
const error = formatErrorMessage(err);
return {
status: error.includes("gateway timeout") || error.includes("gateway request timeout") ? "timeout" : "error",
error,
...isRecoverableAgentWaitError(error) ? { retryableTransportError: true } : {}
};
}
}
/** Read the completed run's reply without inferring delivery from display history. */
async function waitForAgentRunReply(params) {
const wait = await waitForAgentRun(params);
return wait.status === "ok" && wait.terminalReply?.disposition === "visible" ? {
...wait,
replyText: wait.terminalReply.text
} : wait;
}
/** Wait until the current and newly spawned pending run IDs are drained or timed out. */
async function waitForAgentRunsToDrain(params) {
const deadlineAtMs = resolveRunWaitDeadlineAtMs(params);
let pendingRunIds = new Set(normalizePendingRunIds(params.initialPendingRunIds ?? params.getPendingRunIds()));
while (pendingRunIds.size > 0 && Date.now() < deadlineAtMs) {
const remainingMs = Math.max(1, deadlineAtMs - Date.now());
await Promise.allSettled([...pendingRunIds].map((runId) => waitForAgentRun({
runId,
timeoutMs: remainingMs,
callGateway: params.callGateway
})));
pendingRunIds = new Set(normalizePendingRunIds(params.getPendingRunIds()));
}
return {
timedOut: pendingRunIds.size > 0,
pendingRunIds: [...pendingRunIds],
deadlineAtMs
};
}
//#endregion
export { waitForAgentRunsToDrain as i, waitForAgentRun as n, waitForAgentRunReply as r, readLatestAssistantReply as t };