UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

885 lines (884 loc) • 38.2 kB
import { c as isRecord } from "./record-coerce-TIsquoVR.mjs"; import { t as formatErrorMessage } from "./errors-BvOkmQM5.mjs"; import { _ as toAgentStoreSessionKey } from "./session-key-DY3MOX4N.mjs"; import { f as hasRuntimeContextMarker, g as readRuntimeContextMetadata, p as isRuntimeContextMessage } from "./types-CQc6JNG4.mjs"; import { t as truncateUtf8Prefix } from "./utf8-truncate-_hf7tp13.mjs"; import { i as createNativeModelOwnedRuntimeModel } from "./defaults-Jag53zwP.mjs"; import { _ as readAgentAssistantSource, n as bindAgentAssistantSource } from "./agent-events-jWdQ_ESE.mjs"; import { F as appendTextDeltaToAssistantMessage } from "./agent-core-D7okjbP7.mjs"; import "./src-D3dCIQYS.mjs"; import { u as projectRuntimeContextFragments } from "./internal-runtime-context-D5iQhnLo.mjs"; import { i as jsonUtf8BytesOrInfinity } from "./json-utf8-bytes-C0-PtdPX.mjs"; import { n as recordModelFallbackStop } from "./model-fallback-stop-BXDxJQSp.mjs"; import { l as wrapToolWithBeforeToolCallHook } from "./agent-tools.before-tool-call-CQkX3nOj.mjs"; import { a as WORKER_PROTOCOL_MAX_INFERENCE_PAYLOAD_BYTES, t as WORKER_INFERENCE_MAX_CONTEXT_MESSAGES } from "./worker-inference-u-EROH-B.mjs"; import "./worker-admission-BFR8zOaU.mjs"; import { r as AuthStorage, t as ModelRegistry } from "./model-registry-Gn0C-qAI.mjs"; import { o as getShellConfig, r as detectRuntimeShell } from "./shell-utils-BtMbYqT6.mjs"; import { i as projectSessionEntryMessage } from "./session-T8thNqx3.mjs"; import { l as resolveAssistantMessagePhase } from "./chat-message-content-C-d0FKNG.mjs"; import { l as makeZeroUsageSnapshot } from "./usage-B5bs3hAI.mjs"; import { g as wrapToolWithGatewayCallerIdentity } from "./gateway-caller-context-CU8n6n_e.mjs"; import { a as setPluginToolMeta } from "./tool-metadata-D5DsuLcG.mjs"; import { h as bindBeforeToolCallMetadata, i as copyAgentToolMetadata } from "./agent-tool-metadata-DU21au_v.mjs"; import { t as parseReplyDirectives } from "./reply-directives-BbFfofv9.mjs"; import { s as hasModelFallbackStop } from "./failover-error-JM6M2oyF.mjs"; import { c as mergeAgentRunAttemptTerminal, l as normalizeAgentRunAttemptTerminal, u as projectAgentRunAttemptTerminal } from "./agent-run-terminal-outcome-8xoMCk5L.mjs"; import { i as resolveRuntimeOsLabel } from "./os-summary-DH0HB36b.mjs"; import { m as wrapToolWithAbortSignal } from "./tool-contract-error-CznWpQuP.mjs"; import { i as projectMemoryFlushTools } from "./core-coding-tools-DFJcX7yX.mjs"; import { t as event_stream_exports } from "./event-stream-xGBL9CyU.mjs"; import { a as buildRuntimeContextCustomMessage } from "./runtime-context-prompt-DkBUcs_i.mjs"; import { n as DefaultResourceLoader, t as SettingsManager } from "./settings-manager-2KjnbEXR.mjs"; import { n as guardSessionManager, t as buildExecutionHostRuntimeFacts } from "./messages-BZGmsTOt.mjs"; import { t as SessionManager } from "./session-manager-BpvVpTVC.mjs"; import { n as completeSystemPromptRuntime } from "./system-prompt-runtime-D4SrSLUr.mjs"; import { t as createAgentHarnessToolSurfaceRuntimeCore } from "./tool-surface-bridge-Ccc1MrSf.mjs"; import { s as disposeAllCodeModeRuns } from "./code-mode-state-BkXPjhOA.mjs"; import { t as redactAgentDiagnosticPayload } from "./diagnostic-redaction-Cm_Q_ktX.mjs"; import { a as toToolDefinitions } from "./agent-tool-definition-adapter-TatBtYxj.mjs"; import { t as createAgentSession } from "./sdk-COTqBZzn.mjs"; import { t as getMachineDisplayName } from "./machine-name-B4RyiyO6.mjs"; import { r as resolveSystemPromptRepoRoot } from "./system-prompt-params-BLMIKoUV.mjs"; import { t as materializeSkillResources } from "./resources-DSsBHX_5.mjs"; import { i as toWorkerTranscriptMessage, n as isWorkerTranscriptMessageFrameSafe, o as projectWorkerTextOrImageContent, t as WORKER_PROVIDER_REPLAY_LOCAL_RETRY_MESSAGE } from "./transcript-message-BKu_VHXc.mjs"; import { a as createWorkerPlacementTools, i as WORKER_TOOL_CONFIG, r as createWorkerBrowserToolRuntime, t as createWorkerComputerTool } from "./computer-runtime-BrN9iqcU.mjs"; import { n as windowWorkerReplayMessages } from "./replay-message-window-tKUfTLe4.mjs"; import path from "node:path"; import os from "node:os"; import { randomUUID } from "node:crypto"; import { getSupportedThinkingLevels } from "@openclaw/ai/internal/runtime"; //#region src/worker/embedded-agent-live.runtime.ts const MAX_LIVE_EVENT_BYTES = 32768; const MAX_LIVE_PREVIEW_BYTES = 4096; function truncateLiveText(value) { if (value.length <= MAX_LIVE_PREVIEW_BYTES && Buffer.byteLength(value, "utf8") <= MAX_LIVE_PREVIEW_BYTES) return value; const suffix = "…"; return `${truncateUtf8Prefix(value, MAX_LIVE_PREVIEW_BYTES - Buffer.byteLength(suffix, "utf8"))}${suffix}`; } function boundLiveValue(value) { try { const serialized = JSON.stringify(value); if (serialized === void 0) return null; if (Buffer.byteLength(serialized, "utf8") <= MAX_LIVE_PREVIEW_BYTES) return value; return { truncated: true, preview: truncateLiveText(serialized) }; } catch { return { truncated: true, preview: "[unserializable live payload]" }; } } function redactLiveText(value) { const redacted = redactAgentDiagnosticPayload(value); return truncateLiveText(typeof redacted === "string" ? redacted : "[unreadable diagnostic text]"); } function boundLiveEvent(event) { if (!((event.kind === "assistant" || event.kind === "thinking") && event.payload.text.length > MAX_LIVE_EVENT_BYTES) && jsonUtf8BytesOrInfinity(event) <= MAX_LIVE_EVENT_BYTES) return event; let bounded; if (event.kind === "assistant") { const text = truncateLiveText(event.payload.text); bounded = { kind: "assistant", payload: { ...event.payload, text, delta: text, replace: true } }; } else if (event.kind === "thinking") bounded = { kind: "thinking", payload: { text: truncateLiveText(event.payload.text), delta: truncateLiveText(event.payload.delta) } }; else if (event.kind === "tool") bounded = { kind: "tool", payload: { ...event.payload, ...event.payload.phase === "start" ? { args: boundLiveValue(event.payload.args) } : event.payload.phase === "update" ? { partialResult: boundLiveValue(event.payload.partialResult) } : { result: boundLiveValue(event.payload.result) } } }; else throw new Error(`worker live ${event.kind} event exceeds the protocol payload limit`); if (jsonUtf8BytesOrInfinity(bounded) > MAX_LIVE_EVENT_BYTES) throw new Error(`worker live ${event.kind} event cannot fit the protocol payload limit`); return bounded; } function readAssistantSnapshot(message) { if (message.role !== "assistant") return { text: "" }; const blocks = message.content.flatMap((part) => part.type === "text" ? [{ text: part.text, phase: resolveAssistantMessagePhase({ ...message, content: [part] }) }] : []); const firstPhase = blocks[0]?.phase; const phase = blocks.every((block) => block.phase === firstPhase) ? firstPhase : void 0; return { text: blocks.filter((block) => phase === "commentary" || block.phase !== "commentary").map((block) => block.text).join(""), phase }; } function readAssistantThinking(message) { if (message.role !== "assistant") return ""; return message.content.filter((part) => part.type === "thinking").map((part) => part.thinking).join(""); } function createWorkerLiveRuntime(client) { let previewEnabled = true; const enqueueLive = (event) => { if (previewEnabled) previewEnabled = client.enqueuePreview(boundLiveEvent(event)); }; const startedAt = Date.now(); let terminalLiveEvent; let terminalOutcome = { kind: "ok" }; let replayInvalid = false; const enqueueTerminal = (input) => { terminalOutcome = mergeAgentRunAttemptTerminal(terminalOutcome, normalizeAgentRunAttemptTerminal({ aborted: input.aborted, promptError: input.error })); const terminal = projectAgentRunAttemptTerminal(terminalOutcome); const stopReason = terminal.aborted ? "aborted" : terminal.failed ? "error" : input.stopReason; terminalLiveEvent = { kind: "lifecycle", payload: { phase: "finishing", startedAt, endedAt: Date.now(), ...stopReason ? { stopReason } : {}, ...terminal.aborted ? { aborted: true } : {}, ...replayInvalid ? { replayInvalid: true } : {}, ...typeof terminal.promptError === "string" ? { error: redactLiveText(terminal.promptError) } : {} } }; }; let streamedText = ""; let streamedPhase; let assistantSource; let streamedThinking = ""; const emitAssistantSnapshot = (message, complete = false) => { const { text, phase } = readAssistantSnapshot(message); if (complete && !text && message.role === "assistant" && message.stopReason === "aborted") return; const mediaUrls = complete ? parseReplyDirectives(text).mediaUrls : void 0; if (text === streamedText && phase === streamedPhase && !mediaUrls?.length) return; const previousText = streamedPhase === "commentary" && phase !== "commentary" ? "" : streamedText; const replace = !text.startsWith(previousText); enqueueLive({ kind: "assistant", payload: { text, delta: replace ? text : text.slice(previousText.length), ...mediaUrls?.length ? { mediaUrls } : {}, ...replace ? { replace: true } : {}, ...phase ? { phase } : {}, itemId: readAgentAssistantSource(message)?.itemId } }); streamedText = text; streamedPhase = phase; }; const handleSessionEvent = (event) => { if ((event.type === "message_start" || event.type === "message_update" || event.type === "message_end") && event.message.role === "assistant") { if (event.type === "message_start" || !assistantSource) assistantSource = { itemId: randomUUID() }; bindAgentAssistantSource(event.message, assistantSource); } if (!previewEnabled && event.type !== "agent_end") return; if (event.type === "agent_start") { enqueueLive({ kind: "lifecycle", payload: { phase: "start", startedAt } }); return; } if (event.type === "message_start" && event.message.role === "assistant") { streamedText = ""; streamedPhase = void 0; streamedThinking = ""; return; } if (event.type === "message_update") { if (event.assistantMessageEvent.type === "text_delta" || event.assistantMessageEvent.type === "text_end") emitAssistantSnapshot(event.message); else if (event.assistantMessageEvent.type === "thinking_delta") { streamedThinking = readAssistantThinking(event.message); enqueueLive({ kind: "thinking", payload: { text: streamedThinking, delta: event.assistantMessageEvent.delta } }); } return; } if (event.type === "message_end" && event.message.role === "assistant") { emitAssistantSnapshot(event.message, true); const finalThinking = readAssistantThinking(event.message); if (finalThinking !== streamedThinking) enqueueLive({ kind: "thinking", payload: { text: finalThinking, delta: finalThinking } }); return; } if (event.type === "tool_execution_start" || event.type === "tool_execution_update" || event.type === "tool_execution_end") { const tool = { name: event.toolName, toolCallId: event.toolCallId }; enqueueLive({ kind: "tool", payload: { ...event.type === "tool_execution_start" ? { phase: "start", ...tool, args: redactAgentDiagnosticPayload(event.args) } : event.type === "tool_execution_update" ? { phase: "update", ...tool, partialResult: redactAgentDiagnosticPayload(event.partialResult) } : { phase: "result", ...tool, isError: event.isError, result: redactAgentDiagnosticPayload(event.result) }, ...event.hideFromChannelProgress ? { hideFromChannelProgress: true } : {} } }); return; } if (event.type === "agent_end") { const lastAssistant = event.messages.findLast((message) => message.role === "assistant"); enqueueTerminal({ stopReason: lastAssistant?.stopReason, aborted: lastAssistant?.stopReason === "aborted", ...lastAssistant?.stopReason === "error" ? { error: lastAssistant.errorMessage ?? "Worker inference failed." } : {} }); } }; const enqueueRunFailure = (failure) => { replayInvalid ||= hasModelFallbackStop(failure.error); enqueueTerminal({ aborted: failure.aborted, error: formatErrorMessage(failure.error) }); }; const emitTerminal = async () => { if (!terminalLiveEvent) return; await client.emitTerminal(boundLiveEvent(terminalLiveEvent)); }; return { handleSessionEvent, enqueueRunFailure, emitTerminal }; } //#endregion //#region src/worker/embedded-agent-transcript.runtime.ts function toWorkerInferenceMessage(message) { if (hasRuntimeContextMarker(message) && !isRuntimeContextMessage(message)) throw new Error("Cloud worker cannot preserve runtime context with media. Stop or reclaim the cloud worker, then retry locally."); if (isRuntimeContextMessage(message)) return { kind: "complete", message: { role: "user", content: message.content, timestamp: message.timestamp, runtimeContext: readRuntimeContextMetadata(message) } }; if (message.role === "user") return { kind: "complete", message: { role: "user", content: typeof message.content === "string" ? message.content : message.content.map(projectWorkerTextOrImageContent), timestamp: message.timestamp, ...message.operatorMessage ? { operatorMessage: message.operatorMessage } : {} } }; const projected = toWorkerTranscriptMessage(message, "inference"); if (!projected) throw new Error(`Unsupported inference message role: ${message.role}`); return projected; } function toWorkerInferenceContext(context) { const windowed = windowWorkerReplayMessages(context.messages, WORKER_INFERENCE_MAX_CONTEXT_MESSAGES); if (windowed.kind === "provider-replay-unavailable") return windowed; const messages = []; for (const message of windowed.messages) { const projected = toWorkerInferenceMessage(message); if (projected.kind === "provider-replay-unavailable") return projected; messages.push(projected.message); } return { kind: "complete", context: { ...context.systemPrompt === void 0 ? {} : { systemPrompt: context.systemPrompt }, messages, ...context.tools ? { tools: context.tools.map((tool) => ({ name: tool.name, description: tool.description, parameters: structuredClone(tool.parameters) })) } : {} } }; } function createWorkerTranscriptRuntime(client, signal) { const pendingTranscriptMessages = []; let failedCommit; const onMessagePersisted = (message) => { const projected = toWorkerTranscriptMessage(message, "transcript"); if (!projected) return; if (projected.kind === "provider-replay-unavailable") throw new Error(`Worker transcript cannot persist authoritative provider replay: ${projected.details.reason}.`); const itemId = readAgentAssistantSource(message)?.itemId; if (projected.message.role === "assistant" && itemId) projected.message.itemId = itemId; if (!isWorkerTranscriptMessageFrameSafe(projected.message)) throw new Error("Worker transcript message exceeds the protocol payload limit."); pendingTranscriptMessages.push(projected.message); }; const flushTranscript = async () => { while (pendingTranscriptMessages.length > 0) { if (signal?.aborted) { if (failedCommit) throw failedCommit.error; return; } const batch = pendingTranscriptMessages.slice(0, 64); try { await client.commit(batch); } catch (error) { failedCommit = { error }; throw error; } failedCommit = void 0; pendingTranscriptMessages.splice(0, batch.length); } }; let sessionWriteQueue = Promise.resolve(); const withSessionWriteSettlement = (operation) => { const result = sessionWriteQueue.then(async () => { const value = await operation(); await flushTranscript(); return value; }); sessionWriteQueue = result.then(() => void 0, () => void 0); return result; }; return { onMessagePersisted, withSessionWriteSettlement }; } //#endregion //#region src/worker/native-inference-stream.ts function stringParts(value, includeKeys, strings) { let text = ""; if (typeof value === "string") { strings.push(value); text = value; } else if (Array.isArray(value)) for (const item of value) text += stringParts(item, includeKeys, strings); else if (value !== null && typeof value === "object") for (const [key, item] of Object.entries(value)) { if (includeKeys) { strings.push(key); text += key; } text += stringParts(item, includeKeys, strings); } return text; } function generatedOutput(message) { const { content, role: _role, api: _api, provider: _provider, model: _model, usage: _usage, stopReason: _stopReason, timestamp: _timestamp, ...metadata } = message; const strings = []; let text = ""; let structured = ""; let all = ""; for (const block of content) if (block.type === "toolCall") { strings.push(block.id, block.name); const value = block.id + block.name + stringParts(block.arguments, true, strings) + stringParts(block.thoughtSignature, false, strings); structured += value; all += value; } else if (block.type === "text") { text += block.text; const { type: _type, text: _text, ...fields } = block; const value = stringParts(fields, false, strings); structured += value; all += block.text + value; } else { text += block.thinking; const { type: _type, thinking: _thinking, ...fields } = block; const value = stringParts(fields, false, strings); structured += value; all += block.thinking + value; } const metadataText = stringParts(metadata, false, strings); structured += metadataText; all += metadataText; return { all, strings, structured, text }; } /** Guard provider output before the shared loop can publish it or execute generated tools. */ function createNativeInferenceStreamGuard(native) { let completedText = ""; return (start, signal) => { const output = (0, event_stream_exports.createAssistantMessageEventStream)(); (async () => { let pending = []; let bytes = 0; let toolArgumentBytes = 0; let currentMessage; try { const source = await start(); const toolArgumentDeltas = /* @__PURE__ */ new Map(); const incompleteToolArguments = /* @__PURE__ */ new Set(); const clearToolArguments = (contentIndex) => { const delta = toolArgumentDeltas.get(contentIndex); if (delta !== void 0) { toolArgumentBytes -= Buffer.byteLength(delta); toolArgumentDeltas.delete(contentIndex); } incompleteToolArguments.delete(contentIndex); }; const removePendingToolDelta = (contentIndex) => { const priorIndex = pending.findIndex((held) => held.type === "toolcall_delta" && held.contentIndex === contentIndex); if (priorIndex >= 0) { const prior = pending[priorIndex]; if (prior) { bytes -= Buffer.byteLength(JSON.stringify(prior)); pending.splice(priorIndex, 1); } } }; const publish = (raw) => { signal?.throwIfAborted(); let event = structuredClone(raw); const terminal = event.type === "done" || event.type === "error"; currentMessage = (event.type === "done" ? event.message : event.type === "error" ? event.error : event.partial) ?? (event.type === "text_delta" && currentMessage ? appendTextDeltaToAssistantMessage(currentMessage, event.contentIndex, event.delta) : void 0); if (!currentMessage) throw new Error("Native stream delta has no message owner"); native.assertProtocolSafe(event); const generated = generatedOutput(currentMessage); const text = completedText + generated.text; const generatedSequences = [ text, generated.structured, generated.all ]; native.assertProtocolSafe(generatedSequences); const strings = generated.strings; if ("delta" in event) strings.push(event.delta); if ("content" in event) strings.push(event.content); if (event.type === "toolcall_end") strings.push(...generatedOutput({ ...currentMessage, content: [event.toolCall] }).strings); if (event.type === "toolcall_delta") { const priorDelta = toolArgumentDeltas.get(event.contentIndex) ?? ""; const delta = priorDelta + event.delta; if (Buffer.byteLength(delta) > WORKER_PROTOCOL_MAX_INFERENCE_PAYLOAD_BYTES) throw new Error("Native tool arguments exceeded the protocol budget"); toolArgumentBytes += Buffer.byteLength(delta) - Buffer.byteLength(priorDelta); if (toolArgumentBytes > WORKER_PROTOCOL_MAX_INFERENCE_PAYLOAD_BYTES) throw new Error("Native tool arguments exceeded the aggregate protocol budget"); toolArgumentDeltas.set(event.contentIndex, delta); strings.push(delta); try { strings.push(stringParts(JSON.parse(delta), true, strings)); incompleteToolArguments.delete(event.contentIndex); } catch (error) { if (!(error instanceof SyntaxError)) throw error; incompleteToolArguments.add(event.contentIndex); } event = { ...event, delta }; removePendingToolDelta(event.contentIndex); } else if (event.type === "toolcall_end") { removePendingToolDelta(event.contentIndex); clearToolArguments(event.contentIndex); } native.assertProtocolSafe(strings); const hasCredentialPrefix = generatedSequences.some(native.hasCredentialPrefix) || strings.some(native.hasCredentialPrefix); if (terminal && hasCredentialPrefix) throw new Error("Native stream ended with an unresolved credential prefix"); if (!terminal && (incompleteToolArguments.size > 0 || hasCredentialPrefix)) { bytes += Buffer.byteLength(JSON.stringify(event)); if (bytes + toolArgumentBytes > WORKER_PROTOCOL_MAX_INFERENCE_PAYLOAD_BYTES) throw new Error("Native credential-prefix buffer exceeded"); pending.push(event); return; } if (terminal && incompleteToolArguments.size > 0) pending = []; if (event.type === "error" && pending.length) throw new Error("Native stream failed with an unresolved credential prefix"); for (const held of pending) { output.push(held); if (held.type === "toolcall_delta") clearToolArguments(held.contentIndex); } pending = []; bytes = 0; output.push(event); if (event.type === "toolcall_delta") clearToolArguments(event.contentIndex); if (event.type === "done") completedText = text; }; let terminalSeen = false; for await (const event of source) { signal?.throwIfAborted(); if (terminalSeen) throw new Error("Native stream continued after its terminal event"); if (event.type === "done" || event.type === "error") { native.assertProtocolSafe(event); terminalSeen = true; } else publish(event); } const final = structuredClone(await source.result()); publish(final.stopReason === "error" || final.stopReason === "aborted" ? { type: "error", reason: final.stopReason, error: final } : { type: "done", reason: final.stopReason, message: final }); } catch { const aborted = signal?.aborted === true; output.push({ type: "error", reason: aborted ? "aborted" : "error", error: { role: "assistant", content: [], provider: native.model.provider, model: native.model.id, api: native.model.api, timestamp: Date.now(), stopReason: aborted ? "aborted" : "error", errorMessage: aborted ? "Runtime-local inference cancelled" : "Runtime-local inference failed its output boundary", usage: makeZeroUsageSnapshot() } }); } finally { output.end(); } })(); return output; }; } //#endregion //#region src/worker/worker-gateway-tools.ts function createWorkerGatewayToolProxies(surface, client) { const tools = []; for (const entry of surface.tools) { if (entry.execution !== "gateway") continue; const tool = { ...entry.definition, execute: async (toolCallId, args, signal, onUpdate) => { signal?.throwIfAborted(); if (!isRecord(args)) throw new Error("Gateway tool arguments must be an object."); const seconds = entry.timeout?.argument ? args[entry.timeout.argument] : void 0; const timeoutMs = entry.timeout ? Math.max(entry.timeout.minimumMs ?? 0, (typeof seconds === "number" ? seconds : entry.timeout.defaultSeconds ?? 0) * 1e3 + (entry.timeout.paddingMs ?? 0)) : void 0; const invocation = { generation: surface.generation, toolCallId }; const cancel = () => { client.cancelGatewayTool(invocation).catch(() => {}); }; signal?.addEventListener("abort", cancel, { once: true }); try { const response = await client.invokeGatewayTool({ ...invocation, toolId: entry.id, arguments: args }, { replay: entry.replay, timeoutMs, signal, onUpdate: onUpdate ? (result) => onUpdate({ ...result, details: result.details }) : void 0 }); signal?.throwIfAborted(); if (!response.ok) throw new Error(response.error.message); return { ...response.payload, details: response.payload.details }; } finally { signal?.removeEventListener("abort", cancel); } } }; if (entry.plugin) setPluginToolMeta(tool, entry.plugin); bindBeforeToolCallMetadata(tool, { sourceTool: { ...tool }, options: { emitDiagnostics: false } }); tools.push(tool); } return tools; } //#endregion //#region src/worker/embedded-agent.runtime.ts function toWorkerAgentError(value, fallback) { return value instanceof Error ? value : new Error(fallback, { cause: value }); } async function runWorkerEmbeddedTurn(params) { const resources = params.skillResources ? await materializeSkillResources(params.skillResources, () => params.signal?.throwIfAborted(), { sessionId: params.sessionId, workspaceDir: params.cwd }) : void 0; try { if (params.allowedToolNames.includes("browser") !== (params.browser !== void 0)) throw new Error("Worker Browser authority and launch descriptor must be provided together."); if (params.allowedToolNames.includes("computer") !== (params.computer !== void 0)) throw new Error("Worker computer authority and launch descriptor must be provided together."); if (params.operationalRunInstance.runId !== params.runId) throw new Error("worker operational run instance disagrees with the admitted turn"); const toolSurface = params.toolSurface; const model = params.nativeInference?.model ?? createNativeModelOwnedRuntimeModel({ provider: params.modelRef.provider, modelId: params.modelRef.model }); if (!params.nativeInference) { model.contextWindow = toolSurface.policy.modelContextWindowTokens ?? model.contextWindow; if (toolSurface.policy.modelHasVision !== void 0) model.input = toolSurface.policy.modelHasVision ? ["text", "image"] : ["text"]; } const requestedReasoning = params.inferenceOptions?.reasoning; if (params.nativeInference && requestedReasoning === "adaptive") throw new Error("Adaptive thinking is not supported by runtime-local worker inference"); const thinkingLevel = params.nativeInference ? requestedReasoning === "adaptive" ? "off" : requestedReasoning ?? "off" : "medium"; if (params.nativeInference && !getSupportedThinkingLevels(model).includes(thinkingLevel)) throw new Error("Requested thinking level is not supported by the node-local model"); if (params.nativeInference && (params.inferenceOptions?.maxTokens !== void 0 && params.inferenceOptions.maxTokens !== model.maxTokens || Object.values(params.inferenceOptions?.thinkingBudgets ?? {}).some((budget) => budget !== void 0 && budget > model.maxTokens))) throw new Error("Worker inference options exceed or override the node-local model budget"); const authStorage = AuthStorage.inMemory({}); const modelRegistry = ModelRegistry.inMemory(authStorage); const settingsManager = SettingsManager.inMemory({ compaction: { enabled: false }, retry: { enabled: false } }); const resourceLoader = new DefaultResourceLoader({ cwd: params.cwd, agentDir: params.stateDir }); const baseSessionManager = SessionManager.inMemory(params.cwd); for (const message of params.initialMessages ?? []) await baseSessionManager.appendMessageAsync(structuredClone(message)); const transcriptRuntime = createWorkerTranscriptRuntime(params.transcript, params.signal); const sessionManager = guardSessionManager(baseSessionManager, { suppressNextUserMessagePersistence: params.suppressPromptTranscript, onMessagePersisted: transcriptRuntime.onMessagePersisted }); const appendCustomMessage = sessionManager.appendCustomMessageEntryAsync.bind(sessionManager); sessionManager.appendCustomMessageEntryAsync = async (customType, content, display, details) => { const entryId = await appendCustomMessage(customType, content, display, details); const entry = sessionManager.getEntry(entryId); const message = entry && projectSessionEntryMessage(entry); if (!message) throw new Error("Worker custom message was not committed"); transcriptRuntime.onMessagePersisted(message); return entryId; }; const allowedToolNameSet = new Set(params.allowedToolNames); for (const entry of toolSurface.tools) if (entry.execution === "placement" && !allowedToolNameSet.has(entry.definition.name)) throw new Error(`Worker tool surface exceeds launch authority: ${entry.definition.name}`); const activeToolNames = new Set(toolSurface.tools.map(({ definition }) => definition.name)); const coreTools = projectMemoryFlushTools(createWorkerPlacementTools({ ...params, policy: toolSurface.policy, containmentRoot: params.workerContainmentRoot, skillsSnapshot: resources?.snapshot }), toolSurface.policy.memoryFlushWritePath ? { root: params.workerContainmentRoot, relativePath: toolSurface.policy.memoryFlushWritePath } : void 0); const browserRuntime = params.browser && activeToolNames.has("browser") ? await createWorkerBrowserToolRuntime({ descriptor: params.browser, sessionKey: toAgentStoreSessionKey({ agentId: params.agentId, requestKey: params.sessionKey }), stateDir: params.stateDir, workspaceDir: params.cwd, ...params.browserRuntime ? { runtime: params.browserRuntime } : {} }) : void 0; const turnLifetime = new AbortController(); const toolSignal = params.signal ? AbortSignal.any([params.signal, turnLifetime.signal]) : turnLifetime.signal; let computerCleanup; let toolSurfaceRuntime; async function disposeTools(failure) { turnLifetime.abort(); const cleanup = computerCleanup; computerCleanup = void 0; const failures = failure ? [failure] : []; const disposals = await Promise.allSettled([ () => toolSurfaceRuntime?.cleanup(), () => cleanup?.("Worker turn finished"), () => browserRuntime?.dispose(), disposeAllCodeModeRuns ].map(async (dispose) => await dispose())); for (const disposal of disposals) if (disposal.status === "rejected") { const cleanupFailure = toWorkerAgentError(disposal.reason, "Worker tool cleanup failed."); recordModelFallbackStop(cleanupFailure); failures.push(cleanupFailure); } return failures.length > 1 ? new AggregateError(failures, "Worker turn failed") : failures[0]; } const { session } = await (async () => { try { const computerTool = params.computer && activeToolNames.has("computer") ? createWorkerComputerTool({ ...params.computer, runId: params.runId, registerRunCleanup: (cleanup) => { computerCleanup = cleanup; } }) : void 0; const localTools = new Map([ ...coreTools, ...browserRuntime ? [browserRuntime.tool] : [], ...computerTool ? [computerTool] : [] ].map((tool) => [tool.name, tool])); const gatewayTools = new Map(createWorkerGatewayToolProxies(toolSurface, params.gatewayTools).map((tool) => [tool.name, tool])); const tools = toolSurface.tools.map((entry) => { if (entry.execution === "gateway") return wrapToolWithAbortSignal(gatewayTools.get(entry.definition.name), toolSignal); const source = localTools.get(entry.definition.name); if (!source) throw new Error(`Worker placement tool unavailable: ${entry.definition.name}`); const tool = copyAgentToolMetadata(source, { ...source, ...entry.definition }); if (entry.plugin) setPluginToolMeta(tool, entry.plugin); return wrapToolWithGatewayCallerIdentity(wrapToolWithAbortSignal(wrapToolWithBeforeToolCallHook(tool, { agentId: params.agentId, config: WORKER_TOOL_CONFIG, cwd: params.cwd, workspaceDir: params.cwd, sessionKey: params.sessionKey, sessionId: params.sessionId, runId: params.runId, requester: { senderIsOwner: true } }), toolSignal), { agentId: params.agentId, sessionKey: params.sessionKey, operationalRunInstance: params.operationalRunInstance, signedAgentRuntimeIdentityToken: params.agentRuntimeIdentityToken }); }); toolSurfaceRuntime = createAgentHarnessToolSurfaceRuntimeCore({ agentId: params.agentId, sessionId: params.sessionId, sessionKey: params.sessionKey, runId: params.runId, abortSignal: toolSignal, presentation: toolSurface.presentation, supportsDeferredToolCalls: false, modelToolsEnabled: tools.length > 0, model }); const projected = toolSurfaceRuntime.compactTools(tools, { prepared: { abortSignal: toolSignal, preserveToolNames: [] } }).promptToolPolicy.apply(); await resourceLoader.reload(); const systemPrompt = completeSystemPromptRuntime(params.systemPrompt ?? "", { host: await getMachineDisplayName(), os: resolveRuntimeOsLabel(), arch: os.arch(), node: process.version, shell: detectRuntimeShell() ?? path.basename(getShellConfig().shell), repoRoot: resolveSystemPromptRepoRoot({ workspaceDir: params.cwd, cwd: params.cwd }) }); return await createAgentSession({ systemPrompt: resources?.rewriteReferences(systemPrompt) ?? systemPrompt, cwd: params.cwd, modelRegistry, model, thinkingLevel, tools: projected.tools.map((tool) => tool.name), customTools: toToolDefinitions(projected.tools), sessionManager, settingsManager, resourceLoader, withSessionWriteSettlement: transcriptRuntime.withSessionWriteSettlement }); } catch (error) { throw await disposeTools(toWorkerAgentError(error, "Worker agent setup failed.")); } })(); session.agent.sessionId = params.sessionId; const guardNativeStream = params.nativeInference ? createNativeInferenceStreamGuard(params.nativeInference) : void 0; session.agent.streamFn = async (_model, context, options) => { if (params.nativeInference && guardNativeStream) { const native = params.nativeInference; return guardNativeStream(() => native.streamFn(model, context, { ...params.inferenceOptions, reasoning: thinkingLevel, signal: options?.signal }), options?.signal); } const projected = toWorkerInferenceContext(context); if (projected.kind === "provider-replay-unavailable") throw new Error(`${WORKER_PROVIDER_REPLAY_LOCAL_RETRY_MESSAGE} (${projected.details.reason})`); return params.inference.stream({ modelRef: params.modelRef, context: projected.context, options: structuredClone(params.inferenceOptions), ...options?.signal ? { signal: options.signal } : {} }); }; const liveRuntime = createWorkerLiveRuntime(params.live); const unsubscribe = session.subscribe(liveRuntime.handleSessionEvent); const abortTurn = () => session.agent.abort(); params.signal?.addEventListener("abort", abortTurn, { once: true }); let runFailure; try { if (params.signal?.aborted) throw toWorkerAgentError(params.signal.reason, "Worker agent turn aborted."); const content = typeof params.prompt === "string" ? [{ type: "text", text: params.prompt }] : [...params.prompt]; if (resources) { for (const [index, part] of content.entries()) if (part.type === "text") content[index] = { ...part, text: resources.rewriteReferences(part.text) }; } const fragments = params.runtimeContext ? [...buildExecutionHostRuntimeFacts({ ...params, capabilityToolNames: activeToolNames }), ...params.runtimeContext] : []; const runtimeContext = buildRuntimeContextCustomMessage(projectRuntimeContextFragments(fragments), fragments, params.inHistorySystemUpdates); await session.agent.prompt([{ role: "user", content, timestamp: Date.now() }, ...runtimeContext ? [runtimeContext] : []]); await session.agent.waitForIdle(); if (params.signal?.aborted) throw toWorkerAgentError(params.signal.reason, "Worker agent turn aborted."); const terminalAssistant = session.agent.state.messages.toReversed().find((message) => message.role === "assistant"); if (terminalAssistant?.stopReason === "error") throw new Error(terminalAssistant.errorMessage ?? "Worker inference failed."); if (terminalAssistant?.stopReason === "aborted") throw new Error(terminalAssistant.errorMessage ?? "Worker inference was aborted."); } catch (error) { runFailure = params.signal?.aborted ? toWorkerAgentError(params.signal.reason, "Worker agent turn aborted.") : toWorkerAgentError(error, "Worker agent turn failed."); } try { const [cleanupFailure, transcriptFailure] = await Promise.all([disposeTools(runFailure), transcriptRuntime.withSessionWriteSettlement(() => void 0).catch((error) => toWorkerAgentError(error, "Worker transcript flush failed."))]); runFailure = cleanupFailure; if (runFailure) liveRuntime.enqueueRunFailure({ aborted: params.signal?.aborted === true, error: runFailure }); if (transcriptFailure) throw runFailure ?? transcriptFailure; await liveRuntime.emitTerminal(); } finally { turnLifetime.abort(); params.signal?.removeEventListener("abort", abortTurn); unsubscribe(); session.dispose(); } if (runFailure !== void 0) throw runFailure; } finally { await resources?.cleanup(); } } //#endregion export { runWorkerEmbeddedTurn };