openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
394 lines (393 loc) • 16.4 kB
JavaScript
import { $ as normalizeAgentRunTerminalReplySnapshot, Z as buildAgentRunTerminalReplySnapshot, rt as normalizeAgentRunTerminalReceipt, tt as formatAgentRunRouteChange } from "./openclaw-state-db-BRTnL-D8.js";
import { C as requireActivePluginRegistry } from "./runtime-BL4wZfTq.js";
import { i as buildAgentRunTerminalOutcomeFromLifecycleEvent } from "./agent-run-terminal-outcome-CigeY75d.js";
import { m as resolveAgentRunAbortLifecycleFields } from "./run-termination-jabvTOp5.js";
import { t as runWithModelFallback } from "./model-fallback-runner-DfBb8xXp.js";
import { i as createContextEngineLogicalTurnLease, r as finalizeAcceptedContextEngineTurn, t as discardContextEngineTurnAttemptIntent } from "./context-engine-turn-attempt-DYtIyzTH.js";
import { c as selectAgentHarness } from "./selection-CgLPGlZh.js";
import { t as ensureSelectedAgentHarnessPlugin } from "./runtime-plugin-D91_vuOE.js";
import { i as mergeEmbeddedAgentRunResultForModelFallbackExhaustion, t as classifyEmbeddedAgentRunResultForModelFallback } from "./result-fallback-classifier-C3DQO8OC.js";
//#region src/agents/embedded-agent-runner/run-entry.ts
const PRESERVED_FOLLOWUP_RESULT_CODES = /* @__PURE__ */ new Set([
"empty_result",
"reasoning_only_result",
"planning_only_result"
]);
function preserveFollowupResultForDelivery(classification) {
if (!classification || !("code" in classification) || !classification.code || !PRESERVED_FOLLOWUP_RESULT_CODES.has(classification.code)) return classification;
return {
...classification,
preserveResultOnExhaustion: true,
preserveResultPriority: -1
};
}
function resolveTerminalOutcome(params) {
const meta = params.result.meta;
return buildAgentRunTerminalOutcomeFromLifecycleEvent({
phase: params.fallbackExhausted || meta.error ? "error" : "end",
data: {
...meta,
error: meta.error?.message
}
});
}
function canAdvanceContextEngineTurn(params) {
const meta = params.result.meta;
return params.fallbackOutcome === "completed" && params.terminal.outcome.status === "ok" && meta.yielded !== true && meta.aborted !== true && meta.error === void 0 && meta.timeoutPhase === void 0 && meta.stopReason !== "error" && meta.stopReason !== "timeout";
}
function mergeRunEntryExecutionTrace(params) {
const currentTrace = params.result.meta.executionTrace;
const winnerProvider = params.terminalStatus === "ok" ? currentTrace?.winnerProvider ?? params.provider : void 0;
const winnerModel = params.terminalStatus === "ok" ? currentTrace?.winnerModel ?? params.model : void 0;
const outerAttempts = params.fallbackAttempts.map((attempt) => ({
provider: attempt.provider,
model: attempt.model,
result: attempt.reason === "timeout" ? "timeout" : "candidate_failed",
...attempt.reason ? { reason: attempt.reason } : {},
...typeof attempt.status === "number" ? { status: attempt.status } : {}
}));
const innerAttempts = (currentTrace?.attempts ?? []).filter((attempt) => attempt.result !== "success");
const winnerAttempt = currentTrace?.attempts?.findLast((attempt) => attempt.result === "success" && attempt.provider === winnerProvider && attempt.model === winnerModel);
const attempts = [
...outerAttempts,
...innerAttempts,
...winnerProvider && winnerModel ? [winnerAttempt ?? {
provider: winnerProvider,
model: winnerModel,
result: "success"
}] : []
];
const terminalReceipt = params.result.meta.agentMeta?.terminalReceipt;
const requested = {
provider: params.requestedProvider,
model: params.requestedModel
};
const agentMeta = terminalReceipt ? {
...params.result.meta.agentMeta,
terminalReceipt: {
...terminalReceipt,
requested,
rerouted: terminalReceipt.rerouted || terminalReceipt.effective.provider !== requested.provider || terminalReceipt.effective.model !== requested.model
}
} : params.result.meta.agentMeta;
return {
...params.result,
meta: {
...params.result.meta,
agentMeta,
executionTrace: {
...currentTrace,
winnerProvider,
winnerModel,
attempts: attempts.length > 0 ? attempts : void 0,
fallbackUsed: currentTrace?.fallbackUsed === true || outerAttempts.length > 0
}
}
};
}
function buildTerminal(params) {
const meta = params.result.meta;
const outcome = params.outcome;
const internalReply = params.result.messagingToolSourceReplyPayloads?.findLast((payload) => payload.sourceReplyFinal === true);
let terminalReply = normalizeAgentRunTerminalReplySnapshot(meta.terminalReply) ?? buildAgentRunTerminalReplySnapshot(internalReply ? { visibleText: internalReply.text } : {
visibleText: meta.finalAssistantVisibleText,
rawText: meta.finalAssistantRawText,
terminalReplyKind: meta.terminalReplyKind
});
const agentMeta = meta.agentMeta;
const normalizedTerminalReceipt = normalizeAgentRunTerminalReceipt(agentMeta?.terminalReceipt) ?? (params.result.sourceReplyDelivered && agentMeta?.provider && agentMeta.model ? {
runId: params.runId,
sessionId: params.sessionId,
turnId: params.runId,
requested: params.requested,
effective: {
provider: agentMeta.provider,
model: agentMeta.model,
responseModel: agentMeta.model
},
successfulToolNames: ["message"],
sourceReplyDelivered: true,
rerouted: agentMeta.provider !== params.requested.provider || agentMeta.model !== params.requested.model
} : void 0);
const terminalReceipt = normalizedTerminalReceipt?.runId === params.runId ? {
...normalizedTerminalReceipt,
terminalDisposition: terminalReply.disposition === "visible" ? "visible" : "not-visible"
} : void 0;
const modelRouteChange = formatAgentRunRouteChange(terminalReceipt, params.runId);
if (modelRouteChange && terminalReply.disposition === "visible") terminalReply = {
...terminalReply,
modelRouteChange
};
const metadata = { terminalReply };
if (terminalReceipt) metadata.terminalReceipt = terminalReceipt;
if (params.behavior.kind === "channel-delivery" || params.behavior.kind === "followup-delivery") for (const key of [
"stopReason",
"yielded",
"timeoutPhase",
"providerStarted",
"aborted",
"livenessState",
"replayInvalid"
]) {
if (!Object.hasOwn(meta, key)) continue;
metadata[key] = key in outcome ? outcome[key] : meta[key];
}
else {
for (const key of [
"stopReason",
"livenessState",
"timeoutPhase",
"providerStarted"
]) if (outcome[key] !== void 0) metadata[key] = outcome[key];
if (typeof meta.aborted === "boolean") metadata.aborted = meta.aborted;
if (meta.replayInvalid === true) metadata.replayInvalid = true;
if (meta.yielded === true) metadata.yielded = true;
}
return {
outcome,
metadata
};
}
/** Runs one logical turn across model candidates and advances only the accepted winner. */
async function runEmbeddedAgentEntry(params) {
const contextEngineLogicalTurnLease = await createContextEngineLogicalTurnLease({
config: params.selection.cfg,
agentDir: params.selection.agentDir,
workspaceDir: params.harness.workspaceDir
});
let unsettledContextEngineTurnAttempt;
let candidateIndex = 0;
const committedSideEffect = params.behavior.kind === "command-rpc" ? params.behavior.hasCommittedSideEffect : void 0;
const readChannelDeliveryEvidence = params.behavior.kind === "channel-delivery" ? params.behavior.readDeliveryEvidence : void 0;
const preparedHarnessRuntimes = /* @__PURE__ */ new Set();
const prepareHarnessRuntime = async (candidate) => {
const key = [
candidate.provider,
candidate.model,
candidate.agentHarnessRuntimeOverride ?? ""
].join("\0");
if (preparedHarnessRuntimes.has(key)) return;
const prepare = () => ensureSelectedAgentHarnessPlugin({
config: params.selection.cfg,
provider: candidate.provider,
modelId: candidate.model,
agentId: params.identity.agentId,
sessionKey: params.harness.sessionKey,
agentHarnessId: candidate.agentHarnessRuntimeOverride,
agentHarnessRuntimeOverride: candidate.agentHarnessRuntimeOverride,
workspaceDir: params.harness.workspaceDir,
pluginRegistry: requireActivePluginRegistry()
});
if (params.harness.preparation.kind === "measured") await params.harness.preparation.run(prepare);
else await prepare();
preparedHarnessRuntimes.add(key);
};
const canFallbackAfterError = committedSideEffect ? () => !committedSideEffect() : readChannelDeliveryEvidence ? () => {
const evidence = readChannelDeliveryEvidence();
return !evidence.hasDirectlySentBlockReply && !evidence.hasBlockReplyPipelineOutput;
} : void 0;
try {
const fallbackResult = await runWithModelFallback({
...params.selection,
...params.identity,
abortSignal: params.abortSignal,
resolveAgentHarnessRuntimeOverride: params.harness.resolveRuntimeOverride,
prepareCandidateChain: async (candidates) => {
for (const candidate of candidates) try {
const agentHarnessRuntimeOverride = params.harness.resolveRuntimeOverride(candidate.provider, candidate.model);
await prepareHarnessRuntime({
provider: candidate.provider,
model: candidate.model,
...agentHarnessRuntimeOverride ? { agentHarnessRuntimeOverride } : {}
});
const host = params.harness.resolveContextEngineHost?.(candidate.provider, candidate.model) ?? (() => {
const harness = selectAgentHarness({
provider: candidate.provider,
modelId: candidate.model,
config: params.selection.cfg,
agentId: params.identity.agentId,
sessionKey: params.harness.sessionKey,
agentHarnessRuntimeOverride
});
return {
id: `agent-harness:${harness.id}`,
label: `agent harness "${harness.id}"`,
capabilities: harness.contextEngineHostCapabilities ?? []
};
})();
contextEngineLogicalTurnLease.selectForHost({
host,
operation: "agent-run",
requiresDurableCommit: false
});
} catch {
contextEngineLogicalTurnLease.degradeBeforeStart("a model fallback candidate harness could not be validated before dispatch");
return;
}
},
prepareAgentHarnessRuntime: prepareHarnessRuntime,
onFallbackStep: params.onFallbackStep,
...params.behavior.kind === "maintenance" ? {} : { classifyResult: ({ result }) => result.classification },
...canFallbackAfterError ? { canFallbackAfterError } : {},
...params.behavior.kind === "maintenance" ? {} : { mergeExhaustedResult: ({ latestResult, preferredResult }) => ({
result: mergeEmbeddedAgentRunResultForModelFallbackExhaustion({
latestResult: latestResult.result,
preferredResult: preferredResult.result
}),
turnAttempt: latestResult.turnAttempt
}) },
run: async (provider, model, options) => {
if (!options) throw new Error("Model fallback attempt is missing routing provenance");
const isFallbackRetry = candidateIndex > 0;
candidateIndex += 1;
let contextEngineTurnCandidate;
let classified;
const classifyResult = (result) => {
if (!classified || classified.result !== result) {
const classification = params.behavior.kind === "maintenance" ? void 0 : classifyEmbeddedAgentRunResultForModelFallback({
result,
provider,
model,
...readChannelDeliveryEvidence?.()
});
const effectiveClassification = params.behavior.kind === "followup-delivery" ? preserveFollowupResultForDelivery(classification) : classification;
classified = {
result,
value: effectiveClassification && committedSideEffect?.() ? void 0 : effectiveClassification
};
}
return classified.value;
};
const result = await params.runCandidate(provider, model, {
classifyResult,
allowTransientCooldownProbe: options?.allowTransientCooldownProbe,
isFinalFallbackAttempt: options?.isFinalFallbackAttempt,
isFallbackRetry,
modelRoutingProvenance: options.modelRoutingProvenance,
contextEngineLogicalTurnLease,
onContextEngineTurnCandidate: (facts) => {
contextEngineTurnCandidate = facts;
unsettledContextEngineTurnAttempt = facts;
}
});
return {
result,
classification: classifyResult(result),
turnAttempt: contextEngineTurnCandidate
};
}
});
const abortFields = params.behavior.kind === "command-rpc" ? resolveAgentRunAbortLifecycleFields(params.abortSignal) : {};
const candidateResult = abortFields.aborted === true ? {
...fallbackResult.result.result,
meta: {
...fallbackResult.result.result.meta,
...abortFields
}
} : fallbackResult.result.result;
const outcome = fallbackResult.outcome === "exhausted" ? "exhausted" : "completed";
const terminalOutcome = resolveTerminalOutcome({
result: candidateResult,
fallbackExhausted: outcome === "exhausted"
});
const result = mergeRunEntryExecutionTrace({
result: candidateResult,
terminalStatus: terminalOutcome.status,
provider: fallbackResult.provider,
model: fallbackResult.model,
requestedProvider: params.selection.provider,
requestedModel: params.selection.model,
fallbackAttempts: fallbackResult.attempts
});
const settledResult = {
...fallbackResult,
outcome,
result
};
const terminal = buildTerminal({
result,
outcome: terminalOutcome,
behavior: params.behavior,
runId: params.identity.runId,
requested: {
provider: params.selection.provider,
model: params.selection.model
},
sessionId: params.identity.sessionId
});
const acceptedTerminal = !params.abortSignal?.aborted && canAdvanceContextEngineTurn({
result,
fallbackOutcome: settledResult.outcome,
terminal
});
let releaseAcceptedTerminalWork;
if (acceptedTerminal) {
const acceptedTerminalWork = await params.onAcceptedTerminal?.();
if (typeof acceptedTerminalWork === "function") releaseAcceptedTerminalWork = acceptedTerminalWork;
}
try {
if (fallbackResult.result.turnAttempt) {
if (acceptedTerminal) await finalizeAcceptedContextEngineTurn({
facts: fallbackResult.result.turnAttempt,
lease: contextEngineLogicalTurnLease
});
else discardContextEngineTurnAttemptIntent({
facts: fallbackResult.result.turnAttempt,
lease: contextEngineLogicalTurnLease
});
unsettledContextEngineTurnAttempt = void 0;
}
} finally {
releaseAcceptedTerminalWork?.();
}
let sessionOverrideSettled = false;
const settleSessionOverride = async () => {
if (sessionOverrideSettled) return;
sessionOverrideSettled = true;
if (settledResult.outcome === "completed" && params.sessionOverride.kind === "reconcile-completed") await params.sessionOverride.reconcile({
provider: settledResult.provider,
model: settledResult.model
});
};
return {
...settledResult,
terminal,
settleSessionOverride
};
} finally {
if (unsettledContextEngineTurnAttempt) discardContextEngineTurnAttemptIntent({
facts: unsettledContextEngineTurnAttempt,
lease: contextEngineLogicalTurnLease
});
await contextEngineLogicalTurnLease.dispose();
}
}
//#endregion
//#region src/auto-reply/reply/agent-runner-compaction-accounting.ts
function hasSameCompactionWriter(previous, current) {
return previous !== void 0 && previous.agentId === current.agentId && previous.sessionKey === current.sessionKey && previous.storePath === current.storePath && previous.lifecycleRevision === current.lifecycleRevision && previous.activeWriterRunId === current.activeWriterRunId;
}
/** A later opaque candidate may lose freshness, but must never fabricate it. */
function invalidateTurnCompactionContext(compaction) {
compaction.durable = compaction.durable.map((fact) => ({
...fact,
currentContextSnapshot: { tokens: void 0 }
}));
}
/** Fold same-writer facts; only an ordered snapshot may refresh context. */
function recordTurnCompaction(compaction, fact) {
if (fact.count < 0) return;
compaction.count += fact.count;
if (fact.kind !== "durable") return;
const index = compaction.durable.findIndex(({ target }) => hasSameCompactionWriter(target, fact.target));
const previous = compaction.durable[index];
if (!previous && fact.count === 0) return;
if (previous) compaction.durable.splice(index, 1);
compaction.durable.push({
...fact,
count: (previous?.count ?? 0) + fact.count,
currentContextSnapshot: fact.currentContextSnapshot ?? previous?.currentContextSnapshot
});
}
//#endregion
export { runEmbeddedAgentEntry as i, invalidateTurnCompactionContext as n, recordTurnCompaction as r, hasSameCompactionWriter as t };