openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
185 lines (184 loc) • 8.26 kB
JavaScript
import { a as asOptionalRecord } from "./record-coerce-TIsquoVR.mjs";
import "./string-coerce-runtime-B1b8UNzb.mjs";
import { r as appendSessionTranscriptMessageByIdentityStrict } from "./session-transcript-runtime-BP9tuXFg.mjs";
import { i as createAgentHarnessToolResultMessage, r as createAgentHarnessToolCallMessage } from "./agent-harness-attempt-runtime-DclhpkiN.mjs";
import { i as agentsApiNativeToolOutput, n as agentsApiNativeToolDetails, r as agentsApiNativeToolOutcome, t as agentsApiNativeTool } from "./agentsapi-native-items-C9R1hHDa.mjs";
import { t as requireAgentsApiSessionTarget } from "./agentsapi-target-ClP2ww2I.mjs";
//#region extensions/agentsapi/agentsapi-transcript.ts
/** Canonical native facts use the same durable identities during live and historical repair. */
async function recordAgentsApiNativeToolTranscript(params, sessionId, turnId, item, assertCurrent, nextTimestamp, options = {}) {
assertCurrent();
const tool = agentsApiNativeTool(item, params);
if (!tool || ![
"completed",
"failed",
"incomplete"
].includes(item.status ?? "")) return false;
const id = `agentsapi:${sessionId}:${turnId}:${item.id}`;
const outcome = agentsApiNativeToolOutcome(item, options.enclosingStatus);
const output = agentsApiNativeToolOutput(item, options.capturedOutput);
const details = agentsApiNativeToolDetails(sessionId, turnId, item, outcome, options.capturedOutput);
const text = output ?? (item.type === "web_search_call" ? `Web search ${outcome.status}; native search results are unavailable.` : outcome.error ?? `${tool.name} ${outcome.status}`);
await recordAgentsApiNativeToolInvocation(params, sessionId, turnId, item, assertCurrent, nextTimestamp);
await appendAgentsApiTranscriptMessage(params, {
...createAgentHarnessToolResultMessage({
id,
name: tool.name,
text,
isError: outcome.isError,
details
}, nextTimestamp()),
__openclaw: {
toolOutput: {
source: "execution",
modelInput: "unverified",
...outcome.outcomeUnknown ? { outcome: "unknown" } : {},
...options.captureTruncated ? { captureTruncated: true } : {}
},
...item.type === "web_search_call" ? { resultContentSource: "network" } : {}
},
idempotencyKey: `${id}:result`
}, assertCurrent);
return true;
}
/** Canonical invocation facts can precede completion of the native tool. */
async function recordAgentsApiNativeToolInvocation(params, sessionId, turnId, item, assertCurrent, nextTimestamp) {
assertCurrent();
const tool = agentsApiNativeTool(item, params);
if (!tool || !canRecordAgentsApiNativeToolInvocation(item)) return false;
const id = `agentsapi:${sessionId}:${turnId}:${item.id}`;
await appendAgentsApiTranscriptMessage(params, {
...createAgentHarnessToolCallMessage({
api: "openai-agents",
provider: "openai",
modelId: params.model.id
}, {
id,
name: tool.name,
arguments: tool.args
}, nextTimestamp()),
idempotencyKey: `${id}:call`
}, assertCurrent);
return true;
}
/** Persist host tool evidence before its result is acknowledged by the native session. */
async function recordAgentsApiToolTranscript(params, call, result, isError, assertCurrent) {
const identity = `agentsapi:tool:${call.turn_id}:${call.call_id}`;
const attribution = {
api: "openai-agents",
provider: "openai",
modelId: params.model.id
};
const toolCall = {
...createAgentHarnessToolCallMessage(attribution, {
id: call.call_id,
name: call.name,
arguments: asOptionalRecord(call.arguments) ?? {}
}, Date.now()),
idempotencyKey: `${identity}:call`
};
const toolResult = {
...createAgentHarnessToolResultMessage({
id: call.call_id,
name: call.name,
content: result.content,
details: result.details,
isError
}, Date.now()),
idempotencyKey: `${identity}:result`
};
await appendAgentsApiTranscriptMessage(params, toolCall, assertCurrent);
await appendAgentsApiTranscriptMessage(params, toolResult, assertCurrent);
}
async function appendAgentsApiTranscriptMessage(params, message, assertCurrent) {
assertCurrent();
const append = await appendSessionTranscriptMessageByIdentityStrict({
...requireAgentsApiSessionTarget(params),
config: params.config,
message,
prepareMessageAfterIdempotencyCheck: (prepared) => {
assertCurrent();
return prepared;
}
});
assertCurrent();
if (append.kind !== "result") throw new Error("Agents API transcript append was refused");
return append.result.message;
}
/** Walk the retrieved native prefix without another transcript queue or cursor. */
function* iterateAgentsApiTranscriptItems(turnId, items, enclosingStatus, terminalTurn, recordedGatewayCallIds, hasObservedCompletion) {
let transcriptReady = true;
let deferredFinalSeen = false;
for (const item of items) {
if (item.turn_id && item.turn_id !== turnId) throw new Error("Agents API saved item belongs to a different turn");
const completionObserved = hasObservedCompletion(item.id);
const terminal = terminalTurn || [
"completed",
"failed",
"incomplete"
].includes(item.status ?? "") || item.status == null && completionObserved;
if (transcriptReady && (deferredFinalSeen && hasAgentsApiTranscriptRecord(item) || !canRecordAgentsApiTranscriptItem(turnId, item, enclosingStatus, recordedGatewayCallIds, completionObserved))) transcriptReady = false;
deferredFinalSeen ||= isAgentsApiDeferredFinalText(item);
yield {
item,
terminal,
transcriptReady
};
}
}
/** Nonterminal calls require retrieved invocation fields, rather than streamed guesses. */
function canRecordAgentsApiNativeToolInvocation(item) {
if ([
"completed",
"failed",
"incomplete"
].includes(item.status ?? "")) return [
"command_execution",
"mcp_call",
"web_search_call"
].includes(item.type);
if (item.type === "command_execution") return typeof item.command === "string" && (item.cwd === null || typeof item.cwd === "string");
return false;
}
/** Backend publication stops before invocation or text facts that are still incomplete. */
function canRecordAgentsApiTranscriptItem(turnId, item, enclosingStatus, recordedGatewayCallIds, completionObserved = false) {
if ([
"command_execution",
"mcp_call",
"web_search_call"
].includes(item.type)) return canRecordAgentsApiNativeToolInvocation(item);
if (item.type === "function_call") return typeof item.call_id === "string" && recordedGatewayCallIds.has(`${turnId}:${item.call_id}`);
if (item.type === "reasoning" || item.type === "message" && item.role === "assistant" && item.phase === "commentary") return canRecordAgentsApiTranscriptText(item, enclosingStatus, completionObserved);
return true;
}
function canRecordAgentsApiTranscriptText(item, enclosingStatus, completionObserved = false) {
if ([
"completed",
"failed",
"incomplete"
].includes(item.status ?? "")) return true;
return item.status == null && (completionObserved || item.type === "reasoning" && enclosingStatus === "completed");
}
function isAgentsApiDeferredFinalText(item) {
return item.type === "message" && item.role === "assistant" && item.phase !== "commentary" && item.status === "completed" && Boolean(item.content?.some((part) => part.type === "output_text" && part.text));
}
function hasAgentsApiTranscriptRecord(item) {
return [
"command_execution",
"mcp_call",
"web_search_call",
"function_call"
].includes(item.type) || item.type === "reasoning" && Boolean(item.summary?.some((part) => part.type === "summary_text" && part.text)) || item.type === "message" && item.role === "assistant" && item.phase === "commentary" && Boolean(item.content?.some((part) => part.type === "output_text" && part.text));
}
function readTextParts(parts, type) {
const texts = /* @__PURE__ */ new Map();
parts?.forEach((part, index) => {
if (part.type === type) texts.set(index, part.text ?? "");
});
return texts;
}
function joinTextParts(parts) {
return [...parts.entries()].toSorted(([left], [right]) => left - right).map(([, text]) => text).join("");
}
//#endregion
export { readTextParts as a, recordAgentsApiToolTranscript as c, joinTextParts as i, canRecordAgentsApiTranscriptText as n, recordAgentsApiNativeToolInvocation as o, iterateAgentsApiTranscriptItems as r, recordAgentsApiNativeToolTranscript as s, appendAgentsApiTranscriptMessage as t };