openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
174 lines (173 loc) • 6.96 kB
JavaScript
import { C as resolveExpiresAtMsFromDurationMs, S as resolveDateTimestampMs, a as addTimerTimeoutGraceMs, f as clampTimerTimeoutMs, g as parseFiniteNumber, o as asDateTimestampMs } from "./number-coercion-CJQ8TR--.js";
import { i as formatErrorMessage } from "./errors-BXgSefBE.js";
import { o as callGateway } from "./call-B5-GYOlf.js";
import { t as asBoolean } from "./boolean-By0bZpFH.js";
import { a as normalizeAgentRunTimeoutPhase, l as normalizeBlockedLivenessWaitStatus, n as buildAgentRunTerminalOutcomeFromWaitResult } from "./agent-run-terminal-outcome-wSgVLvyR.js";
import { r as stripToolMessages, t as extractAssistantText } from "./chat-history-text-D43w1rZt.js";
let runWaitDeps = { callGateway };
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, wait) {
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)
};
}
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,
/\b(ECONNRESET|ECONNREFUSED|ETIMEDOUT|EPIPE|EHOSTUNREACH|ENETUNREACH)\b/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")) return false;
return 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 resolveLatestAssistantReplySnapshot(messages) {
for (let i = messages.length - 1; i >= 0; i -= 1) {
const candidate = messages[i];
if (!candidate || typeof candidate !== "object") continue;
if (candidate.role !== "assistant") continue;
const text = extractAssistantText(candidate);
if (!text?.trim()) continue;
let fingerprint;
try {
fingerprint = JSON.stringify(candidate);
} catch {
fingerprint = text;
}
return {
text,
fingerprint
};
}
return {};
}
/** Read the latest non-tool assistant message for a session. */
async function readLatestAssistantReplySnapshot(params) {
const history = await (params.callGateway ?? runWaitDeps.callGateway)({
method: "chat.history",
params: {
sessionKey: params.sessionKey,
limit: params.limit ?? 50
}
});
return resolveLatestAssistantReplySnapshot(stripToolMessages(Array.isArray(history?.messages) ? history.messages : []));
}
/** Read only the latest assistant text for call sites that do not need fingerprints. */
async function readLatestAssistantReply(params) {
return (await readLatestAssistantReplySnapshot({
sessionKey: params.sessionKey,
limit: params.limit,
callGateway: params.callGateway
})).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 ?? runWaitDeps.callGateway)({
method: "agent.wait",
params: {
runId: params.runId,
timeoutMs
},
timeoutMs: addTimerTimeoutGraceMs(timeoutMs, 2e3)
});
if (wait?.status === "timeout") return normalizeAgentWaitResult("timeout", wait);
if (wait?.status === "pending") return normalizeAgentWaitResult("pending", wait);
if (wait?.status === "error") return normalizeAgentWaitResult("error", wait);
return normalizeAgentWaitResult("ok", wait);
} catch (err) {
const error = formatErrorMessage(err);
return {
status: error.includes("gateway timeout") ? "timeout" : "error",
error
};
}
}
/** Wait for a run and return a reply only when it differs from the supplied baseline. */
async function waitForAgentRunAndReadUpdatedAssistantReply(params) {
const wait = await waitForAgentRun({
runId: params.runId,
timeoutMs: params.timeoutMs,
callGateway: params.callGateway
});
if (wait.status !== "ok") return wait;
const latestReply = await readLatestAssistantReplySnapshot({
sessionKey: params.sessionKey,
limit: params.limit,
callGateway: params.callGateway
});
const baselineFingerprint = params.baseline?.fingerprint;
return {
status: "ok",
replyText: latestReply.text && (!baselineFingerprint || latestReply.fingerprint !== baselineFingerprint) ? latestReply.text : void 0
};
}
/** 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 { waitForAgentRunAndReadUpdatedAssistantReply as a, waitForAgentRun as i, readLatestAssistantReply as n, waitForAgentRunsToDrain as o, readLatestAssistantReplySnapshot as r, isRecoverableAgentWaitError as t };