openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
835 lines (834 loc) • 32.9 kB
JavaScript
import { i as loadBundledPluginPublicSurfaceModuleSyncCore } from "./facade-loader-CVT-xVW5.js";
import { u as SESSIONS_SEND_RESULT_GUIDANCE } from "./tool-description-presets-1n9mie5B.js";
import { N as WorkerPortalParamsSchema, Z as WorkerComputerParamsSchema, h as WORKER_SESSION_TOOL_MAX_TEXT_LENGTH } from "./worker-admission-C1l9t3yY.js";
import { t as truncateUtf8Prefix } from "./utf8-truncate-CCgJrv8A.js";
import { t as WORKER_INFERENCE_MAX_CONTEXT_MESSAGES } from "./worker-inference-CNQgy0yo.js";
import { dt as ModelRegistry, nt as SettingsManager, xt as AuthStorage } from "./sessions-BdNAJTEP.js";
import { s as resolveAssistantMessagePhase } from "./chat-message-content-Dws_XUEQ.js";
import { t as SessionManager } from "./session-manager-DO0bDxav.js";
import { t as DEFAULT_AGENTS_FILENAME, y as loadWorkspaceBootstrapFiles } from "./workspace-ConDEamr.js";
import { d as normalizeAgentRunAttemptTerminal, f as projectAgentRunAttemptTerminal, u as mergeAgentRunAttemptTerminal } from "./agent-run-terminal-outcome-CigeY75d.js";
import { s as wrapToolWithGatewayCallerIdentity } from "./gateway-caller-context-BxVUFvkR.js";
import { n as resolveSessionPermissionCoreToolPolicy } from "./session-permission-exec-mode-CJHB47SR.js";
import { n as buildBootstrapContextForFiles } from "./bootstrap-files-DbaSu-Uk.js";
import { t as createComputerTool } from "./computer-tool-B5vR3VVG.js";
import { t as guardSessionManager } from "./session-tool-result-guard-wrapper-D4c8vGkv.js";
import { n as wrapToolWithAbortSignal } from "./agent-tools.abort-BzukmNyv.js";
import { t as redactAgentDiagnosticPayload } from "./diagnostic-redaction-Cb1h8pct.js";
import { a as toToolDefinitions } from "./agent-tool-definition-adapter-_QXAVYIM.js";
import { n as createAgentSession, t as createEmbeddedAgentResourceLoader } from "./resource-loader-Ccy9Zzbt.js";
import { n as isApplyPatchAllowedForModel, r as finalizeAgentTools, t as createCoreCodingTools } from "./core-coding-tools-DMh_iw8e.js";
import { t as resolveToolLoopDetectionConfig } from "./tool-loop-detection-config-Bg911KiY.js";
import { n as PortalOutputSchema, r as PortalToolSchema, t as PORTAL_TOOL_DESCRIPTION } from "./portal-tool-contract-D7THiYXg.js";
import { t as SkillLibraryWorkshopSchema } from "./worker-skill-workshop-4AWiTHUY.js";
import { t as createLibrarySkillWorkshopDescriptor } from "./skill-workshop-tool-library-C4JsOree.js";
import { n as createNativeModelOwnedRuntimeModel } from "./setup-CUgHTEHc.js";
import { t as materializeSkillResources } from "./resources-BYST4V_Q.js";
import { i as WORKER_TOOL_NAMES, n as WORKER_REQUIRED_LOCAL_TOOL_NAMES, r as WORKER_SESSION_TOOL_NAMES, t as WORKER_LOCAL_TOOL_NAMES } from "./tool-authority-Duog_7TW.js";
import { i as isWorkerTranscriptMessageFrameSafe, n as cloneImageContent, o as toWorkerTranscriptMessage, r as cloneTextContent, t as WORKER_PROVIDER_REPLAY_LOCAL_RETRY_MESSAGE } from "./transcript-message-DP74Q5JC.js";
import { n as windowWorkerReplayMessages } from "./replay-message-window-OY0cC2EN.js";
import { execFile } from "node:child_process";
import { Type } from "typebox";
import { Value } from "typebox/value";
//#region src/worker/browser-runtime.ts
/** Core-private adapter for the bundled Browser plugin's attached worker runtime. */
const WORKER_BROWSER_LAUNCH_TIMEOUT_MS = 3e4;
const WORKER_BROWSER_LAUNCH_OUTPUT_LIMIT_BYTES = 65536;
function runWorkerBrowserLauncher(launcherPath) {
return new Promise((resolve, reject) => {
execFile(launcherPath, [], {
timeout: WORKER_BROWSER_LAUNCH_TIMEOUT_MS,
maxBuffer: WORKER_BROWSER_LAUNCH_OUTPUT_LIMIT_BYTES,
windowsHide: true
}, (error) => {
if (error) {
reject(new Error(`Worker Browser launcher failed: ${error.message}`, { cause: error }));
return;
}
resolve();
});
});
}
/** Materialize the exact bundled Browser runtime; no descriptor-controlled plugin path is used. */
async function createWorkerBrowserToolRuntime(params) {
return await (params.runtime ?? loadBundledPluginPublicSurfaceModuleSyncCore({
dirName: "browser",
artifactBasename: "runtime-api.js",
trackedPluginId: "browser"
})).createAttachedBrowserToolRuntime({
cdpUrl: params.descriptor.cdpUrl,
ensureAttachTarget: async () => await runWorkerBrowserLauncher(params.descriptor.launcherPath),
agentSessionKey: params.sessionKey,
agentDir: params.stateDir,
workspaceDir: params.workspaceDir
});
}
//#endregion
//#region src/worker/computer-runtime.ts
function createWorkerComputerTool(params) {
let closing;
const close = (request) => closing ??= Promise.resolve().then(() => params.requestComputer(request));
const transport = {
computerUse: params.descriptor.computerUse,
resolveNode: async (query, signal) => {
signal?.throwIfAborted();
if (query !== void 0 && query !== params.descriptor.nodeId) throw new Error("Computer input is bound to this session's desktop.");
return params.descriptor;
},
invoke: async ({ nodeId, command, commandParams, timeoutMs, idempotencyKey, signal }) => {
signal?.throwIfAborted();
if (nodeId !== params.descriptor.nodeId) throw new Error("Computer input is bound to this session's desktop.");
const isClose = command === "computer.act" && commandParams.action === "__close_execution";
if (closing && !isClose) throw new Error("Computer execution is closed.");
const request = {
command,
paramsJson: JSON.stringify(commandParams),
...timeoutMs === void 0 ? {} : { timeoutMs },
...idempotencyKey === void 0 ? {} : { idempotencyKey }
};
if (!Value.Check(WorkerComputerParamsSchema, request)) throw new Error("Computer request exceeds the worker protocol limits.");
const abort = () => {
close({
command: "computer.act",
paramsJson: JSON.stringify({
action: "__close_execution",
executionId: commandParams.executionId,
reason: "Worker computer invocation aborted"
})
}).catch(() => {});
};
signal?.addEventListener("abort", abort, { once: true });
try {
const response = await (isClose ? close(request) : params.requestComputer(request).catch((error) => {
abort();
throw error;
}));
signal?.throwIfAborted();
if (!response.ok) throw new Error(response.error.message);
return JSON.parse(response.payload.resultJson);
} finally {
signal?.removeEventListener("abort", abort);
}
}
};
return createComputerTool({
transport,
contextEpoch: params.contextEpoch,
idempotencyScope: params.runId,
registerRunCleanup: params.registerRunCleanup
});
}
//#endregion
//#region src/worker/embedded-agent-live.runtime.ts
const MAX_LIVE_EVENT_BYTES = 32768;
const MAX_LIVE_PREVIEW_BYTES = 4096;
function liveEventBytes(event) {
try {
return Buffer.byteLength(JSON.stringify(event), "utf8");
} catch {
return Number.POSITIVE_INFINITY;
}
}
function truncateLiveText(value) {
if (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 (liveEventBytes(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") {
if (event.payload.phase === "start") bounded = {
kind: "tool",
payload: {
...event.payload,
args: boundLiveValue(event.payload.args)
}
};
else if (event.payload.phase === "update") bounded = {
kind: "tool",
payload: {
...event.payload,
partialResult: boundLiveValue(event.payload.partialResult)
}
};
else bounded = {
kind: "tool",
payload: {
...event.payload,
result: boundLiveValue(event.payload.result)
}
};
} else if (event.kind === "lifecycle" && event.payload.phase === "error") bounded = {
kind: "lifecycle",
payload: {
...event.payload,
error: truncateLiveText(event.payload.error)
}
};
else throw new Error(`worker live ${event.kind} event exceeds the protocol payload limit`);
if (liveEventBytes(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" };
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 } : {},
...!terminal.aborted && typeof terminal.promptError === "string" ? { error: redactLiveText(terminal.promptError) } : {}
}
};
};
let streamedText = "";
let streamedPhase;
let assistantMessageIndex = 0;
let streamedThinking = "";
const emitAssistantSnapshot = (message) => {
const { text, phase } = readAssistantSnapshot(message);
if (text === streamedText && phase === streamedPhase) 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),
...replace ? { replace: true } : {},
...phase ? { phase } : {},
itemId: `assistant-${assistantMessageIndex}`
}
});
streamedText = text;
streamedPhase = phase;
};
const handleSessionEvent = (event) => {
if (event.type === "agent_start") {
enqueueLive({
kind: "lifecycle",
payload: {
phase: "start",
startedAt
}
});
return;
}
if (event.type === "message_start" && event.message.role === "assistant") {
assistantMessageIndex += 1;
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);
const finalThinking = readAssistantThinking(event.message);
if (finalThinking !== streamedThinking) enqueueLive({
kind: "thinking",
payload: {
text: finalThinking,
delta: finalThinking
}
});
return;
}
if (event.type === "tool_execution_start") {
enqueueLive({
kind: "tool",
payload: {
phase: "start",
name: event.toolName,
toolCallId: event.toolCallId,
args: redactAgentDiagnosticPayload(event.args),
...event.hideFromChannelProgress ? { hideFromChannelProgress: true } : {}
}
});
return;
}
if (event.type === "tool_execution_update") {
enqueueLive({
kind: "tool",
payload: {
phase: "update",
name: event.toolName,
toolCallId: event.toolCallId,
partialResult: redactAgentDiagnosticPayload(event.partialResult),
...event.hideFromChannelProgress ? { hideFromChannelProgress: true } : {}
}
});
return;
}
if (event.type === "tool_execution_end") {
enqueueLive({
kind: "tool",
payload: {
phase: "result",
name: event.toolName,
toolCallId: event.toolCallId,
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) => {
enqueueTerminal({
aborted: failure.aborted,
error: failure.error.message
});
};
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 toAgentMessage(message) {
if (message.role === "user") return {
role: "user",
content: message.content.map((part) => part.type === "text" ? cloneTextContent(part) : cloneImageContent(part)),
timestamp: message.timestamp
};
if (message.role === "toolResult") return {
role: "toolResult",
toolCallId: message.toolCallId,
toolName: message.toolName,
content: message.content.map((part) => part.type === "text" ? cloneTextContent(part) : cloneImageContent(part)),
...message.details === void 0 ? {} : { details: structuredClone(message.details) },
isError: message.isError,
timestamp: message.timestamp
};
return structuredClone(message);
}
function toWorkerInferenceMessage(message) {
if (message.role === "user") return {
kind: "complete",
message: {
role: "user",
content: typeof message.content === "string" ? message.content : message.content.map((part) => part.type === "text" ? cloneTextContent(part) : cloneImageContent(part)),
timestamp: message.timestamp,
...message.runtimeContextCarrier ? { runtimeContextCarrier: true } : {}
}
};
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) {
const pendingTranscriptMessages = [];
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}.`);
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) {
const batch = pendingTranscriptMessages.slice(0, 64);
await client.commit(batch);
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/worker-session-tools.ts
function parseToolResult(frame) {
if (!frame.ok) throw new Error(frame.error.message);
let parsed;
try {
parsed = JSON.parse(frame.payload.resultJson);
} catch (error) {
throw new Error("Gateway returned an invalid worker session tool result", { cause: error });
}
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.content)) throw new Error("Gateway returned an invalid worker session tool result");
return parsed;
}
function createWorkerSessionTools(client, skillAuthoring) {
const workshop = skillAuthoring ? {
...createLibrarySkillWorkshopDescriptor(skillAuthoring.multipleProfiles),
parameters: SkillLibraryWorkshopSchema,
execute: async (toolCallId, raw) => {
if (!Value.Check(SkillLibraryWorkshopSchema, raw) || !client.requestSkillWorkshop) throw new Error("Worker Workshop transport is unavailable or arguments are invalid.");
return parseToolResult(await client.requestSkillWorkshop({
toolCallId,
arguments: raw
}));
}
} : void 0;
return [
...workshop ? [workshop] : [],
{
label: "Sessions",
name: "sessions_spawn",
description: "Spawn a visible cloud child session in a fresh managed worktree. The child inherits the current cloud placement profile and attenuated tool policy.",
parameters: Type.Object({
task: Type.String({
minLength: 1,
maxLength: WORKER_SESSION_TOOL_MAX_TEXT_LENGTH
}),
label: Type.Optional(Type.String({
minLength: 1,
maxLength: 256
})),
agentId: Type.Optional(Type.String({
minLength: 1,
maxLength: 256
})),
model: Type.Optional(Type.String({
minLength: 1,
maxLength: 256
})),
runTimeoutSeconds: Type.Optional(Type.Integer({
minimum: 0,
maximum: 86400
}))
}),
execute: async (toolCallId, raw) => {
const params = raw;
return parseToolResult(await client.requestSessionsSpawn({
toolCallId,
...params
}));
}
},
{
label: "Session Send",
name: "sessions_send",
description: `Send a message to an authorized parent, child, or sibling session on this Gateway, whether it runs on the Gateway, a paired device, or a cloud worker. Cross-tree and stale-incarnation targets are denied by the Gateway. ${SESSIONS_SEND_RESULT_GUIDANCE} Status "no_reply" is terminal; do not wait for another result.`,
parameters: Type.Object({
sessionKey: Type.String({
minLength: 1,
maxLength: 1024
}),
message: Type.String({
minLength: 1,
maxLength: WORKER_SESSION_TOOL_MAX_TEXT_LENGTH
}),
timeoutSeconds: Type.Optional(Type.Integer({
minimum: 0,
maximum: 86400
}))
}),
execute: async (toolCallId, raw) => {
const params = raw;
return parseToolResult(await client.requestSessionsSend({
toolCallId,
...params
}));
}
},
{
label: "Portal",
name: "portal",
description: PORTAL_TOOL_DESCRIPTION,
parameters: PortalToolSchema,
outputSchema: PortalOutputSchema,
execute: async (toolCallId, raw) => {
if (!Value.Check(PortalToolSchema, raw)) throw new Error("Invalid portal tool arguments");
const params = {
toolCallId,
...raw
};
if (!Value.Check(WorkerPortalParamsSchema, params)) throw new Error("Portal tool arguments exceed the worker protocol limits");
return parseToolResult(await client.requestPortal(params));
}
}
];
}
//#endregion
//#region src/worker/embedded-agent.runtime.ts
function toWorkerAgentError(value, fallback) {
return value instanceof Error ? value : new Error(fallback, { cause: value });
}
const WORKER_TOOL_CONFIG = { plugins: { enabled: false } };
async function runWorkerEmbeddedTurn(params) {
const resources = params.skillResources ? await materializeSkillResources(params.skillResources, () => params.signal?.throwIfAborted()) : void 0;
try {
await runWorkerEmbeddedTurnWithResources({
...params,
prompt: resources ? typeof params.prompt === "string" ? resources.rewriteReferences(params.prompt) : params.prompt.map((part) => part.type === "text" ? {
...part,
text: resources.rewriteReferences(part.text)
} : part) : params.prompt,
systemPrompt: [params.systemPrompt, resources?.snapshot.prompt].filter(Boolean).join("\n\n") || void 0
}, resources?.snapshot);
} finally {
await resources?.cleanup();
}
}
async function runWorkerEmbeddedTurnWithResources(params, skillsSnapshot) {
if (params.allowedToolNames.includes("skill_workshop") !== Boolean(params.skillAuthoring)) throw new Error("Worker Workshop capability and tool authority must agree.");
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 model = createNativeModelOwnedRuntimeModel({
provider: params.modelRef.provider,
modelId: params.modelRef.model
});
const authStorage = AuthStorage.inMemory({});
const modelRegistry = ModelRegistry.inMemory(authStorage);
const settingsManager = SettingsManager.inMemory({
compaction: { enabled: false },
retry: { enabled: false }
});
const bootstrapFiles = (await loadWorkspaceBootstrapFiles(params.cwd)).filter((file) => file.name === DEFAULT_AGENTS_FILENAME);
const contextFiles = buildBootstrapContextForFiles(bootstrapFiles, {});
const resourceLoader = createEmbeddedAgentResourceLoader({
cwd: params.cwd,
agentDir: params.stateDir,
settingsManager,
appendSystemPromptTransform: () => params.systemPrompt === void 0 ? [] : [params.systemPrompt],
agentsFilesOverride: () => ({ agentsFiles: contextFiles })
});
await resourceLoader.reload();
const baseSessionManager = SessionManager.inMemory(params.cwd);
for (const message of params.initialMessages ?? []) baseSessionManager.appendMessage(toAgentMessage(message));
const transcriptRuntime = createWorkerTranscriptRuntime(params.transcript);
const sessionManager = guardSessionManager(baseSessionManager, {
suppressNextUserMessagePersistence: params.suppressPromptTranscript,
onMessagePersisted: transcriptRuntime.onMessagePersisted
});
const allowedToolNameSet = new Set(params.allowedToolNames);
const localToolNameSet = new Set(WORKER_LOCAL_TOOL_NAMES);
const permissionToolPolicy = params.permissionMode ? resolveSessionPermissionCoreToolPolicy({ mode: params.permissionMode }) : void 0;
const omittedToolNames = permissionToolPolicy?.readOnly ? /* @__PURE__ */ new Set([
"write",
"edit",
"apply_patch"
]) : void 0;
const activeToolNames = WORKER_TOOL_NAMES.filter((name) => allowedToolNameSet.has(name) && !omittedToolNames?.has(name));
const headlessApprovalText = params.permissionMode ? `Exec denied (approval_required) in worker ${params.permissionMode} permission mode. Run this command locally for interactive approval, or ask an administrator to clear the session permission mode.` : void 0;
const coreTools = createCoreCodingTools({
skillsSnapshot,
codingRoot: params.cwd,
containmentRoot: params.workerContainmentRoot,
includeBaseCodingTools: true,
includeShellTools: true,
workspaceOnly: permissionToolPolicy?.workspaceOnly ?? false,
readOnly: permissionToolPolicy?.readOnly ?? false,
modelContextWindowTokens: model.contextWindow,
imageSanitization: {},
applyPatchEnabled: permissionToolPolicy?.readOnly !== true && isApplyPatchAllowedForModel({
modelProvider: params.modelRef.provider,
modelId: params.modelRef.model
}),
applyPatchWorkspaceOnly: permissionToolPolicy?.applyPatchWorkspaceOnly ?? true,
execDefaults: {
bypassHostApprovalFloors: permissionToolPolicy?.bypassHostApprovalFloors,
host: "gateway",
mode: permissionToolPolicy?.execMode ?? "full",
security: "full",
ask: "off",
nonInteractiveApproval: Boolean(permissionToolPolicy && permissionToolPolicy.execMode !== "full"),
approvalFollowupText: headlessApprovalText,
config: WORKER_TOOL_CONFIG,
...params.github ? { preparedRunEnvironment: params.github } : {},
commandHighlighting: false,
agentId: params.agentId,
allowBackground: true,
scopeKey: params.sessionKey,
sessionKey: params.sessionKey,
runId: params.runId,
notifySessionKey: params.sessionKey,
sessionId: params.sessionId,
eventRouting: { preserveSessionKey: false }
},
processDefaults: { scopeKey: params.sessionKey }
});
const browserRuntime = params.browser ? await createWorkerBrowserToolRuntime({
descriptor: params.browser,
sessionKey: 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;
const disposeComputer = async () => {
const cleanup = computerCleanup;
computerCleanup = void 0;
await cleanup?.("Worker turn finished");
};
const { session } = await (async () => {
try {
const computerTool = params.computer ? createWorkerComputerTool({
...params.computer,
runId: params.runId,
registerRunCleanup: (cleanup) => {
computerCleanup = cleanup;
}
}) : void 0;
const localTools = finalizeAgentTools({
tools: [
...coreTools,
...browserRuntime ? [browserRuntime.tool] : [],
...computerTool ? [computerTool] : []
],
modelProvider: params.modelRef.provider,
modelId: params.modelRef.model,
hookContext: {
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 },
loopDetection: resolveToolLoopDetectionConfig({
cfg: WORKER_TOOL_CONFIG,
agentId: params.agentId
})
},
agentId: params.agentId,
abortSignal: toolSignal
}).filter((tool) => localToolNameSet.has(tool.name)).map((tool) => wrapToolWithGatewayCallerIdentity(tool, {
agentId: params.agentId,
sessionKey: params.sessionKey,
operationalRunInstance: params.operationalRunInstance,
signedAgentRuntimeIdentityToken: params.agentRuntimeIdentityToken
}));
const discoveredToolNames = new Set(localTools.map((tool) => tool.name));
for (const toolName of WORKER_REQUIRED_LOCAL_TOOL_NAMES) {
if (omittedToolNames?.has(toolName)) continue;
if (!discoveredToolNames.has(toolName)) throw new Error(`Worker coding tool unavailable: ${toolName}`);
}
if (WORKER_SESSION_TOOL_NAMES.filter((name) => allowedToolNameSet.has(name)).length > 0 && !params.sessions) throw new Error("Worker session tool client unavailable");
const sessionTools = params.sessions ? createWorkerSessionTools(params.sessions, params.skillAuthoring).filter((tool) => allowedToolNameSet.has(tool.name)) : [];
return await createAgentSession({
cwd: params.cwd,
agentDir: params.stateDir,
authStorage,
modelRegistry,
model,
thinkingLevel: "medium",
tools: [...activeToolNames],
customTools: toToolDefinitions([...localTools.filter((tool) => allowedToolNameSet.has(tool.name)), ...sessionTools.map((tool) => wrapToolWithAbortSignal(tool, toolSignal))]),
noTools: "all",
sessionManager,
settingsManager,
resourceLoader,
withSessionWriteSettlement: transcriptRuntime.withSessionWriteSettlement
});
} catch (error) {
turnLifetime.abort();
try {
await disposeComputer();
} finally {
await browserRuntime?.dispose();
}
throw error;
}
})();
session.agent.sessionId = params.sessionId;
session.setActiveToolsByName([...activeToolNames]);
session.agent.streamFn = (_model, context, options) => {
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.");
await session.agent.prompt({
role: "user",
content: typeof params.prompt === "string" ? [{
type: "text",
text: params.prompt
}] : params.prompt,
timestamp: Date.now()
});
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.");
liveRuntime.enqueueRunFailure({
aborted: params.signal?.aborted === true,
error: runFailure
});
}
let finalTranscriptFailure;
try {
turnLifetime.abort();
try {
await disposeComputer();
} catch (error) {
runFailure ??= toWorkerAgentError(error, "Worker computer cleanup failed.");
liveRuntime.enqueueRunFailure({
aborted: params.signal?.aborted === true,
error: runFailure
});
}
try {
await transcriptRuntime.withSessionWriteSettlement(() => void 0);
} catch (error) {
finalTranscriptFailure = toWorkerAgentError(error, "Worker transcript flush failed.");
}
if (finalTranscriptFailure === void 0) await liveRuntime.emitTerminal();
} finally {
turnLifetime.abort();
params.signal?.removeEventListener("abort", abortTurn);
unsubscribe();
session.dispose();
await browserRuntime?.dispose();
}
if (runFailure !== void 0) throw runFailure;
if (finalTranscriptFailure !== void 0) throw finalTranscriptFailure;
}
//#endregion
export { runWorkerEmbeddedTurn };