@seanhogg/builderforce-agents 2026.10.8 → 2026.10.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{audio-preflight-hAkEddaf.js → audio-preflight-CBZJKBS8.js} +1 -1
- package/dist/{audio-preflight-Dm9zyfJq.js → audio-preflight-CvvHdoWy.js} +1 -1
- package/dist/build-info.json +3 -3
- package/dist/{builderforce-wxtgU9xu.js → builderforce-BmrsVdFw.js} +1 -1
- package/dist/{builderforce-BWCp-M9e.js → builderforce-Bwyw0GJ6.js} +1 -1
- package/dist/bundled/boot-md/handler.js +1 -1
- package/dist/bundled/session-memory/handler.js +1 -1
- package/dist/chrome-Brg9KsB9.js +4 -0
- package/dist/chrome-CtTMuhww.js +4 -0
- package/dist/{command-registry-CiR8PUQY.js → command-registry-4_K-lfW5.js} +1 -1
- package/dist/{command-registry-D_LkOB4n.js → command-registry-COUcUWby.js} +1 -1
- package/dist/{completion-cli-CBES884A.js → completion-cli-B2qcVd6q.js} +1 -1
- package/dist/{completion-cli-C7munVvb.js → completion-cli-DlvHqL86.js} +1 -1
- package/dist/{completion-cli-CijodCzU.js → completion-cli-DpFvU4Qn.js} +1 -1
- package/dist/{completion-cli-B72GX2so.js → completion-cli-SdcQ-kQj.js} +1 -1
- package/dist/{doctor-completion-FErwMP3Y.js → doctor-completion-BSotFS6p.js} +1 -1
- package/dist/{doctor-completion-Bv2ztEeW.js → doctor-completion-DVxbs-74.js} +1 -1
- package/dist/{embedded-DXDHduG1.js → embedded-BcHzh_y8.js} +6 -6
- package/dist/{embedded-cvG_LpfQ.js → embedded-D-citLF2.js} +6 -6
- package/dist/entry.js +1 -1
- package/dist/extensionAPI.js +1 -1
- package/dist/{gateway-cli-DpJjgVYH.js → gateway-cli-D2Bg1Um6.js} +3 -3
- package/dist/{gateway-cli-U8RlUIuT.js → gateway-cli-DFEo8C0z.js} +3 -3
- package/dist/index.js +2 -2
- package/dist/llm-slug-generator.js +1 -1
- package/dist/{onboard-7JGHJRfj.js → onboard-BVmZ4jMw.js} +1 -1
- package/dist/{onboard-O2gyfB4L.js → onboard-DfMVh1bM.js} +1 -1
- package/dist/{onboard-interactive-FWIy0HTu.js → onboard-interactive-B3-KBZbZ.js} +1 -1
- package/dist/{onboard-interactive-DqpGicxB.js → onboard-interactive-DYJttZ2w.js} +1 -1
- package/dist/{onboard-interactive-DboEcFKf.js → onboard-interactive-Dc_7URYb.js} +1 -1
- package/dist/{onboard-interactive-Gl5J4kVC.js → onboard-interactive-zgjmwJz-.js} +1 -1
- package/dist/{onboarding-Ctq_bdkC.js → onboarding-CP9rrz8y.js} +1 -1
- package/dist/{onboarding-hPRXZReM.js → onboarding-D4-HDbmU.js} +1 -1
- package/dist/{onboarding.finalize-B4Tc4IDN.js → onboarding.finalize-BfWXPgX7.js} +1 -1
- package/dist/{onboarding.finalize-Db6smxZL.js → onboarding.finalize-C2kZi7F0.js} +1 -1
- package/dist/{program-DHWSgdol.js → program-2w7Ue902.js} +2 -2
- package/dist/{program-context-7XUznN5J.js → program-context-BCwL3wlM.js} +1 -1
- package/dist/{prompt-select-styled-Depbcbpw.js → prompt-select-styled-Dd5swW2b.js} +1 -1
- package/dist/{prompt-select-styled-Cy70lEMU.js → prompt-select-styled-hB_nLOn1.js} +1 -1
- package/dist/{pw-ai-UGketqfG.js → pw-ai-CsJmJcNk.js} +1 -1
- package/dist/{pw-ai-D8HJAvv-.js → pw-ai-DaxMmaVL.js} +1 -1
- package/dist/{register.maintenance-ohxXhvKX.js → register.maintenance-BELfCO_C.js} +1 -1
- package/dist/{register.maintenance-BmnagkdH.js → register.maintenance-DukWFS_D.js} +1 -1
- package/dist/{register.onboard-FSaweh3A.js → register.onboard-B9DMNfHE.js} +1 -1
- package/dist/{register.onboard-C2Cnd-e9.js → register.onboard-Dt7rzAq-.js} +1 -1
- package/dist/{register.setup-Dk9hBOkZ.js → register.setup-BRRVPrx7.js} +1 -1
- package/dist/{register.setup-CyTZQLXP.js → register.setup-CSF-2ImP.js} +1 -1
- package/dist/{register.subclis-lGgi7ATb.js → register.subclis-4eDV9jKH.js} +1 -1
- package/dist/{register.subclis-DqO3Ciiv.js → register.subclis-Cr9DoYcv.js} +1 -1
- package/dist/{run-main-BipfHjUR.js → run-main-DLcDEJgv.js} +1 -1
- package/dist/{runner-CviGu3Nk.js → runner-COldX-El.js} +3 -3
- package/dist/{runner-B70PGCqP.js → runner-D7af_RBH.js} +3 -3
- package/dist/{tui-C5ChWpBg.js → tui-BwWMByVp.js} +1 -1
- package/dist/{tui-DHjIJOFN.js → tui-DylWbrDx.js} +1 -1
- package/dist/{tui-cli-wPseN93O.js → tui-cli-Bh1V8-w9.js} +1 -1
- package/dist/{tui-cli-BeFWcUWU.js → tui-cli-C_TTgabr.js} +1 -1
- package/dist/{update-cli-CRkZnN_N.js → update-cli-CA3LuuJ7.js} +1 -1
- package/dist/{update-cli-DekV-CP_.js → update-cli-DUrUIrYQ.js} +1 -1
- package/dist/{web-DONGrYXq.js → web-BQPd6gQJ.js} +1 -1
- package/dist/{web-D44jKC1F.js → web-DPGGHIc_.js} +1 -1
- package/package.json +1 -1
- package/dist/chrome-Blk2Jftn.js +0 -4
- package/dist/chrome-DUBgoZpO.js +0 -4
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import{i as resolveGatewayPort,t as STATE_DIR,u as resolveRequiredHomeDir}from"./paths-BVMSM1lQ.js";import{c as normalizeAccountId$1,d as resolveAgentIdFromSessionKey,i as buildAgentMainSessionKey,l as normalizeAgentId,n as DEFAULT_AGENT_ID,u as normalizeMainKey}from"./session-key-CYpYHc1e.js";import{E as escapeRegExp,I as resolveUserPath,M as normalizeE164,R as shortenHomeInString,U as truncateUtf16Safe,X as shouldLogVerbose,i as getChatChannelMeta,k as isRecord$1,l as requireActivePluginRegistry,n as CHAT_CHANNEL_ORDER,q as logVerbose,t as CHANNEL_IDS,x as CONFIG_DIR}from"./registry-D_frpmHZ.js";import{f as normalizeSkillFilter,n as resolveAgentConfig,u as resolveSessionAgentId}from"./agent-scope-EkacNLg3.js";import{r as defaultRuntime,t as createSubsystemLogger}from"./subsystem-BakM_oqD.js";import{_ as resolveBuilderForceAgentsPackageRootSync,a as DEFAULT_IDENTITY_FILENAME,c as DEFAULT_USER_FILENAME,i as DEFAULT_HEARTBEAT_FILENAME,l as ensureAgentWorkspace,n as DEFAULT_AGENT_WORKSPACE_DIR,o as DEFAULT_SOUL_FILENAME,r as DEFAULT_BOOTSTRAP_FILENAME,s as DEFAULT_TOOLS_FILENAME,t as DEFAULT_AGENTS_FILENAME,y as runExec}from"./workspace-BCjtHT4u.js";import{B as stripReasoningTagsFromText,G as CURRENT_SESSION_VERSION,H as nativeComplete,K as SessionManager,St as normalizeBaseUrl$1,X as resolveApiKeyForProvider,Y as requireApiKey,bt as normalizeSecretInput,l as normalizeProviderId$1,q as getApiKeyForModel,v as normalizeGoogleModelId,vt as resolveProcessScopedMap,yt as isPidAlive}from"./model-selection-BJ0saFZo.js";import{n as formatCliCommand}from"./env-BhRm7_r8.js";import{t as parseBooleanValue}from"./boolean-ssGoaBPk.js";import{t as stableStringify}from"./src-CLWuGn12.js";import{_ as resolveDiscordAccount,a as normalizeWhatsAppTarget,d as resolveSlackAccount,f as resolveSlackReplyToMode,l as resolveTelegramAccount,r as normalizeChannelId}from"./plugins-J28Y83f5.js";import{t as resolveWhatsAppAccount}from"./accounts-193gfWbH.js";import{t as normalizeChatType}from"./chat-type-C1695gSf.js";import{n as resolveSignalAccount}from"./accounts-DHeA4T6C.js";import{S as isAudioFileName,b as getFileExtension,c as buildImageResizeSideGrid,i as fetchWithSsrFGuard,n as fetchRemoteMedia,o as fetchWithTimeout,p as resizeToJpeg,s as IMAGE_REDUCE_QUALITY_STEPS,t as MediaFetchError,u as getImageMetadata,v as detectMime,w as kindFromMime}from"./fetch-BklyLYYN.js";import{a as ensureBuilderForceAgentsModelsJson,n as loadModelCatalog,o as discoverAuthStorage,r as modelSupportsVision,s as discoverModels,t as findModelInCatalog}from"./model-catalog-Abk8cdEI.js";import{n as redactToolDetail}from"./redact-sNZY-pGx.js";import{n as formatErrorMessage,t as extractErrorCode}from"./errors-BmztzL5I.js";import{_ as resolveEnableState,a as writeConfigFile,d as parseDurationMs,g as normalizePluginsConfig,n as loadConfig,p as loadPluginManifestRegistry,s as parseByteSize,t as createConfigIO,v as resolveMemorySlotDecision}from"./config-bxyMjPHU.js";import{A as DEFAULT_BUILDERFORCE_AGENTS_BROWSER_ENABLED,D as DEFAULT_BROWSER_DEFAULT_PROFILE_NAME,E as DEFAULT_AI_SNAPSHOT_MAX_CHARS,O as DEFAULT_BROWSER_EVALUATE_ENABLED,T as DEFAULT_AI_SNAPSHOT_EFFICIENT_MAX_CHARS,a as resolveBuilderForceAgentsUserDataDir,c as captureScreenshot,d as normalizeCdpWsUrl,f as snapshotAria,h as fetchOk,i as launchBuilderForceAgentsChrome,j as DEFAULT_BUILDERFORCE_AGENTS_BROWSER_PROFILE_NAME,k as DEFAULT_BUILDERFORCE_AGENTS_BROWSER_COLOR,l as createTargetViaCdp,m as fetchJson,n as isChromeCdpReady,o as stopBuilderForceAgentsChrome,p as appendCdpPath,r as isChromeReachable,s as resolveBrowserExecutableForPlatform,v as ensureChromeExtensionRelayServer,w as DEFAULT_AI_SNAPSHOT_EFFICIENT_DEPTH,x as isLoopbackHost,y as stopChromeExtensionRelayServer}from"./chrome-
|
|
1
|
+
import{i as resolveGatewayPort,t as STATE_DIR,u as resolveRequiredHomeDir}from"./paths-BVMSM1lQ.js";import{c as normalizeAccountId$1,d as resolveAgentIdFromSessionKey,i as buildAgentMainSessionKey,l as normalizeAgentId,n as DEFAULT_AGENT_ID,u as normalizeMainKey}from"./session-key-CYpYHc1e.js";import{E as escapeRegExp,I as resolveUserPath,M as normalizeE164,R as shortenHomeInString,U as truncateUtf16Safe,X as shouldLogVerbose,i as getChatChannelMeta,k as isRecord$1,l as requireActivePluginRegistry,n as CHAT_CHANNEL_ORDER,q as logVerbose,t as CHANNEL_IDS,x as CONFIG_DIR}from"./registry-D_frpmHZ.js";import{f as normalizeSkillFilter,n as resolveAgentConfig,u as resolveSessionAgentId}from"./agent-scope-EkacNLg3.js";import{r as defaultRuntime,t as createSubsystemLogger}from"./subsystem-BakM_oqD.js";import{_ as resolveBuilderForceAgentsPackageRootSync,a as DEFAULT_IDENTITY_FILENAME,c as DEFAULT_USER_FILENAME,i as DEFAULT_HEARTBEAT_FILENAME,l as ensureAgentWorkspace,n as DEFAULT_AGENT_WORKSPACE_DIR,o as DEFAULT_SOUL_FILENAME,r as DEFAULT_BOOTSTRAP_FILENAME,s as DEFAULT_TOOLS_FILENAME,t as DEFAULT_AGENTS_FILENAME,y as runExec}from"./workspace-BCjtHT4u.js";import{B as stripReasoningTagsFromText,G as CURRENT_SESSION_VERSION,H as nativeComplete,K as SessionManager,St as normalizeBaseUrl$1,X as resolveApiKeyForProvider,Y as requireApiKey,bt as normalizeSecretInput,l as normalizeProviderId$1,q as getApiKeyForModel,v as normalizeGoogleModelId,vt as resolveProcessScopedMap,yt as isPidAlive}from"./model-selection-BJ0saFZo.js";import{n as formatCliCommand}from"./env-BhRm7_r8.js";import{t as parseBooleanValue}from"./boolean-ssGoaBPk.js";import{t as stableStringify}from"./src-CLWuGn12.js";import{_ as resolveDiscordAccount,a as normalizeWhatsAppTarget,d as resolveSlackAccount,f as resolveSlackReplyToMode,l as resolveTelegramAccount,r as normalizeChannelId}from"./plugins-J28Y83f5.js";import{t as resolveWhatsAppAccount}from"./accounts-193gfWbH.js";import{t as normalizeChatType}from"./chat-type-C1695gSf.js";import{n as resolveSignalAccount}from"./accounts-DHeA4T6C.js";import{S as isAudioFileName,b as getFileExtension,c as buildImageResizeSideGrid,i as fetchWithSsrFGuard,n as fetchRemoteMedia,o as fetchWithTimeout,p as resizeToJpeg,s as IMAGE_REDUCE_QUALITY_STEPS,t as MediaFetchError,u as getImageMetadata,v as detectMime,w as kindFromMime}from"./fetch-BklyLYYN.js";import{a as ensureBuilderForceAgentsModelsJson,n as loadModelCatalog,o as discoverAuthStorage,r as modelSupportsVision,s as discoverModels,t as findModelInCatalog}from"./model-catalog-Abk8cdEI.js";import{n as redactToolDetail}from"./redact-sNZY-pGx.js";import{n as formatErrorMessage,t as extractErrorCode}from"./errors-BmztzL5I.js";import{_ as resolveEnableState,a as writeConfigFile,d as parseDurationMs,g as normalizePluginsConfig,n as loadConfig,p as loadPluginManifestRegistry,s as parseByteSize,t as createConfigIO,v as resolveMemorySlotDecision}from"./config-bxyMjPHU.js";import{A as DEFAULT_BUILDERFORCE_AGENTS_BROWSER_ENABLED,D as DEFAULT_BROWSER_DEFAULT_PROFILE_NAME,E as DEFAULT_AI_SNAPSHOT_MAX_CHARS,O as DEFAULT_BROWSER_EVALUATE_ENABLED,T as DEFAULT_AI_SNAPSHOT_EFFICIENT_MAX_CHARS,a as resolveBuilderForceAgentsUserDataDir,c as captureScreenshot,d as normalizeCdpWsUrl,f as snapshotAria,h as fetchOk,i as launchBuilderForceAgentsChrome,j as DEFAULT_BUILDERFORCE_AGENTS_BROWSER_PROFILE_NAME,k as DEFAULT_BUILDERFORCE_AGENTS_BROWSER_COLOR,l as createTargetViaCdp,m as fetchJson,n as isChromeCdpReady,o as stopBuilderForceAgentsChrome,p as appendCdpPath,r as isChromeReachable,s as resolveBrowserExecutableForPlatform,v as ensureChromeExtensionRelayServer,w as DEFAULT_AI_SNAPSHOT_EFFICIENT_DEPTH,x as isLoopbackHost,y as stopChromeExtensionRelayServer}from"./chrome-CtTMuhww.js";import{a as resolveBuilderForceAgentsManifestBlock,c as resolveBuilderForceAgentsManifestRequires,d as hasBinary$1,f as isConfigPathTruthyWithDefaults,i as parseFrontmatterBool,l as parseFrontmatterBlock,n as normalizeStringList,o as resolveBuilderForceAgentsManifestInstall,p as resolveRuntimePlatform,r as parseBuilderForceAgentsManifestInstallBase,s as resolveBuilderForceAgentsManifestOs,t as getFrontmatterString,u as evaluateRuntimeRequires}from"./frontmatter-DBVosMFY.js";import{t as resolvePreferredBuilderForceAgentsTmpDir}from"./tmp-builderforce-dir-CB4MW5x2.js";import{r as saveMediaBuffer,t as ensureMediaDir}from"./store-DN9AcI73.js";import{o as listDeliverableMessageChannels,s as normalizeMessageChannel}from"./message-channel-tBuQgYSD.js";import{n as resolveConversationLabel}from"./conversation-label-CujjHinm.js";import{t as resolveIMessageAccount}from"./accounts-C7FtIPYB.js";import{a as resolveSessionTranscriptPathInDir,i as resolveSessionTranscriptPath,n as resolveSessionFilePath,s as resolveStorePath,t as resolveDefaultSessionStorePath}from"./paths-CzXKnIOb.js";import{i as emitSessionTranscriptUpdate,n as collectProviderApiKeysForExecution,r as executeWithApiKeyRotation,t as parseGeminiAuth}from"./gemini-auth-CzGGgJ0p.js";import{t as sanitizeContentBlocksImages}from"./tool-images-mfC2sOfJ.js";import fs from"node:fs/promises";import os from"node:os";import path,{basename,dirname,join,posix}from"node:path";import fs$1,{constants,existsSync,readFileSync,readdirSync,realpathSync,statSync}from"node:fs";import crypto,{createHash,timingSafeEqual}from"node:crypto";import{fileURLToPath}from"node:url";import{spawn}from"node:child_process";import"node:process";import express from"express";function isBase64Signature(value){let trimmed=value.trim();if(!trimmed)return!1;let compact=trimmed.replace(/\s+/g,``);if(!/^[A-Za-z0-9+/=_-]+$/.test(compact))return!1;let isUrl=compact.includes(`-`)||compact.includes(`_`);try{let buf=Buffer.from(compact,isUrl?`base64url`:`base64`);if(buf.length===0)return!1;let encoded=buf.toString(isUrl?`base64url`:`base64`),normalize=input=>input.replace(/=+$/g,``);return normalize(encoded)===normalize(compact)}catch{return!1}}function stripThoughtSignatures(content,options){if(!Array.isArray(content))return content;let allowBase64Only=options?.allowBase64Only??!1,includeCamelCase=options?.includeCamelCase??!1,shouldStripSignature=value=>allowBase64Only?typeof value!=`string`||!isBase64Signature(value):typeof value==`string`&&value.startsWith(`msg_`);return content.map(block=>{if(!block||typeof block!=`object`)return block;let rec=block,stripSnake=shouldStripSignature(rec.thought_signature),stripCamel=includeCamelCase?shouldStripSignature(rec.thoughtSignature):!1;if(!stripSnake&&!stripCamel)return block;let next={...rec};return stripSnake&&delete next.thought_signature,stripCamel&&delete next.thoughtSignature,next})}const DEFAULT_BOOTSTRAP_MAX_CHARS=2e4,DEFAULT_BOOTSTRAP_TOTAL_MAX_CHARS=15e4;function resolveBootstrapMaxChars(cfg){let raw=cfg?.agents?.defaults?.bootstrapMaxChars;return typeof raw==`number`&&Number.isFinite(raw)&&raw>0?Math.floor(raw):DEFAULT_BOOTSTRAP_MAX_CHARS}function resolveBootstrapTotalMaxChars(cfg){let raw=cfg?.agents?.defaults?.bootstrapTotalMaxChars;return typeof raw==`number`&&Number.isFinite(raw)&&raw>0?Math.floor(raw):DEFAULT_BOOTSTRAP_TOTAL_MAX_CHARS}function trimBootstrapContent(content,fileName,maxChars){let trimmed=content.trimEnd();if(trimmed.length<=maxChars)return{content:trimmed,truncated:!1,maxChars,originalLength:trimmed.length};let headChars=Math.floor(maxChars*.7),tailChars=Math.floor(maxChars*.2),head=trimmed.slice(0,headChars),tail=trimmed.slice(-tailChars);return{content:[head,[``,`[...truncated, read ${fileName} for full content...]`,`…(truncated ${fileName}: kept ${headChars}+${tailChars} chars of ${trimmed.length})…`,``].join(`
|
|
2
2
|
`),tail].join(`
|
|
3
3
|
`),truncated:!0,maxChars,originalLength:trimmed.length}}function clampToBudget(content,budget){return budget<=0?``:content.length<=budget?content:budget<=3?truncateUtf16Safe(content,budget):`${truncateUtf16Safe(content,budget-1)}…`}async function ensureSessionHeader$1(params){let file=params.sessionFile;try{await fs.stat(file);return}catch{}await fs.mkdir(path.dirname(file),{recursive:!0});let entry={type:`session`,version:2,id:params.sessionId,timestamp:new Date().toISOString(),cwd:params.cwd};await fs.writeFile(file,`${JSON.stringify(entry)}\n`,`utf-8`)}function buildBootstrapContextFiles(files,opts){let maxChars=opts?.maxChars??DEFAULT_BOOTSTRAP_MAX_CHARS,remainingTotalChars=Math.max(1,Math.floor(opts?.totalMaxChars??Math.max(maxChars,DEFAULT_BOOTSTRAP_TOTAL_MAX_CHARS))),result=[];for(let file of files){if(remainingTotalChars<=0)break;if(file.missing){let cappedMissingText=clampToBudget(`[MISSING] Expected at: ${file.path}`,remainingTotalChars);if(!cappedMissingText)break;remainingTotalChars=Math.max(0,remainingTotalChars-cappedMissingText.length),result.push({path:file.path,content:cappedMissingText});continue}if(remainingTotalChars<64){opts?.warn?.(`remaining bootstrap budget is ${remainingTotalChars} chars (<64); skipping additional bootstrap files`);break}let fileMaxChars=Math.max(1,Math.min(maxChars,remainingTotalChars)),trimmed=trimBootstrapContent(file.content??``,file.name,fileMaxChars),contentWithinBudget=clampToBudget(trimmed.content,remainingTotalChars);contentWithinBudget&&((trimmed.truncated||contentWithinBudget.length<trimmed.content.length)&&opts?.warn?.(`workspace bootstrap file ${file.name} is ${trimmed.originalLength} chars (limit ${trimmed.maxChars}); truncating in injected context`),remainingTotalChars=Math.max(0,remainingTotalChars-contentWithinBudget.length),result.push({path:file.path,content:contentWithinBudget}))}return result}function sanitizeGoogleTurnOrdering(messages){let GOOGLE_TURN_ORDER_BOOTSTRAP_TEXT=`(session bootstrap)`,first=messages[0],role=first?.role,content=first?.content;return role===`user`&&typeof content==`string`&&content.trim()===GOOGLE_TURN_ORDER_BOOTSTRAP_TEXT||role!==`assistant`?messages:[{role:`user`,content:GOOGLE_TURN_ORDER_BOOTSTRAP_TEXT,timestamp:Date.now()},...messages]}const DEFAULT_SANDBOX_WORKSPACE_ROOT=path.join(STATE_DIR,`sandboxes`),DEFAULT_SANDBOX_IMAGE=`builderforce-sandbox:bookworm-slim`,DEFAULT_TOOL_ALLOW=[`exec`,`process`,`read`,`write`,`edit`,`apply_patch`,`image`,`sessions_list`,`sessions_history`,`sessions_send`,`sessions_spawn`,`subagents`,`session_status`],DEFAULT_TOOL_DENY=[`browser`,`canvas`,`nodes`,`cron`,`gateway`,...CHANNEL_IDS],DEFAULT_SANDBOX_BROWSER_IMAGE=`builderforce-sandbox-browser:bookworm-slim`,SANDBOX_AGENT_WORKSPACE_MOUNT=`/agent`,SANDBOX_STATE_DIR=path.join(STATE_DIR,`sandbox`),SANDBOX_REGISTRY_PATH=path.join(SANDBOX_STATE_DIR,`containers.json`),SANDBOX_BROWSER_REGISTRY_PATH=path.join(SANDBOX_STATE_DIR,`browsers.json`);function escapeRegex(value){return value.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function compileGlobPattern(params){let normalized=params.normalize(params.raw);return normalized?normalized===`*`?{kind:`all`}:normalized.includes(`*`)?{kind:`regex`,value:RegExp(`^${escapeRegex(normalized).replaceAll(`\\*`,`.*`)}$`)}:{kind:`exact`,value:normalized}:{kind:`exact`,value:``}}function compileGlobPatterns(params){return Array.isArray(params.raw)?params.raw.map(raw=>compileGlobPattern({raw,normalize:params.normalize})).filter(pattern=>pattern.kind!==`exact`||pattern.value):[]}function matchesAnyGlobPattern(value,patterns){for(let pattern of patterns)if(pattern.kind===`all`||pattern.kind===`exact`&&value===pattern.value||pattern.kind===`regex`&&pattern.value.test(value))return!0;return!1}const TOOL_NAME_ALIASES={bash:`exec`,"apply-patch":`apply_patch`,str_replace_editor:`edit`},TOOL_GROUPS={"group:memory":[`memory_search`,`memory_get`],"group:web":[`web_search`,`web_fetch`],"group:fs":[`read`,`write`,`edit`,`apply_patch`],"group:runtime":[`exec`,`process`],"group:sessions":[`sessions_list`,`sessions_history`,`sessions_send`,`sessions_spawn`,`subagents`,`session_status`],"group:ui":[`browser`,`canvas`],"group:automation":[`cron`,`gateway`],"group:messaging":[`message`],"group:nodes":[`nodes`],"group:builderforce":[`browser`,`canvas`,`nodes`,`cron`,`message`,`gateway`,`agents_list`,`sessions_list`,`sessions_history`,`sessions_send`,`sessions_spawn`,`subagents`,`session_status`,`memory_search`,`memory_get`,`web_search`,`web_fetch`,`image`]},OWNER_ONLY_TOOL_NAMES=new Set([`whatsapp_login`]),TOOL_PROFILES={minimal:{allow:[`session_status`]},coding:{allow:[`group:fs`,`group:runtime`,`group:sessions`,`group:memory`,`image`]},messaging:{allow:[`group:messaging`,`sessions_list`,`sessions_history`,`sessions_send`,`session_status`]},full:{}};function normalizeToolName$1(name){let normalized=name.trim().toLowerCase();return TOOL_NAME_ALIASES[normalized]??normalized}function isOwnerOnlyToolName(name){return OWNER_ONLY_TOOL_NAMES.has(normalizeToolName$1(name))}function applyOwnerOnlyToolPolicy(tools,senderIsOwner){let withGuard=tools.map(tool=>!isOwnerOnlyToolName(tool.name)||senderIsOwner||!tool.execute?tool:{...tool,execute:async()=>{throw Error(`Tool restricted to owner senders.`)}});return senderIsOwner?withGuard:withGuard.filter(tool=>!isOwnerOnlyToolName(tool.name))}function normalizeToolList(list){return list?list.map(normalizeToolName$1).filter(Boolean):[]}function expandToolGroups(list){let normalized=normalizeToolList(list),expanded=[];for(let value of normalized){let group=TOOL_GROUPS[value];if(group){expanded.push(...group);continue}expanded.push(value)}return Array.from(new Set(expanded))}function collectExplicitAllowlist(policies){let entries=[];for(let policy of policies)if(policy?.allow)for(let value of policy.allow){if(typeof value!=`string`)continue;let trimmed=value.trim();trimmed&&entries.push(trimmed)}return entries}function buildPluginToolGroups(params){let all=[],byPlugin=new Map;for(let tool of params.tools){let meta=params.toolMeta(tool);if(!meta)continue;let name=normalizeToolName$1(tool.name);all.push(name);let pluginId=meta.pluginId.toLowerCase(),list=byPlugin.get(pluginId)??[];list.push(name),byPlugin.set(pluginId,list)}return{all,byPlugin}}function expandPluginGroups(list,groups){if(!list||list.length===0)return list;let expanded=[];for(let entry of list){let normalized=normalizeToolName$1(entry);if(normalized===`group:plugins`){groups.all.length>0?expanded.push(...groups.all):expanded.push(normalized);continue}let tools=groups.byPlugin.get(normalized);if(tools&&tools.length>0){expanded.push(...tools);continue}expanded.push(normalized)}return Array.from(new Set(expanded))}function expandPolicyWithPluginGroups(policy,groups){if(policy)return{allow:expandPluginGroups(policy.allow,groups),deny:expandPluginGroups(policy.deny,groups)}}function stripPluginOnlyAllowlist(policy,groups,coreTools){if(!policy?.allow||policy.allow.length===0)return{policy,unknownAllowlist:[],strippedAllowlist:!1};let normalized=normalizeToolList(policy.allow);if(normalized.length===0)return{policy,unknownAllowlist:[],strippedAllowlist:!1};let pluginIds=new Set(groups.byPlugin.keys()),pluginTools=new Set(groups.all),unknownAllowlist=[],hasCoreEntry=!1;for(let entry of normalized){if(entry===`*`){hasCoreEntry=!0;continue}let isPluginEntry=entry===`group:plugins`||pluginIds.has(entry)||pluginTools.has(entry),isCoreEntry=expandToolGroups([entry]).some(tool=>coreTools.has(tool));isCoreEntry&&(hasCoreEntry=!0),!isCoreEntry&&!isPluginEntry&&unknownAllowlist.push(entry)}let strippedAllowlist=!hasCoreEntry;return{policy:strippedAllowlist?{...policy,allow:void 0}:policy,unknownAllowlist:Array.from(new Set(unknownAllowlist)),strippedAllowlist}}function resolveToolProfilePolicy(profile){if(!profile)return;let resolved=TOOL_PROFILES[profile];if(resolved&&!(!resolved.allow&&!resolved.deny))return{allow:resolved.allow?[...resolved.allow]:void 0,deny:resolved.deny?[...resolved.deny]:void 0}}function mergeAlsoAllowPolicy(policy,alsoAllow){return!policy?.allow||!Array.isArray(alsoAllow)||alsoAllow.length===0?policy:{...policy,allow:Array.from(new Set([...policy.allow,...alsoAllow]))}}function normalizeGlob(value){return value.trim().toLowerCase()}function isToolAllowed(policy,name){let normalized=normalizeGlob(name);if(matchesAnyGlobPattern(normalized,compileGlobPatterns({raw:expandToolGroups(policy.deny??[]),normalize:normalizeGlob})))return!1;let allow=compileGlobPatterns({raw:expandToolGroups(policy.allow??[]),normalize:normalizeGlob});return allow.length===0?!0:matchesAnyGlobPattern(normalized,allow)}function resolveSandboxToolPolicyForAgent(cfg,agentId){let agentConfig=cfg&&agentId?resolveAgentConfig(cfg,agentId):void 0,agentAllow=agentConfig?.tools?.sandbox?.tools?.allow,agentDeny=agentConfig?.tools?.sandbox?.tools?.deny,globalAllow=cfg?.tools?.sandbox?.tools?.allow,globalDeny=cfg?.tools?.sandbox?.tools?.deny,allowSource=Array.isArray(agentAllow)?{source:`agent`,key:`agents.list[].tools.sandbox.tools.allow`}:Array.isArray(globalAllow)?{source:`global`,key:`tools.sandbox.tools.allow`}:{source:`default`,key:`tools.sandbox.tools.allow`},denySource=Array.isArray(agentDeny)?{source:`agent`,key:`agents.list[].tools.sandbox.tools.deny`}:Array.isArray(globalDeny)?{source:`global`,key:`tools.sandbox.tools.deny`}:{source:`default`,key:`tools.sandbox.tools.deny`},deny=Array.isArray(agentDeny)?agentDeny:Array.isArray(globalDeny)?globalDeny:[...DEFAULT_TOOL_DENY],allow=Array.isArray(agentAllow)?agentAllow:Array.isArray(globalAllow)?globalAllow:[...DEFAULT_TOOL_ALLOW],expandedDeny=expandToolGroups(deny),expandedAllow=expandToolGroups(allow);return expandedAllow.length>0&&!expandedDeny.map(v=>v.toLowerCase()).includes(`image`)&&!expandedAllow.map(v=>v.toLowerCase()).includes(`image`)&&(expandedAllow=[...expandedAllow,`image`]),{allow:expandedAllow,deny:expandedDeny,sources:{allow:allowSource,deny:denySource}}}function resolveSandboxBrowserDockerCreateConfig(params){let base={...params.docker,network:`bridge`,image:params.browser.image};return params.browser.binds===void 0?base:{...base,binds:params.browser.binds}}function resolveSandboxScope(params){return params.scope?params.scope:typeof params.perSession==`boolean`?params.perSession?`session`:`shared`:`agent`}function resolveSandboxDockerConfig(params){let agentDocker=params.scope===`shared`?void 0:params.agentDocker,globalDocker=params.globalDocker,env=agentDocker?.env?{...globalDocker?.env??{LANG:`C.UTF-8`},...agentDocker.env}:globalDocker?.env??{LANG:`C.UTF-8`},ulimits=agentDocker?.ulimits?{...globalDocker?.ulimits,...agentDocker.ulimits}:globalDocker?.ulimits,binds=[...globalDocker?.binds??[],...agentDocker?.binds??[]];return{image:agentDocker?.image??globalDocker?.image??DEFAULT_SANDBOX_IMAGE,containerPrefix:agentDocker?.containerPrefix??globalDocker?.containerPrefix??`builderforce-sbx-`,workdir:agentDocker?.workdir??globalDocker?.workdir??`/workspace`,readOnlyRoot:agentDocker?.readOnlyRoot??globalDocker?.readOnlyRoot??!0,tmpfs:agentDocker?.tmpfs??globalDocker?.tmpfs??[`/tmp`,`/var/tmp`,`/run`],network:agentDocker?.network??globalDocker?.network??`none`,user:agentDocker?.user??globalDocker?.user,capDrop:agentDocker?.capDrop??globalDocker?.capDrop??[`ALL`],env,setupCommand:agentDocker?.setupCommand??globalDocker?.setupCommand,pidsLimit:agentDocker?.pidsLimit??globalDocker?.pidsLimit,memory:agentDocker?.memory??globalDocker?.memory,memorySwap:agentDocker?.memorySwap??globalDocker?.memorySwap,cpus:agentDocker?.cpus??globalDocker?.cpus,ulimits,seccompProfile:agentDocker?.seccompProfile??globalDocker?.seccompProfile,apparmorProfile:agentDocker?.apparmorProfile??globalDocker?.apparmorProfile,dns:agentDocker?.dns??globalDocker?.dns,extraHosts:agentDocker?.extraHosts??globalDocker?.extraHosts,binds:binds.length?binds:void 0}}function resolveSandboxBrowserConfig(params){let agentBrowser=params.scope===`shared`?void 0:params.agentBrowser,globalBrowser=params.globalBrowser,binds=[...globalBrowser?.binds??[],...agentBrowser?.binds??[]],bindsConfigured=globalBrowser?.binds!==void 0||agentBrowser?.binds!==void 0;return{enabled:agentBrowser?.enabled??globalBrowser?.enabled??!1,image:agentBrowser?.image??globalBrowser?.image??DEFAULT_SANDBOX_BROWSER_IMAGE,containerPrefix:agentBrowser?.containerPrefix??globalBrowser?.containerPrefix??`builderforce-sbx-browser-`,cdpPort:agentBrowser?.cdpPort??globalBrowser?.cdpPort??9222,vncPort:agentBrowser?.vncPort??globalBrowser?.vncPort??5900,noVncPort:agentBrowser?.noVncPort??globalBrowser?.noVncPort??6080,headless:agentBrowser?.headless??globalBrowser?.headless??!1,enableNoVnc:agentBrowser?.enableNoVnc??globalBrowser?.enableNoVnc??!0,allowHostControl:agentBrowser?.allowHostControl??globalBrowser?.allowHostControl??!1,autoStart:agentBrowser?.autoStart??globalBrowser?.autoStart??!0,autoStartTimeoutMs:agentBrowser?.autoStartTimeoutMs??globalBrowser?.autoStartTimeoutMs??12e3,binds:bindsConfigured?binds:void 0}}function resolveSandboxPruneConfig(params){let agentPrune=params.scope===`shared`?void 0:params.agentPrune,globalPrune=params.globalPrune;return{idleHours:agentPrune?.idleHours??globalPrune?.idleHours??24,maxAgeDays:agentPrune?.maxAgeDays??globalPrune?.maxAgeDays??7}}function resolveSandboxConfigForAgent(cfg,agentId){let agent=cfg?.agents?.defaults?.sandbox,agentSandbox,agentConfig=cfg&&agentId?resolveAgentConfig(cfg,agentId):void 0;agentConfig?.sandbox&&(agentSandbox=agentConfig.sandbox);let scope=resolveSandboxScope({scope:agentSandbox?.scope??agent?.scope,perSession:agentSandbox?.perSession??agent?.perSession}),toolPolicy=resolveSandboxToolPolicyForAgent(cfg,agentId);return{mode:agentSandbox?.mode??agent?.mode??`off`,scope,workspaceAccess:agentSandbox?.workspaceAccess??agent?.workspaceAccess??`none`,workspaceRoot:agentSandbox?.workspaceRoot??agent?.workspaceRoot??DEFAULT_SANDBOX_WORKSPACE_ROOT,docker:resolveSandboxDockerConfig({scope,globalDocker:agent?.docker,agentDocker:agentSandbox?.docker}),browser:resolveSandboxBrowserConfig({scope,globalBrowser:agent?.browser,agentBrowser:agentSandbox?.browser}),tools:{allow:toolPolicy.allow,deny:toolPolicy.deny},prune:resolveSandboxPruneConfig({scope,globalPrune:agent?.prune,agentPrune:agentSandbox?.prune})}}function safeEqualSecret(provided,expected){if(typeof provided!=`string`||typeof expected!=`string`)return!1;let providedBuffer=Buffer.from(provided),expectedBuffer=Buffer.from(expected);return providedBuffer.length===expectedBuffer.length?timingSafeEqual(providedBuffer,expectedBuffer):!1}function resolveGatewayAuth(params){let authConfig=params.authConfig??{},token=authConfig.token??void 0,password=authConfig.password??void 0,trustedProxy=authConfig.trustedProxy,mode;mode=authConfig.mode?authConfig.mode:password?`password`:token?`token`:`none`;let allowTailscale=authConfig.allowTailscale??(params.tailscaleMode===`serve`&&mode!==`password`&&mode!==`trusted-proxy`);return{mode,token,password,allowTailscale,trustedProxy}}function resolveBrowserControlAuth(cfg,env=process.env){let auth=resolveGatewayAuth({authConfig:cfg?.gateway?.auth,env,tailscaleMode:cfg?.gateway?.tailscale?.mode}),token=typeof auth.token==`string`?auth.token.trim():``,password=typeof auth.password==`string`?auth.password.trim():``;return{token:token||void 0,password:password||void 0}}function shouldAutoGenerateBrowserAuth(env){if((env.NODE_ENV??``).trim().toLowerCase()===`test`)return!1;let vitest=(env.VITEST??``).trim().toLowerCase();return!(vitest&&vitest!==`0`&&vitest!==`false`&&vitest!==`off`)}async function ensureBrowserControlAuth(params){let env=params.env??process.env,auth=resolveBrowserControlAuth(params.cfg,env);if(auth.token||auth.password||!shouldAutoGenerateBrowserAuth(env)||params.cfg.gateway?.auth?.mode===`password`||params.cfg.gateway?.auth?.mode===`trusted-proxy`)return{auth};let latestCfg=loadConfig(),latestAuth=resolveBrowserControlAuth(latestCfg,env);if(latestAuth.token||latestAuth.password||latestCfg.gateway?.auth?.mode===`password`||latestCfg.gateway?.auth?.mode===`trusted-proxy`)return{auth:latestAuth};let generatedToken=crypto.randomBytes(24).toString(`hex`);return await writeConfigFile({...latestCfg,gateway:{...latestCfg.gateway,auth:{...latestCfg.gateway?.auth,mode:`token`,token:generatedToken}}}),{auth:{token:generatedToken},generatedToken}}function parseFrontmatter(content){return parseFrontmatterBlock(content)}function parseInstallSpec(input){let parsed=parseBuilderForceAgentsManifestInstallBase(input,[`brew`,`node`,`go`,`uv`,`download`]);if(!parsed)return;let{raw}=parsed,spec={kind:parsed.kind};parsed.id&&(spec.id=parsed.id),parsed.label&&(spec.label=parsed.label),parsed.bins&&(spec.bins=parsed.bins);let osList=normalizeStringList(raw.os);return osList.length>0&&(spec.os=osList),typeof raw.formula==`string`&&(spec.formula=raw.formula),typeof raw.package==`string`&&(spec.package=raw.package),typeof raw.module==`string`&&(spec.module=raw.module),typeof raw.url==`string`&&(spec.url=raw.url),typeof raw.archive==`string`&&(spec.archive=raw.archive),typeof raw.extract==`boolean`&&(spec.extract=raw.extract),typeof raw.stripComponents==`number`&&(spec.stripComponents=raw.stripComponents),typeof raw.targetDir==`string`&&(spec.targetDir=raw.targetDir),spec}function resolveBuilderForceAgentsMetadata(frontmatter){let metadataObj=resolveBuilderForceAgentsManifestBlock({frontmatter}),author=getFrontmatterString(frontmatter,`author`),authorUrl=getFrontmatterString(frontmatter,`author-url`),license=getFrontmatterString(frontmatter,`license`);if(!metadataObj)return author||authorUrl||license?{author,authorUrl,license}:void 0;let requires=resolveBuilderForceAgentsManifestRequires(metadataObj),install=resolveBuilderForceAgentsManifestInstall(metadataObj,parseInstallSpec),osRaw=resolveBuilderForceAgentsManifestOs(metadataObj);return{always:typeof metadataObj.always==`boolean`?metadataObj.always:void 0,emoji:typeof metadataObj.emoji==`string`?metadataObj.emoji:void 0,homepage:typeof metadataObj.homepage==`string`?metadataObj.homepage:void 0,skillKey:typeof metadataObj.skillKey==`string`?metadataObj.skillKey:void 0,primaryEnv:typeof metadataObj.primaryEnv==`string`?metadataObj.primaryEnv:void 0,author,authorUrl,license,os:osRaw.length>0?osRaw:void 0,requires,install:install.length>0?install:void 0}}function resolveSkillInvocationPolicy(frontmatter){return{userInvocable:parseFrontmatterBool(getFrontmatterString(frontmatter,`user-invocable`),!0),disableModelInvocation:parseFrontmatterBool(getFrontmatterString(frontmatter,`disable-model-invocation`),!1)}}function resolveSkillKey(skill,entry){return entry?.metadata?.skillKey??skill.name}const DEFAULT_CONFIG_VALUES={"browser.enabled":!0,"browser.evaluateEnabled":!0};function isConfigPathTruthy(config,pathStr){return isConfigPathTruthyWithDefaults(config,pathStr,DEFAULT_CONFIG_VALUES)}function resolveSkillConfig(config,skillKey){let skills=config?.skills?.entries;if(!skills||typeof skills!=`object`)return;let entry=skills[skillKey];if(!(!entry||typeof entry!=`object`))return entry}function normalizeAllowlist(input){if(!input||!Array.isArray(input))return;let normalized=input.map(entry=>String(entry).trim()).filter(Boolean);return normalized.length>0?normalized:void 0}const BUNDLED_SOURCES=new Set([`builderforce-bundled`]);function isBundledSkill(entry){return BUNDLED_SOURCES.has(entry.skill.source)}function isBundledSkillAllowed(entry,allowlist){if(!allowlist||allowlist.length===0||!isBundledSkill(entry))return!0;let key=resolveSkillKey(entry.skill,entry);return allowlist.includes(key)||allowlist.includes(entry.skill.name)}function shouldIncludeSkill(params){let{entry,config,eligibility}=params,skillConfig=resolveSkillConfig(config,resolveSkillKey(entry.skill,entry)),allowBundled=normalizeAllowlist(config?.skills?.allowBundled),osList=entry.metadata?.os??[],remotePlatforms=eligibility?.remote?.platforms??[];return skillConfig?.enabled===!1||!isBundledSkillAllowed(entry,allowBundled)||osList.length>0&&!osList.includes(resolveRuntimePlatform())&&!remotePlatforms.some(platform=>osList.includes(platform))?!1:entry.metadata?.always===!0?!0:evaluateRuntimeRequires({requires:entry.metadata?.requires,hasBin:hasBinary$1,hasRemoteBin:eligibility?.remote?.hasBin,hasAnyRemoteBin:eligibility?.remote?.hasAnyBin,hasEnv:envName=>!!(process.env[envName]||skillConfig?.env?.[envName]||skillConfig?.apiKey&&entry.metadata?.primaryEnv===envName),isConfigPathTruthy:configPath=>isConfigPathTruthy(config,configPath)})}function applySkillConfigEnvOverrides(params){let{updates,skillConfig,primaryEnv}=params;if(skillConfig.env)for(let[envKey,envValue]of Object.entries(skillConfig.env))!envValue||process.env[envKey]||(updates.push({key:envKey,prev:process.env[envKey]}),process.env[envKey]=envValue);primaryEnv&&skillConfig.apiKey&&!process.env[primaryEnv]&&(updates.push({key:primaryEnv,prev:process.env[primaryEnv]}),process.env[primaryEnv]=skillConfig.apiKey)}function createEnvReverter(updates){return()=>{for(let update of updates)update.prev===void 0?delete process.env[update.key]:process.env[update.key]=update.prev}}function applySkillEnvOverrides(params){let{skills,config}=params,updates=[];for(let entry of skills){let skillConfig=resolveSkillConfig(config,resolveSkillKey(entry.skill,entry));skillConfig&&applySkillConfigEnvOverrides({updates,skillConfig,primaryEnv:entry.metadata?.primaryEnv})}return createEnvReverter(updates)}function applySkillEnvOverridesFromSnapshot(params){let{snapshot,config}=params;if(!snapshot)return()=>{};let updates=[];for(let skill of snapshot.skills){let skillConfig=resolveSkillConfig(config,skill.name);skillConfig&&applySkillConfigEnvOverrides({updates,skillConfig,primaryEnv:skill.primaryEnv})}return createEnvReverter(updates)}const UNICODE_SPACES=/[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g,HTTP_URL_RE=/^https?:\/\//i,DATA_URL_RE=/^data:/i;function normalizeUnicodeSpaces(str){return str.replace(UNICODE_SPACES,` `)}function expandPath(filePath){let normalized=normalizeUnicodeSpaces(filePath);return normalized===`~`?os.homedir():normalized.startsWith(`~/`)?os.homedir()+normalized.slice(1):normalized}function resolveToCwd(filePath,cwd){let expanded=expandPath(filePath);return path.isAbsolute(expanded)?expanded:path.resolve(cwd,expanded)}function resolveSandboxInputPath(filePath,cwd){return resolveToCwd(filePath,cwd)}function resolveSandboxPath(params){let resolved=resolveSandboxInputPath(params.filePath,params.cwd),rootResolved=path.resolve(params.root),relative=path.relative(rootResolved,resolved);if(!relative||relative===``)return{resolved,relative:``};if(relative.startsWith(`..`)||path.isAbsolute(relative))throw Error(`Path escapes sandbox root (${shortPath(rootResolved)}): ${params.filePath}`);return{resolved,relative}}async function assertSandboxPath(params){let resolved=resolveSandboxPath(params);return await assertNoSymlinkEscape(resolved.relative,path.resolve(params.root),{allowFinalSymlink:params.allowFinalSymlink}),resolved}function assertMediaNotDataUrl(media){let raw=media.trim();if(DATA_URL_RE.test(raw))throw Error(`data: URLs are not supported for media. Use buffer instead.`)}async function resolveSandboxedMediaSource(params){let raw=params.media.trim();if(!raw||HTTP_URL_RE.test(raw))return raw;let candidate=raw;if(/^file:\/\//i.test(candidate))try{candidate=fileURLToPath(candidate)}catch{throw Error(`Invalid file:// URL for sandboxed media: ${raw}`)}return(await assertSandboxPath({filePath:candidate,cwd:params.sandboxRoot,root:params.sandboxRoot})).resolved}async function assertNoSymlinkEscape(relative,root,options){if(!relative)return;let rootReal=await tryRealpath(root),parts=relative.split(path.sep).filter(Boolean),current=root;for(let idx=0;idx<parts.length;idx+=1){let part=parts[idx],isLast=idx===parts.length-1;current=path.join(current,part);try{if((await fs.lstat(current)).isSymbolicLink()){if(options?.allowFinalSymlink&&isLast)return;let target=await tryRealpath(current);if(!isPathInside(rootReal,target))throw Error(`Symlink escapes sandbox root (${shortPath(rootReal)}): ${shortPath(current)}`);current=target}}catch(err){if(err.code===`ENOENT`)return;throw err}}}async function tryRealpath(value){try{return await fs.realpath(value)}catch{return path.resolve(value)}}function isPathInside(root,target){let relative=path.relative(root,target);return!relative||relative===``?!0:!(relative.startsWith(`..`)||path.isAbsolute(relative))}function shortPath(value){return value.startsWith(os.homedir())?`~${value.slice(os.homedir().length)}`:value}function looksLikeSkillsDir(dir){try{let entries=fs$1.readdirSync(dir,{withFileTypes:!0});for(let entry of entries){if(entry.name.startsWith(`.`))continue;let fullPath=path.join(dir,entry.name);if(entry.isFile()&&entry.name.endsWith(`.md`)||entry.isDirectory()&&fs$1.existsSync(path.join(fullPath,`SKILL.md`)))return!0}}catch{return!1}return!1}function resolveBundledSkillsDir(opts={}){let override=process.env.BUILDERFORCE_AGENTS_BUNDLED_SKILLS_DIR?.trim();if(override)return override;try{let execPath=opts.execPath??process.execPath,execDir=path.dirname(execPath),sibling=path.join(execDir,`skills`);if(fs$1.existsSync(sibling))return sibling}catch{}try{let moduleUrl=opts.moduleUrl??import.meta.url,moduleDir=path.dirname(fileURLToPath(moduleUrl)),packageRoot=resolveBuilderForceAgentsPackageRootSync({argv1:opts.argv1??process.argv[1],moduleUrl,cwd:opts.cwd??process.cwd()});if(packageRoot){let candidate=path.join(packageRoot,`skills`);if(looksLikeSkillsDir(candidate))return candidate}let current=moduleDir;for(let depth=0;depth<6;depth+=1){let candidate=path.join(current,`skills`);if(looksLikeSkillsDir(candidate))return candidate;let next=path.dirname(current);if(next===current)break;current=next}}catch{}}function skillDependencyNames(entry){return entry.metadata?.requires?.skills??[]}function resolveSkillDependencies(params){let{all,eligible,selected}=params,eligibleByName=new Map;for(let entry of eligible)eligibleByName.set(entry.skill.name,entry);let collectMissing=(name,stack)=>{let entry=eligibleByName.get(name);if(!entry)return[name];let missing=[];for(let dep of skillDependencyNames(entry))if(!stack.has(dep)){if(!eligibleByName.has(dep)){missing.push(dep);continue}missing.push(...collectMissing(dep,new Set(stack).add(dep)))}return missing},unmet=[],kept=[];for(let entry of selected){let missing=Array.from(new Set(collectMissing(entry.skill.name,new Set([entry.skill.name]))));if(missing.length>0){unmet.push({skill:entry.skill.name,missing});continue}kept.push(entry)}let includedNames=new Set,addWithDeps=(entry,stack)=>{if(!includedNames.has(entry.skill.name)){includedNames.add(entry.skill.name);for(let dep of skillDependencyNames(entry)){if(stack.has(dep))continue;let depEntry=eligibleByName.get(dep);depEntry&&addWithDeps(depEntry,new Set(stack).add(dep))}}};for(let entry of kept)addWithDeps(entry,new Set([entry.skill.name]));return{included:all.filter(entry=>includedNames.has(entry.skill.name)),unmet}}function loadSkillFromFile(filePath,source){let diagnostics=[];try{let frontmatter=parseFrontmatterBlock(readFileSync(filePath,`utf-8`)),skillDir=dirname(filePath),description=typeof frontmatter.description==`string`?frontmatter.description:``;return description.trim()?{skill:{name:typeof frontmatter.name==`string`&&frontmatter.name||basename(skillDir),description,filePath,baseDir:skillDir,source,disableModelInvocation:frontmatter[`disable-model-invocation`]===!0},diagnostics}:{skill:null,diagnostics}}catch(error){return diagnostics.push({type:`warning`,message:error instanceof Error?error.message:`failed to parse skill file`,path:filePath}),{skill:null,diagnostics}}}function loadSkillsFromDirInternal(dir,source,includeRootFiles){let skills=[],diagnostics=[];if(!existsSync(dir))return{skills,diagnostics};for(let entry of readdirSync(dir,{withFileTypes:!0})){if(entry.name.startsWith(`.`)||entry.name===`node_modules`)continue;let fullPath=join(dir,entry.name),isDirectory=entry.isDirectory(),isFile=entry.isFile();if(entry.isSymbolicLink())try{let stats=statSync(fullPath);isDirectory=stats.isDirectory(),isFile=stats.isFile()}catch{continue}if(isDirectory){let sub=loadSkillsFromDirInternal(fullPath,source,!1);skills.push(...sub.skills),diagnostics.push(...sub.diagnostics);continue}if(!isFile)continue;let isRootMd=includeRootFiles&&entry.name.endsWith(`.md`),isSkillMd=!includeRootFiles&&entry.name===`SKILL.md`;if(!isRootMd&&!isSkillMd)continue;let result=loadSkillFromFile(fullPath,source);result.skill&&skills.push(result.skill),diagnostics.push(...result.diagnostics)}return{skills,diagnostics}}function loadSkillsFromDir(options){return loadSkillsFromDirInternal(options.dir,options.source,!0)}function escapeXml(str){return str.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`).replace(/'/g,`'`)}function formatSkillsForPrompt(skills){let visible=skills.filter(s=>!s.disableModelInvocation);if(visible.length===0)return``;let lines=[`
|
|
4
4
|
|
|
5
5
|
The following skills provide specialized instructions for specific tasks.`,`Use the read tool to load a skill's file when the task matches its description.`,`When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.`,``,`<available_skills>`];for(let skill of visible)lines.push(` <skill>`),lines.push(` <name>${escapeXml(skill.name)}</name>`),lines.push(` <description>${escapeXml(skill.description)}</description>`),lines.push(` <location>${escapeXml(skill.filePath)}</location>`),lines.push(` </skill>`);return lines.push(`</available_skills>`),lines.join(`
|
|
6
6
|
`)}const log$1=createSubsystemLogger(`skills`);function resolvePluginSkillDirs(params){let workspaceDir=params.workspaceDir.trim();if(!workspaceDir)return[];let registry=loadPluginManifestRegistry({workspaceDir,config:params.config});if(registry.plugins.length===0)return[];let normalizedPlugins=normalizePluginsConfig(params.config?.plugins),memorySlot=normalizedPlugins.slots.memory,selectedMemoryPluginId=null,seen=new Set,resolved=[];for(let record of registry.plugins){if(!record.skills||record.skills.length===0||!resolveEnableState(record.id,record.origin,normalizedPlugins).enabled)continue;let memoryDecision=resolveMemorySlotDecision({id:record.id,kind:record.kind,slot:memorySlot,selectedId:selectedMemoryPluginId});if(memoryDecision.enabled){memoryDecision.selected&&record.kind===`memory`&&(selectedMemoryPluginId=record.id);for(let raw of record.skills){let trimmed=raw.trim();if(!trimmed)continue;let candidate=path.resolve(record.rootDir,trimmed);if(!fs$1.existsSync(candidate)){log$1.warn(`plugin skill path not found (${record.id}): ${candidate}`);continue}seen.has(candidate)||(seen.add(candidate),resolved.push(candidate))}}}return resolved}const SKILLS_SYNC_QUEUE=new Map;async function serializeByKey(key,task){let next=(SKILLS_SYNC_QUEUE.get(key)??Promise.resolve()).then(task,task);SKILLS_SYNC_QUEUE.set(key,next);try{return await next}finally{SKILLS_SYNC_QUEUE.get(key)===next&&SKILLS_SYNC_QUEUE.delete(key)}}const fsp=fs$1.promises,skillsLogger=createSubsystemLogger(`skills`),skillCommandDebugOnce=new Set;function compactSkillPaths(skills){let home=os.homedir();if(!home)return skills;let prefix=home.endsWith(path.sep)?home:home+path.sep;return skills.map(s=>({...s,filePath:s.filePath.startsWith(prefix)?`~/`+s.filePath.slice(prefix.length):s.filePath}))}function debugSkillCommandOnce(messageKey,message,meta){skillCommandDebugOnce.has(messageKey)||(skillCommandDebugOnce.add(messageKey),skillsLogger.debug(message,meta))}function filterSkillEntries(entries,config,skillFilter,eligibility){let eligible=entries.filter(entry=>shouldIncludeSkill({entry,config,eligibility})),selected=eligible;if(skillFilter!==void 0){let normalized=normalizeSkillFilter(skillFilter)??[],label=normalized.length>0?normalized.join(`, `):`(none)`;skillsLogger.debug(`Applying skill filter: ${label}`),selected=normalized.length>0?eligible.filter(entry=>normalized.includes(entry.skill.name)):[],skillsLogger.debug(`After skill filter: ${selected.map(entry=>entry.skill.name).join(`, `)||`(none)`}`)}let{included,unmet}=resolveSkillDependencies({all:entries,eligible,selected});for(let{skill,missing}of unmet)skillsLogger.debug(`Skill "${skill}" excluded: missing required skill(s): ${missing.join(`, `)}`);return included}function sanitizeSkillCommandName(raw){return raw.toLowerCase().replace(/[^a-z0-9_]+/g,`_`).replace(/_+/g,`_`).replace(/^_+|_+$/g,``).slice(0,32)||`skill`}function resolveUniqueSkillCommandName(base,used){let normalizedBase=base.toLowerCase();if(!used.has(normalizedBase))return base;for(let index=2;index<1e3;index+=1){let suffix=`_${index}`,maxBaseLength=Math.max(1,32-suffix.length),candidate=`${base.slice(0,maxBaseLength)}${suffix}`,candidateKey=candidate.toLowerCase();if(!used.has(candidateKey))return candidate}return`${base.slice(0,30)}_x`}function resolveSkillsLimits(config){let limits=config?.skills?.limits;return{maxCandidatesPerRoot:limits?.maxCandidatesPerRoot??300,maxSkillsLoadedPerSource:limits?.maxSkillsLoadedPerSource??200,maxSkillsInPrompt:limits?.maxSkillsInPrompt??150,maxSkillsPromptChars:limits?.maxSkillsPromptChars??3e4,maxSkillFileBytes:limits?.maxSkillFileBytes??256e3}}function listChildDirectories(dir){try{let entries=fs$1.readdirSync(dir,{withFileTypes:!0}),dirs=[];for(let entry of entries){if(entry.name.startsWith(`.`)||entry.name===`node_modules`)continue;let fullPath=path.join(dir,entry.name);if(entry.isDirectory()){dirs.push(entry.name);continue}if(entry.isSymbolicLink())try{fs$1.statSync(fullPath).isDirectory()&&dirs.push(entry.name)}catch{}}return dirs}catch{return[]}}function resolveNestedSkillsRoot(dir,opts){let nested=path.join(dir,`skills`);try{if(!fs$1.existsSync(nested)||!fs$1.statSync(nested).isDirectory())return{baseDir:dir}}catch{return{baseDir:dir}}let nestedDirs=listChildDirectories(nested),scanLimit=Math.max(0,opts?.maxEntriesToScan??100),toScan=scanLimit===0?[]:nestedDirs.slice(0,Math.min(nestedDirs.length,scanLimit));for(let name of toScan){let skillMd=path.join(nested,name,`SKILL.md`);if(fs$1.existsSync(skillMd))return{baseDir:nested,note:`Detected nested skills root at ${nested}`}}return{baseDir:dir}}function unwrapLoadedSkills(loaded){if(Array.isArray(loaded))return loaded;if(loaded&&typeof loaded==`object`&&`skills`in loaded){let skills=loaded.skills;if(Array.isArray(skills))return skills}return[]}function loadSkillEntries(workspaceDir,opts){let limits=resolveSkillsLimits(opts?.config),loadSkills=params=>{let baseDir=resolveNestedSkillsRoot(params.dir,{maxEntriesToScan:limits.maxCandidatesPerRoot}).baseDir,rootSkillMd=path.join(baseDir,`SKILL.md`);if(fs$1.existsSync(rootSkillMd)){try{let size=fs$1.statSync(rootSkillMd).size;if(size>limits.maxSkillFileBytes)return skillsLogger.warn(`Skipping skills root due to oversized SKILL.md.`,{dir:baseDir,filePath:rootSkillMd,size,maxSkillFileBytes:limits.maxSkillFileBytes}),[]}catch{return[]}return unwrapLoadedSkills(loadSkillsFromDir({dir:baseDir,source:params.source}))}let childDirs=listChildDirectories(baseDir),suspicious=childDirs.length>limits.maxCandidatesPerRoot,maxCandidates=Math.max(0,limits.maxSkillsLoadedPerSource),limitedChildren=childDirs.slice().sort().slice(0,maxCandidates);suspicious?skillsLogger.warn(`Skills root looks suspiciously large, truncating discovery.`,{dir:params.dir,baseDir,childDirCount:childDirs.length,maxCandidatesPerRoot:limits.maxCandidatesPerRoot,maxSkillsLoadedPerSource:limits.maxSkillsLoadedPerSource}):childDirs.length>maxCandidates&&skillsLogger.warn(`Skills root has many entries, truncating discovery.`,{dir:params.dir,baseDir,childDirCount:childDirs.length,maxSkillsLoadedPerSource:limits.maxSkillsLoadedPerSource});let loadedSkills=[];for(let name of limitedChildren){let skillDir=path.join(baseDir,name),skillMd=path.join(skillDir,`SKILL.md`);if(!fs$1.existsSync(skillMd))continue;try{let size=fs$1.statSync(skillMd).size;if(size>limits.maxSkillFileBytes){skillsLogger.warn(`Skipping skill due to oversized SKILL.md.`,{skill:name,filePath:skillMd,size,maxSkillFileBytes:limits.maxSkillFileBytes});continue}}catch{continue}let loaded=loadSkillsFromDir({dir:skillDir,source:params.source});if(loadedSkills.push(...unwrapLoadedSkills(loaded)),loadedSkills.length>=limits.maxSkillsLoadedPerSource)break}return loadedSkills.length>limits.maxSkillsLoadedPerSource?loadedSkills.slice().sort((a,b)=>a.name.localeCompare(b.name)).slice(0,limits.maxSkillsLoadedPerSource):loadedSkills},managedSkillsDir=opts?.managedSkillsDir??path.join(CONFIG_DIR,`skills`),workspaceSkillsDir=path.resolve(workspaceDir,`skills`),bundledSkillsDir=opts?.bundledSkillsDir??resolveBundledSkillsDir(),extraDirs=(opts?.config?.skills?.load?.extraDirs??[]).map(d=>typeof d==`string`?d.trim():``).filter(Boolean),pluginSkillDirs=resolvePluginSkillDirs({workspaceDir,config:opts?.config}),mergedExtraDirs=[...extraDirs,...pluginSkillDirs],bundledSkills=bundledSkillsDir?loadSkills({dir:bundledSkillsDir,source:`builderforce-bundled`}):[],extraSkills=mergedExtraDirs.flatMap(dir=>loadSkills({dir:resolveUserPath(dir),source:`builderforce-extra`})),managedSkills=loadSkills({dir:managedSkillsDir,source:`builderforce-managed`}),personalAgentsSkills=loadSkills({dir:path.resolve(os.homedir(),`.agents`,`skills`),source:`agents-skills-personal`}),projectAgentsSkills=loadSkills({dir:path.resolve(workspaceDir,`.agents`,`skills`),source:`agents-skills-project`}),workspaceSkills=loadSkills({dir:workspaceSkillsDir,source:`builderforce-workspace`}),merged=new Map;for(let skill of extraSkills)merged.set(skill.name,skill);for(let skill of bundledSkills)merged.set(skill.name,skill);for(let skill of managedSkills)merged.set(skill.name,skill);for(let skill of personalAgentsSkills)merged.set(skill.name,skill);for(let skill of projectAgentsSkills)merged.set(skill.name,skill);for(let skill of workspaceSkills)merged.set(skill.name,skill);return Array.from(merged.values()).map(skill=>{let frontmatter={};try{frontmatter=parseFrontmatter(fs$1.readFileSync(skill.filePath,`utf-8`))}catch{}return{skill,frontmatter,metadata:resolveBuilderForceAgentsMetadata(frontmatter),invocation:resolveSkillInvocationPolicy(frontmatter)}})}function applySkillsPromptLimits(params){let limits=resolveSkillsLimits(params.config),total=params.skills.length,byCount=params.skills.slice(0,Math.max(0,limits.maxSkillsInPrompt)),skillsForPrompt=byCount,truncated=total>byCount.length,truncatedReason=truncated?`count`:null,fits=skills=>formatSkillsForPrompt(skills).length<=limits.maxSkillsPromptChars;if(!fits(skillsForPrompt)){let lo=0,hi=skillsForPrompt.length;for(;lo<hi;){let mid=Math.ceil((lo+hi)/2);fits(skillsForPrompt.slice(0,mid))?lo=mid:hi=mid-1}skillsForPrompt=skillsForPrompt.slice(0,lo),truncated=!0,truncatedReason=`chars`}return{skillsForPrompt,truncated,truncatedReason}}function buildWorkspaceSkillSnapshot(workspaceDir,opts){let eligible=filterSkillEntries(opts?.entries??loadSkillEntries(workspaceDir,opts),opts?.config,opts?.skillFilter,opts?.eligibility),resolvedSkills=eligible.filter(entry=>entry.invocation?.disableModelInvocation!==!0).map(entry=>entry.skill),remoteNote=opts?.eligibility?.remote?.note?.trim(),{skillsForPrompt,truncated}=applySkillsPromptLimits({skills:resolvedSkills,config:opts?.config}),prompt=[remoteNote,truncated?`⚠️ Skills truncated: included ${skillsForPrompt.length} of ${resolvedSkills.length}. Run \`builderforce skills check\` to audit.`:``,formatSkillsForPrompt(compactSkillPaths(skillsForPrompt))].filter(Boolean).join(`
|
|
7
7
|
`),skillFilter=normalizeSkillFilter(opts?.skillFilter);return{prompt,skills:eligible.map(entry=>({name:entry.skill.name,primaryEnv:entry.metadata?.primaryEnv})),...skillFilter===void 0?{}:{skillFilter},resolvedSkills,version:opts?.snapshotVersion}}function buildWorkspaceSkillsPrompt(workspaceDir,opts){let promptEntries=filterSkillEntries(opts?.entries??loadSkillEntries(workspaceDir,opts),opts?.config,opts?.skillFilter,opts?.eligibility).filter(entry=>entry.invocation?.disableModelInvocation!==!0),remoteNote=opts?.eligibility?.remote?.note?.trim(),resolvedSkills=promptEntries.map(entry=>entry.skill),{skillsForPrompt,truncated}=applySkillsPromptLimits({skills:resolvedSkills,config:opts?.config});return[remoteNote,truncated?`⚠️ Skills truncated: included ${skillsForPrompt.length} of ${resolvedSkills.length}. Run \`builderforce skills check\` to audit.`:``,formatSkillsForPrompt(compactSkillPaths(skillsForPrompt))].filter(Boolean).join(`
|
|
8
|
-
`)}function resolveSkillsPromptForRun(params){let snapshotPrompt=params.skillsSnapshot?.prompt?.trim();if(snapshotPrompt)return snapshotPrompt;if(params.entries&¶ms.entries.length>0){let prompt=buildWorkspaceSkillsPrompt(params.workspaceDir,{entries:params.entries,config:params.config});return prompt.trim()?prompt:``}return``}function loadWorkspaceSkillEntries(workspaceDir,opts){return loadSkillEntries(workspaceDir,opts)}function resolveUniqueSyncedSkillDirName(base,used){if(!used.has(base))return used.add(base),base;for(let index=2;index<1e4;index+=1){let candidate=`${base}-${index}`;if(!used.has(candidate))return used.add(candidate),candidate}let fallbackIndex=1e4,fallback=`${base}-${fallbackIndex}`;for(;used.has(fallback);)fallbackIndex+=1,fallback=`${base}-${fallbackIndex}`;return used.add(fallback),fallback}function resolveSyncedSkillDestinationPath(params){let sourceDirName=path.basename(params.entry.skill.baseDir).trim();return!sourceDirName||sourceDirName===`.`||sourceDirName===`..`?null:resolveSandboxPath({filePath:resolveUniqueSyncedSkillDirName(sourceDirName,params.usedDirNames),cwd:params.targetSkillsDir,root:params.targetSkillsDir}).resolved}async function syncSkillsToWorkspace(params){let sourceDir=resolveUserPath(params.sourceWorkspaceDir),targetDir=resolveUserPath(params.targetWorkspaceDir);sourceDir!==targetDir&&await serializeByKey(`syncSkills:${targetDir}`,async()=>{let targetSkillsDir=path.join(targetDir,`skills`),entries=loadSkillEntries(sourceDir,{config:params.config,managedSkillsDir:params.managedSkillsDir,bundledSkillsDir:params.bundledSkillsDir});await fsp.rm(targetSkillsDir,{recursive:!0,force:!0}),await fsp.mkdir(targetSkillsDir,{recursive:!0});let usedDirNames=new Set;for(let entry of entries){let dest=null;try{dest=resolveSyncedSkillDestinationPath({targetSkillsDir,entry,usedDirNames})}catch(error){let message=error instanceof Error?error.message:JSON.stringify(error);console.warn(`[skills] Failed to resolve safe destination for ${entry.skill.name}: ${message}`);continue}if(!dest){console.warn(`[skills] Failed to resolve safe destination for ${entry.skill.name}: invalid source directory name`);continue}try{await fsp.cp(entry.skill.baseDir,dest,{recursive:!0,force:!0})}catch(error){let message=error instanceof Error?error.message:JSON.stringify(error);console.warn(`[skills] Failed to copy ${entry.skill.name} to sandbox: ${message}`)}}})}function buildWorkspaceSkillCommandSpecs(workspaceDir,opts){let userInvocable=filterSkillEntries(opts?.entries??loadSkillEntries(workspaceDir,opts),opts?.config,opts?.skillFilter,opts?.eligibility).filter(entry=>entry.invocation?.userInvocable!==!1),used=new Set;for(let reserved of opts?.reservedNames??[])used.add(reserved.toLowerCase());let specs=[];for(let entry of userInvocable){let rawName=entry.skill.name,base=sanitizeSkillCommandName(rawName);base!==rawName&&debugSkillCommandOnce(`sanitize:${rawName}:${base}`,`Sanitized skill command name "${rawName}" to "/${base}".`,{rawName,sanitized:`/${base}`});let unique=resolveUniqueSkillCommandName(base,used);unique!==base&&debugSkillCommandOnce(`dedupe:${rawName}:${unique}`,`De-duplicated skill command name for "${rawName}" to "/${unique}".`,{rawName,deduped:`/${unique}`}),used.add(unique.toLowerCase());let rawDescription=entry.skill.description?.trim()||rawName,description=rawDescription.length>100?rawDescription.slice(0,99)+`…`:rawDescription,dispatch=(()=>{let kindRaw=(entry.frontmatter?.[`command-dispatch`]??entry.frontmatter?.command_dispatch??``).trim().toLowerCase();if(!kindRaw||kindRaw!==`tool`)return;let toolName=(entry.frontmatter?.[`command-tool`]??entry.frontmatter?.command_tool??``).trim();if(!toolName){debugSkillCommandOnce(`dispatch:missingTool:${rawName}`,`Skill command "/${unique}" requested tool dispatch but did not provide command-tool. Ignoring dispatch.`,{skillName:rawName,command:unique});return}let argModeRaw=(entry.frontmatter?.[`command-arg-mode`]??entry.frontmatter?.command_arg_mode??``).trim().toLowerCase();return!argModeRaw||argModeRaw===`raw`||debugSkillCommandOnce(`dispatch:badArgMode:${rawName}:${argModeRaw}`,`Skill command "/${unique}" requested tool dispatch but has unknown command-arg-mode. Falling back to raw.`,{skillName:rawName,command:unique,argMode:argModeRaw}),{kind:`tool`,toolName,argMode:`raw`}})();specs.push({name:unique,skillName:rawName,description,...dispatch?{dispatch}:{}})}return specs}const authByPort=new Map;function setBridgeAuthForPort(port,auth){if(!Number.isFinite(port)||port<=0)return;let token=typeof auth.token==`string`?auth.token.trim():``,password=typeof auth.password==`string`?auth.password.trim():``;authByPort.set(port,{token:token||void 0,password:password||void 0})}function getBridgeAuthForPort(port){if(!(!Number.isFinite(port)||port<=0))return authByPort.get(port)}function deleteBridgeAuthForPort(port){!Number.isFinite(port)||port<=0||authByPort.delete(port)}const ACT_KINDS=[`click`,`close`,`drag`,`evaluate`,`fill`,`hover`,`scrollIntoView`,`press`,`resize`,`select`,`type`,`wait`];function isActKind(value){return typeof value==`string`?ACT_KINDS.includes(value):!1}const ALLOWED_CLICK_MODIFIERS=new Set([`Alt`,`Control`,`ControlOrMeta`,`Meta`,`Shift`]);function parseClickButton(raw){if(raw===`left`||raw===`right`||raw===`middle`)return raw}function parseClickModifiers(raw){return raw.filter(m=>!ALLOWED_CLICK_MODIFIERS.has(m)).length?{error:`modifiers must be Alt|Control|ControlOrMeta|Meta|Shift`}:{modifiers:raw.length?raw:void 0}}let pwAiModuleSoft=null,pwAiModuleStrict=null;function isModuleNotFoundError(err){if(extractErrorCode(err)===`ERR_MODULE_NOT_FOUND`)return!0;let msg=formatErrorMessage(err);return msg.includes(`Cannot find module`)||msg.includes(`Cannot find package`)||msg.includes(`Failed to resolve import`)||msg.includes(`Failed to resolve entry for package`)||msg.includes(`Failed to load url`)}async function loadPwAiModule(mode){try{return await import(`./pw-ai-
|
|
8
|
+
`)}function resolveSkillsPromptForRun(params){let snapshotPrompt=params.skillsSnapshot?.prompt?.trim();if(snapshotPrompt)return snapshotPrompt;if(params.entries&¶ms.entries.length>0){let prompt=buildWorkspaceSkillsPrompt(params.workspaceDir,{entries:params.entries,config:params.config});return prompt.trim()?prompt:``}return``}function loadWorkspaceSkillEntries(workspaceDir,opts){return loadSkillEntries(workspaceDir,opts)}function resolveUniqueSyncedSkillDirName(base,used){if(!used.has(base))return used.add(base),base;for(let index=2;index<1e4;index+=1){let candidate=`${base}-${index}`;if(!used.has(candidate))return used.add(candidate),candidate}let fallbackIndex=1e4,fallback=`${base}-${fallbackIndex}`;for(;used.has(fallback);)fallbackIndex+=1,fallback=`${base}-${fallbackIndex}`;return used.add(fallback),fallback}function resolveSyncedSkillDestinationPath(params){let sourceDirName=path.basename(params.entry.skill.baseDir).trim();return!sourceDirName||sourceDirName===`.`||sourceDirName===`..`?null:resolveSandboxPath({filePath:resolveUniqueSyncedSkillDirName(sourceDirName,params.usedDirNames),cwd:params.targetSkillsDir,root:params.targetSkillsDir}).resolved}async function syncSkillsToWorkspace(params){let sourceDir=resolveUserPath(params.sourceWorkspaceDir),targetDir=resolveUserPath(params.targetWorkspaceDir);sourceDir!==targetDir&&await serializeByKey(`syncSkills:${targetDir}`,async()=>{let targetSkillsDir=path.join(targetDir,`skills`),entries=loadSkillEntries(sourceDir,{config:params.config,managedSkillsDir:params.managedSkillsDir,bundledSkillsDir:params.bundledSkillsDir});await fsp.rm(targetSkillsDir,{recursive:!0,force:!0}),await fsp.mkdir(targetSkillsDir,{recursive:!0});let usedDirNames=new Set;for(let entry of entries){let dest=null;try{dest=resolveSyncedSkillDestinationPath({targetSkillsDir,entry,usedDirNames})}catch(error){let message=error instanceof Error?error.message:JSON.stringify(error);console.warn(`[skills] Failed to resolve safe destination for ${entry.skill.name}: ${message}`);continue}if(!dest){console.warn(`[skills] Failed to resolve safe destination for ${entry.skill.name}: invalid source directory name`);continue}try{await fsp.cp(entry.skill.baseDir,dest,{recursive:!0,force:!0})}catch(error){let message=error instanceof Error?error.message:JSON.stringify(error);console.warn(`[skills] Failed to copy ${entry.skill.name} to sandbox: ${message}`)}}})}function buildWorkspaceSkillCommandSpecs(workspaceDir,opts){let userInvocable=filterSkillEntries(opts?.entries??loadSkillEntries(workspaceDir,opts),opts?.config,opts?.skillFilter,opts?.eligibility).filter(entry=>entry.invocation?.userInvocable!==!1),used=new Set;for(let reserved of opts?.reservedNames??[])used.add(reserved.toLowerCase());let specs=[];for(let entry of userInvocable){let rawName=entry.skill.name,base=sanitizeSkillCommandName(rawName);base!==rawName&&debugSkillCommandOnce(`sanitize:${rawName}:${base}`,`Sanitized skill command name "${rawName}" to "/${base}".`,{rawName,sanitized:`/${base}`});let unique=resolveUniqueSkillCommandName(base,used);unique!==base&&debugSkillCommandOnce(`dedupe:${rawName}:${unique}`,`De-duplicated skill command name for "${rawName}" to "/${unique}".`,{rawName,deduped:`/${unique}`}),used.add(unique.toLowerCase());let rawDescription=entry.skill.description?.trim()||rawName,description=rawDescription.length>100?rawDescription.slice(0,99)+`…`:rawDescription,dispatch=(()=>{let kindRaw=(entry.frontmatter?.[`command-dispatch`]??entry.frontmatter?.command_dispatch??``).trim().toLowerCase();if(!kindRaw||kindRaw!==`tool`)return;let toolName=(entry.frontmatter?.[`command-tool`]??entry.frontmatter?.command_tool??``).trim();if(!toolName){debugSkillCommandOnce(`dispatch:missingTool:${rawName}`,`Skill command "/${unique}" requested tool dispatch but did not provide command-tool. Ignoring dispatch.`,{skillName:rawName,command:unique});return}let argModeRaw=(entry.frontmatter?.[`command-arg-mode`]??entry.frontmatter?.command_arg_mode??``).trim().toLowerCase();return!argModeRaw||argModeRaw===`raw`||debugSkillCommandOnce(`dispatch:badArgMode:${rawName}:${argModeRaw}`,`Skill command "/${unique}" requested tool dispatch but has unknown command-arg-mode. Falling back to raw.`,{skillName:rawName,command:unique,argMode:argModeRaw}),{kind:`tool`,toolName,argMode:`raw`}})();specs.push({name:unique,skillName:rawName,description,...dispatch?{dispatch}:{}})}return specs}const authByPort=new Map;function setBridgeAuthForPort(port,auth){if(!Number.isFinite(port)||port<=0)return;let token=typeof auth.token==`string`?auth.token.trim():``,password=typeof auth.password==`string`?auth.password.trim():``;authByPort.set(port,{token:token||void 0,password:password||void 0})}function getBridgeAuthForPort(port){if(!(!Number.isFinite(port)||port<=0))return authByPort.get(port)}function deleteBridgeAuthForPort(port){!Number.isFinite(port)||port<=0||authByPort.delete(port)}const ACT_KINDS=[`click`,`close`,`drag`,`evaluate`,`fill`,`hover`,`scrollIntoView`,`press`,`resize`,`select`,`type`,`wait`];function isActKind(value){return typeof value==`string`?ACT_KINDS.includes(value):!1}const ALLOWED_CLICK_MODIFIERS=new Set([`Alt`,`Control`,`ControlOrMeta`,`Meta`,`Shift`]);function parseClickButton(raw){if(raw===`left`||raw===`right`||raw===`middle`)return raw}function parseClickModifiers(raw){return raw.filter(m=>!ALLOWED_CLICK_MODIFIERS.has(m)).length?{error:`modifiers must be Alt|Control|ControlOrMeta|Meta|Shift`}:{modifiers:raw.length?raw:void 0}}let pwAiModuleSoft=null,pwAiModuleStrict=null;function isModuleNotFoundError(err){if(extractErrorCode(err)===`ERR_MODULE_NOT_FOUND`)return!0;let msg=formatErrorMessage(err);return msg.includes(`Cannot find module`)||msg.includes(`Cannot find package`)||msg.includes(`Failed to resolve import`)||msg.includes(`Failed to resolve entry for package`)||msg.includes(`Failed to load url`)}async function loadPwAiModule(mode){try{return await import(`./pw-ai-CsJmJcNk.js`)}catch(err){if(mode===`soft`||isModuleNotFoundError(err))return null;throw err}}async function getPwAiModule$1(opts){return(opts?.mode??`soft`)===`soft`?(pwAiModuleSoft||=loadPwAiModule(`soft`),await pwAiModuleSoft):(pwAiModuleStrict||=loadPwAiModule(`strict`),await pwAiModuleStrict)}function getProfileContext(req,ctx){let profileName;if(typeof req.query.profile==`string`&&(profileName=req.query.profile.trim()||void 0),!profileName&&req.body&&typeof req.body==`object`){let body=req.body;typeof body.profile==`string`&&(profileName=body.profile.trim()||void 0)}try{return ctx.forProfile(profileName)}catch(err){return{error:String(err),status:404}}}function jsonError(res,status,message){res.status(status).json({error:message})}function toStringOrEmpty(value){return typeof value==`string`?value.trim():typeof value==`number`||typeof value==`boolean`?String(value).trim():``}function toNumber(value){if(typeof value==`number`&&Number.isFinite(value))return value;if(typeof value==`string`&&value.trim()){let parsed=Number(value);return Number.isFinite(parsed)?parsed:void 0}}function toBoolean(value){return parseBooleanValue(value,{truthy:[`true`,`1`,`yes`],falsy:[`false`,`0`,`no`]})}function toStringArray(value){if(!Array.isArray(value))return;let strings=value.map(v=>toStringOrEmpty(v)).filter(Boolean);return strings.length?strings:void 0}const SELECTOR_UNSUPPORTED_MESSAGE=[`Error: 'selector' is not supported. Use 'ref' from snapshot instead.`,``,`Example workflow:`,`1. snapshot action to get page state with refs`,`2. act with ref: "e123" to interact with element`,``,`This is more reliable for modern SPAs.`].join(`
|
|
9
9
|
`);function readBody(req){let body=req.body;return!body||typeof body!=`object`||Array.isArray(body)?{}:body}function resolveTargetIdFromBody(body){return(typeof body.targetId==`string`?body.targetId.trim():``)||void 0}function resolveTargetIdFromQuery(query){return(typeof query.targetId==`string`?query.targetId.trim():``)||void 0}function handleRouteError(ctx,res,err){let mapped=ctx.mapTabError(err);if(mapped)return jsonError(res,mapped.status,mapped.message);jsonError(res,500,String(err))}function resolveProfileContext(req,res,ctx){let profileCtx=getProfileContext(req,ctx);return`error`in profileCtx?(jsonError(res,profileCtx.status,profileCtx.error),null):profileCtx}async function getPwAiModule(){return await getPwAiModule$1({mode:`soft`})}async function requirePwAi(res,feature){return await getPwAiModule()||(jsonError(res,501,[`Playwright is not available in this gateway build; '${feature}' is unsupported.`,`Install the full Playwright package (not playwright-core) and restart the gateway, or reinstall with browser support.`,`Docs: /tools/browser#playwright-requirement`].join(`
|
|
10
10
|
`)),null)}async function withRouteTabContext(params){let profileCtx=resolveProfileContext(params.req,params.res,params.ctx);if(profileCtx)try{let tab=await profileCtx.ensureTabAvailable(params.targetId);return await params.run({profileCtx,tab,cdpUrl:profileCtx.profile.cdpUrl})}catch(err){handleRouteError(params.ctx,params.res,err);return}}async function withPlaywrightRouteContext(params){return await withRouteTabContext({req:params.req,res:params.res,ctx:params.ctx,targetId:params.targetId,run:async({profileCtx,tab,cdpUrl})=>{let pw=await requirePwAi(params.res,params.feature);if(pw)return await params.run({profileCtx,tab,cdpUrl,pw})}})}const DEFAULT_BROWSER_TMP_DIR=resolvePreferredBuilderForceAgentsTmpDir(),DEFAULT_TRACE_DIR=DEFAULT_BROWSER_TMP_DIR,DEFAULT_DOWNLOAD_DIR=path.join(DEFAULT_BROWSER_TMP_DIR,`downloads`),DEFAULT_UPLOAD_DIR=path.join(DEFAULT_BROWSER_TMP_DIR,`uploads`);function resolvePathWithinRoot(params){let root=path.resolve(params.rootDir),raw=params.requestedPath.trim();if(!raw)return params.defaultFileName?{ok:!0,path:path.join(root,params.defaultFileName)}:{ok:!1,error:`path is required`};let resolved=path.resolve(root,raw),rel=path.relative(root,resolved);return!rel||rel.startsWith(`..`)||path.isAbsolute(rel)?{ok:!1,error:`Invalid path: must stay within ${params.scopeLabel}`}:{ok:!0,path:resolved}}function resolvePathsWithinRoot(params){let resolvedPaths=[];for(let raw of params.requestedPaths){let pathResult=resolvePathWithinRoot({rootDir:params.rootDir,requestedPath:raw,scopeLabel:params.scopeLabel});if(!pathResult.ok)return{ok:!1,error:pathResult.error};resolvedPaths.push(pathResult.path)}return{ok:!0,paths:resolvedPaths}}function resolveDownloadPathOrRespond(res,requestedPath){let downloadPathResult=resolvePathWithinRoot({rootDir:DEFAULT_DOWNLOAD_DIR,requestedPath,scopeLabel:`downloads directory`});return downloadPathResult.ok?downloadPathResult.path:(res.status(400).json({error:downloadPathResult.error}),null)}function buildDownloadRequestBase(cdpUrl,targetId,timeoutMs){return{cdpUrl,targetId,timeoutMs:timeoutMs??void 0}}function respondWithDownloadResult(res,targetId,result){res.json({ok:!0,targetId,download:result})}function registerBrowserAgentActRoutes(app,ctx){app.post(`/act`,async(req,res)=>{let body=readBody(req),kindRaw=toStringOrEmpty(body.kind);if(!isActKind(kindRaw))return jsonError(res,400,`kind is required`);let kind=kindRaw,targetId=resolveTargetIdFromBody(body);if(Object.hasOwn(body,`selector`)&&kind!==`wait`)return jsonError(res,400,SELECTOR_UNSUPPORTED_MESSAGE);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`act:${kind}`,run:async({cdpUrl,tab,pw})=>{let evaluateEnabled=ctx.state().resolved.evaluateEnabled;switch(kind){case`click`:{let ref=toStringOrEmpty(body.ref);if(!ref)return jsonError(res,400,`ref is required`);let doubleClick=toBoolean(body.doubleClick)??!1,timeoutMs=toNumber(body.timeoutMs),buttonRaw=toStringOrEmpty(body.button)||``,button=buttonRaw?parseClickButton(buttonRaw):void 0;if(buttonRaw&&!button)return jsonError(res,400,`button must be left|right|middle`);let parsedModifiers=parseClickModifiers(toStringArray(body.modifiers)??[]);if(parsedModifiers.error)return jsonError(res,400,parsedModifiers.error);let modifiers=parsedModifiers.modifiers,clickRequest={cdpUrl,targetId:tab.targetId,ref,doubleClick};return button&&(clickRequest.button=button),modifiers&&(clickRequest.modifiers=modifiers),timeoutMs&&(clickRequest.timeoutMs=timeoutMs),await pw.clickViaPlaywright(clickRequest),res.json({ok:!0,targetId:tab.targetId,url:tab.url})}case`type`:{let ref=toStringOrEmpty(body.ref);if(!ref)return jsonError(res,400,`ref is required`);if(typeof body.text!=`string`)return jsonError(res,400,`text is required`);let text=body.text,submit=toBoolean(body.submit)??!1,slowly=toBoolean(body.slowly)??!1,timeoutMs=toNumber(body.timeoutMs),typeRequest={cdpUrl,targetId:tab.targetId,ref,text,submit,slowly};return timeoutMs&&(typeRequest.timeoutMs=timeoutMs),await pw.typeViaPlaywright(typeRequest),res.json({ok:!0,targetId:tab.targetId})}case`press`:{let key=toStringOrEmpty(body.key);if(!key)return jsonError(res,400,`key is required`);let delayMs=toNumber(body.delayMs);return await pw.pressKeyViaPlaywright({cdpUrl,targetId:tab.targetId,key,delayMs:delayMs??void 0}),res.json({ok:!0,targetId:tab.targetId})}case`hover`:{let ref=toStringOrEmpty(body.ref);if(!ref)return jsonError(res,400,`ref is required`);let timeoutMs=toNumber(body.timeoutMs);return await pw.hoverViaPlaywright({cdpUrl,targetId:tab.targetId,ref,timeoutMs:timeoutMs??void 0}),res.json({ok:!0,targetId:tab.targetId})}case`scrollIntoView`:{let ref=toStringOrEmpty(body.ref);if(!ref)return jsonError(res,400,`ref is required`);let timeoutMs=toNumber(body.timeoutMs),scrollRequest={cdpUrl,targetId:tab.targetId,ref};return timeoutMs&&(scrollRequest.timeoutMs=timeoutMs),await pw.scrollIntoViewViaPlaywright(scrollRequest),res.json({ok:!0,targetId:tab.targetId})}case`drag`:{let startRef=toStringOrEmpty(body.startRef),endRef=toStringOrEmpty(body.endRef);if(!startRef||!endRef)return jsonError(res,400,`startRef and endRef are required`);let timeoutMs=toNumber(body.timeoutMs);return await pw.dragViaPlaywright({cdpUrl,targetId:tab.targetId,startRef,endRef,timeoutMs:timeoutMs??void 0}),res.json({ok:!0,targetId:tab.targetId})}case`select`:{let ref=toStringOrEmpty(body.ref),values=toStringArray(body.values);if(!ref||!values?.length)return jsonError(res,400,`ref and values are required`);let timeoutMs=toNumber(body.timeoutMs);return await pw.selectOptionViaPlaywright({cdpUrl,targetId:tab.targetId,ref,values,...timeoutMs===void 0?{}:{timeoutMs}}),res.json({ok:!0,targetId:tab.targetId})}case`fill`:{let fields=(Array.isArray(body.fields)?body.fields:[]).map(field=>{if(!field||typeof field!=`object`)return null;let rec=field,ref=toStringOrEmpty(rec.ref),type=toStringOrEmpty(rec.type);if(!ref||!type)return null;let value=typeof rec.value==`string`||typeof rec.value==`number`||typeof rec.value==`boolean`?rec.value:void 0;return value===void 0?{ref,type}:{ref,type,value}}).filter(field=>field!==null);if(!fields.length)return jsonError(res,400,`fields are required`);let timeoutMs=toNumber(body.timeoutMs);return await pw.fillFormViaPlaywright({cdpUrl,targetId:tab.targetId,fields,...timeoutMs===void 0?{}:{timeoutMs}}),res.json({ok:!0,targetId:tab.targetId})}case`resize`:{let width=toNumber(body.width),height=toNumber(body.height);return!width||!height?jsonError(res,400,`width and height are required`):(await pw.resizeViewportViaPlaywright({cdpUrl,targetId:tab.targetId,width,height}),res.json({ok:!0,targetId:tab.targetId,url:tab.url}))}case`wait`:{let timeMs=toNumber(body.timeMs),text=toStringOrEmpty(body.text)||void 0,textGone=toStringOrEmpty(body.textGone)||void 0,selector=toStringOrEmpty(body.selector)||void 0,url=toStringOrEmpty(body.url)||void 0,loadStateRaw=toStringOrEmpty(body.loadState),loadState=loadStateRaw===`load`||loadStateRaw===`domcontentloaded`||loadStateRaw===`networkidle`?loadStateRaw:void 0,fn=toStringOrEmpty(body.fn)||void 0,timeoutMs=toNumber(body.timeoutMs)??void 0;return fn&&!evaluateEnabled?jsonError(res,403,[`wait --fn is disabled by config (browser.evaluateEnabled=false).`,`Docs: /gateway/configuration#browser-builderforce-managed-browser`].join(`
|
|
11
11
|
`)):timeMs===void 0&&!text&&!textGone&&!selector&&!url&&!loadState&&!fn?jsonError(res,400,`wait requires at least one of: timeMs, text, textGone, selector, url, loadState, fn`):(await pw.waitForViaPlaywright({cdpUrl,targetId:tab.targetId,timeMs,text,textGone,selector,url,loadState,fn,timeoutMs}),res.json({ok:!0,targetId:tab.targetId}))}case`evaluate`:{if(!evaluateEnabled)return jsonError(res,403,[`act:evaluate is disabled by config (browser.evaluateEnabled=false).`,`Docs: /gateway/configuration#browser-builderforce-managed-browser`].join(`
|
|
12
|
-
`));let fn=toStringOrEmpty(body.fn);if(!fn)return jsonError(res,400,`fn is required`);let ref=toStringOrEmpty(body.ref)||void 0,evalTimeoutMs=toNumber(body.timeoutMs),evalRequest={cdpUrl,targetId:tab.targetId,fn,ref,signal:req.signal};evalTimeoutMs!==void 0&&(evalRequest.timeoutMs=evalTimeoutMs);let result=await pw.evaluateViaPlaywright(evalRequest);return res.json({ok:!0,targetId:tab.targetId,url:tab.url,result})}case`close`:return await pw.closePageViaPlaywright({cdpUrl,targetId:tab.targetId}),res.json({ok:!0,targetId:tab.targetId});default:return jsonError(res,400,`unsupported kind`)}}})}),app.post(`/hooks/file-chooser`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),ref=toStringOrEmpty(body.ref)||void 0,inputRef=toStringOrEmpty(body.inputRef)||void 0,element=toStringOrEmpty(body.element)||void 0,paths=toStringArray(body.paths)??[],timeoutMs=toNumber(body.timeoutMs);if(!paths.length)return jsonError(res,400,`paths are required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`file chooser hook`,run:async({cdpUrl,tab,pw})=>{let uploadPathsResult=resolvePathsWithinRoot({rootDir:DEFAULT_UPLOAD_DIR,requestedPaths:paths,scopeLabel:`uploads directory (${DEFAULT_UPLOAD_DIR})`});if(!uploadPathsResult.ok){res.status(400).json({error:uploadPathsResult.error});return}let resolvedPaths=uploadPathsResult.paths;if(inputRef||element){if(ref)return jsonError(res,400,`ref cannot be combined with inputRef/element`);await pw.setInputFilesViaPlaywright({cdpUrl,targetId:tab.targetId,inputRef,element,paths:resolvedPaths})}else await pw.armFileUploadViaPlaywright({cdpUrl,targetId:tab.targetId,paths:resolvedPaths,timeoutMs:timeoutMs??void 0}),ref&&await pw.clickViaPlaywright({cdpUrl,targetId:tab.targetId,ref});res.json({ok:!0})}})}),app.post(`/hooks/dialog`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),accept=toBoolean(body.accept),promptText=toStringOrEmpty(body.promptText)||void 0,timeoutMs=toNumber(body.timeoutMs);if(accept===void 0)return jsonError(res,400,`accept is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`dialog hook`,run:async({cdpUrl,tab,pw})=>{await pw.armDialogViaPlaywright({cdpUrl,targetId:tab.targetId,accept,promptText,timeoutMs:timeoutMs??void 0}),res.json({ok:!0})}})}),app.post(`/wait/download`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),out=toStringOrEmpty(body.path)||``,timeoutMs=toNumber(body.timeoutMs);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`wait for download`,run:async({cdpUrl,tab,pw})=>{let downloadPath;if(out.trim()){let resolvedDownloadPath=resolveDownloadPathOrRespond(res,out);if(!resolvedDownloadPath)return;downloadPath=resolvedDownloadPath}let requestBase=buildDownloadRequestBase(cdpUrl,tab.targetId,timeoutMs),result=await pw.waitForDownloadViaPlaywright({...requestBase,path:downloadPath});respondWithDownloadResult(res,tab.targetId,result)}})}),app.post(`/download`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),ref=toStringOrEmpty(body.ref),out=toStringOrEmpty(body.path),timeoutMs=toNumber(body.timeoutMs);if(!ref)return jsonError(res,400,`ref is required`);if(!out)return jsonError(res,400,`path is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`download`,run:async({cdpUrl,tab,pw})=>{let downloadPath=resolveDownloadPathOrRespond(res,out);if(!downloadPath)return;let requestBase=buildDownloadRequestBase(cdpUrl,tab.targetId,timeoutMs),result=await pw.downloadViaPlaywright({...requestBase,ref,path:downloadPath});respondWithDownloadResult(res,tab.targetId,result)}})}),app.post(`/response/body`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),url=toStringOrEmpty(body.url),timeoutMs=toNumber(body.timeoutMs),maxChars=toNumber(body.maxChars);if(!url)return jsonError(res,400,`url is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`response body`,run:async({cdpUrl,tab,pw})=>{let result=await pw.responseBodyViaPlaywright({cdpUrl,targetId:tab.targetId,url,timeoutMs:timeoutMs??void 0,maxChars:maxChars??void 0});res.json({ok:!0,targetId:tab.targetId,response:result})}})}),app.post(`/highlight`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),ref=toStringOrEmpty(body.ref);if(!ref)return jsonError(res,400,`ref is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`highlight`,run:async({cdpUrl,tab,pw})=>{await pw.highlightViaPlaywright({cdpUrl,targetId:tab.targetId,ref}),res.json({ok:!0,targetId:tab.targetId})}})})}function registerBrowserAgentDebugRoutes(app,ctx){app.get(`/console`,async(req,res)=>{let targetId=resolveTargetIdFromQuery(req.query),level=typeof req.query.level==`string`?req.query.level:``;await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`console messages`,run:async({cdpUrl,tab,pw})=>{let messages=await pw.getConsoleMessagesViaPlaywright({cdpUrl,targetId:tab.targetId,level:level.trim()||void 0});res.json({ok:!0,messages,targetId:tab.targetId})}})}),app.get(`/errors`,async(req,res)=>{let targetId=resolveTargetIdFromQuery(req.query),clear=toBoolean(req.query.clear)??!1;await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`page errors`,run:async({cdpUrl,tab,pw})=>{let result=await pw.getPageErrorsViaPlaywright({cdpUrl,targetId:tab.targetId,clear});res.json({ok:!0,targetId:tab.targetId,...result})}})}),app.get(`/requests`,async(req,res)=>{let targetId=resolveTargetIdFromQuery(req.query),filter=typeof req.query.filter==`string`?req.query.filter:``,clear=toBoolean(req.query.clear)??!1;await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`network requests`,run:async({cdpUrl,tab,pw})=>{let result=await pw.getNetworkRequestsViaPlaywright({cdpUrl,targetId:tab.targetId,filter:filter.trim()||void 0,clear});res.json({ok:!0,targetId:tab.targetId,...result})}})}),app.post(`/trace/start`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),screenshots=toBoolean(body.screenshots)??void 0,snapshots=toBoolean(body.snapshots)??void 0,sources=toBoolean(body.sources)??void 0;await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`trace start`,run:async({cdpUrl,tab,pw})=>{await pw.traceStartViaPlaywright({cdpUrl,targetId:tab.targetId,screenshots,snapshots,sources}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/trace/stop`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),out=toStringOrEmpty(body.path)||``;await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`trace stop`,run:async({cdpUrl,tab,pw})=>{let id=crypto.randomUUID(),dir=DEFAULT_TRACE_DIR;await fs.mkdir(dir,{recursive:!0});let tracePathResult=resolvePathWithinRoot({rootDir:dir,requestedPath:out,scopeLabel:`trace directory`,defaultFileName:`browser-trace-${id}.zip`});if(!tracePathResult.ok){res.status(400).json({error:tracePathResult.error});return}let tracePath=tracePathResult.path;await pw.traceStopViaPlaywright({cdpUrl,targetId:tab.targetId,path:tracePath}),res.json({ok:!0,targetId:tab.targetId,path:path.resolve(tracePath)})}})})}const DEFAULT_BROWSER_SCREENSHOT_MAX_SIDE=2e3,DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES=5*1024*1024;async function normalizeBrowserScreenshot(buffer,opts){let maxSide=Math.max(1,Math.round(opts?.maxSide??DEFAULT_BROWSER_SCREENSHOT_MAX_SIDE)),maxBytes=Math.max(1,Math.round(opts?.maxBytes??DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES)),meta=await getImageMetadata(buffer),width=Number(meta?.width??0),height=Number(meta?.height??0),maxDim=Math.max(width,height);if(buffer.byteLength<=maxBytes&&(maxDim===0||width<=maxSide&&height<=maxSide))return{buffer};let sideGrid=buildImageResizeSideGrid(maxSide,maxDim>0?Math.min(maxSide,maxDim):maxSide),smallest=null;for(let side of sideGrid)for(let quality of IMAGE_REDUCE_QUALITY_STEPS){let out=await resizeToJpeg({buffer,maxSide:side,quality,withoutEnlargement:!0});if((!smallest||out.byteLength<smallest.size)&&(smallest={buffer:out,size:out.byteLength}),out.byteLength<=maxBytes)return{buffer:out,contentType:`image/jpeg`}}let best=smallest?.buffer??buffer;throw Error(`Browser screenshot could not be reduced below ${(maxBytes/(1024*1024)).toFixed(0)}MB (got ${(best.byteLength/(1024*1024)).toFixed(2)}MB)`)}async function saveBrowserMediaResponse(params){await ensureMediaDir();let saved=await saveMediaBuffer(params.buffer,params.contentType,`browser`,params.maxBytes);params.res.json({ok:!0,path:path.resolve(saved.path),targetId:params.targetId,url:params.url})}function registerBrowserAgentSnapshotRoutes(app,ctx){app.post(`/navigate`,async(req,res)=>{let body=readBody(req),url=toStringOrEmpty(body.url),targetId=toStringOrEmpty(body.targetId)||void 0;if(!url)return jsonError(res,400,`url is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`navigate`,run:async({cdpUrl,tab,pw})=>{let result=await pw.navigateViaPlaywright({cdpUrl,targetId:tab.targetId,url});res.json({ok:!0,targetId:tab.targetId,...result})}})}),app.post(`/pdf`,async(req,res)=>{await withPlaywrightRouteContext({req,res,ctx,targetId:toStringOrEmpty(readBody(req).targetId)||void 0,feature:`pdf`,run:async({cdpUrl,tab,pw})=>{let pdf=await pw.pdfViaPlaywright({cdpUrl,targetId:tab.targetId});await saveBrowserMediaResponse({res,buffer:pdf.buffer,contentType:`application/pdf`,maxBytes:pdf.buffer.byteLength,targetId:tab.targetId,url:tab.url})}})}),app.post(`/screenshot`,async(req,res)=>{let body=readBody(req),targetId=toStringOrEmpty(body.targetId)||void 0,fullPage=toBoolean(body.fullPage)??!1,ref=toStringOrEmpty(body.ref)||void 0,element=toStringOrEmpty(body.element)||void 0,type=body.type===`jpeg`?`jpeg`:`png`;if(fullPage&&(ref||element))return jsonError(res,400,`fullPage is not supported for element screenshots`);await withRouteTabContext({req,res,ctx,targetId,run:async({profileCtx,tab,cdpUrl})=>{let buffer;if(profileCtx.profile.driver===`extension`||!tab.wsUrl||ref||element){let pw=await requirePwAi(res,`screenshot`);if(!pw)return;buffer=(await pw.takeScreenshotViaPlaywright({cdpUrl,targetId:tab.targetId,ref,element,fullPage,type})).buffer}else buffer=await captureScreenshot({wsUrl:tab.wsUrl??``,fullPage,format:type,quality:type===`jpeg`?85:void 0});let normalized=await normalizeBrowserScreenshot(buffer,{maxSide:DEFAULT_BROWSER_SCREENSHOT_MAX_SIDE,maxBytes:DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES});await saveBrowserMediaResponse({res,buffer:normalized.buffer,contentType:normalized.contentType??`image/${type}`,maxBytes:DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES,targetId:tab.targetId,url:tab.url})}})}),app.get(`/snapshot`,async(req,res)=>{let profileCtx=resolveProfileContext(req,res,ctx);if(!profileCtx)return;let targetId=typeof req.query.targetId==`string`?req.query.targetId.trim():``,mode=req.query.mode===`efficient`?`efficient`:void 0,labels=toBoolean(req.query.labels)??void 0,format=(req.query.format===`aria`?`aria`:req.query.format===`ai`?`ai`:void 0)??(mode||await getPwAiModule()?`ai`:`aria`),limitRaw=typeof req.query.limit==`string`?Number(req.query.limit):void 0,hasMaxChars=Object.hasOwn(req.query,`maxChars`),maxCharsRaw=typeof req.query.maxChars==`string`?Number(req.query.maxChars):void 0,limit=Number.isFinite(limitRaw)?limitRaw:void 0,resolvedMaxChars=format===`ai`?hasMaxChars?typeof maxCharsRaw==`number`&&Number.isFinite(maxCharsRaw)&&maxCharsRaw>0?Math.floor(maxCharsRaw):void 0:mode===`efficient`?DEFAULT_AI_SNAPSHOT_EFFICIENT_MAX_CHARS:DEFAULT_AI_SNAPSHOT_MAX_CHARS:void 0,interactiveRaw=toBoolean(req.query.interactive),compactRaw=toBoolean(req.query.compact),depthRaw=toNumber(req.query.depth),refsModeRaw=toStringOrEmpty(req.query.refs).trim(),refsMode=refsModeRaw===`aria`?`aria`:refsModeRaw===`role`?`role`:void 0,interactive=interactiveRaw??(mode===`efficient`?!0:void 0),compact=compactRaw??(mode===`efficient`?!0:void 0),depth=depthRaw??(mode===`efficient`?DEFAULT_AI_SNAPSHOT_EFFICIENT_DEPTH:void 0),selector=toStringOrEmpty(req.query.selector),frameSelector=toStringOrEmpty(req.query.frame),selectorValue=selector.trim()||void 0,frameSelectorValue=frameSelector.trim()||void 0;try{let tab=await profileCtx.ensureTabAvailable(targetId||void 0);if((labels||mode===`efficient`)&&format===`aria`)return jsonError(res,400,`labels/mode=efficient require format=ai`);if(format===`ai`){let pw=await requirePwAi(res,`ai snapshot`);if(!pw)return;let wantsRoleSnapshot=labels===!0||mode===`efficient`||interactive===!0||compact===!0||depth!==void 0||!!selectorValue||!!frameSelectorValue,roleSnapshotArgs={cdpUrl:profileCtx.profile.cdpUrl,targetId:tab.targetId,selector:selectorValue,frameSelector:frameSelectorValue,refsMode,options:{interactive:interactive??void 0,compact:compact??void 0,maxDepth:depth??void 0}},snap=wantsRoleSnapshot?await pw.snapshotRoleViaPlaywright(roleSnapshotArgs):await pw.snapshotAiViaPlaywright({cdpUrl:profileCtx.profile.cdpUrl,targetId:tab.targetId,...typeof resolvedMaxChars==`number`?{maxChars:resolvedMaxChars}:{}}).catch(async err=>{if(String(err).toLowerCase().includes(`_snapshotforai`))return await pw.snapshotRoleViaPlaywright(roleSnapshotArgs);throw err});if(labels){let labeled=await pw.screenshotWithLabelsViaPlaywright({cdpUrl:profileCtx.profile.cdpUrl,targetId:tab.targetId,refs:`refs`in snap?snap.refs:{},type:`png`}),normalized=await normalizeBrowserScreenshot(labeled.buffer,{maxSide:DEFAULT_BROWSER_SCREENSHOT_MAX_SIDE,maxBytes:DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES});await ensureMediaDir();let saved=await saveMediaBuffer(normalized.buffer,normalized.contentType??`image/png`,`browser`,DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES),imageType=normalized.contentType?.includes(`jpeg`)?`jpeg`:`png`;return res.json({ok:!0,format,targetId:tab.targetId,url:tab.url,labels:!0,labelsCount:labeled.labels,labelsSkipped:labeled.skipped,imagePath:path.resolve(saved.path),imageType,...snap})}return res.json({ok:!0,format,targetId:tab.targetId,url:tab.url,...snap})}let snap=profileCtx.profile.driver===`extension`||!tab.wsUrl?requirePwAi(res,`aria snapshot`).then(async pw=>pw?await pw.snapshotAriaViaPlaywright({cdpUrl:profileCtx.profile.cdpUrl,targetId:tab.targetId,limit}):null):snapshotAria({wsUrl:tab.wsUrl??``,limit}),resolved=await Promise.resolve(snap);return resolved?res.json({ok:!0,format,targetId:tab.targetId,url:tab.url,...resolved}):void 0}catch(err){handleRouteError(ctx,res,err)}})}function parseStorageKind(raw){return raw===`local`||raw===`session`?raw:null}function parseStorageMutationRequest(kindParam,body){return{kind:parseStorageKind(toStringOrEmpty(kindParam)),targetId:resolveTargetIdFromBody(body)}}function parseRequiredStorageMutationRequest(kindParam,body){let parsed=parseStorageMutationRequest(kindParam,body);return parsed.kind?{kind:parsed.kind,targetId:parsed.targetId}:null}function parseStorageMutationOrRespond(res,kindParam,body){return parseRequiredStorageMutationRequest(kindParam,body)||(jsonError(res,400,`kind must be local|session`),null)}function parseStorageMutationFromRequest(req,res){let body=readBody(req),parsed=parseStorageMutationOrRespond(res,req.params.kind,body);return parsed?{body,parsed}:null}function registerBrowserAgentStorageRoutes(app,ctx){app.get(`/cookies`,async(req,res)=>{await withPlaywrightRouteContext({req,res,ctx,targetId:resolveTargetIdFromQuery(req.query),feature:`cookies`,run:async({cdpUrl,tab,pw})=>{let result=await pw.cookiesGetViaPlaywright({cdpUrl,targetId:tab.targetId});res.json({ok:!0,targetId:tab.targetId,...result})}})}),app.post(`/cookies/set`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),cookie=body.cookie&&typeof body.cookie==`object`&&!Array.isArray(body.cookie)?body.cookie:null;if(!cookie)return jsonError(res,400,`cookie is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`cookies set`,run:async({cdpUrl,tab,pw})=>{await pw.cookiesSetViaPlaywright({cdpUrl,targetId:tab.targetId,cookie:{name:toStringOrEmpty(cookie.name),value:toStringOrEmpty(cookie.value),url:toStringOrEmpty(cookie.url)||void 0,domain:toStringOrEmpty(cookie.domain)||void 0,path:toStringOrEmpty(cookie.path)||void 0,expires:toNumber(cookie.expires)??void 0,httpOnly:toBoolean(cookie.httpOnly)??void 0,secure:toBoolean(cookie.secure)??void 0,sameSite:cookie.sameSite===`Lax`||cookie.sameSite===`None`||cookie.sameSite===`Strict`?cookie.sameSite:void 0}}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/cookies/clear`,async(req,res)=>{await withPlaywrightRouteContext({req,res,ctx,targetId:resolveTargetIdFromBody(readBody(req)),feature:`cookies clear`,run:async({cdpUrl,tab,pw})=>{await pw.cookiesClearViaPlaywright({cdpUrl,targetId:tab.targetId}),res.json({ok:!0,targetId:tab.targetId})}})}),app.get(`/storage/:kind`,async(req,res)=>{let kind=parseStorageKind(toStringOrEmpty(req.params.kind));if(!kind)return jsonError(res,400,`kind must be local|session`);let targetId=resolveTargetIdFromQuery(req.query),key=toStringOrEmpty(req.query.key);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`storage get`,run:async({cdpUrl,tab,pw})=>{let result=await pw.storageGetViaPlaywright({cdpUrl,targetId:tab.targetId,kind,key:key.trim()||void 0});res.json({ok:!0,targetId:tab.targetId,...result})}})}),app.post(`/storage/:kind/set`,async(req,res)=>{let mutation=parseStorageMutationFromRequest(req,res);if(!mutation)return;let key=toStringOrEmpty(mutation.body.key);if(!key)return jsonError(res,400,`key is required`);let value=typeof mutation.body.value==`string`?mutation.body.value:``;await withPlaywrightRouteContext({req,res,ctx,targetId:mutation.parsed.targetId,feature:`storage set`,run:async({cdpUrl,tab,pw})=>{await pw.storageSetViaPlaywright({cdpUrl,targetId:tab.targetId,kind:mutation.parsed.kind,key,value}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/storage/:kind/clear`,async(req,res)=>{let mutation=parseStorageMutationFromRequest(req,res);mutation&&await withPlaywrightRouteContext({req,res,ctx,targetId:mutation.parsed.targetId,feature:`storage clear`,run:async({cdpUrl,tab,pw})=>{await pw.storageClearViaPlaywright({cdpUrl,targetId:tab.targetId,kind:mutation.parsed.kind}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/set/offline`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),offline=toBoolean(body.offline);if(offline===void 0)return jsonError(res,400,`offline is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`offline`,run:async({cdpUrl,tab,pw})=>{await pw.setOfflineViaPlaywright({cdpUrl,targetId:tab.targetId,offline}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/set/headers`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),headers=body.headers&&typeof body.headers==`object`&&!Array.isArray(body.headers)?body.headers:null;if(!headers)return jsonError(res,400,`headers is required`);let parsed={};for(let[k,v]of Object.entries(headers))typeof v==`string`&&(parsed[k]=v);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`headers`,run:async({cdpUrl,tab,pw})=>{await pw.setExtraHTTPHeadersViaPlaywright({cdpUrl,targetId:tab.targetId,headers:parsed}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/set/credentials`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),clear=toBoolean(body.clear)??!1,username=toStringOrEmpty(body.username)||void 0,password=typeof body.password==`string`?body.password:void 0;await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`http credentials`,run:async({cdpUrl,tab,pw})=>{await pw.setHttpCredentialsViaPlaywright({cdpUrl,targetId:tab.targetId,username,password,clear}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/set/geolocation`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),clear=toBoolean(body.clear)??!1,latitude=toNumber(body.latitude),longitude=toNumber(body.longitude),accuracy=toNumber(body.accuracy)??void 0,origin=toStringOrEmpty(body.origin)||void 0;await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`geolocation`,run:async({cdpUrl,tab,pw})=>{await pw.setGeolocationViaPlaywright({cdpUrl,targetId:tab.targetId,latitude,longitude,accuracy,origin,clear}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/set/media`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),schemeRaw=toStringOrEmpty(body.colorScheme),colorScheme=schemeRaw===`dark`||schemeRaw===`light`||schemeRaw===`no-preference`?schemeRaw:schemeRaw===`none`?null:void 0;if(colorScheme===void 0)return jsonError(res,400,`colorScheme must be dark|light|no-preference|none`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`media emulation`,run:async({cdpUrl,tab,pw})=>{await pw.emulateMediaViaPlaywright({cdpUrl,targetId:tab.targetId,colorScheme}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/set/timezone`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),timezoneId=toStringOrEmpty(body.timezoneId);if(!timezoneId)return jsonError(res,400,`timezoneId is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`timezone`,run:async({cdpUrl,tab,pw})=>{await pw.setTimezoneViaPlaywright({cdpUrl,targetId:tab.targetId,timezoneId}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/set/locale`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),locale=toStringOrEmpty(body.locale);if(!locale)return jsonError(res,400,`locale is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`locale`,run:async({cdpUrl,tab,pw})=>{await pw.setLocaleViaPlaywright({cdpUrl,targetId:tab.targetId,locale}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/set/device`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),name=toStringOrEmpty(body.name);if(!name)return jsonError(res,400,`name is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`device emulation`,run:async({cdpUrl,tab,pw})=>{await pw.setDeviceViaPlaywright({cdpUrl,targetId:tab.targetId,name}),res.json({ok:!0,targetId:tab.targetId})}})})}function registerBrowserAgentRoutes(app,ctx){registerBrowserAgentSnapshotRoutes(app,ctx),registerBrowserAgentActRoutes(app,ctx),registerBrowserAgentDebugRoutes(app,ctx),registerBrowserAgentStorageRoutes(app,ctx)}function isValidPort(port){return Number.isFinite(port)&&port>0&&port<=65535}function clampPort(port,fallback){return isValidPort(port)?port:fallback}function derivePort(base,offset,fallback){return clampPort(base+offset,fallback)}const DEFAULT_BROWSER_CONTROL_PORT=18791,DEFAULT_BROWSER_CDP_PORT_RANGE_START=18800,DEFAULT_BROWSER_CDP_PORT_RANGE_END=18899;function deriveDefaultBrowserControlPort(gatewayPort){return derivePort(gatewayPort,2,DEFAULT_BROWSER_CONTROL_PORT)}function deriveDefaultBrowserCdpPortRange(browserControlPort){let start=derivePort(browserControlPort,9,DEFAULT_BROWSER_CDP_PORT_RANGE_START),end=clampPort(start+(DEFAULT_BROWSER_CDP_PORT_RANGE_END-DEFAULT_BROWSER_CDP_PORT_RANGE_START),DEFAULT_BROWSER_CDP_PORT_RANGE_END);return end<start?{start,end:start}:{start,end}}const CDP_PORT_RANGE_START=18800,PROFILE_NAME_REGEX=/^[a-z0-9][a-z0-9-]*$/;function isValidProfileName(name){return!name||name.length>64?!1:PROFILE_NAME_REGEX.test(name)}function allocateCdpPort(usedPorts,range){let start=range?.start??CDP_PORT_RANGE_START,end=range?.end??18899;if(!Number.isFinite(start)||!Number.isFinite(end)||start<=0||end<=0||start>end)return null;for(let port=start;port<=end;port++)if(!usedPorts.has(port))return port;return null}function getUsedPorts(profiles){if(!profiles)return new Set;let used=new Set;for(let profile of Object.values(profiles)){if(typeof profile.cdpPort==`number`){used.add(profile.cdpPort);continue}let rawUrl=profile.cdpUrl?.trim();if(rawUrl)try{let parsed=new URL(rawUrl),port=parsed.port&&Number.parseInt(parsed.port,10)>0?Number.parseInt(parsed.port,10):parsed.protocol===`https:`?443:80;!Number.isNaN(port)&&port>0&&port<=65535&&used.add(port)}catch{}}return used}const PROFILE_COLORS=[`#FF4500`,`#0066CC`,`#00AA00`,`#9933FF`,`#FF6699`,`#00CCCC`,`#FF9900`,`#6666FF`,`#CC3366`,`#339966`];function allocateColor(usedColors){for(let color of PROFILE_COLORS)if(!usedColors.has(color.toUpperCase()))return color;return PROFILE_COLORS[usedColors.size%PROFILE_COLORS.length]??PROFILE_COLORS[0]}function getUsedColors(profiles){return profiles?new Set(Object.values(profiles).map(p=>p.color.toUpperCase())):new Set}function normalizeHexColor(raw){let value=(raw??``).trim();if(!value)return DEFAULT_BUILDERFORCE_AGENTS_BROWSER_COLOR;let normalized=value.startsWith(`#`)?value:`#${value}`;return/^#[0-9a-fA-F]{6}$/.test(normalized)?normalized.toUpperCase():DEFAULT_BUILDERFORCE_AGENTS_BROWSER_COLOR}function normalizeTimeoutMs(raw,fallback){let value=typeof raw==`number`&&Number.isFinite(raw)?Math.floor(raw):fallback;return value<0?fallback:value}function parseHttpUrl(raw,label){let trimmed=raw.trim(),parsed=new URL(trimmed);if(parsed.protocol!==`http:`&&parsed.protocol!==`https:`)throw Error(`${label} must be http(s), got: ${parsed.protocol.replace(`:`,``)}`);let port=parsed.port&&Number.parseInt(parsed.port,10)>0?Number.parseInt(parsed.port,10):parsed.protocol===`https:`?443:80;if(Number.isNaN(port)||port<=0||port>65535)throw Error(`${label} has invalid port: ${parsed.port}`);return{parsed,port,normalized:parsed.toString().replace(/\/$/,``)}}function ensureDefaultProfile(profiles,defaultColor,legacyCdpPort,derivedDefaultCdpPort){let result={...profiles};return result[DEFAULT_BUILDERFORCE_AGENTS_BROWSER_PROFILE_NAME]||(result[DEFAULT_BUILDERFORCE_AGENTS_BROWSER_PROFILE_NAME]={cdpPort:legacyCdpPort??derivedDefaultCdpPort??CDP_PORT_RANGE_START,color:defaultColor}),result}function ensureDefaultChromeExtensionProfile(profiles,controlPort){let result={...profiles};if(result.chrome)return result;let relayPort=controlPort+1;return!Number.isFinite(relayPort)||relayPort<=0||relayPort>65535||getUsedPorts(result).has(relayPort)||(result.chrome={driver:`extension`,cdpUrl:`http://127.0.0.1:${relayPort}`,color:`#00AA00`}),result}function resolveBrowserConfig(cfg,rootConfig){let enabled=cfg?.enabled??DEFAULT_BUILDERFORCE_AGENTS_BROWSER_ENABLED,evaluateEnabled=cfg?.evaluateEnabled??DEFAULT_BROWSER_EVALUATE_ENABLED,controlPort=deriveDefaultBrowserControlPort(resolveGatewayPort(rootConfig)??DEFAULT_BROWSER_CONTROL_PORT),defaultColor=normalizeHexColor(cfg?.color),remoteCdpTimeoutMs=normalizeTimeoutMs(cfg?.remoteCdpTimeoutMs,1500),remoteCdpHandshakeTimeoutMs=normalizeTimeoutMs(cfg?.remoteCdpHandshakeTimeoutMs,Math.max(2e3,remoteCdpTimeoutMs*2)),derivedCdpRange=deriveDefaultBrowserCdpPortRange(controlPort),rawCdpUrl=(cfg?.cdpUrl??``).trim(),cdpInfo;if(rawCdpUrl)cdpInfo=parseHttpUrl(rawCdpUrl,`browser.cdpUrl`);else{let derivedPort=controlPort+1;if(derivedPort>65535)throw Error(`Derived CDP port (${derivedPort}) is too high; check gateway port configuration.`);let derived=new URL(`http://127.0.0.1:${derivedPort}`);cdpInfo={parsed:derived,port:derivedPort,normalized:derived.toString().replace(/\/$/,``)}}let headless=cfg?.headless===!0,noSandbox=cfg?.noSandbox===!0,attachOnly=cfg?.attachOnly===!0,executablePath=cfg?.executablePath?.trim()||void 0,defaultProfileFromConfig=cfg?.defaultProfile?.trim()||void 0,legacyCdpPort=rawCdpUrl?cdpInfo.port:void 0,profiles=ensureDefaultChromeExtensionProfile(ensureDefaultProfile(cfg?.profiles,defaultColor,legacyCdpPort,derivedCdpRange.start),controlPort),cdpProtocol=cdpInfo.parsed.protocol===`https:`?`https`:`http`,defaultProfile=defaultProfileFromConfig??(profiles[DEFAULT_BROWSER_DEFAULT_PROFILE_NAME]?DEFAULT_BROWSER_DEFAULT_PROFILE_NAME:DEFAULT_BUILDERFORCE_AGENTS_BROWSER_PROFILE_NAME),extraArgs=Array.isArray(cfg?.extraArgs)?cfg.extraArgs.filter(a=>typeof a==`string`&&a.trim().length>0):[];return{enabled,evaluateEnabled,controlPort,cdpProtocol,cdpHost:cdpInfo.parsed.hostname,cdpIsLoopback:isLoopbackHost(cdpInfo.parsed.hostname),remoteCdpTimeoutMs,remoteCdpHandshakeTimeoutMs,color:defaultColor,executablePath,headless,noSandbox,attachOnly,defaultProfile,profiles,extraArgs}}function resolveProfile(resolved,profileName){let profile=resolved.profiles[profileName];if(!profile)return null;let rawProfileUrl=profile.cdpUrl?.trim()??``,cdpHost=resolved.cdpHost,cdpPort=profile.cdpPort??0,cdpUrl=``,driver=profile.driver===`extension`?`extension`:`builderforce`;if(rawProfileUrl){let parsed=parseHttpUrl(rawProfileUrl,`browser.profiles.${profileName}.cdpUrl`);cdpHost=parsed.parsed.hostname,cdpPort=parsed.port,cdpUrl=parsed.normalized}else if(cdpPort)cdpUrl=`${resolved.cdpProtocol}://${resolved.cdpHost}:${cdpPort}`;else throw Error(`Profile "${profileName}" must define cdpPort or cdpUrl.`);return{name:profileName,cdpPort,cdpUrl,cdpHost,cdpIsLoopback:isLoopbackHost(cdpHost),color:profile.color,driver}}async function movePathToTrash(targetPath){try{return await runExec(`trash`,[targetPath],{timeoutMs:1e4}),targetPath}catch{let trashDir=path.join(os.homedir(),`.Trash`);fs$1.mkdirSync(trashDir,{recursive:!0});let base=path.basename(targetPath),dest=path.join(trashDir,`${base}-${Date.now()}`);return fs$1.existsSync(dest)&&(dest=path.join(trashDir,`${base}-${Date.now()}-${Math.random()}`)),fs$1.renameSync(targetPath,dest),dest}}const HEX_COLOR_RE=/^#[0-9A-Fa-f]{6}$/;function createBrowserProfilesService(ctx){return{listProfiles:async()=>await ctx.listProfiles(),createProfile:async params=>{let name=params.name.trim(),rawCdpUrl=params.cdpUrl?.trim()||void 0,driver=params.driver===`extension`?`extension`:void 0;if(!isValidProfileName(name))throw Error(`invalid profile name: use lowercase letters, numbers, and hyphens only`);let state=ctx.state(),resolvedProfiles=state.resolved.profiles;if(name in resolvedProfiles)throw Error(`profile "${name}" already exists`);let cfg=loadConfig(),rawProfiles=cfg.browser?.profiles??{};if(name in rawProfiles)throw Error(`profile "${name}" already exists`);let usedColors=getUsedColors(resolvedProfiles),profileColor=params.color&&HEX_COLOR_RE.test(params.color)?params.color:allocateColor(usedColors),profileConfig;if(rawCdpUrl)profileConfig={cdpUrl:parseHttpUrl(rawCdpUrl,`browser.profiles.cdpUrl`).normalized,...driver?{driver}:{},color:profileColor};else{let cdpPort=allocateCdpPort(getUsedPorts(resolvedProfiles),deriveDefaultBrowserCdpPortRange(state.resolved.controlPort));if(cdpPort===null)throw Error(`no available CDP ports in range`);profileConfig={cdpPort,...driver?{driver}:{},color:profileColor}}await writeConfigFile({...cfg,browser:{...cfg.browser,profiles:{...rawProfiles,[name]:profileConfig}}}),state.resolved.profiles[name]=profileConfig;let resolved=resolveProfile(state.resolved,name);if(!resolved)throw Error(`profile "${name}" not found after creation`);return{ok:!0,profile:name,cdpPort:resolved.cdpPort,cdpUrl:resolved.cdpUrl,color:resolved.color,isRemote:!resolved.cdpIsLoopback}},deleteProfile:async nameRaw=>{let name=nameRaw.trim();if(!name)throw Error(`profile name is required`);if(!isValidProfileName(name))throw Error(`invalid profile name`);let cfg=loadConfig(),profiles=cfg.browser?.profiles??{};if(!(name in profiles))throw Error(`profile "${name}" not found`);if(name===(cfg.browser?.defaultProfile??DEFAULT_BROWSER_DEFAULT_PROFILE_NAME))throw Error(`cannot delete the default profile "${name}"; change browser.defaultProfile first`);let deleted=!1,state=ctx.state();if(resolveProfile(state.resolved,name)?.cdpIsLoopback){try{await ctx.forProfile(name).stopRunningBrowser()}catch{}let userDataDir=resolveBuilderForceAgentsUserDataDir(name),profileDir=path.dirname(userDataDir);fs$1.existsSync(profileDir)&&(await movePathToTrash(profileDir),deleted=!0)}let{[name]:_removed,...remainingProfiles}=profiles;return await writeConfigFile({...cfg,browser:{...cfg.browser,profiles:remainingProfiles}}),delete state.resolved.profiles[name],state.profiles.delete(name),{ok:!0,profile:name,deleted}}}}async function withBasicProfileRoute(params){let profileCtx=resolveProfileContext(params.req,params.res,params.ctx);if(profileCtx)try{await params.run(profileCtx)}catch(err){jsonError(params.res,500,String(err))}}function registerBrowserBasicRoutes(app,ctx){app.get(`/profiles`,async(_req,res)=>{try{let profiles=await createBrowserProfilesService(ctx).listProfiles();res.json({profiles})}catch(err){jsonError(res,500,String(err))}}),app.get(`/`,async(req,res)=>{let current;try{current=ctx.state()}catch{return jsonError(res,503,`browser server not started`)}let profileCtx=getProfileContext(req,ctx);if(`error`in profileCtx)return jsonError(res,profileCtx.status,profileCtx.error);let[cdpHttp,cdpReady]=await Promise.all([profileCtx.isHttpReachable(300),profileCtx.isReachable(600)]),profileState=current.profiles.get(profileCtx.profile.name),detectedBrowser=null,detectedExecutablePath=null,detectError=null;try{let detected=resolveBrowserExecutableForPlatform(current.resolved,process.platform);detected&&(detectedBrowser=detected.kind,detectedExecutablePath=detected.path)}catch(err){detectError=String(err)}res.json({enabled:current.resolved.enabled,profile:profileCtx.profile.name,running:cdpReady,cdpReady,cdpHttp,pid:profileState?.running?.pid??null,cdpPort:profileCtx.profile.cdpPort,cdpUrl:profileCtx.profile.cdpUrl,chosenBrowser:profileState?.running?.exe.kind??null,detectedBrowser,detectedExecutablePath,detectError,userDataDir:profileState?.running?.userDataDir??null,color:profileCtx.profile.color,headless:current.resolved.headless,noSandbox:current.resolved.noSandbox,executablePath:current.resolved.executablePath??null,attachOnly:current.resolved.attachOnly})}),app.post(`/start`,async(req,res)=>{await withBasicProfileRoute({req,res,ctx,run:async profileCtx=>{await profileCtx.ensureBrowserAvailable(),res.json({ok:!0,profile:profileCtx.profile.name})}})}),app.post(`/stop`,async(req,res)=>{await withBasicProfileRoute({req,res,ctx,run:async profileCtx=>{let result=await profileCtx.stopRunningBrowser();res.json({ok:!0,stopped:result.stopped,profile:profileCtx.profile.name})}})}),app.post(`/reset-profile`,async(req,res)=>{await withBasicProfileRoute({req,res,ctx,run:async profileCtx=>{let result=await profileCtx.resetProfile();res.json({ok:!0,profile:profileCtx.profile.name,...result})}})}),app.post(`/profiles/create`,async(req,res)=>{let name=toStringOrEmpty(req.body?.name),color=toStringOrEmpty(req.body?.color),cdpUrl=toStringOrEmpty(req.body?.cdpUrl),driver=toStringOrEmpty(req.body?.driver);if(!name)return jsonError(res,400,`name is required`);try{let result=await createBrowserProfilesService(ctx).createProfile({name,color:color||void 0,cdpUrl:cdpUrl||void 0,driver:driver===`extension`?`extension`:void 0});res.json(result)}catch(err){let msg=String(err);if(msg.includes(`already exists`))return jsonError(res,409,msg);if(msg.includes(`invalid profile name`))return jsonError(res,400,msg);if(msg.includes(`no available CDP ports`))return jsonError(res,507,msg);if(msg.includes(`cdpUrl`))return jsonError(res,400,msg);jsonError(res,500,msg)}}),app.delete(`/profiles/:name`,async(req,res)=>{let name=toStringOrEmpty(req.params.name);if(!name)return jsonError(res,400,`profile name is required`);try{let result=await createBrowserProfilesService(ctx).deleteProfile(name);res.json(result)}catch(err){let msg=String(err);if(msg.includes(`invalid profile name`)||msg.includes(`default profile`))return jsonError(res,400,msg);if(msg.includes(`not found`))return jsonError(res,404,msg);jsonError(res,500,msg)}})}function resolveTabsProfileContext(req,res,ctx){let profileCtx=getProfileContext(req,ctx);return`error`in profileCtx?(jsonError(res,profileCtx.status,profileCtx.error),null):profileCtx}function handleTabsRouteError(ctx,res,err,opts){if(opts?.mapTabError){let mapped=ctx.mapTabError(err);if(mapped)return jsonError(res,mapped.status,mapped.message)}return jsonError(res,500,String(err))}async function withTabsProfileRoute(params){let profileCtx=resolveTabsProfileContext(params.req,params.res,params.ctx);if(profileCtx)try{await params.run(profileCtx)}catch(err){handleTabsRouteError(params.ctx,params.res,err,{mapTabError:params.mapTabError})}}async function ensureBrowserRunning(profileCtx,res){return await profileCtx.isReachable(300)?!0:(jsonError(res,409,`browser not running`),!1)}function resolveIndexedTab(tabs,index){return typeof index==`number`?tabs[index]:tabs.at(0)}function parseRequiredTargetId(res,rawTargetId){return toStringOrEmpty(rawTargetId)||(jsonError(res,400,`targetId is required`),null)}async function runTabTargetMutation(params){await withTabsProfileRoute({req:params.req,res:params.res,ctx:params.ctx,mapTabError:!0,run:async profileCtx=>{await ensureBrowserRunning(profileCtx,params.res)&&(await params.mutate(profileCtx,params.targetId),params.res.json({ok:!0}))}})}function registerBrowserTabRoutes(app,ctx){app.get(`/tabs`,async(req,res)=>{await withTabsProfileRoute({req,res,ctx,run:async profileCtx=>{if(!await profileCtx.isReachable(300))return res.json({running:!1,tabs:[]});let tabs=await profileCtx.listTabs();res.json({running:!0,tabs})}})}),app.post(`/tabs/open`,async(req,res)=>{let url=toStringOrEmpty(req.body?.url);if(!url)return jsonError(res,400,`url is required`);await withTabsProfileRoute({req,res,ctx,run:async profileCtx=>{await profileCtx.ensureBrowserAvailable();let tab=await profileCtx.openTab(url);res.json(tab)}})}),app.post(`/tabs/focus`,async(req,res)=>{let targetId=parseRequiredTargetId(res,req.body?.targetId);targetId&&await runTabTargetMutation({req,res,ctx,targetId,mutate:async(profileCtx,id)=>{await profileCtx.focusTab(id)}})}),app.delete(`/tabs/:targetId`,async(req,res)=>{let targetId=parseRequiredTargetId(res,req.params.targetId);targetId&&await runTabTargetMutation({req,res,ctx,targetId,mutate:async(profileCtx,id)=>{await profileCtx.closeTab(id)}})}),app.post(`/tabs/action`,async(req,res)=>{let action=toStringOrEmpty(req.body?.action),index=toNumber(req.body?.index);await withTabsProfileRoute({req,res,ctx,mapTabError:!0,run:async profileCtx=>{if(action===`list`){if(!await profileCtx.isReachable(300))return res.json({ok:!0,tabs:[]});let tabs=await profileCtx.listTabs();return res.json({ok:!0,tabs})}if(action===`new`){await profileCtx.ensureBrowserAvailable();let tab=await profileCtx.openTab(`about:blank`);return res.json({ok:!0,tab})}if(action===`close`){let target=resolveIndexedTab(await profileCtx.listTabs(),index);return target?(await profileCtx.closeTab(target.targetId),res.json({ok:!0,targetId:target.targetId})):jsonError(res,404,`tab not found`)}if(action===`select`){if(typeof index!=`number`)return jsonError(res,400,`index is required`);let target=(await profileCtx.listTabs())[index];return target?(await profileCtx.focusTab(target.targetId),res.json({ok:!0,targetId:target.targetId})):jsonError(res,404,`tab not found`)}return jsonError(res,400,`unknown tab action`)}})})}function registerBrowserRoutes(app,ctx){registerBrowserBasicRoutes(app,ctx),registerBrowserTabRoutes(app,ctx),registerBrowserAgentRoutes(app,ctx)}function applyResolvedConfig(current,freshResolved){current.resolved=freshResolved;for(let[name,runtime]of current.profiles){let nextProfile=resolveProfile(freshResolved,name);if(nextProfile){runtime.profile=nextProfile;continue}runtime.running||current.profiles.delete(name)}}function refreshResolvedBrowserConfigFromDisk(params){if(!params.refreshConfigFromDisk)return;let cfg=params.mode===`fresh`?createConfigIO().loadConfig():loadConfig(),freshResolved=resolveBrowserConfig(cfg.browser,cfg);applyResolvedConfig(params.current,freshResolved)}function resolveBrowserProfileWithHotReload(params){refreshResolvedBrowserConfigFromDisk({current:params.current,refreshConfigFromDisk:params.refreshConfigFromDisk,mode:`cached`});let profile=resolveProfile(params.current.resolved,params.name);return profile||(refreshResolvedBrowserConfigFromDisk({current:params.current,refreshConfigFromDisk:params.refreshConfigFromDisk,mode:`fresh`}),profile=resolveProfile(params.current.resolved,params.name),profile)}function resolveTargetIdFromTabs(input,tabs){let needle=input.trim();if(!needle)return{ok:!1,reason:`not_found`};let exact=tabs.find(t=>t.targetId===needle);if(exact)return{ok:!0,targetId:exact.targetId};let lower=needle.toLowerCase(),matches=tabs.map(t=>t.targetId).filter(id=>id.toLowerCase().startsWith(lower)),only=matches.length===1?matches[0]:void 0;return only?{ok:!0,targetId:only}:matches.length===0?{ok:!1,reason:`not_found`}:{ok:!1,reason:`ambiguous`,matches}}function normalizeWsUrl(raw,cdpBaseUrl){if(raw)try{return normalizeCdpWsUrl(raw,cdpBaseUrl)}catch{return raw}}function createProfileContext(opts,profile){let state=()=>{let current=opts.getState();if(!current)throw Error(`Browser server not started`);return current},getProfileState=()=>{let current=state(),profileState=current.profiles.get(profile.name);return profileState||(profileState={profile,running:null,lastTargetId:null},current.profiles.set(profile.name,profileState)),profileState},setProfileRunning=running=>{let profileState=getProfileState();profileState.running=running},listTabs=async()=>{if(!profile.cdpIsLoopback){let listPagesViaPlaywright=(await getPwAiModule$1({mode:`strict`}))?.listPagesViaPlaywright;if(typeof listPagesViaPlaywright==`function`)return(await listPagesViaPlaywright({cdpUrl:profile.cdpUrl})).map(p=>({targetId:p.targetId,title:p.title,url:p.url,type:p.type}))}return(await fetchJson(appendCdpPath(profile.cdpUrl,`/json/list`))).map(t=>({targetId:t.id??``,title:t.title??``,url:t.url??``,wsUrl:normalizeWsUrl(t.webSocketDebuggerUrl,profile.cdpUrl),type:t.type})).filter(t=>!!t.targetId)},openTab=async url=>{if(!profile.cdpIsLoopback){let createPageViaPlaywright=(await getPwAiModule$1({mode:`strict`}))?.createPageViaPlaywright;if(typeof createPageViaPlaywright==`function`){let page=await createPageViaPlaywright({cdpUrl:profile.cdpUrl,url}),profileState=getProfileState();return profileState.lastTargetId=page.targetId,{targetId:page.targetId,title:page.title,url:page.url,type:page.type}}}let createdViaCdp=await createTargetViaCdp({cdpUrl:profile.cdpUrl,url}).then(r=>r.targetId).catch(()=>null);if(createdViaCdp){let profileState=getProfileState();profileState.lastTargetId=createdViaCdp;let deadline=Date.now()+2e3;for(;Date.now()<deadline;){let found=(await listTabs().catch(()=>[])).find(t=>t.targetId===createdViaCdp);if(found)return found;await new Promise(r=>setTimeout(r,100))}return{targetId:createdViaCdp,title:``,url,type:`page`}}let encoded=encodeURIComponent(url),endpointUrl=new URL(appendCdpPath(profile.cdpUrl,`/json/new`)),endpoint=endpointUrl.search?(endpointUrl.searchParams.set(`url`,url),endpointUrl.toString()):`${endpointUrl.toString()}?${encoded}`,created=await fetchJson(endpoint,1500,{method:`PUT`}).catch(async err=>{if(String(err).includes(`HTTP 405`))return await fetchJson(endpoint,1500);throw err});if(!created.id)throw Error(`Failed to open tab (missing id)`);let profileState=getProfileState();return profileState.lastTargetId=created.id,{targetId:created.id,title:created.title??``,url:created.url??url,wsUrl:normalizeWsUrl(created.webSocketDebuggerUrl,profile.cdpUrl),type:created.type}},resolveRemoteHttpTimeout=timeoutMs=>{if(profile.cdpIsLoopback)return timeoutMs??300;let resolved=state().resolved;return typeof timeoutMs==`number`&&Number.isFinite(timeoutMs)?Math.max(Math.floor(timeoutMs),resolved.remoteCdpTimeoutMs):resolved.remoteCdpTimeoutMs},resolveRemoteWsTimeout=timeoutMs=>{if(profile.cdpIsLoopback){let base=timeoutMs??300;return Math.max(200,Math.min(2e3,base*2))}let resolved=state().resolved;return typeof timeoutMs==`number`&&Number.isFinite(timeoutMs)?Math.max(Math.floor(timeoutMs)*2,resolved.remoteCdpHandshakeTimeoutMs):resolved.remoteCdpHandshakeTimeoutMs},isReachable=async timeoutMs=>{let httpTimeout=resolveRemoteHttpTimeout(timeoutMs),wsTimeout=resolveRemoteWsTimeout(timeoutMs);return await isChromeCdpReady(profile.cdpUrl,httpTimeout,wsTimeout)},isHttpReachable=async timeoutMs=>{let httpTimeout=resolveRemoteHttpTimeout(timeoutMs);return await isChromeReachable(profile.cdpUrl,httpTimeout)},attachRunning=running=>{setProfileRunning(running),running.proc.on(`exit`,()=>{opts.getState()&&getProfileState().running?.pid===running.pid&&setProfileRunning(null)})},ensureBrowserAvailable=async()=>{let current=state(),remoteCdp=!profile.cdpIsLoopback,isExtension=profile.driver===`extension`,profileState=getProfileState(),httpReachable=await isHttpReachable();if(isExtension&&remoteCdp)throw Error(`Profile "${profile.name}" uses driver=extension but cdpUrl is not loopback (${profile.cdpUrl}).`);if(isExtension){if(!httpReachable&&(await ensureChromeExtensionRelayServer({cdpUrl:profile.cdpUrl}),!await isHttpReachable(1200)))throw Error(`Chrome extension relay for profile "${profile.name}" is not reachable at ${profile.cdpUrl}.`);if(await isReachable(600))return;throw Error(`Chrome extension relay is running, but no tab is connected. Click the BuilderForceAgents Chrome extension icon on a tab to attach it (profile "${profile.name}").`)}if(!httpReachable){if((current.resolved.attachOnly||remoteCdp)&&opts.onEnsureAttachTarget&&(await opts.onEnsureAttachTarget(profile),await isHttpReachable(1200)))return;if(current.resolved.attachOnly||remoteCdp)throw Error(remoteCdp?`Remote CDP for profile "${profile.name}" is not reachable at ${profile.cdpUrl}.`:`Browser attachOnly is enabled and profile "${profile.name}" is not running.`);attachRunning(await launchBuilderForceAgentsChrome(current.resolved,profile));return}if(!await isReachable()){if(!profileState.running)throw Error(`Port ${profile.cdpPort} is in use for profile "${profile.name}" but not by builderforce. Run action=reset-profile profile=${profile.name} to kill the process.`);if(current.resolved.attachOnly||remoteCdp){if(opts.onEnsureAttachTarget&&(await opts.onEnsureAttachTarget(profile),await isReachable(1200)))return;throw Error(remoteCdp?`Remote CDP websocket for profile "${profile.name}" is not reachable.`:`Browser attachOnly is enabled and CDP websocket for profile "${profile.name}" is not reachable.`)}if(await stopBuilderForceAgentsChrome(profileState.running),setProfileRunning(null),attachRunning(await launchBuilderForceAgentsChrome(current.resolved,profile)),!await isReachable(600))throw Error(`Chrome CDP websocket for profile "${profile.name}" is not reachable after restart.`)}},ensureTabAvailable=async targetId=>{await ensureBrowserAvailable();let profileState=getProfileState();if((await listTabs()).length===0){if(profile.driver===`extension`)throw Error(`tab not found (no attached Chrome tabs for profile "${profile.name}"). Click the BuilderForceAgents Browser Relay toolbar icon on the tab you want to control (badge ON).`);await openTab(`about:blank`)}let tabs=await listTabs(),candidates=profile.driver===`extension`||!profile.cdpIsLoopback?tabs:tabs.filter(t=>!!t.wsUrl),resolveById=raw=>{let resolved=resolveTargetIdFromTabs(raw,candidates);return resolved.ok?candidates.find(t=>t.targetId===resolved.targetId)??null:resolved.reason===`ambiguous`?`AMBIGUOUS`:null},chosen=targetId?resolveById(targetId):(()=>{let last=profileState.lastTargetId?.trim()||``,lastResolved=last?resolveById(last):null;return lastResolved&&lastResolved!==`AMBIGUOUS`?lastResolved:candidates.find(t=>(t.type??`page`)===`page`)??candidates.at(0)??null})();if(!chosen&&profile.driver===`extension`&&candidates.length===1&&(chosen=candidates[0]??null),chosen===`AMBIGUOUS`)throw Error(`ambiguous target id prefix`);if(!chosen)throw Error(`tab not found`);return profileState.lastTargetId=chosen.targetId,chosen},focusTab=async targetId=>{let resolved=resolveTargetIdFromTabs(targetId,await listTabs());if(!resolved.ok)throw resolved.reason===`ambiguous`?Error(`ambiguous target id prefix`):Error(`tab not found`);if(!profile.cdpIsLoopback){let focusPageByTargetIdViaPlaywright=(await getPwAiModule$1({mode:`strict`}))?.focusPageByTargetIdViaPlaywright;if(typeof focusPageByTargetIdViaPlaywright==`function`){await focusPageByTargetIdViaPlaywright({cdpUrl:profile.cdpUrl,targetId:resolved.targetId});let profileState=getProfileState();profileState.lastTargetId=resolved.targetId;return}}await fetchOk(appendCdpPath(profile.cdpUrl,`/json/activate/${resolved.targetId}`));let profileState=getProfileState();profileState.lastTargetId=resolved.targetId},closeTab=async targetId=>{let resolved=resolveTargetIdFromTabs(targetId,await listTabs());if(!resolved.ok)throw resolved.reason===`ambiguous`?Error(`ambiguous target id prefix`):Error(`tab not found`);if(!profile.cdpIsLoopback){let closePageByTargetIdViaPlaywright=(await getPwAiModule$1({mode:`strict`}))?.closePageByTargetIdViaPlaywright;if(typeof closePageByTargetIdViaPlaywright==`function`){await closePageByTargetIdViaPlaywright({cdpUrl:profile.cdpUrl,targetId:resolved.targetId});return}}await fetchOk(appendCdpPath(profile.cdpUrl,`/json/close/${resolved.targetId}`))},stopRunningBrowser=async()=>{if(profile.driver===`extension`)return{stopped:await stopChromeExtensionRelayServer({cdpUrl:profile.cdpUrl})};let profileState=getProfileState();return profileState.running?(await stopBuilderForceAgentsChrome(profileState.running),setProfileRunning(null),{stopped:!0}):{stopped:!1}};return{profile,ensureBrowserAvailable,ensureTabAvailable,isHttpReachable,isReachable,listTabs,openTab,focusTab,closeTab,stopRunningBrowser,resetProfile:async()=>{if(profile.driver===`extension`)return await stopChromeExtensionRelayServer({cdpUrl:profile.cdpUrl}).catch(()=>{}),{moved:!1,from:profile.cdpUrl};if(!profile.cdpIsLoopback)throw Error(`reset-profile is only supported for local profiles (profile "${profile.name}" is remote).`);let userDataDir=resolveBuilderForceAgentsUserDataDir(profile.name),profileState=getProfileState();if(await isHttpReachable(300)&&!profileState.running)try{await(await import(`./pw-ai-UGketqfG.js`)).closePlaywrightBrowserConnection()}catch{}profileState.running&&await stopRunningBrowser();try{await(await import(`./pw-ai-UGketqfG.js`)).closePlaywrightBrowserConnection()}catch{}return fs$1.existsSync(userDataDir)?{moved:!0,from:userDataDir,to:await movePathToTrash(userDataDir)}:{moved:!1,from:userDataDir}}}}function createBrowserRouteContext(opts){let refreshConfigFromDisk=opts.refreshConfigFromDisk===!0,state=()=>{let current=opts.getState();if(!current)throw Error(`Browser server not started`);return current},forProfile=profileName=>{let current=state(),name=profileName??current.resolved.defaultProfile,profile=resolveBrowserProfileWithHotReload({current,refreshConfigFromDisk,name});if(!profile){let available=Object.keys(current.resolved.profiles).join(`, `);throw Error(`Profile "${name}" not found. Available profiles: ${available||`(none)`}`)}return createProfileContext(opts,profile)},listProfiles=async()=>{let current=state();refreshResolvedBrowserConfigFromDisk({current,refreshConfigFromDisk,mode:`cached`});let result=[];for(let name of Object.keys(current.resolved.profiles)){let profileState=current.profiles.get(name),profile=resolveProfile(current.resolved,name);if(!profile)continue;let tabCount=0,running=!1;if(profileState?.running){running=!0;try{tabCount=(await createProfileContext(opts,profile).listTabs()).filter(t=>t.type===`page`).length}catch{}}else try{await isChromeReachable(profile.cdpUrl,200)&&(running=!0,tabCount=(await createProfileContext(opts,profile).listTabs().catch(()=>[])).filter(t=>t.type===`page`).length)}catch{}result.push({name,cdpPort:profile.cdpPort,cdpUrl:profile.cdpUrl,color:profile.color,running,tabCount,isDefault:name===current.resolved.defaultProfile,isRemote:!profile.cdpIsLoopback})}return result},getDefaultContext=()=>forProfile();return{state,forProfile,listProfiles,ensureBrowserAvailable:()=>getDefaultContext().ensureBrowserAvailable(),ensureTabAvailable:targetId=>getDefaultContext().ensureTabAvailable(targetId),isHttpReachable:timeoutMs=>getDefaultContext().isHttpReachable(timeoutMs),isReachable:timeoutMs=>getDefaultContext().isReachable(timeoutMs),listTabs:()=>getDefaultContext().listTabs(),openTab:url=>getDefaultContext().openTab(url),focusTab:targetId=>getDefaultContext().focusTab(targetId),closeTab:targetId=>getDefaultContext().closeTab(targetId),stopRunningBrowser:()=>getDefaultContext().stopRunningBrowser(),resetProfile:()=>getDefaultContext().resetProfile(),mapTabError:err=>{let msg=String(err);return msg.includes(`ambiguous target id prefix`)?{status:409,message:`ambiguous target id prefix`}:msg.includes(`tab not found`)||msg.includes(`not found`)?{status:404,message:msg}:null}}}function firstHeader(value){return Array.isArray(value)?value[0]??``:value??``}function isMutatingMethod(method){let m=(method||``).trim().toUpperCase();return m===`POST`||m===`PUT`||m===`PATCH`||m===`DELETE`}function isLoopbackUrl(value){let v=value.trim();if(!v||v===`null`)return!1;try{return isLoopbackHost(new URL(v).hostname)}catch{return!1}}function shouldRejectBrowserMutation(params){if(!isMutatingMethod(params.method))return!1;if((params.secFetchSite??``).trim().toLowerCase()===`cross-site`)return!0;let origin=(params.origin??``).trim();if(origin)return!isLoopbackUrl(origin);let referer=(params.referer??``).trim();return referer?!isLoopbackUrl(referer):!1}function browserMutationGuardMiddleware(){return(req,res,next)=>{let method=(req.method||``).trim().toUpperCase();if(method===`OPTIONS`)return next();if(shouldRejectBrowserMutation({method,origin:firstHeader(req.headers.origin),referer:firstHeader(req.headers.referer),secFetchSite:firstHeader(req.headers[`sec-fetch-site`])})){res.status(403).send(`Forbidden`);return}next()}}function firstHeaderValue(value){return Array.isArray(value)?value[0]??``:value??``}function parseBearerToken(authorization){if(!(!authorization||!authorization.toLowerCase().startsWith(`bearer `)))return authorization.slice(7).trim()||void 0}function parseBasicPassword(authorization){if(!authorization||!authorization.toLowerCase().startsWith(`basic `))return;let encoded=authorization.slice(6).trim();if(encoded)try{let decoded=Buffer.from(encoded,`base64`).toString(`utf8`),sep=decoded.indexOf(`:`);return sep<0?void 0:decoded.slice(sep+1).trim()||void 0}catch{return}}function isAuthorizedBrowserRequest(req,auth){let authorization=firstHeaderValue(req.headers.authorization).trim();if(auth.token){let bearer=parseBearerToken(authorization);if(bearer&&safeEqualSecret(bearer,auth.token))return!0}if(auth.password){let passwordHeader=(firstHeaderValue(req.headers[`x-builderforce-password`])||firstHeaderValue(req.headers[`x-builderforce-password`])).trim();if(passwordHeader&&safeEqualSecret(passwordHeader,auth.password))return!0;let basicPassword=parseBasicPassword(authorization);if(basicPassword&&safeEqualSecret(basicPassword,auth.password))return!0}return!1}function installBrowserCommonMiddleware(app){app.use((req,res,next)=>{let ctrl=new AbortController,abort=()=>ctrl.abort(Error(`request aborted`));req.once(`aborted`,abort),res.once(`close`,()=>{res.writableEnded||abort()}),Object.defineProperty(req,`signal`,{value:ctrl.signal,configurable:!0,enumerable:!0,writable:!0}),next()}),app.use(express.json({limit:`1mb`})),app.use(browserMutationGuardMiddleware())}function installBrowserAuthMiddleware(app,auth){!auth.token&&!auth.password||app.use((req,res,next)=>{if(isAuthorizedBrowserRequest(req,auth))return next();res.status(401).send(`Unauthorized`)})}async function startBrowserBridgeServer(params){let host=params.host??`127.0.0.1`;if(!isLoopbackHost(host))throw Error(`bridge server must bind to loopback host (got ${host})`);let port=params.port??0,app=express();installBrowserCommonMiddleware(app);let authToken=params.authToken?.trim()||void 0,authPassword=params.authPassword?.trim()||void 0;if(!authToken&&!authPassword)throw Error(`bridge server requires auth (authToken/authPassword missing)`);installBrowserAuthMiddleware(app,{token:authToken,password:authPassword});let state={server:null,port,resolved:params.resolved,profiles:new Map};registerBrowserRoutes(app,createBrowserRouteContext({getState:()=>state,onEnsureAttachTarget:params.onEnsureAttachTarget}));let server=await new Promise((resolve,reject)=>{let s=app.listen(port,host,()=>resolve(s));s.once(`error`,reject)}),resolvedPort=server.address()?.port??port;return state.server=server,state.port=resolvedPort,state.resolved.controlPort=resolvedPort,setBridgeAuthForPort(resolvedPort,{token:authToken,password:authPassword}),{server,port:resolvedPort,baseUrl:`http://${host}:${resolvedPort}`,state}}async function stopBrowserBridgeServer(server){try{let address=server.address();address?.port&&deleteBridgeAuthForPort(address.port)}catch{}await new Promise(resolve=>{server.close(()=>resolve())})}const BROWSER_BRIDGES=new Map;function hashTextSha256(value){return crypto.createHash(`sha256`).update(value).digest(`hex`)}function normalizeForHash(value){if(value!==void 0){if(Array.isArray(value))return value.map(normalizeForHash).filter(item=>item!==void 0);if(value&&typeof value==`object`){let entries=Object.entries(value).toSorted(([a],[b])=>a.localeCompare(b)),normalized={};for(let[key,entryValue]of entries){let next=normalizeForHash(entryValue);next!==void 0&&(normalized[key]=next)}return normalized}return value}}function computeSandboxConfigHash(input){return computeHash(input)}function computeSandboxBrowserConfigHash(input){return computeHash(input)}function computeHash(input){let payload=normalizeForHash(input);return hashTextSha256(JSON.stringify(payload))}const BLOCKED_ENV_VAR_PATTERNS=[/^ANTHROPIC_API_KEY$/i,/^OPENAI_API_KEY$/i,/^GEMINI_API_KEY$/i,/^OPENROUTER_API_KEY$/i,/^MINIMAX_API_KEY$/i,/^ELEVENLABS_API_KEY$/i,/^SYNTHETIC_API_KEY$/i,/^TELEGRAM_BOT_TOKEN$/i,/^DISCORD_BOT_TOKEN$/i,/^SLACK_(BOT|APP)_TOKEN$/i,/^LINE_CHANNEL_SECRET$/i,/^LINE_CHANNEL_ACCESS_TOKEN$/i,/^BUILDERFORCE_AGENTS_GATEWAY_(TOKEN|PASSWORD)$/i,/^AWS_(SECRET_ACCESS_KEY|SECRET_KEY|SESSION_TOKEN)$/i,/^(GH|GITHUB)_TOKEN$/i,/^(AZURE|AZURE_OPENAI|COHERE|AI_GATEWAY|OPENROUTER)_API_KEY$/i,/_?(API_KEY|TOKEN|PASSWORD|PRIVATE_KEY|SECRET)$/i],ALLOWED_ENV_VAR_PATTERNS=[/^LANG$/,/^LC_.*$/i,/^PATH$/i,/^HOME$/i,/^USER$/i,/^SHELL$/i,/^TERM$/i,/^TZ$/i,/^NODE_ENV$/i];function validateEnvVarValue(value){if(value.includes(`\0`))return`Contains null bytes`;if(value.length>32768)return`Value exceeds maximum length`;if(/^[A-Za-z0-9+/=]{80,}$/.test(value))return`Value looks like base64-encoded credential data`}function matchesAnyPattern(value,patterns){return patterns.some(pattern=>pattern.test(value))}function sanitizeEnvVars(envVars,options={}){let allowed={},blocked=[],warnings=[],blockedPatterns=[...BLOCKED_ENV_VAR_PATTERNS,...options.customBlockedPatterns??[]],allowedPatterns=[...ALLOWED_ENV_VAR_PATTERNS,...options.customAllowedPatterns??[]];for(let[rawKey,value]of Object.entries(envVars)){let key=rawKey.trim();if(!key)continue;if(matchesAnyPattern(key,blockedPatterns)){blocked.push(key);continue}if(options.strictMode&&!matchesAnyPattern(key,allowedPatterns)){blocked.push(key);continue}let warning=validateEnvVarValue(value);if(warning){if(warning===`Contains null bytes`){blocked.push(key);continue}warnings.push(`${key}: ${warning}`)}allowed[key]=value}return{allowed,blocked,warnings}}const CLEANUP_SIGNALS=[`SIGINT`,`SIGTERM`,`SIGQUIT`,`SIGABRT`],CLEANUP_STATE_KEY=Symbol.for(`builderforce.sessionWriteLockCleanupState`),HELD_LOCKS_KEY=Symbol.for(`builderforce.sessionWriteLockHeldLocks`),WATCHDOG_STATE_KEY=Symbol.for(`builderforce.sessionWriteLockWatchdogState`),DEFAULT_MAX_HOLD_MS=300*1e3,DEFAULT_WATCHDOG_INTERVAL_MS=6e4,MAX_LOCK_HOLD_MS=2147e6,HELD_LOCKS=resolveProcessScopedMap(HELD_LOCKS_KEY);function resolveCleanupState(){let proc=process;return proc[CLEANUP_STATE_KEY]||(proc[CLEANUP_STATE_KEY]={registered:!1,cleanupHandlers:new Map}),proc[CLEANUP_STATE_KEY]}function resolveWatchdogState(){let proc=process;return proc[WATCHDOG_STATE_KEY]||(proc[WATCHDOG_STATE_KEY]={started:!1,intervalMs:DEFAULT_WATCHDOG_INTERVAL_MS}),proc[WATCHDOG_STATE_KEY]}function resolvePositiveMs(value,fallback,opts={}){return typeof value!=`number`||Number.isNaN(value)||value<=0?fallback:value===1/0?opts.allowInfinity?value:fallback:Number.isFinite(value)?value:fallback}function resolveSessionLockMaxHoldFromTimeout(params){let minMs=resolvePositiveMs(params.minMs,DEFAULT_MAX_HOLD_MS),timeoutMs=resolvePositiveMs(params.timeoutMs,minMs,{allowInfinity:!0});if(timeoutMs===1/0)return MAX_LOCK_HOLD_MS;let graceMs=resolvePositiveMs(params.graceMs,12e4);return Math.min(MAX_LOCK_HOLD_MS,Math.max(minMs,timeoutMs+graceMs))}async function releaseHeldLock(normalizedSessionFile,held,opts={}){if(HELD_LOCKS.get(normalizedSessionFile)!==held)return!1;if(opts.force)held.count=0;else if(--held.count,held.count>0)return!1;if(held.releasePromise)return await held.releasePromise.catch(()=>void 0),!0;HELD_LOCKS.delete(normalizedSessionFile),held.releasePromise=(async()=>{try{await held.handle.close()}catch{}try{await fs.rm(held.lockPath,{force:!0})}catch{}})();try{return await held.releasePromise,!0}finally{held.releasePromise=void 0}}function releaseAllLocksSync(){for(let[sessionFile,held]of HELD_LOCKS){try{typeof held.handle.close==`function`&&held.handle.close().catch(()=>{})}catch{}try{fs$1.rmSync(held.lockPath,{force:!0})}catch{}HELD_LOCKS.delete(sessionFile)}}async function runLockWatchdogCheck(nowMs=Date.now()){let released=0;for(let[sessionFile,held]of HELD_LOCKS.entries()){let heldForMs=nowMs-held.acquiredAt;heldForMs<=held.maxHoldMs||(console.warn(`[session-write-lock] releasing lock held for ${heldForMs}ms (max=${held.maxHoldMs}ms): ${held.lockPath}`),await releaseHeldLock(sessionFile,held,{force:!0})&&(released+=1))}return released}function ensureWatchdogStarted(intervalMs){let watchdogState=resolveWatchdogState();watchdogState.started||(watchdogState.started=!0,watchdogState.intervalMs=intervalMs,watchdogState.timer=setInterval(()=>{runLockWatchdogCheck().catch(()=>{})},intervalMs),watchdogState.timer.unref?.())}function handleTerminationSignal(signal){releaseAllLocksSync();let cleanupState=resolveCleanupState();if(process.listenerCount(signal)===1){let handler=cleanupState.cleanupHandlers.get(signal);handler&&(process.off(signal,handler),cleanupState.cleanupHandlers.delete(signal));try{process.kill(process.pid,signal)}catch{}}}function registerCleanupHandlers(){let cleanupState=resolveCleanupState();cleanupState.registered||(cleanupState.registered=!0,process.on(`exit`,()=>{releaseAllLocksSync()})),ensureWatchdogStarted(DEFAULT_WATCHDOG_INTERVAL_MS);for(let signal of CLEANUP_SIGNALS)if(!cleanupState.cleanupHandlers.has(signal))try{let handler=()=>handleTerminationSignal(signal);cleanupState.cleanupHandlers.set(signal,handler),process.on(signal,handler)}catch{}}async function readLockPayload(lockPath){try{let raw=await fs.readFile(lockPath,`utf8`),parsed=JSON.parse(raw),payload={};return typeof parsed.pid==`number`&&(payload.pid=parsed.pid),typeof parsed.createdAt==`string`&&(payload.createdAt=parsed.createdAt),payload}catch{return null}}function inspectLockPayload(payload,staleMs,nowMs){let pid=typeof payload?.pid==`number`?payload.pid:null,pidAlive=pid===null?!1:isPidAlive(pid),createdAt=typeof payload?.createdAt==`string`?payload.createdAt:null,createdAtMs=createdAt?Date.parse(createdAt):NaN,ageMs=Number.isFinite(createdAtMs)?Math.max(0,nowMs-createdAtMs):null,staleReasons=[];return pid===null?staleReasons.push(`missing-pid`):pidAlive||staleReasons.push(`dead-pid`),ageMs===null?staleReasons.push(`invalid-createdAt`):ageMs>staleMs&&staleReasons.push(`too-old`),{pid,pidAlive,createdAt,ageMs,stale:staleReasons.length>0,staleReasons}}async function acquireSessionWriteLock(params){registerCleanupHandlers();let timeoutMs=resolvePositiveMs(params.timeoutMs,1e4,{allowInfinity:!0}),staleMs=resolvePositiveMs(params.staleMs,18e5),maxHoldMs=resolvePositiveMs(params.maxHoldMs,DEFAULT_MAX_HOLD_MS),sessionFile=path.resolve(params.sessionFile),sessionDir=path.dirname(sessionFile);await fs.mkdir(sessionDir,{recursive:!0});let normalizedDir=sessionDir;try{normalizedDir=await fs.realpath(sessionDir)}catch{}let normalizedSessionFile=path.join(normalizedDir,path.basename(sessionFile)),lockPath=`${normalizedSessionFile}.lock`,allowReentrant=params.allowReentrant??!0,held=HELD_LOCKS.get(normalizedSessionFile);if(allowReentrant&&held)return held.count+=1,{release:async()=>{await releaseHeldLock(normalizedSessionFile,held)}};let startedAt=Date.now(),attempt=0;for(;Date.now()-startedAt<timeoutMs;){attempt+=1;try{let handle=await fs.open(lockPath,`wx`),createdAt=new Date().toISOString();await handle.writeFile(JSON.stringify({pid:process.pid,createdAt},null,2),`utf8`);let createdHeld={count:1,handle,lockPath,acquiredAt:Date.now(),maxHoldMs};return HELD_LOCKS.set(normalizedSessionFile,createdHeld),{release:async()=>{await releaseHeldLock(normalizedSessionFile,createdHeld)}}}catch(err){if(err.code!==`EEXIST`)throw err;if(inspectLockPayload(await readLockPayload(lockPath),staleMs,Date.now()).stale){await fs.rm(lockPath,{force:!0});continue}let delay=Math.min(1e3,50*attempt);await new Promise(r=>setTimeout(r,delay))}}let payload=await readLockPayload(lockPath),owner=typeof payload?.pid==`number`?`pid=${payload.pid}`:`unknown`;throw Error(`session file locked (timeout ${timeoutMs}ms): ${owner} ${lockPath}`)}[...CLEANUP_SIGNALS];function isRecord(value){return!!value&&typeof value==`object`}function isRegistryEntry(value){return isRecord(value)&&typeof value.containerName==`string`}function isRegistryFile(value){if(!isRecord(value))return!1;let maybeEntries=value.entries;return Array.isArray(maybeEntries)&&maybeEntries.every(isRegistryEntry)}async function withRegistryLock(registryPath,fn){let lock=await acquireSessionWriteLock({sessionFile:registryPath,allowReentrant:!1});try{return await fn()}finally{await lock.release()}}async function readRegistryFromFile(registryPath,mode){try{let raw=await fs.readFile(registryPath,`utf-8`),parsed=JSON.parse(raw);if(isRegistryFile(parsed))return parsed;if(mode===`fallback`)return{entries:[]};throw Error(`Invalid sandbox registry format: ${registryPath}`)}catch(error){if(error?.code===`ENOENT`||mode===`fallback`)return{entries:[]};throw error instanceof Error?error:Error(`Failed to read sandbox registry file: ${registryPath}`,{cause:error})}}async function writeRegistryFile(registryPath,registry){await fs.mkdir(SANDBOX_STATE_DIR,{recursive:!0});let payload=`${JSON.stringify(registry,null,2)}\n`,registryDir=path.dirname(registryPath),tempPath=path.join(registryDir,`${path.basename(registryPath)}.${crypto.randomUUID()}.tmp`);await fs.writeFile(tempPath,payload,`utf-8`);try{await fs.rename(tempPath,registryPath)}catch(error){throw await fs.rm(tempPath,{force:!0}),error}}async function readRegistry(){return await readRegistryFromFile(SANDBOX_REGISTRY_PATH,`fallback`)}function upsertEntry(entries,entry){let existing=entries.find(item=>item.containerName===entry.containerName),next=entries.filter(item=>item.containerName!==entry.containerName);return next.push({...entry,createdAtMs:existing?.createdAtMs??entry.createdAtMs,image:existing?.image??entry.image,configHash:entry.configHash??existing?.configHash}),next}function removeEntry(entries,containerName){return entries.filter(entry=>entry.containerName!==containerName)}async function withRegistryMutation(registryPath,mutate){await withRegistryLock(registryPath,async()=>{let next=mutate((await readRegistryFromFile(registryPath,`strict`)).entries);next!==null&&await writeRegistryFile(registryPath,{entries:next})})}async function updateRegistry(entry){await withRegistryMutation(SANDBOX_REGISTRY_PATH,entries=>upsertEntry(entries,entry))}async function removeRegistryEntry(containerName){await withRegistryMutation(SANDBOX_REGISTRY_PATH,entries=>{let next=removeEntry(entries,containerName);return next.length===entries.length?null:next})}async function readBrowserRegistry(){return await readRegistryFromFile(SANDBOX_BROWSER_REGISTRY_PATH,`fallback`)}async function updateBrowserRegistry(entry){await withRegistryMutation(SANDBOX_BROWSER_REGISTRY_PATH,entries=>upsertEntry(entries,entry))}async function removeBrowserRegistryEntry(containerName){await withRegistryMutation(SANDBOX_BROWSER_REGISTRY_PATH,entries=>{let next=removeEntry(entries,containerName);return next.length===entries.length?null:next})}function slugifySessionKey(value){let trimmed=value.trim()||`session`,hash=hashTextSha256(trimmed).slice(0,8);return`${trimmed.toLowerCase().replace(/[^a-z0-9._-]+/g,`-`).replace(/^-+|-+$/g,``).slice(0,32)||`session`}-${hash}`}function resolveSandboxWorkspaceDir(root,sessionKey){let resolvedRoot=resolveUserPath(root),slug=slugifySessionKey(sessionKey);return path.join(resolvedRoot,slug)}function resolveSandboxScopeKey(scope,sessionKey){let trimmed=sessionKey.trim()||`main`;return scope===`shared`?`shared`:scope===`session`?trimmed:`agent:${resolveAgentIdFromSessionKey(trimmed)}`}function resolveSandboxAgentId(scopeKey){let trimmed=scopeKey.trim();if(!trimmed||trimmed===`shared`)return;let parts=trimmed.split(`:`).filter(Boolean);return parts[0]===`agent`&&parts[1]?normalizeAgentId(parts[1]):resolveAgentIdFromSessionKey(trimmed)}const BLOCKED_HOST_PATHS=[`/etc`,`/private/etc`,`/proc`,`/sys`,`/dev`,`/root`,`/boot`,`/run`,`/var/run`,`/private/var/run`,`/var/run/docker.sock`,`/private/var/run/docker.sock`,`/run/docker.sock`],BLOCKED_NETWORK_MODES=new Set([`host`]),BLOCKED_SECCOMP_PROFILES=new Set([`unconfined`]),BLOCKED_APPARMOR_PROFILES=new Set([`unconfined`]);function parseBindSourcePath(bind){let trimmed=bind.trim(),firstColon=trimmed.indexOf(`:`);return firstColon<=0?trimmed:trimmed.slice(0,firstColon)}function normalizeHostPath(raw){let trimmed=raw.trim();return normalizeBaseUrl$1(posix.normalize(trimmed))||`/`}function getBlockedBindReason(bind){let sourceRaw=parseBindSourcePath(bind);return sourceRaw.startsWith(`/`)?getBlockedReasonForSourcePath(normalizeHostPath(sourceRaw)):{kind:`non_absolute`,sourcePath:sourceRaw}}function getBlockedReasonForSourcePath(sourceNormalized){if(sourceNormalized===`/`)return{kind:`covers`,blockedPath:`/`};for(let blocked of BLOCKED_HOST_PATHS)if(sourceNormalized===blocked||sourceNormalized.startsWith(blocked+`/`))return{kind:`targets`,blockedPath:blocked};return null}function tryRealpathAbsolute(path){if(!path.startsWith(`/`)||!existsSync(path))return path;try{return normalizeHostPath(realpathSync.native(path))}catch{return path}}function formatBindBlockedError(params){if(params.reason.kind===`non_absolute`)return Error(`Sandbox security: bind mount "${params.bind}" uses a non-absolute source path "${params.reason.sourcePath}". Only absolute POSIX paths are supported for sandbox binds.`);let verb=params.reason.kind===`covers`?`covers`:`targets`;return Error(`Sandbox security: bind mount "${params.bind}" ${verb} blocked path "${params.reason.blockedPath}". Mounting system directories (or Docker socket paths) into sandbox containers is not allowed. Use project-specific paths instead (e.g. /home/user/myproject).`)}function validateBindMounts(binds){if(binds?.length)for(let rawBind of binds){let bind=rawBind.trim();if(!bind)continue;let blocked=getBlockedBindReason(bind);if(blocked)throw formatBindBlockedError({bind,reason:blocked});let sourceNormalized=normalizeHostPath(parseBindSourcePath(bind)),sourceReal=tryRealpathAbsolute(sourceNormalized);if(sourceReal!==sourceNormalized){let reason=getBlockedReasonForSourcePath(sourceReal);if(reason)throw formatBindBlockedError({bind,reason})}}}function validateNetworkMode(network){if(network&&BLOCKED_NETWORK_MODES.has(network.trim().toLowerCase()))throw Error(`Sandbox security: network mode "${network}" is blocked. Network "host" mode bypasses container network isolation. Use "bridge" or "none" instead.`)}function validateSeccompProfile(profile){if(profile&&BLOCKED_SECCOMP_PROFILES.has(profile.trim().toLowerCase()))throw Error(`Sandbox security: seccomp profile "${profile}" is blocked. Disabling seccomp removes syscall filtering and weakens sandbox isolation. Use a custom seccomp profile file or omit this setting.`)}function validateApparmorProfile(profile){if(profile&&BLOCKED_APPARMOR_PROFILES.has(profile.trim().toLowerCase()))throw Error(`Sandbox security: apparmor profile "${profile}" is blocked. Disabling AppArmor removes mandatory access controls and weakens sandbox isolation. Use a named AppArmor profile or omit this setting.`)}function validateSandboxSecurity(cfg){validateBindMounts(cfg.binds),validateNetworkMode(cfg.network),validateSeccompProfile(cfg.seccompProfile),validateApparmorProfile(cfg.apparmorProfile)}function createAbortError(){let err=Error(`Aborted`);return err.name=`AbortError`,err}function execDockerRaw(args,opts){return new Promise((resolve,reject)=>{let child=spawn(`docker`,args,{stdio:[`pipe`,`pipe`,`pipe`]}),stdoutChunks=[],stderrChunks=[],aborted=!1,signal=opts?.signal,handleAbort=()=>{aborted||(aborted=!0,child.kill(`SIGTERM`))};signal&&(signal.aborted?handleAbort():signal.addEventListener(`abort`,handleAbort,{once:!0})),child.stdout?.on(`data`,chunk=>{stdoutChunks.push(Buffer.isBuffer(chunk)?chunk:Buffer.from(chunk))}),child.stderr?.on(`data`,chunk=>{stderrChunks.push(Buffer.isBuffer(chunk)?chunk:Buffer.from(chunk))}),child.on(`error`,error=>{signal&&signal.removeEventListener(`abort`,handleAbort),reject(error)}),child.on(`close`,code=>{signal&&signal.removeEventListener(`abort`,handleAbort);let stdout=Buffer.concat(stdoutChunks),stderr=Buffer.concat(stderrChunks);if(aborted||signal?.aborted){reject(createAbortError());return}let exitCode=code??0;if(exitCode!==0&&!opts?.allowFailure){let message=stderr.length>0?stderr.toString(`utf8`).trim():``;reject(Object.assign(Error(message||`docker ${args.join(` `)} failed`),{code:exitCode,stdout,stderr}));return}resolve({stdout,stderr,code:exitCode})});let stdin=child.stdin;stdin&&(opts?.input===void 0?stdin.end():stdin.end(opts.input))})}async function execDocker(args,opts){let result=await execDockerRaw(args,opts);return{stdout:result.stdout.toString(`utf8`),stderr:result.stderr.toString(`utf8`),code:result.code}}async function readDockerContainerLabel(containerName,label){let result=await execDocker([`inspect`,`-f`,`{{ index .Config.Labels "${label}" }}`,containerName],{allowFailure:!0});if(result.code!==0)return null;let raw=result.stdout.trim();return!raw||raw===`<no value>`?null:raw}async function readDockerPort(containerName,port){let result=await execDocker([`port`,containerName,`${port}/tcp`],{allowFailure:!0});if(result.code!==0)return null;let match=(result.stdout.trim().split(/\r?\n/)[0]??``).match(/:(\d+)\s*$/);if(!match)return null;let mapped=Number.parseInt(match[1]??``,10);return Number.isFinite(mapped)?mapped:null}async function dockerImageExists(image){let result=await execDocker([`image`,`inspect`,image],{allowFailure:!0});if(result.code===0)return!0;let stderr=result.stderr.trim();if(stderr.includes(`No such image`))return!1;throw Error(`Failed to inspect sandbox image: ${stderr}`)}async function ensureDockerImage(image){if(!await dockerImageExists(image)){if(image===DEFAULT_SANDBOX_IMAGE){await execDocker([`pull`,`debian:bookworm-slim`]),await execDocker([`tag`,`debian:bookworm-slim`,DEFAULT_SANDBOX_IMAGE]);return}throw Error(`Sandbox image not found: ${image}. Build or pull it first.`)}}async function dockerContainerState(name){let result=await execDocker([`inspect`,`-f`,`{{.State.Running}}`,name],{allowFailure:!0});return result.code===0?{exists:!0,running:result.stdout.trim()===`true`}:{exists:!1,running:!1}}function normalizeDockerLimit(value){return value==null?void 0:typeof value==`number`?Number.isFinite(value)?String(value):void 0:value.trim()||void 0}function formatUlimitValue(name,value){if(!name.trim())return null;if(typeof value==`number`||typeof value==`string`){let raw=String(value).trim();return raw?`${name}=${raw}`:null}let soft=typeof value.soft==`number`?Math.max(0,value.soft):void 0,hard=typeof value.hard==`number`?Math.max(0,value.hard):void 0;return soft===void 0&&hard===void 0?null:soft===void 0?`${name}=${hard}`:hard===void 0?`${name}=${soft}`:`${name}=${soft}:${hard}`}function buildSandboxCreateArgs(params){validateSandboxSecurity(params.cfg);let createdAtMs=params.createdAtMs??Date.now(),args=[`create`,`--name`,params.name];args.push(`--label`,`builderforce.sandbox=1`),args.push(`--label`,`builderforce.sessionKey=${params.scopeKey}`),args.push(`--label`,`builderforce.createdAtMs=${createdAtMs}`),params.configHash&&args.push(`--label`,`builderforce.configHash=${params.configHash}`);for(let[key,value]of Object.entries(params.labels??{}))key&&value&&args.push(`--label`,`${key}=${value}`);params.cfg.readOnlyRoot&&args.push(`--read-only`);for(let entry of params.cfg.tmpfs)args.push(`--tmpfs`,entry);params.cfg.network&&args.push(`--network`,params.cfg.network),params.cfg.user&&args.push(`--user`,params.cfg.user);let envSanitization=sanitizeEnvVars(params.cfg.env??{});envSanitization.blocked.length>0&&console.warn(`[Security] Blocked sensitive environment variables:`,envSanitization.blocked.join(`, `)),envSanitization.warnings.length>0&&console.warn(`[Security] Suspicious environment variables:`,envSanitization.warnings);for(let[key,value]of Object.entries(envSanitization.allowed))args.push(`--env`,`${key}=${value}`);for(let cap of params.cfg.capDrop)args.push(`--cap-drop`,cap);args.push(`--security-opt`,`no-new-privileges`),params.cfg.seccompProfile&&args.push(`--security-opt`,`seccomp=${params.cfg.seccompProfile}`),params.cfg.apparmorProfile&&args.push(`--security-opt`,`apparmor=${params.cfg.apparmorProfile}`);for(let entry of params.cfg.dns??[])entry.trim()&&args.push(`--dns`,entry);for(let entry of params.cfg.extraHosts??[])entry.trim()&&args.push(`--add-host`,entry);typeof params.cfg.pidsLimit==`number`&¶ms.cfg.pidsLimit>0&&args.push(`--pids-limit`,String(params.cfg.pidsLimit));let memory=normalizeDockerLimit(params.cfg.memory);memory&&args.push(`--memory`,memory);let memorySwap=normalizeDockerLimit(params.cfg.memorySwap);memorySwap&&args.push(`--memory-swap`,memorySwap),typeof params.cfg.cpus==`number`&¶ms.cfg.cpus>0&&args.push(`--cpus`,String(params.cfg.cpus));for(let[name,value]of Object.entries(params.cfg.ulimits??{})){let formatted=formatUlimitValue(name,value);formatted&&args.push(`--ulimit`,formatted)}if(params.cfg.binds?.length)for(let bind of params.cfg.binds)args.push(`-v`,bind);return args}async function createSandboxContainer(params){let{name,cfg,workspaceDir,scopeKey}=params;await ensureDockerImage(cfg.image);let args=buildSandboxCreateArgs({name,cfg,scopeKey,configHash:params.configHash});args.push(`--workdir`,cfg.workdir);let mainMountSuffix=params.workspaceAccess===`ro`&&workspaceDir===params.agentWorkspaceDir?`:ro`:``;if(args.push(`-v`,`${workspaceDir}:${cfg.workdir}${mainMountSuffix}`),params.workspaceAccess!==`none`&&workspaceDir!==params.agentWorkspaceDir){let agentMountSuffix=params.workspaceAccess===`ro`?`:ro`:``;args.push(`-v`,`${params.agentWorkspaceDir}:${SANDBOX_AGENT_WORKSPACE_MOUNT}${agentMountSuffix}`)}args.push(cfg.image,`sleep`,`infinity`),await execDocker(args),await execDocker([`start`,name]),cfg.setupCommand?.trim()&&await execDocker([`exec`,`-i`,name,`sh`,`-lc`,cfg.setupCommand])}async function readContainerConfigHash(containerName){return await readDockerContainerLabel(containerName,`builderforce.configHash`)}function formatSandboxRecreateHint(params){return params.scope===`session`?formatCliCommand(`builderforce sandbox recreate --session ${params.sessionKey}`):params.scope===`agent`?formatCliCommand(`builderforce sandbox recreate --agent ${resolveSandboxAgentId(params.sessionKey)??`main`}`):formatCliCommand(`builderforce sandbox recreate --all`)}async function ensureSandboxContainer(params){let scopeKey=resolveSandboxScopeKey(params.cfg.scope,params.sessionKey),slug=params.cfg.scope===`shared`?`shared`:slugifySessionKey(scopeKey),containerName=`${params.cfg.docker.containerPrefix}${slug}`.slice(0,63),expectedHash=computeSandboxConfigHash({docker:params.cfg.docker,workspaceAccess:params.cfg.workspaceAccess,workspaceDir:params.workspaceDir,agentWorkspaceDir:params.agentWorkspaceDir}),now=Date.now(),state=await dockerContainerState(containerName),hasContainer=state.exists,running=state.running,currentHash=null,hashMismatch=!1,registryEntry;if(hasContainer&&(registryEntry=(await readRegistry()).entries.find(entry=>entry.containerName===containerName),currentHash=await readContainerConfigHash(containerName),currentHash||=registryEntry?.configHash??null,hashMismatch=!currentHash||currentHash!==expectedHash,hashMismatch)){let lastUsedAtMs=registryEntry?.lastUsedAtMs;if(running&&(typeof lastUsedAtMs!=`number`||now-lastUsedAtMs<3e5)){let hint=formatSandboxRecreateHint({scope:params.cfg.scope,sessionKey:scopeKey});defaultRuntime.log(`Sandbox config changed for ${containerName} (recently used). Recreate to apply: ${hint}`)}else await execDocker([`rm`,`-f`,containerName],{allowFailure:!0}),hasContainer=!1,running=!1}return hasContainer?running||await execDocker([`start`,containerName]):await createSandboxContainer({name:containerName,cfg:params.cfg.docker,workspaceDir:params.workspaceDir,workspaceAccess:params.cfg.workspaceAccess,agentWorkspaceDir:params.agentWorkspaceDir,scopeKey,configHash:expectedHash}),await updateRegistry({containerName,sessionKey:scopeKey,createdAtMs:now,lastUsedAtMs:now,image:params.cfg.docker.image,configHash:hashMismatch&&running?currentHash??void 0:expectedHash}),containerName}async function waitForSandboxCdp(params){let deadline=Date.now()+Math.max(0,params.timeoutMs),url=`http://127.0.0.1:${params.cdpPort}/json/version`;for(;Date.now()<deadline;){try{let ctrl=new AbortController,t=setTimeout(ctrl.abort.bind(ctrl),1e3);try{if((await fetch(url,{signal:ctrl.signal})).ok)return!0}finally{clearTimeout(t)}}catch{}await new Promise(r=>setTimeout(r,150))}return!1}function buildSandboxBrowserResolvedConfig(params){return{enabled:!0,evaluateEnabled:params.evaluateEnabled,controlPort:params.controlPort,cdpProtocol:`http`,cdpHost:`127.0.0.1`,cdpIsLoopback:!0,remoteCdpTimeoutMs:1500,remoteCdpHandshakeTimeoutMs:3e3,color:DEFAULT_BUILDERFORCE_AGENTS_BROWSER_COLOR,executablePath:void 0,headless:params.headless,noSandbox:!1,attachOnly:!0,defaultProfile:DEFAULT_BUILDERFORCE_AGENTS_BROWSER_PROFILE_NAME,extraArgs:[],profiles:{[DEFAULT_BUILDERFORCE_AGENTS_BROWSER_PROFILE_NAME]:{cdpPort:params.cdpPort,color:DEFAULT_BUILDERFORCE_AGENTS_BROWSER_COLOR}}}}async function ensureSandboxBrowserImage(image){if((await execDocker([`image`,`inspect`,image],{allowFailure:!0})).code!==0)throw Error(`Sandbox browser image not found: ${image}. Build it with scripts/sandbox-browser-setup.sh.`)}async function ensureSandboxBrowser(params){if(!params.cfg.browser.enabled||!isToolAllowed(params.cfg.tools,`browser`))return null;let slug=params.cfg.scope===`shared`?`shared`:slugifySessionKey(params.scopeKey),containerName=`${params.cfg.browser.containerPrefix}${slug}`.slice(0,63),state=await dockerContainerState(containerName),browserImage=params.cfg.browser.image??DEFAULT_SANDBOX_BROWSER_IMAGE,browserDockerCfg=resolveSandboxBrowserDockerCreateConfig({docker:params.cfg.docker,browser:{...params.cfg.browser,image:browserImage}}),expectedHash=computeSandboxBrowserConfigHash({docker:browserDockerCfg,browser:{cdpPort:params.cfg.browser.cdpPort,vncPort:params.cfg.browser.vncPort,noVncPort:params.cfg.browser.noVncPort,headless:params.cfg.browser.headless,enableNoVnc:params.cfg.browser.enableNoVnc},workspaceAccess:params.cfg.workspaceAccess,workspaceDir:params.workspaceDir,agentWorkspaceDir:params.agentWorkspaceDir}),now=Date.now(),hasContainer=state.exists,running=state.running,currentHash=null,hashMismatch=!1;if(hasContainer){let registryEntry=(await readBrowserRegistry()).entries.find(entry=>entry.containerName===containerName);if(currentHash=await readDockerContainerLabel(containerName,`builderforce.configHash`),hashMismatch=!currentHash||currentHash!==expectedHash,currentHash||(currentHash=registryEntry?.configHash??null,hashMismatch=!currentHash||currentHash!==expectedHash),hashMismatch){let lastUsedAtMs=registryEntry?.lastUsedAtMs;if(running&&(typeof lastUsedAtMs!=`number`||now-lastUsedAtMs<3e5)){let hint=params.cfg.scope===`session`?`builderforce sandbox recreate --browser --session ${params.scopeKey}`:params.cfg.scope===`agent`?`builderforce sandbox recreate --browser --agent ${resolveSandboxAgentId(params.scopeKey)??`main`}`:`builderforce sandbox recreate --browser --all`;defaultRuntime.log(`Sandbox browser config changed for ${containerName} (recently used). Recreate to apply: ${hint}`)}else await execDocker([`rm`,`-f`,containerName],{allowFailure:!0}),hasContainer=!1,running=!1}}if(hasContainer)running||await execDocker([`start`,containerName]);else{await ensureSandboxBrowserImage(browserImage);let args=buildSandboxCreateArgs({name:containerName,cfg:browserDockerCfg,scopeKey:params.scopeKey,labels:{"builderforce.sandboxBrowser":`1`},configHash:expectedHash}),mainMountSuffix=params.cfg.workspaceAccess===`ro`&¶ms.workspaceDir===params.agentWorkspaceDir?`:ro`:``;if(args.push(`-v`,`${params.workspaceDir}:${params.cfg.docker.workdir}${mainMountSuffix}`),params.cfg.workspaceAccess!==`none`&¶ms.workspaceDir!==params.agentWorkspaceDir){let agentMountSuffix=params.cfg.workspaceAccess===`ro`?`:ro`:``;args.push(`-v`,`${params.agentWorkspaceDir}:${SANDBOX_AGENT_WORKSPACE_MOUNT}${agentMountSuffix}`)}args.push(`-p`,`127.0.0.1::${params.cfg.browser.cdpPort}`),params.cfg.browser.enableNoVnc&&!params.cfg.browser.headless&&args.push(`-p`,`127.0.0.1::${params.cfg.browser.noVncPort}`),args.push(`-e`,`BUILDERFORCE_AGENTS_BROWSER_HEADLESS=${params.cfg.browser.headless?`1`:`0`}`),args.push(`-e`,`BUILDERFORCE_AGENTS_BROWSER_ENABLE_NOVNC=${params.cfg.browser.enableNoVnc?`1`:`0`}`),args.push(`-e`,`BUILDERFORCE_AGENTS_BROWSER_CDP_PORT=${params.cfg.browser.cdpPort}`),args.push(`-e`,`BUILDERFORCE_AGENTS_BROWSER_VNC_PORT=${params.cfg.browser.vncPort}`),args.push(`-e`,`BUILDERFORCE_AGENTS_BROWSER_NOVNC_PORT=${params.cfg.browser.noVncPort}`),args.push(browserImage),await execDocker(args),await execDocker([`start`,containerName])}let mappedCdp=await readDockerPort(containerName,params.cfg.browser.cdpPort);if(!mappedCdp)throw Error(`Failed to resolve CDP port mapping for ${containerName}.`);let mappedNoVnc=params.cfg.browser.enableNoVnc&&!params.cfg.browser.headless?await readDockerPort(containerName,params.cfg.browser.noVncPort):null,existing=BROWSER_BRIDGES.get(params.scopeKey),existingProfile=existing?resolveProfile(existing.bridge.state.resolved,DEFAULT_BUILDERFORCE_AGENTS_BROWSER_PROFILE_NAME):null,desiredAuthToken=params.bridgeAuth?.token?.trim()||void 0,desiredAuthPassword=params.bridgeAuth?.password?.trim()||void 0;!desiredAuthToken&&!desiredAuthPassword&&(desiredAuthToken=existing?.authToken,desiredAuthPassword=existing?.authPassword,!desiredAuthToken&&!desiredAuthPassword&&(desiredAuthToken=crypto.randomBytes(24).toString(`hex`)));let shouldReuse=existing&&existing.containerName===containerName&&existingProfile?.cdpPort===mappedCdp,authMatches=!existing||existing.authToken===desiredAuthToken&&existing.authPassword===desiredAuthPassword;existing&&!shouldReuse&&(await stopBrowserBridgeServer(existing.bridge.server).catch(()=>void 0),BROWSER_BRIDGES.delete(params.scopeKey)),existing&&shouldReuse&&!authMatches&&(await stopBrowserBridgeServer(existing.bridge.server).catch(()=>void 0),BROWSER_BRIDGES.delete(params.scopeKey));let bridge=shouldReuse&&authMatches&&existing?existing.bridge:null,resolvedBridge=await(async()=>{if(bridge)return bridge;let onEnsureAttachTarget=params.cfg.browser.autoStart?async()=>{let state=await dockerContainerState(containerName);if(state.exists&&!state.running&&await execDocker([`start`,containerName]),!await waitForSandboxCdp({cdpPort:mappedCdp,timeoutMs:params.cfg.browser.autoStartTimeoutMs}))throw Error(`Sandbox browser CDP did not become reachable on 127.0.0.1:${mappedCdp} within ${params.cfg.browser.autoStartTimeoutMs}ms.`)}:void 0;return await startBrowserBridgeServer({resolved:buildSandboxBrowserResolvedConfig({controlPort:0,cdpPort:mappedCdp,headless:params.cfg.browser.headless,evaluateEnabled:params.evaluateEnabled??DEFAULT_BROWSER_EVALUATE_ENABLED}),authToken:desiredAuthToken,authPassword:desiredAuthPassword,onEnsureAttachTarget})})();(!shouldReuse||!authMatches)&&BROWSER_BRIDGES.set(params.scopeKey,{bridge:resolvedBridge,containerName,authToken:desiredAuthToken,authPassword:desiredAuthPassword}),await updateBrowserRegistry({containerName,sessionKey:params.scopeKey,createdAtMs:now,lastUsedAtMs:now,image:browserImage,configHash:hashMismatch&&running?currentHash??void 0:expectedHash,cdpPort:mappedCdp,noVncPort:mappedNoVnc??void 0});let noVncUrl=mappedNoVnc&¶ms.cfg.browser.enableNoVnc&&!params.cfg.browser.headless?`http://127.0.0.1:${mappedNoVnc}/vnc.html?autoconnect=1&resize=remote`:void 0;return{bridgeUrl:resolvedBridge.baseUrl,noVncUrl,containerName}}function parseSandboxBindMount(spec){let trimmed=spec.trim();if(!trimmed)return null;let parsed=splitBindSpec(trimmed);if(!parsed)return null;let hostToken=parsed.host.trim(),containerToken=parsed.container.trim();if(!hostToken||!containerToken||!path.posix.isAbsolute(containerToken))return null;let optionsToken=parsed.options.trim().toLowerCase(),writable=!(optionsToken?optionsToken.split(`,`).map(entry=>entry.trim()).filter(Boolean):[]).includes(`ro`);return{hostRoot:path.resolve(hostToken),containerRoot:normalizeContainerPath(containerToken),writable}}function splitBindSpec(spec){let separator=getHostContainerSeparatorIndex(spec);if(separator===-1)return null;let host=spec.slice(0,separator),rest=spec.slice(separator+1),optionsStart=rest.indexOf(`:`);return optionsStart===-1?{host,container:rest,options:``}:{host,container:rest.slice(0,optionsStart),options:rest.slice(optionsStart+1)}}function getHostContainerSeparatorIndex(spec){let hasDriveLetterPrefix=/^[A-Za-z]:[\\/]/.test(spec);for(let i=hasDriveLetterPrefix?2:0;i<spec.length;i+=1)if(spec[i]===`:`)return i;return-1}function buildSandboxFsMounts(sandbox){let mounts=[{hostRoot:path.resolve(sandbox.workspaceDir),containerRoot:normalizeContainerPath(sandbox.containerWorkdir),writable:sandbox.workspaceAccess===`rw`,source:`workspace`}];sandbox.workspaceAccess!==`none`&&path.resolve(sandbox.agentWorkspaceDir)!==path.resolve(sandbox.workspaceDir)&&mounts.push({hostRoot:path.resolve(sandbox.agentWorkspaceDir),containerRoot:SANDBOX_AGENT_WORKSPACE_MOUNT,writable:sandbox.workspaceAccess===`rw`,source:`agent`});for(let bind of sandbox.docker.binds??[]){let parsed=parseSandboxBindMount(bind);parsed&&mounts.push({hostRoot:parsed.hostRoot,containerRoot:parsed.containerRoot,writable:parsed.writable,source:`bind`})}return dedupeMounts(mounts)}function resolveSandboxFsPathWithMounts(params){let mountsByContainer=[...params.mounts].toSorted((a,b)=>b.containerRoot.length-a.containerRoot.length),mountsByHost=[...params.mounts].toSorted((a,b)=>b.hostRoot.length-a.hostRoot.length),input=params.filePath,inputPosix=normalizePosixInput(input);if(path.posix.isAbsolute(inputPosix)){let containerMount=findMountByContainerPath(mountsByContainer,inputPosix);if(containerMount){let rel=path.posix.relative(containerMount.containerRoot,inputPosix);return{hostPath:rel?path.resolve(containerMount.hostRoot,...toHostSegments(rel)):containerMount.hostRoot,containerPath:rel?path.posix.join(containerMount.containerRoot,rel):containerMount.containerRoot,relativePath:toDisplayRelative({containerPath:rel?path.posix.join(containerMount.containerRoot,rel):containerMount.containerRoot,defaultContainerRoot:params.defaultContainerRoot}),writable:containerMount.writable}}}let hostResolved=resolveSandboxInputPath(input,params.cwd),hostMount=findMountByHostPath(mountsByHost,hostResolved);if(hostMount){let relHost=path.relative(hostMount.hostRoot,hostResolved),relPosix=relHost?relHost.split(path.sep).join(path.posix.sep):``,containerPath=relPosix?path.posix.join(hostMount.containerRoot,relPosix):hostMount.containerRoot;return{hostPath:hostResolved,containerPath,relativePath:toDisplayRelative({containerPath,defaultContainerRoot:params.defaultContainerRoot}),writable:hostMount.writable}}throw resolveSandboxPath({filePath:input,cwd:params.cwd,root:params.defaultWorkspaceRoot}),Error(`Path escapes sandbox root (${params.defaultWorkspaceRoot}): ${input}`)}function dedupeMounts(mounts){let seen=new Set,deduped=[];for(let mount of mounts){let key=`${mount.hostRoot}=>${mount.containerRoot}`;seen.has(key)||(seen.add(key),deduped.push(mount))}return deduped}function findMountByContainerPath(mounts,target){for(let mount of mounts)if(isPathInsidePosix(mount.containerRoot,target))return mount;return null}function findMountByHostPath(mounts,target){for(let mount of mounts)if(isPathInsideHost(mount.hostRoot,target))return mount;return null}function isPathInsidePosix(root,target){let rel=path.posix.relative(root,target);return rel?!(rel.startsWith(`..`)||path.posix.isAbsolute(rel)):!0}function isPathInsideHost(root,target){let rel=path.relative(root,target);return rel?!(rel.startsWith(`..`)||path.isAbsolute(rel)):!0}function toHostSegments(relativePosix){return relativePosix.split(`/`).filter(Boolean)}function toDisplayRelative(params){let rel=path.posix.relative(params.defaultContainerRoot,params.containerPath);return rel?!rel.startsWith(`..`)&&!path.posix.isAbsolute(rel)?rel:params.containerPath:``}function normalizeContainerPath(value){let normalized=path.posix.normalize(value);return normalized===`.`?`/`:normalized}function normalizePosixInput(value){return value.replace(/\\/g,`/`).trim()}function createSandboxFsBridge(params){return new SandboxFsBridgeImpl(params.sandbox)}var SandboxFsBridgeImpl=class{constructor(sandbox){this.sandbox=sandbox,this.mounts=buildSandboxFsMounts(sandbox)}resolvePath(params){let target=this.resolveResolvedPath(params);return{hostPath:target.hostPath,relativePath:target.relativePath,containerPath:target.containerPath}}async readFile(params){let target=this.resolveResolvedPath(params);return(await this.runCommand(`set -eu; cat -- "$1"`,{args:[target.containerPath],signal:params.signal})).stdout}async writeFile(params){let target=this.resolveResolvedPath(params);this.ensureWriteAccess(target,`write files`);let buffer=Buffer.isBuffer(params.data)?params.data:Buffer.from(params.data,params.encoding??`utf8`),script=params.mkdir===!1?`set -eu; cat >"$1"`:`set -eu; dir=$(dirname -- "$1"); if [ "$dir" != "." ]; then mkdir -p -- "$dir"; fi; cat >"$1"`;await this.runCommand(script,{args:[target.containerPath],stdin:buffer,signal:params.signal})}async mkdirp(params){let target=this.resolveResolvedPath(params);this.ensureWriteAccess(target,`create directories`),await this.runCommand(`set -eu; mkdir -p -- "$1"`,{args:[target.containerPath],signal:params.signal})}async remove(params){let target=this.resolveResolvedPath(params);this.ensureWriteAccess(target,`remove files`);let flags=[params.force===!1?``:`-f`,params.recursive?`-r`:``].filter(Boolean),rmCommand=flags.length>0?`rm ${flags.join(` `)}`:`rm`;await this.runCommand(`set -eu; ${rmCommand} -- "$1"`,{args:[target.containerPath],signal:params.signal})}async rename(params){let from=this.resolveResolvedPath({filePath:params.from,cwd:params.cwd}),to=this.resolveResolvedPath({filePath:params.to,cwd:params.cwd});this.ensureWriteAccess(from,`rename files`),this.ensureWriteAccess(to,`rename files`),await this.runCommand(`set -eu; dir=$(dirname -- "$2"); if [ "$dir" != "." ]; then mkdir -p -- "$dir"; fi; mv -- "$1" "$2"`,{args:[from.containerPath,to.containerPath],signal:params.signal})}async stat(params){let target=this.resolveResolvedPath(params),result=await this.runCommand(`set -eu; stat -c "%F|%s|%Y" -- "$1"`,{args:[target.containerPath],signal:params.signal,allowFailure:!0});if(result.code!==0){let stderr=result.stderr.toString(`utf8`);if(stderr.includes(`No such file or directory`))return null;let message=stderr.trim()||`stat failed with code ${result.code}`;throw Error(`stat failed for ${target.containerPath}: ${message}`)}let[typeRaw,sizeRaw,mtimeRaw]=result.stdout.toString(`utf8`).trim().split(`|`),size=Number.parseInt(sizeRaw??`0`,10),mtime=Number.parseInt(mtimeRaw??`0`,10)*1e3;return{type:coerceStatType(typeRaw),size:Number.isFinite(size)?size:0,mtimeMs:Number.isFinite(mtime)?mtime:0}}async runCommand(script,options={}){let dockerArgs=[`exec`,`-i`,this.sandbox.containerName,`sh`,`-c`,script,`builderforce-sandbox-fs`];return options.args?.length&&dockerArgs.push(...options.args),execDockerRaw(dockerArgs,{input:options.stdin,allowFailure:options.allowFailure,signal:options.signal})}ensureWriteAccess(target,action){if(!allowsWrites(this.sandbox.workspaceAccess)||!target.writable)throw Error(`Sandbox path is read-only; cannot ${action}: ${target.containerPath}`)}resolveResolvedPath(params){return resolveSandboxFsPathWithMounts({filePath:params.filePath,cwd:params.cwd??this.sandbox.workspaceDir,defaultWorkspaceRoot:this.sandbox.workspaceDir,defaultContainerRoot:this.sandbox.containerWorkdir,mounts:this.mounts})}};function allowsWrites(access){return access===`rw`}function coerceStatType(typeRaw){if(!typeRaw)return`other`;let normalized=typeRaw.trim().toLowerCase();return normalized.includes(`directory`)?`directory`:normalized.includes(`file`)?`file`:`other`}let lastPruneAtMs=0;function shouldPruneSandboxEntry(cfg,now,entry){let idleHours=cfg.prune.idleHours,maxAgeDays=cfg.prune.maxAgeDays;if(idleHours===0&&maxAgeDays===0)return!1;let idleMs=now-entry.lastUsedAtMs,ageMs=now-entry.createdAtMs;return idleHours>0&&idleMs>idleHours*60*60*1e3||maxAgeDays>0&&ageMs>maxAgeDays*24*60*60*1e3}async function pruneSandboxRegistryEntries(params){let now=Date.now();if(params.cfg.prune.idleHours===0&¶ms.cfg.prune.maxAgeDays===0)return;let registry=await params.read();for(let entry of registry.entries)if(shouldPruneSandboxEntry(params.cfg,now,entry))try{await execDocker([`rm`,`-f`,entry.containerName],{allowFailure:!0})}catch{}finally{await params.remove(entry.containerName),await params.onRemoved?.(entry)}}async function pruneSandboxContainers(cfg){await pruneSandboxRegistryEntries({cfg,read:readRegistry,remove:removeRegistryEntry})}async function pruneSandboxBrowsers(cfg){await pruneSandboxRegistryEntries({cfg,read:readBrowserRegistry,remove:removeBrowserRegistryEntry,onRemoved:async entry=>{let bridge=BROWSER_BRIDGES.get(entry.sessionKey);bridge?.containerName===entry.containerName&&(await stopBrowserBridgeServer(bridge.bridge.server).catch(()=>void 0),BROWSER_BRIDGES.delete(entry.sessionKey))}})}async function maybePruneSandboxes(cfg){let now=Date.now();if(!(now-lastPruneAtMs<300*1e3)){lastPruneAtMs=now;try{await pruneSandboxContainers(cfg),await pruneSandboxBrowsers(cfg)}catch(error){let message=error instanceof Error?error.message:typeof error==`string`?error:JSON.stringify(error);defaultRuntime.error?.(`Sandbox prune failed: ${message??`unknown error`}`)}}}function normalizeStringEntries(list){return(list??[]).map(entry=>String(entry).trim()).filter(Boolean)}function normalizeStringEntriesLower(list){return normalizeStringEntries(list).map(entry=>entry.toLowerCase())}function normalizeHyphenSlug(raw){let trimmed=raw?.trim().toLowerCase()??``;return trimmed?trimmed.replace(/\s+/g,`-`).replace(/[^a-z0-9#@._+-]+/g,`-`).replace(/-{2,}/g,`-`).replace(/^[-.]+|[-.]+$/g,``):``}function normalizeAtHashSlug(raw){let trimmed=raw?.trim().toLowerCase()??``;return trimmed?trimmed.replace(/^[@#]+/,``).replace(/[\s_]+/g,`-`).replace(/[^a-z0-9-]+/g,`-`).replace(/-{2,}/g,`-`).replace(/^-+|-+$/g,``):``}const getGroupSurfaces=()=>new Set([...listDeliverableMessageChannels(),`webchat`]);function normalizeGroupLabel(raw){return normalizeHyphenSlug(raw)}function shortenGroupId(value){let trimmed=value?.trim()??``;return trimmed?trimmed.length<=14?trimmed:`${trimmed.slice(0,6)}...${trimmed.slice(-4)}`:``}function buildGroupDisplayName(params){let providerKey=(params.provider?.trim().toLowerCase()||`group`).trim(),groupChannel=params.groupChannel?.trim(),space=params.space?.trim(),subject=params.subject?.trim(),detail=(groupChannel&&space?`${space}${groupChannel.startsWith(`#`)?``:`#`}${groupChannel}`:groupChannel||subject||space||``)||``,fallbackId=params.id?.trim()||params.key,rawLabel=detail||fallbackId,token=normalizeGroupLabel(rawLabel);return token||=normalizeGroupLabel(shortenGroupId(rawLabel)),!params.groupChannel&&token.startsWith(`#`)&&(token=token.replace(/^#+/,``)),token&&!/^[@#]/.test(token)&&!token.startsWith(`g-`)&&!token.includes(`#`)&&(token=`g-${token}`),token?`${providerKey}:${token}`:providerKey}function resolveGroupSessionKey(ctx){let from=typeof ctx.From==`string`?ctx.From.trim():``,chatType=ctx.ChatType?.trim().toLowerCase(),normalizedChatType=chatType===`channel`?`channel`:chatType===`group`?`group`:void 0,isWhatsAppGroupId=from.toLowerCase().endsWith(`@g.us`);if(!(normalizedChatType===`group`||normalizedChatType===`channel`||from.includes(`:group:`)||from.includes(`:channel:`)||isWhatsAppGroupId))return null;let providerHint=ctx.Provider?.trim().toLowerCase(),parts=from.split(`:`).filter(Boolean),head=parts[0]?.trim().toLowerCase()??``,headIsSurface=head?getGroupSurfaces().has(head):!1,provider=headIsSurface?head:providerHint??(isWhatsAppGroupId?`whatsapp`:void 0);if(!provider)return null;let second=parts[1]?.trim().toLowerCase(),secondIsKind=second===`group`||second===`channel`,kind=secondIsKind?second:from.includes(`:channel:`)||normalizedChatType===`channel`?`channel`:`group`,finalId=(headIsSurface?secondIsKind?parts.slice(2).join(`:`):parts.slice(1).join(`:`):from).trim().toLowerCase();return finalId?{key:`${provider}:${kind}:${finalId}`,channel:provider,id:finalId,chatType:kind===`channel`?`channel`:`group`}:null}function resolveChannelGroupConfig(groups,groupId,caseInsensitive=!1){if(!groups)return;let direct=groups[groupId];if(direct)return direct;if(!caseInsensitive)return;let target=groupId.toLowerCase(),matchedKey=Object.keys(groups).find(key=>key!==`*`&&key.toLowerCase()===target);if(matchedKey)return groups[matchedKey]}function normalizeSenderKey(value){let trimmed=value.trim();return trimmed?(trimmed.startsWith(`@`)?trimmed.slice(1):trimmed).toLowerCase():``}function resolveToolsBySender(params){let toolsBySender=params.toolsBySender;if(!toolsBySender)return;let entries=Object.entries(toolsBySender);if(entries.length===0)return;let normalized=new Map,wildcard;for(let[rawKey,policy]of entries){if(!policy)continue;let key=normalizeSenderKey(rawKey);if(key){if(key===`*`){wildcard=policy;continue}normalized.has(key)||normalized.set(key,policy)}}let candidates=[],pushCandidate=value=>{let trimmed=value?.trim();trimmed&&candidates.push(trimmed)};pushCandidate(params.senderId),pushCandidate(params.senderE164),pushCandidate(params.senderUsername),pushCandidate(params.senderName);for(let candidate of candidates){let key=normalizeSenderKey(candidate);if(!key)continue;let match=normalized.get(key);if(match)return match}return wildcard}function resolveChannelGroups(cfg,channel,accountId){let normalizedAccountId=normalizeAccountId$1(accountId),channelConfig=cfg.channels?.[channel];if(channelConfig)return channelConfig.accounts?.[normalizedAccountId]?.groups??channelConfig.accounts?.[Object.keys(channelConfig.accounts??{}).find(key=>key.toLowerCase()===normalizedAccountId.toLowerCase())??``]?.groups??channelConfig.groups}function resolveChannelGroupPolicy(params){let{cfg,channel}=params,groups=resolveChannelGroups(cfg,channel,params.accountId),allowlistEnabled=!!(groups&&Object.keys(groups).length>0),normalizedId=params.groupId?.trim(),groupConfig=normalizedId?resolveChannelGroupConfig(groups,normalizedId,params.groupIdCaseInsensitive):void 0,defaultConfig=groups?.[`*`];return{allowlistEnabled,allowed:!allowlistEnabled||allowlistEnabled&&!!(groups&&Object.hasOwn(groups,`*`))||!!groupConfig,groupConfig,defaultConfig}}function resolveChannelGroupRequireMention(params){let{requireMentionOverride,overrideOrder=`after-config`}=params,{groupConfig,defaultConfig}=resolveChannelGroupPolicy(params),configMention=typeof groupConfig?.requireMention==`boolean`?groupConfig.requireMention:typeof defaultConfig?.requireMention==`boolean`?defaultConfig.requireMention:void 0;return overrideOrder===`before-config`&&typeof requireMentionOverride==`boolean`?requireMentionOverride:typeof configMention==`boolean`?configMention:overrideOrder!==`before-config`&&typeof requireMentionOverride==`boolean`?requireMentionOverride:!0}function resolveChannelGroupToolsPolicy(params){let{groupConfig,defaultConfig}=resolveChannelGroupPolicy(params),groupSenderPolicy=resolveToolsBySender({toolsBySender:groupConfig?.toolsBySender,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164});if(groupSenderPolicy)return groupSenderPolicy;if(groupConfig?.tools)return groupConfig.tools;let defaultSenderPolicy=resolveToolsBySender({toolsBySender:defaultConfig?.toolsBySender,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164});if(defaultSenderPolicy)return defaultSenderPolicy;if(defaultConfig?.tools)return defaultConfig.tools}function buildSlackThreadingToolContext(params){let configuredReplyToMode=resolveSlackReplyToMode(resolveSlackAccount({cfg:params.cfg,accountId:params.accountId}),params.context.ChatType),effectiveReplyToMode=params.context.ThreadLabel?`all`:configuredReplyToMode,threadId=params.context.MessageThreadId??params.context.ReplyToId;return{currentChannelId:params.context.To?.startsWith(`channel:`)?params.context.To.slice(8):void 0,currentThreadTs:threadId==null?void 0:String(threadId),replyToMode:effectiveReplyToMode,hasRepliedRef:params.hasRepliedRef}}function normalizeDiscordSlug(value){return normalizeAtHashSlug(value)}function parseTelegramGroupId(value){let raw=value?.trim()??``;if(!raw)return{chatId:void 0,topicId:void 0};let parts=raw.split(`:`).filter(Boolean);return parts.length>=3&&parts[1]===`topic`&&/^-?\d+$/.test(parts[0])&&/^\d+$/.test(parts[2])?{chatId:parts[0],topicId:parts[2]}:parts.length>=2&&/^-?\d+$/.test(parts[0])&&/^\d+$/.test(parts[1])?{chatId:parts[0],topicId:parts[1]}:{chatId:raw,topicId:void 0}}function resolveTelegramRequireMention(params){let{cfg,chatId,topicId}=params;if(!chatId)return;let groupConfig=cfg.channels?.telegram?.groups?.[chatId],groupDefault=cfg.channels?.telegram?.groups?.[`*`],topicConfig=topicId&&groupConfig?.topics?groupConfig.topics[topicId]:void 0,defaultTopicConfig=topicId&&groupDefault?.topics?groupDefault.topics[topicId]:void 0;if(typeof topicConfig?.requireMention==`boolean`)return topicConfig.requireMention;if(typeof defaultTopicConfig?.requireMention==`boolean`)return defaultTopicConfig.requireMention;if(typeof groupConfig?.requireMention==`boolean`)return groupConfig.requireMention;if(typeof groupDefault?.requireMention==`boolean`)return groupDefault.requireMention}function resolveDiscordGuildEntry(guilds,groupSpace){if(!guilds||Object.keys(guilds).length===0)return null;let space=groupSpace?.trim()??``;if(space&&guilds[space])return guilds[space];let normalized=normalizeDiscordSlug(space);if(normalized&&guilds[normalized])return guilds[normalized];if(normalized){let match=Object.values(guilds).find(entry=>normalizeDiscordSlug(entry?.slug??void 0)===normalized);if(match)return match}return guilds[`*`]??null}function resolveDiscordChannelEntry(channelEntries,params){if(!channelEntries||Object.keys(channelEntries).length===0)return;let groupChannel=params.groupChannel,channelSlug=normalizeDiscordSlug(groupChannel);return(params.groupId?channelEntries[params.groupId]:void 0)??(channelSlug?channelEntries[channelSlug]??channelEntries[`#${channelSlug}`]:void 0)??(groupChannel?channelEntries[normalizeDiscordSlug(groupChannel)]:void 0)}function resolveSlackChannelPolicyEntry(params){let channels=resolveSlackAccount({cfg:params.cfg,accountId:params.accountId}).channels??{};if(Object.keys(channels).length===0)return;let channelId=params.groupId?.trim(),channelName=params.groupChannel?.replace(/^#/,``),normalizedName=normalizeHyphenSlug(channelName),candidates=[channelId??``,channelName?`#${channelName}`:``,channelName??``,normalizedName].filter(Boolean);for(let candidate of candidates)if(candidate&&channels[candidate])return channels[candidate];return channels[`*`]}function resolveTelegramGroupRequireMention(params){let{chatId,topicId}=parseTelegramGroupId(params.groupId),requireMention=resolveTelegramRequireMention({cfg:params.cfg,chatId,topicId});return typeof requireMention==`boolean`?requireMention:resolveChannelGroupRequireMention({cfg:params.cfg,channel:`telegram`,groupId:chatId??params.groupId,accountId:params.accountId})}function resolveWhatsAppGroupRequireMention(params){return resolveChannelGroupRequireMention({cfg:params.cfg,channel:`whatsapp`,groupId:params.groupId,accountId:params.accountId})}function resolveIMessageGroupRequireMention(params){return resolveChannelGroupRequireMention({cfg:params.cfg,channel:`imessage`,groupId:params.groupId,accountId:params.accountId})}function resolveDiscordGroupRequireMention(params){let guildEntry=resolveDiscordGuildEntry(params.cfg.channels?.discord?.guilds,params.groupSpace),channelEntries=guildEntry?.channels;if(channelEntries&&Object.keys(channelEntries).length>0){let entry=resolveDiscordChannelEntry(channelEntries,params);if(entry&&typeof entry.requireMention==`boolean`)return entry.requireMention}return typeof guildEntry?.requireMention==`boolean`?guildEntry.requireMention:!0}function resolveGoogleChatGroupRequireMention(params){return resolveChannelGroupRequireMention({cfg:params.cfg,channel:`googlechat`,groupId:params.groupId,accountId:params.accountId})}function resolveGoogleChatGroupToolPolicy(params){return resolveChannelGroupToolsPolicy({cfg:params.cfg,channel:`googlechat`,groupId:params.groupId,accountId:params.accountId,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164})}function resolveSlackGroupRequireMention(params){let resolved=resolveSlackChannelPolicyEntry(params);return typeof resolved?.requireMention==`boolean`?resolved.requireMention:!0}function resolveTelegramGroupToolPolicy(params){let{chatId}=parseTelegramGroupId(params.groupId);return resolveChannelGroupToolsPolicy({cfg:params.cfg,channel:`telegram`,groupId:chatId??params.groupId,accountId:params.accountId,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164})}function resolveWhatsAppGroupToolPolicy(params){return resolveChannelGroupToolsPolicy({cfg:params.cfg,channel:`whatsapp`,groupId:params.groupId,accountId:params.accountId,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164})}function resolveIMessageGroupToolPolicy(params){return resolveChannelGroupToolsPolicy({cfg:params.cfg,channel:`imessage`,groupId:params.groupId,accountId:params.accountId,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164})}function resolveDiscordGroupToolPolicy(params){let guildEntry=resolveDiscordGuildEntry(params.cfg.channels?.discord?.guilds,params.groupSpace),channelEntries=guildEntry?.channels;if(channelEntries&&Object.keys(channelEntries).length>0){let entry=resolveDiscordChannelEntry(channelEntries,params),senderPolicy=resolveToolsBySender({toolsBySender:entry?.toolsBySender,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164});if(senderPolicy)return senderPolicy;if(entry?.tools)return entry.tools}let guildSenderPolicy=resolveToolsBySender({toolsBySender:guildEntry?.toolsBySender,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164});if(guildSenderPolicy)return guildSenderPolicy;if(guildEntry?.tools)return guildEntry.tools}function resolveSlackGroupToolPolicy(params){let resolved=resolveSlackChannelPolicyEntry(params);if(!resolved)return;let senderPolicy=resolveToolsBySender({toolsBySender:resolved?.toolsBySender,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164});if(senderPolicy)return senderPolicy;if(resolved?.tools)return resolved.tools}function normalizeSignalMessagingTarget(raw){let trimmed=raw.trim();if(!trimmed)return;let normalized=trimmed;if(normalized.toLowerCase().startsWith(`signal:`)&&(normalized=normalized.slice(7).trim()),!normalized)return;let lower=normalized.toLowerCase();if(lower.startsWith(`group:`)){let id=normalized.slice(6).trim();return id?`group:${id}`:void 0}if(lower.startsWith(`username:`)){let id=normalized.slice(9).trim();return id?`username:${id}`.toLowerCase():void 0}if(lower.startsWith(`u:`)){let id=normalized.slice(2).trim();return id?`username:${id}`.toLowerCase():void 0}if(lower.startsWith(`uuid:`)){let id=normalized.slice(5).trim();return id?id.toLowerCase():void 0}return normalized.toLowerCase()}const formatLower=allowFrom=>allowFrom.map(entry=>String(entry).trim()).filter(Boolean).map(entry=>entry.toLowerCase()),formatDiscordAllowFrom=allowFrom=>allowFrom.map(entry=>String(entry).trim().replace(/^<@!?/,``).replace(/>$/,``).replace(/^discord:/i,``).replace(/^user:/i,``).replace(/^pk:/i,``).trim().toLowerCase()).filter(Boolean);function resolveDirectOrGroupChannelId(context){return(context.ChatType?.toLowerCase()===`direct`?context.From??context.To:context.To)?.trim()||void 0}function buildSignalThreadToolContext(params){let currentChannelIdRaw=resolveDirectOrGroupChannelId(params.context);return{currentChannelId:currentChannelIdRaw?normalizeSignalMessagingTarget(currentChannelIdRaw)??currentChannelIdRaw.trim():void 0,currentThreadTs:params.context.ReplyToId,hasRepliedRef:params.hasRepliedRef}}function buildIMessageThreadToolContext(params){return{currentChannelId:resolveDirectOrGroupChannelId(params.context),currentThreadTs:params.context.ReplyToId,hasRepliedRef:params.hasRepliedRef}}function resolveCaseInsensitiveAccount(accounts,accountId){if(!accounts)return;let normalized=normalizeAccountId$1(accountId);return accounts[normalized]??accounts[Object.keys(accounts).find(key=>key.toLowerCase()===normalized.toLowerCase())??``]}const DOCKS={telegram:{id:`telegram`,capabilities:{chatTypes:[`direct`,`group`,`channel`,`thread`],nativeCommands:!0,blockStreaming:!0},outbound:{textChunkLimit:4e3},config:{resolveAllowFrom:({cfg,accountId})=>(resolveTelegramAccount({cfg,accountId}).config.allowFrom??[]).map(entry=>String(entry)),formatAllowFrom:({allowFrom})=>allowFrom.map(entry=>String(entry).trim()).filter(Boolean).map(entry=>entry.replace(/^(telegram|tg):/i,``)).map(entry=>entry.toLowerCase())},groups:{resolveRequireMention:resolveTelegramGroupRequireMention,resolveToolPolicy:resolveTelegramGroupToolPolicy},threading:{resolveReplyToMode:({cfg})=>cfg.channels?.telegram?.replyToMode??`off`,buildToolContext:({context,hasRepliedRef})=>{let threadId=context.MessageThreadId??context.ReplyToId;return{currentChannelId:context.To?.trim()||void 0,currentThreadTs:threadId==null?void 0:String(threadId),hasRepliedRef}}}},whatsapp:{id:`whatsapp`,capabilities:{chatTypes:[`direct`,`group`],polls:!0,reactions:!0,media:!0},commands:{enforceOwnerForCommands:!0,skipWhenConfigEmpty:!0},outbound:{textChunkLimit:4e3},config:{resolveAllowFrom:({cfg,accountId})=>resolveWhatsAppAccount({cfg,accountId}).allowFrom??[],formatAllowFrom:({allowFrom})=>allowFrom.map(entry=>String(entry).trim()).filter(entry=>!!entry).map(entry=>entry===`*`?entry:normalizeWhatsAppTarget(entry)).filter(entry=>!!entry)},groups:{resolveRequireMention:resolveWhatsAppGroupRequireMention,resolveToolPolicy:resolveWhatsAppGroupToolPolicy,resolveGroupIntroHint:()=>`WhatsApp IDs: SenderId is the participant JID (group participant id).`},mentions:{stripPatterns:({ctx})=>{let selfE164=(ctx.To??``).replace(/^whatsapp:/,``);if(!selfE164)return[];let escaped=escapeRegExp(selfE164);return[escaped,`@${escaped}`]}},threading:{buildToolContext:({context,hasRepliedRef})=>({currentChannelId:context.From?.trim()||context.To?.trim()||void 0,currentThreadTs:context.ReplyToId,hasRepliedRef})}},discord:{id:`discord`,capabilities:{chatTypes:[`direct`,`channel`,`thread`],polls:!0,reactions:!0,media:!0,nativeCommands:!0,threads:!0},outbound:{textChunkLimit:2e3},streaming:{blockStreamingCoalesceDefaults:{minChars:1500,idleMs:1e3}},elevated:{allowFromFallback:({cfg})=>cfg.channels?.discord?.allowFrom??cfg.channels?.discord?.dm?.allowFrom},config:{resolveAllowFrom:({cfg,accountId})=>{let account=resolveDiscordAccount({cfg,accountId});return(account.config.allowFrom??account.config.dm?.allowFrom??[]).map(entry=>String(entry))},formatAllowFrom:({allowFrom})=>formatDiscordAllowFrom(allowFrom)},groups:{resolveRequireMention:resolveDiscordGroupRequireMention,resolveToolPolicy:resolveDiscordGroupToolPolicy},mentions:{stripPatterns:()=>[`<@!?\\d+>`]},threading:{resolveReplyToMode:({cfg})=>cfg.channels?.discord?.replyToMode??`off`,buildToolContext:({context,hasRepliedRef})=>({currentChannelId:context.To?.trim()||void 0,currentThreadTs:context.ReplyToId,hasRepliedRef})}},irc:{id:`irc`,capabilities:{chatTypes:[`direct`,`group`],media:!0,blockStreaming:!0},outbound:{textChunkLimit:350},streaming:{blockStreamingCoalesceDefaults:{minChars:300,idleMs:1e3}},config:{resolveAllowFrom:({cfg,accountId})=>{let channel=cfg.channels?.irc;return(resolveCaseInsensitiveAccount(channel?.accounts,accountId)?.allowFrom??channel?.allowFrom??[]).map(entry=>String(entry))},formatAllowFrom:({allowFrom})=>allowFrom.map(entry=>String(entry).trim()).filter(Boolean).map(entry=>entry.replace(/^irc:/i,``).replace(/^user:/i,``).toLowerCase())},groups:{resolveRequireMention:({cfg,accountId,groupId})=>groupId?resolveChannelGroupRequireMention({cfg,channel:`irc`,groupId,accountId,groupIdCaseInsensitive:!0}):!0,resolveToolPolicy:({cfg,accountId,groupId,senderId,senderName,senderUsername})=>{if(groupId)return resolveChannelGroupToolsPolicy({cfg,channel:`irc`,groupId,accountId,groupIdCaseInsensitive:!0,senderId,senderName,senderUsername})}}},googlechat:{id:`googlechat`,capabilities:{chatTypes:[`direct`,`group`,`thread`],reactions:!0,media:!0,threads:!0,blockStreaming:!0},outbound:{textChunkLimit:4e3},config:{resolveAllowFrom:({cfg,accountId})=>{let channel=cfg.channels?.googlechat;return(resolveCaseInsensitiveAccount(channel?.accounts,accountId)?.dm?.allowFrom??channel?.dm?.allowFrom??[]).map(entry=>String(entry))},formatAllowFrom:({allowFrom})=>allowFrom.map(entry=>String(entry).trim()).filter(Boolean).map(entry=>entry.replace(/^(googlechat|google-chat|gchat):/i,``).replace(/^user:/i,``).replace(/^users\//i,``).toLowerCase())},groups:{resolveRequireMention:resolveGoogleChatGroupRequireMention,resolveToolPolicy:resolveGoogleChatGroupToolPolicy},threading:{resolveReplyToMode:({cfg})=>cfg.channels?.googlechat?.replyToMode??`off`,buildToolContext:({context,hasRepliedRef})=>{let threadId=context.MessageThreadId??context.ReplyToId;return{currentChannelId:context.To?.trim()||void 0,currentThreadTs:threadId==null?void 0:String(threadId),hasRepliedRef}}}},slack:{id:`slack`,capabilities:{chatTypes:[`direct`,`channel`,`thread`],reactions:!0,media:!0,nativeCommands:!0,threads:!0},outbound:{textChunkLimit:4e3},streaming:{blockStreamingCoalesceDefaults:{minChars:1500,idleMs:1e3}},config:{resolveAllowFrom:({cfg,accountId})=>{let account=resolveSlackAccount({cfg,accountId});return(account.config.allowFrom??account.dm?.allowFrom??[]).map(entry=>String(entry))},formatAllowFrom:({allowFrom})=>formatLower(allowFrom)},groups:{resolveRequireMention:resolveSlackGroupRequireMention,resolveToolPolicy:resolveSlackGroupToolPolicy},mentions:{stripPatterns:()=>[`<@[^>]+>`]},threading:{resolveReplyToMode:({cfg,accountId,chatType})=>resolveSlackReplyToMode(resolveSlackAccount({cfg,accountId}),chatType),allowExplicitReplyTagsWhenOff:!0,buildToolContext:params=>buildSlackThreadingToolContext(params)}},signal:{id:`signal`,capabilities:{chatTypes:[`direct`,`group`],reactions:!0,media:!0},outbound:{textChunkLimit:4e3},streaming:{blockStreamingCoalesceDefaults:{minChars:1500,idleMs:1e3}},config:{resolveAllowFrom:({cfg,accountId})=>(resolveSignalAccount({cfg,accountId}).config.allowFrom??[]).map(entry=>String(entry)),formatAllowFrom:({allowFrom})=>allowFrom.map(entry=>String(entry).trim()).filter(Boolean).map(entry=>entry===`*`?`*`:normalizeE164(entry.replace(/^signal:/i,``))).filter(Boolean)},threading:{buildToolContext:({context,hasRepliedRef})=>buildSignalThreadToolContext({context,hasRepliedRef})}},imessage:{id:`imessage`,capabilities:{chatTypes:[`direct`,`group`],reactions:!0,media:!0},outbound:{textChunkLimit:4e3},config:{resolveAllowFrom:({cfg,accountId})=>(resolveIMessageAccount({cfg,accountId}).config.allowFrom??[]).map(entry=>String(entry)),formatAllowFrom:({allowFrom})=>allowFrom.map(entry=>String(entry).trim()).filter(Boolean)},groups:{resolveRequireMention:resolveIMessageGroupRequireMention,resolveToolPolicy:resolveIMessageGroupToolPolicy},threading:{buildToolContext:({context,hasRepliedRef})=>buildIMessageThreadToolContext({context,hasRepliedRef})}}};function buildDockFromPlugin(plugin){return{id:plugin.id,capabilities:plugin.capabilities,commands:plugin.commands,outbound:plugin.outbound?.textChunkLimit?{textChunkLimit:plugin.outbound.textChunkLimit}:void 0,streaming:plugin.streaming?{blockStreamingCoalesceDefaults:plugin.streaming.blockStreamingCoalesceDefaults}:void 0,elevated:plugin.elevated,config:plugin.config?{resolveAllowFrom:plugin.config.resolveAllowFrom,formatAllowFrom:plugin.config.formatAllowFrom}:void 0,groups:plugin.groups,mentions:plugin.mentions,threading:plugin.threading,agentPrompt:plugin.agentPrompt}}function listPluginDockEntries(){let registry=requireActivePluginRegistry(),entries=[],seen=new Set;for(let entry of registry.channels){let plugin=entry.plugin,id=String(plugin.id).trim();if(!id||seen.has(id)||(seen.add(id),CHAT_CHANNEL_ORDER.includes(plugin.id)))continue;let dock=entry.dock??buildDockFromPlugin(plugin);entries.push({id:plugin.id,dock,order:plugin.meta.order})}return entries}function listChannelDocks(){let baseEntries=CHAT_CHANNEL_ORDER.map(id=>({id,dock:DOCKS[id],order:getChatChannelMeta(id).order})),pluginEntries=listPluginDockEntries(),combined=[...baseEntries,...pluginEntries];return combined.sort((a,b)=>{let indexA=CHAT_CHANNEL_ORDER.indexOf(a.id),indexB=CHAT_CHANNEL_ORDER.indexOf(b.id),orderA=a.order??(indexA===-1?999:indexA),orderB=b.order??(indexB===-1?999:indexB);return orderA===orderB?String(a.id).localeCompare(String(b.id)):orderA-orderB}),combined.map(entry=>entry.dock)}function getChannelDock(id){let core=DOCKS[id];if(core)return core;let pluginEntry=requireActivePluginRegistry().channels.find(entry=>entry.plugin.id===id);if(pluginEntry)return pluginEntry.dock??buildDockFromPlugin(pluginEntry.plugin)}const mergeOrigin=(existing,next)=>{if(!existing&&!next)return;let merged=existing?{...existing}:{};return next?.label&&(merged.label=next.label),next?.provider&&(merged.provider=next.provider),next?.surface&&(merged.surface=next.surface),next?.chatType&&(merged.chatType=next.chatType),next?.from&&(merged.from=next.from),next?.to&&(merged.to=next.to),next?.accountId&&(merged.accountId=next.accountId),next?.threadId!=null&&next.threadId!==``&&(merged.threadId=next.threadId),Object.keys(merged).length>0?merged:void 0};function deriveSessionOrigin(ctx){let label=resolveConversationLabel(ctx)?.trim(),provider=normalizeMessageChannel(typeof ctx.OriginatingChannel==`string`&&ctx.OriginatingChannel||ctx.Surface||ctx.Provider),surface=ctx.Surface?.trim().toLowerCase(),chatType=normalizeChatType(ctx.ChatType)??void 0,from=ctx.From?.trim(),to=(typeof ctx.OriginatingTo==`string`?ctx.OriginatingTo:ctx.To)?.trim()??void 0,accountId=ctx.AccountId?.trim(),threadId=ctx.MessageThreadId??void 0,origin={};return label&&(origin.label=label),provider&&(origin.provider=provider),surface&&(origin.surface=surface),chatType&&(origin.chatType=chatType),from&&(origin.from=from),to&&(origin.to=to),accountId&&(origin.accountId=accountId),threadId!=null&&threadId!==``&&(origin.threadId=threadId),Object.keys(origin).length>0?origin:void 0}function deriveGroupSessionPatch(params){let resolution=params.groupResolution??resolveGroupSessionKey(params.ctx);if(!resolution?.channel)return null;let channel=resolution.channel,subject=params.ctx.GroupSubject?.trim(),space=params.ctx.GroupSpace?.trim(),explicitChannel=params.ctx.GroupChannel?.trim(),normalizedChannel=normalizeChannelId(channel),isChannelProvider=!!(normalizedChannel&&getChannelDock(normalizedChannel)?.capabilities.chatTypes.includes(`channel`)),nextGroupChannel=explicitChannel??((resolution.chatType===`channel`||isChannelProvider)&&subject&&subject.startsWith(`#`)?subject:void 0),nextSubject=nextGroupChannel?void 0:subject,patch={chatType:resolution.chatType??`group`,channel,groupId:resolution.id};nextSubject&&(patch.subject=nextSubject),nextGroupChannel&&(patch.groupChannel=nextGroupChannel),space&&(patch.space=space);let displayName=buildGroupDisplayName({provider:channel,subject:nextSubject??params.existing?.subject,groupChannel:nextGroupChannel??params.existing?.groupChannel,space:space??params.existing?.space,id:resolution.id,key:params.sessionKey});return displayName&&(patch.displayName=displayName),patch}function deriveSessionMetaPatch(params){let groupPatch=deriveGroupSessionPatch(params),origin=deriveSessionOrigin(params.ctx);if(!groupPatch&&!origin)return null;let patch=groupPatch?{...groupPatch}:{},mergedOrigin=mergeOrigin(params.existing?.origin,origin);return mergedOrigin&&(patch.origin=mergedOrigin),Object.keys(patch).length>0?patch:null}function resolveMainSessionKey(cfg){if(cfg?.session?.scope===`global`)return`global`;let agents=cfg?.agents?.list??[];return buildAgentMainSessionKey({agentId:normalizeAgentId(agents.find(agent=>agent?.default)?.id??agents[0]?.id??DEFAULT_AGENT_ID),mainKey:normalizeMainKey(cfg?.session?.mainKey)})}function resolveAgentMainSessionKey(params){let mainKey=normalizeMainKey(params.cfg?.session?.mainKey);return buildAgentMainSessionKey({agentId:params.agentId,mainKey})}function resolveExplicitAgentSessionKey(params){let agentId=params.agentId?.trim();if(agentId)return resolveAgentMainSessionKey({cfg:params.cfg,agentId})}function canonicalizeMainSessionAlias(params){let raw=params.sessionKey.trim();if(!raw)return raw;let agentId=normalizeAgentId(params.agentId),mainKey=normalizeMainKey(params.cfg?.session?.mainKey),agentMainSessionKey=buildAgentMainSessionKey({agentId,mainKey}),agentMainAliasKey=buildAgentMainSessionKey({agentId,mainKey:`main`}),isMainAlias=raw===`main`||raw===mainKey||raw===agentMainSessionKey||raw===agentMainAliasKey;return params.cfg?.session?.scope===`global`&&isMainAlias?`global`:isMainAlias?agentMainSessionKey:raw}function mergeSessionEntry(existing,patch){let sessionId=patch.sessionId??existing?.sessionId??crypto.randomUUID(),updatedAt=Math.max(existing?.updatedAt??0,patch.updatedAt??0,Date.now());return existing?{...existing,...patch,sessionId,updatedAt}:{...patch,sessionId,updatedAt}}function resolveFreshSessionTotalTokens(entry){let total=entry?.totalTokens;if(!(typeof total!=`number`||!Number.isFinite(total)||total<0)&&entry?.totalTokensFresh!==!1)return total}const DEFAULT_RESET_TRIGGERS=[`/new`,`/reset`],THREAD_SESSION_MARKERS=[`:thread:`,`:topic:`],GROUP_SESSION_MARKERS=[`:group:`,`:channel:`];function isThreadSessionKey(sessionKey){let normalized=(sessionKey??``).toLowerCase();return normalized?THREAD_SESSION_MARKERS.some(marker=>normalized.includes(marker)):!1}function resolveSessionResetType(params){if(params.isThread||isThreadSessionKey(params.sessionKey))return`thread`;if(params.isGroup)return`group`;let normalized=(params.sessionKey??``).toLowerCase();return GROUP_SESSION_MARKERS.some(marker=>normalized.includes(marker))?`group`:`direct`}function resolveThreadFlag(params){return params.messageThreadId!=null||params.threadLabel?.trim()||params.threadStarterBody?.trim()||params.parentSessionKey?.trim()?!0:isThreadSessionKey(params.sessionKey)}function resolveDailyResetAtMs(now,atHour){let normalizedAtHour=normalizeResetAtHour(atHour),resetAt=new Date(now);return resetAt.setHours(normalizedAtHour,0,0,0),now<resetAt.getTime()&&resetAt.setDate(resetAt.getDate()-1),resetAt.getTime()}function resolveSessionResetPolicy(params){let sessionCfg=params.sessionCfg,baseReset=params.resetOverride??sessionCfg?.reset,typeReset=params.resetOverride?void 0:sessionCfg?.resetByType?.[params.resetType]??(params.resetType===`direct`?sessionCfg?.resetByType?.dm:void 0),hasExplicitReset=!!(baseReset||sessionCfg?.resetByType),legacyIdleMinutes=params.resetOverride?void 0:sessionCfg?.idleMinutes,mode=typeReset?.mode??baseReset?.mode??(!hasExplicitReset&&legacyIdleMinutes!=null?`idle`:`daily`),atHour=normalizeResetAtHour(typeReset?.atHour??baseReset?.atHour??4),idleMinutesRaw=typeReset?.idleMinutes??baseReset?.idleMinutes??legacyIdleMinutes,idleMinutes;if(idleMinutesRaw!=null){let normalized=Math.floor(idleMinutesRaw);Number.isFinite(normalized)&&(idleMinutes=Math.max(normalized,1))}else mode===`idle`&&(idleMinutes=60);return{mode,atHour,idleMinutes}}function resolveChannelResetConfig(params){let resetByChannel=params.sessionCfg?.resetByChannel;if(!resetByChannel)return;let normalized=normalizeMessageChannel(params.channel),fallback=params.channel?.trim().toLowerCase(),key=normalized??fallback;if(key)return resetByChannel[key]??resetByChannel[key.toLowerCase()]}function evaluateSessionFreshness(params){let dailyResetAt=params.policy.mode===`daily`?resolveDailyResetAtMs(params.now,params.policy.atHour):void 0,idleExpiresAt=params.policy.idleMinutes==null?void 0:params.updatedAt+params.policy.idleMinutes*6e4,staleDaily=dailyResetAt!=null&¶ms.updatedAt<dailyResetAt,staleIdle=idleExpiresAt!=null&¶ms.now>idleExpiresAt;return{fresh:!(staleDaily||staleIdle),dailyResetAt,idleExpiresAt}}function normalizeResetAtHour(value){if(typeof value!=`number`||!Number.isFinite(value))return 4;let normalized=Math.floor(value);return Number.isFinite(normalized)?normalized<0?0:normalized>23?23:normalized:4}function deriveSessionKey(scope,ctx){if(scope===`global`)return`global`;let resolvedGroup=resolveGroupSessionKey(ctx);return resolvedGroup?resolvedGroup.key:(ctx.From?normalizeE164(ctx.From):``)||`unknown`}function resolveSessionKey(scope,ctx,mainKey){let explicit=ctx.SessionKey?.trim();if(explicit)return explicit.toLowerCase();let raw=deriveSessionKey(scope,ctx);if(scope===`global`)return raw;let canonical=buildAgentMainSessionKey({agentId:DEFAULT_AGENT_ID,mainKey:normalizeMainKey(mainKey)});return raw.includes(`:group:`)||raw.includes(`:channel:`)?`agent:${DEFAULT_AGENT_ID}:${raw}`:canonical}const INPUT_PROVENANCE_KIND_VALUES=[`external_user`,`inter_session`,`internal_system`];function normalizeOptionalString(value){return typeof value==`string`&&value.trim()||void 0}function isInputProvenanceKind(value){return typeof value==`string`&&INPUT_PROVENANCE_KIND_VALUES.includes(value)}function normalizeInputProvenance(value){if(!value||typeof value!=`object`)return;let record=value;if(isInputProvenanceKind(record.kind))return{kind:record.kind,sourceSessionKey:normalizeOptionalString(record.sourceSessionKey),sourceChannel:normalizeOptionalString(record.sourceChannel),sourceTool:normalizeOptionalString(record.sourceTool)}}function applyInputProvenanceToUserMessage(message,inputProvenance){return!inputProvenance||message.role!==`user`||normalizeInputProvenance(message.provenance)?message:{...message,provenance:inputProvenance}}function isInterSessionInputProvenance(value){return normalizeInputProvenance(value)?.kind===`inter_session`}function hasInterSessionUserProvenance(message){return!message||message.role!==`user`?!1:isInterSessionInputProvenance(message.provenance)}const TOOL_CALL_TYPES$1=new Set([`tool_use`,`toolcall`,`tool_call`]),TOOL_RESULT_TYPES=new Set([`tool_result`,`tool_result_error`]),normalizeType=value=>typeof value==`string`?value.trim().toLowerCase():``,extractToolCallNames=message=>{let names=new Set,toolNameRaw=message.toolName??message.tool_name;typeof toolNameRaw==`string`&&toolNameRaw.trim()&&names.add(toolNameRaw.trim());let content=message.content;if(!Array.isArray(content))return Array.from(names);for(let entry of content){if(!entry||typeof entry!=`object`)continue;let block=entry,type=normalizeType(block.type);if(!TOOL_CALL_TYPES$1.has(type))continue;let name=block.name;typeof name==`string`&&name.trim()&&names.add(name.trim())}return Array.from(names)},countToolResults=message=>{let content=message.content;if(!Array.isArray(content))return{total:0,errors:0};let total=0,errors=0;for(let entry of content){if(!entry||typeof entry!=`object`)continue;let block=entry,type=normalizeType(block.type);TOOL_RESULT_TYPES.has(type)&&(total+=1,block.is_error===!0&&(errors+=1))}return{total,errors}};function resolveSessionTranscriptCandidates(sessionId,storePath,sessionFile,agentId){let candidates=[],pushCandidate=resolve=>{try{candidates.push(resolve())}catch{}};if(storePath){let sessionsDir=path.dirname(storePath);sessionFile&&pushCandidate(()=>resolveSessionFilePath(sessionId,{sessionFile},{sessionsDir,agentId})),pushCandidate(()=>resolveSessionTranscriptPathInDir(sessionId,sessionsDir))}else if(sessionFile)if(agentId)pushCandidate(()=>resolveSessionFilePath(sessionId,{sessionFile},{agentId}));else{let trimmed=sessionFile.trim();trimmed&&candidates.push(path.resolve(trimmed))}agentId&&pushCandidate(()=>resolveSessionTranscriptPath(sessionId,agentId));let home=resolveRequiredHomeDir(process.env,os.homedir),legacyDir=path.join(home,`.builderforce`,`sessions`);return pushCandidate(()=>resolveSessionTranscriptPathInDir(sessionId,legacyDir)),Array.from(new Set(candidates))}function archiveFileOnDisk(filePath,reason){let archived=`${filePath}.${reason}.${new Date().toISOString().replaceAll(`:`,`-`)}`;return fs$1.renameSync(filePath,archived),archived}function archiveSessionTranscripts(opts){let archived=[];for(let candidate of resolveSessionTranscriptCandidates(opts.sessionId,opts.storePath,opts.sessionFile,opts.agentId))if(fs$1.existsSync(candidate))try{archived.push(archiveFileOnDisk(candidate,opts.reason))}catch{}return archived}function restoreArchiveTimestamp(raw){let[datePart,timePart]=raw.split(`T`);return!datePart||!timePart?raw:`${datePart}T${timePart.replace(/-/g,`:`)}`}function parseArchivedTimestamp(fileName,reason){let marker=`.${reason}.`,index=fileName.lastIndexOf(marker);if(index<0)return null;let raw=fileName.slice(index+marker.length);if(!raw)return null;let timestamp=Date.parse(restoreArchiveTimestamp(raw));return Number.isNaN(timestamp)?null:timestamp}async function cleanupArchivedSessionTranscripts(opts){if(!Number.isFinite(opts.olderThanMs)||opts.olderThanMs<0)return{removed:0,scanned:0};let now=opts.nowMs??Date.now(),reason=opts.reason??`deleted`,directories=Array.from(new Set(opts.directories.map(dir=>path.resolve(dir)))),removed=0,scanned=0;for(let dir of directories){let entries=await fs$1.promises.readdir(dir).catch(()=>[]);for(let entry of entries){let timestamp=parseArchivedTimestamp(entry,reason);if(timestamp==null||(scanned+=1,now-timestamp<=opts.olderThanMs))continue;let fullPath=path.join(dir,entry);(await fs$1.promises.stat(fullPath).catch(()=>null))?.isFile()&&(await fs$1.promises.rm(fullPath).catch(()=>void 0),removed+=1)}}return{removed,scanned}}function jsonUtf8Bytes(value){try{return Buffer.byteLength(JSON.stringify(value),`utf8`)}catch{return Buffer.byteLength(String(value),`utf8`)}}function capArrayByJsonBytes(items,maxBytes){if(items.length===0)return{items,bytes:2};let parts=items.map(item=>jsonUtf8Bytes(item)),bytes=2+parts.reduce((a,b)=>a+b,0)+(items.length-1),start=0;for(;bytes>maxBytes&&start<items.length-1;)bytes-=parts[start]+1,start+=1;return{items:start>0?items.slice(start):items,bytes}}function normalizeAccountId(value){if(typeof value==`string`)return value.trim()||void 0}function normalizeDeliveryContext(context){if(!context)return;let channel=typeof context.channel==`string`?normalizeMessageChannel(context.channel)??context.channel.trim():void 0,to=typeof context.to==`string`?context.to.trim():void 0,accountId=normalizeAccountId(context.accountId),threadId=typeof context.threadId==`number`&&Number.isFinite(context.threadId)?Math.trunc(context.threadId):typeof context.threadId==`string`?context.threadId.trim():void 0,normalizedThreadId=typeof threadId==`string`?threadId||void 0:threadId;if(!channel&&!to&&!accountId&&normalizedThreadId==null)return;let normalized={channel:channel||void 0,to:to||void 0,accountId};return normalizedThreadId!=null&&(normalized.threadId=normalizedThreadId),normalized}function normalizeSessionDeliveryFields(source){if(!source)return{deliveryContext:void 0,lastChannel:void 0,lastTo:void 0,lastAccountId:void 0,lastThreadId:void 0};let merged=mergeDeliveryContext(normalizeDeliveryContext({channel:source.lastChannel??source.channel,to:source.lastTo,accountId:source.lastAccountId,threadId:source.lastThreadId}),normalizeDeliveryContext(source.deliveryContext));return merged?{deliveryContext:merged,lastChannel:merged.channel,lastTo:merged.to,lastAccountId:merged.accountId,lastThreadId:merged.threadId}:{deliveryContext:void 0,lastChannel:void 0,lastTo:void 0,lastAccountId:void 0,lastThreadId:void 0}}function deliveryContextFromSession(entry){if(entry)return normalizeSessionDeliveryFields({channel:entry.channel,lastChannel:entry.lastChannel,lastTo:entry.lastTo,lastAccountId:entry.lastAccountId,lastThreadId:entry.lastThreadId??entry.deliveryContext?.threadId??entry.origin?.threadId,deliveryContext:entry.deliveryContext}).deliveryContext}function mergeDeliveryContext(primary,fallback){let normalizedPrimary=normalizeDeliveryContext(primary),normalizedFallback=normalizeDeliveryContext(fallback);if(!(!normalizedPrimary&&!normalizedFallback))return normalizeDeliveryContext({channel:normalizedPrimary?.channel??normalizedFallback?.channel,to:normalizedPrimary?.to??normalizedFallback?.to,accountId:normalizedPrimary?.accountId??normalizedFallback?.accountId,threadId:normalizedPrimary?.threadId??normalizedFallback?.threadId})}function deliveryContextKey(context){let normalized=normalizeDeliveryContext(context);if(!normalized?.channel||!normalized?.to)return;let threadId=normalized.threadId!=null&&normalized.threadId!==``?String(normalized.threadId):``;return`${normalized.channel}|${normalized.to}|${normalized.accountId??``}|${threadId}`}function resolveCacheTtlMs(params){let{envValue,defaultTtlMs}=params;if(envValue){let parsed=Number.parseInt(envValue,10);if(Number.isFinite(parsed)&&parsed>=0)return parsed}return defaultTtlMs}function isCacheEnabled(ttlMs){return ttlMs>0}function getFileMtimeMs(filePath){try{return fs$1.statSync(filePath).mtimeMs}catch{return}}const log=createSubsystemLogger(`sessions/store`),SESSION_STORE_CACHE=new Map;function isSessionStoreRecord(value){return!!value&&typeof value==`object`&&!Array.isArray(value)}function getSessionStoreTtl(){return resolveCacheTtlMs({envValue:process.env.BUILDERFORCE_AGENTS_SESSION_CACHE_TTL_MS,defaultTtlMs:45e3})}function isSessionStoreCacheEnabled(){return isCacheEnabled(getSessionStoreTtl())}function isSessionStoreCacheValid(entry){let now=Date.now(),ttl=getSessionStoreTtl();return now-entry.loadedAt<=ttl}function invalidateSessionStoreCache(storePath){SESSION_STORE_CACHE.delete(storePath)}function normalizeSessionEntryDelivery(entry){let normalized=normalizeSessionDeliveryFields({channel:entry.channel,lastChannel:entry.lastChannel,lastTo:entry.lastTo,lastAccountId:entry.lastAccountId,lastThreadId:entry.lastThreadId??entry.deliveryContext?.threadId??entry.origin?.threadId,deliveryContext:entry.deliveryContext}),nextDelivery=normalized.deliveryContext,sameDelivery=(entry.deliveryContext?.channel??void 0)===nextDelivery?.channel&&(entry.deliveryContext?.to??void 0)===nextDelivery?.to&&(entry.deliveryContext?.accountId??void 0)===nextDelivery?.accountId&&(entry.deliveryContext?.threadId??void 0)===nextDelivery?.threadId,sameLast=entry.lastChannel===normalized.lastChannel&&entry.lastTo===normalized.lastTo&&entry.lastAccountId===normalized.lastAccountId&&entry.lastThreadId===normalized.lastThreadId;return sameDelivery&&sameLast?entry:{...entry,deliveryContext:nextDelivery,lastChannel:normalized.lastChannel,lastTo:normalized.lastTo,lastAccountId:normalized.lastAccountId,lastThreadId:normalized.lastThreadId}}function removeThreadFromDeliveryContext(context){if(!context||context.threadId==null)return context;let next={...context};return delete next.threadId,next}function normalizeSessionStore(store){for(let[key,entry]of Object.entries(store)){if(!entry)continue;let normalized=normalizeSessionEntryDelivery(entry);normalized!==entry&&(store[key]=normalized)}}function loadSessionStore(storePath,opts={}){if(!opts.skipCache&&isSessionStoreCacheEnabled()){let cached=SESSION_STORE_CACHE.get(storePath);if(cached&&isSessionStoreCacheValid(cached)){if(getFileMtimeMs(storePath)===cached.mtimeMs)return structuredClone(cached.store);invalidateSessionStoreCache(storePath)}}let store={},mtimeMs=getFileMtimeMs(storePath),maxReadAttempts=process.platform===`win32`?3:1,retryBuf=maxReadAttempts>1?new Int32Array(new SharedArrayBuffer(4)):void 0;for(let attempt=0;attempt<maxReadAttempts;attempt++)try{let raw=fs$1.readFileSync(storePath,`utf-8`);if(raw.length===0&&attempt<maxReadAttempts-1){Atomics.wait(retryBuf,0,0,50);continue}let parsed=JSON.parse(raw);isSessionStoreRecord(parsed)&&(store=parsed),mtimeMs=getFileMtimeMs(storePath)??mtimeMs;break}catch{if(attempt<maxReadAttempts-1){Atomics.wait(retryBuf,0,0,50);continue}}for(let entry of Object.values(store)){if(!entry||typeof entry!=`object`)continue;let rec=entry;typeof rec.channel!=`string`&&typeof rec.provider==`string`&&(rec.channel=rec.provider,delete rec.provider),typeof rec.lastChannel!=`string`&&typeof rec.lastProvider==`string`&&(rec.lastChannel=rec.lastProvider,delete rec.lastProvider),typeof rec.groupChannel!=`string`&&typeof rec.room==`string`?(rec.groupChannel=rec.room,delete rec.room):`room`in rec&&delete rec.room}return!opts.skipCache&&isSessionStoreCacheEnabled()&&SESSION_STORE_CACHE.set(storePath,{store:structuredClone(store),loadedAt:Date.now(),storePath,mtimeMs}),structuredClone(store)}function readSessionUpdatedAt(params){try{return loadSessionStore(params.storePath)[params.sessionKey]?.updatedAt}catch{return}}const DEFAULT_SESSION_PRUNE_AFTER_MS=720*60*60*1e3,DEFAULT_SESSION_ROTATE_BYTES=10485760;function resolvePruneAfterMs(maintenance){let raw=maintenance?.pruneAfter??maintenance?.pruneDays;if(raw==null||raw===``)return DEFAULT_SESSION_PRUNE_AFTER_MS;try{return parseDurationMs(String(raw).trim(),{defaultUnit:`d`})}catch{return DEFAULT_SESSION_PRUNE_AFTER_MS}}function resolveRotateBytes(maintenance){let raw=maintenance?.rotateBytes;if(raw==null||raw===``)return DEFAULT_SESSION_ROTATE_BYTES;try{return parseByteSize(String(raw).trim(),{defaultUnit:`b`})}catch{return DEFAULT_SESSION_ROTATE_BYTES}}function resolveMaintenanceConfig(){let maintenance;try{maintenance=loadConfig().session?.maintenance}catch{}return{mode:maintenance?.mode??`warn`,pruneAfterMs:resolvePruneAfterMs(maintenance),maxEntries:maintenance?.maxEntries??500,rotateBytes:resolveRotateBytes(maintenance)}}function pruneStaleEntries(store,overrideMaxAgeMs,opts={}){let maxAgeMs=overrideMaxAgeMs??resolveMaintenanceConfig().pruneAfterMs,cutoffMs=Date.now()-maxAgeMs,pruned=0;for(let[key,entry]of Object.entries(store))entry?.updatedAt!=null&&entry.updatedAt<cutoffMs&&(opts.onPruned?.({key,entry}),delete store[key],pruned++);return pruned>0&&opts.log!==!1&&log.info(`pruned stale session entries`,{pruned,maxAgeMs}),pruned}function getEntryUpdatedAt(entry){return entry?.updatedAt??-1/0}function getActiveSessionMaintenanceWarning(params){let activeSessionKey=params.activeSessionKey.trim();if(!activeSessionKey)return null;let activeEntry=params.store[activeSessionKey];if(!activeEntry)return null;let cutoffMs=(params.nowMs??Date.now())-params.pruneAfterMs,wouldPrune=activeEntry.updatedAt==null?!1:activeEntry.updatedAt<cutoffMs,keys=Object.keys(params.store),wouldCap=keys.length>params.maxEntries&&keys.toSorted((a,b)=>getEntryUpdatedAt(params.store[b])-getEntryUpdatedAt(params.store[a])).slice(params.maxEntries).includes(activeSessionKey);return!wouldPrune&&!wouldCap?null:{activeSessionKey,activeUpdatedAt:activeEntry.updatedAt,totalEntries:keys.length,pruneAfterMs:params.pruneAfterMs,maxEntries:params.maxEntries,wouldPrune,wouldCap}}function capEntryCount(store,overrideMax,opts={}){let maxEntries=overrideMax??resolveMaintenanceConfig().maxEntries,keys=Object.keys(store);if(keys.length<=maxEntries)return 0;let toRemove=keys.toSorted((a,b)=>{let aTime=getEntryUpdatedAt(store[a]);return getEntryUpdatedAt(store[b])-aTime}).slice(maxEntries);for(let key of toRemove)delete store[key];return opts.log!==!1&&log.info(`capped session entry count`,{removed:toRemove.length,maxEntries}),toRemove.length}async function getSessionFileSize(storePath){try{return(await fs$1.promises.stat(storePath)).size}catch{return null}}async function rotateSessionFile(storePath,overrideBytes){let maxBytes=overrideBytes??resolveMaintenanceConfig().rotateBytes,fileSize=await getSessionFileSize(storePath);if(fileSize==null||fileSize<=maxBytes)return!1;let backupPath=`${storePath}.bak.${Date.now()}`;try{await fs$1.promises.rename(storePath,backupPath),log.info(`rotated session store file`,{backupPath:path.basename(backupPath),sizeBytes:fileSize})}catch{return!1}try{let dir=path.dirname(storePath),baseName=path.basename(storePath),backups=(await fs$1.promises.readdir(dir)).filter(f=>f.startsWith(`${baseName}.bak.`)).toSorted().toReversed();if(backups.length>3){let toDelete=backups.slice(3);for(let old of toDelete)await fs$1.promises.unlink(path.join(dir,old)).catch(()=>void 0);log.info(`cleaned up old session store backups`,{deleted:toDelete.length})}}catch{}return!0}async function saveSessionStoreUnlocked(storePath,store,opts){if(invalidateSessionStoreCache(storePath),normalizeSessionStore(store),!opts?.skipMaintenance){let maintenance=resolveMaintenanceConfig();if(maintenance.mode===`warn`){let activeSessionKey=opts?.activeSessionKey?.trim();if(activeSessionKey){let warning=getActiveSessionMaintenanceWarning({store,activeSessionKey,pruneAfterMs:maintenance.pruneAfterMs,maxEntries:maintenance.maxEntries});warning&&(log.warn(`session maintenance would evict active session; skipping enforcement`,{activeSessionKey:warning.activeSessionKey,wouldPrune:warning.wouldPrune,wouldCap:warning.wouldCap,pruneAfterMs:warning.pruneAfterMs,maxEntries:warning.maxEntries}),await opts?.onWarn?.(warning))}}else{let prunedSessionFiles=new Map;pruneStaleEntries(store,maintenance.pruneAfterMs,{onPruned:({entry})=>{(!prunedSessionFiles.has(entry.sessionId)||entry.sessionFile)&&prunedSessionFiles.set(entry.sessionId,entry.sessionFile)}}),capEntryCount(store,maintenance.maxEntries);let archivedDirs=new Set;for(let[sessionId,sessionFile]of prunedSessionFiles){let archived=archiveSessionTranscripts({sessionId,storePath,sessionFile,reason:`deleted`});for(let archivedPath of archived)archivedDirs.add(path.dirname(archivedPath))}archivedDirs.size>0&&await cleanupArchivedSessionTranscripts({directories:[...archivedDirs],olderThanMs:maintenance.pruneAfterMs,reason:`deleted`}),await rotateSessionFile(storePath,maintenance.rotateBytes)}}await fs$1.promises.mkdir(path.dirname(storePath),{recursive:!0});let json=JSON.stringify(store,null,2);if(process.platform===`win32`){let tmp=`${storePath}.${process.pid}.${crypto.randomUUID()}.tmp`;try{await fs$1.promises.writeFile(tmp,json,`utf-8`);for(let i=0;i<5;i++)try{await fs$1.promises.rename(tmp,storePath);break}catch{i<4&&await new Promise(r=>setTimeout(r,50*(i+1))),i===4&&console.warn(`[session-store] rename failed after 5 attempts: ${storePath}`)}}catch(err){if((err&&typeof err==`object`&&`code`in err?String(err.code):null)===`ENOENT`)return;throw err}finally{await fs$1.promises.rm(tmp,{force:!0}).catch(()=>void 0)}return}let tmp=`${storePath}.${process.pid}.${crypto.randomUUID()}.tmp`;try{await fs$1.promises.writeFile(tmp,json,{mode:384,encoding:`utf-8`}),await fs$1.promises.rename(tmp,storePath),await fs$1.promises.chmod(storePath,384)}catch(err){if((err&&typeof err==`object`&&`code`in err?String(err.code):null)===`ENOENT`){try{await fs$1.promises.mkdir(path.dirname(storePath),{recursive:!0}),await fs$1.promises.writeFile(storePath,json,{mode:384,encoding:`utf-8`}),await fs$1.promises.chmod(storePath,384)}catch(err2){if((err2&&typeof err2==`object`&&`code`in err2?String(err2.code):null)===`ENOENT`)return;throw err2}return}throw err}finally{await fs$1.promises.rm(tmp,{force:!0})}}async function updateSessionStore(storePath,mutator,opts){return await withSessionStoreLock(storePath,async()=>{let store=loadSessionStore(storePath,{skipCache:!0}),result=await mutator(store);return await saveSessionStoreUnlocked(storePath,store,opts),result})}const LOCK_QUEUES=new Map;function lockTimeoutError(storePath){return Error(`timeout waiting for session store lock: ${storePath}`)}function getOrCreateLockQueue(storePath){let existing=LOCK_QUEUES.get(storePath);if(existing)return existing;let created={running:!1,pending:[]};return LOCK_QUEUES.set(storePath,created),created}async function drainSessionStoreLockQueue(storePath){let queue=LOCK_QUEUES.get(storePath);if(!(!queue||queue.running)){queue.running=!0;try{for(;queue.pending.length>0;){let task=queue.pending.shift();if(!task)continue;let remainingTimeoutMs=task.timeoutMs??1/0;if(task.timeoutMs!=null&&remainingTimeoutMs<=0){task.reject(lockTimeoutError(storePath));continue}let lock,result,failed,hasFailure=!1;try{lock=await acquireSessionWriteLock({sessionFile:storePath,timeoutMs:remainingTimeoutMs,staleMs:task.staleMs}),result=await task.fn()}catch(err){hasFailure=!0,failed=err}finally{await lock?.release().catch(()=>void 0)}if(hasFailure){task.reject(failed);continue}task.resolve(result)}}finally{queue.running=!1,queue.pending.length===0?LOCK_QUEUES.delete(storePath):queueMicrotask(()=>{drainSessionStoreLockQueue(storePath)})}}}async function withSessionStoreLock(storePath,fn,opts={}){if(!storePath||typeof storePath!=`string`)throw Error(`withSessionStoreLock: storePath must be a non-empty string, got ${JSON.stringify(storePath)}`);let timeoutMs=opts.timeoutMs??1e4,staleMs=opts.staleMs??3e4;opts.pollIntervalMs;let hasTimeout=timeoutMs>0&&Number.isFinite(timeoutMs),queue=getOrCreateLockQueue(storePath);return await new Promise((resolve,reject)=>{let task={fn:async()=>await fn(),resolve:value=>resolve(value),reject,timeoutMs:hasTimeout?timeoutMs:void 0,staleMs};queue.pending.push(task),drainSessionStoreLockQueue(storePath)})}async function updateSessionStoreEntry(params){let{storePath,sessionKey,update}=params;return await withSessionStoreLock(storePath,async()=>{let store=loadSessionStore(storePath),existing=store[sessionKey];if(!existing)return null;let patch=await update(existing);if(!patch)return existing;let next=mergeSessionEntry(existing,patch);return store[sessionKey]=next,await saveSessionStoreUnlocked(storePath,store,{activeSessionKey:sessionKey}),next})}async function recordSessionMetaFromInbound(params){let{storePath,sessionKey,ctx}=params,createIfMissing=params.createIfMissing??!0;return await updateSessionStore(storePath,store=>{let existing=store[sessionKey],patch=deriveSessionMetaPatch({ctx,sessionKey,existing,groupResolution:params.groupResolution});if(!patch)return existing??null;if(!existing&&!createIfMissing)return null;let next=mergeSessionEntry(existing,patch);return store[sessionKey]=next,next},{activeSessionKey:sessionKey})}async function updateLastRoute(params){let{storePath,sessionKey,channel,to,accountId,threadId,ctx}=params;return await withSessionStoreLock(storePath,async()=>{let store=loadSessionStore(storePath),existing=store[sessionKey],now=Date.now(),explicitContext=normalizeDeliveryContext(params.deliveryContext),inlineContext=normalizeDeliveryContext({channel,to,accountId,threadId}),mergedInput=mergeDeliveryContext(explicitContext,inlineContext),explicitDeliveryContext=params.deliveryContext,explicitThreadValue=(explicitDeliveryContext!=null&&Object.prototype.hasOwnProperty.call(explicitDeliveryContext,`threadId`)?explicitDeliveryContext.threadId:void 0)??(threadId!=null&&threadId!==``?threadId:void 0),merged=mergeDeliveryContext(mergedInput,(explicitContext?.channel||explicitContext?.to||inlineContext?.channel||inlineContext?.to)&&explicitThreadValue==null?removeThreadFromDeliveryContext(deliveryContextFromSession(existing)):deliveryContextFromSession(existing)),normalized=normalizeSessionDeliveryFields({deliveryContext:{channel:merged?.channel,to:merged?.to,accountId:merged?.accountId,threadId:merged?.threadId}}),metaPatch=ctx?deriveSessionMetaPatch({ctx,sessionKey,existing,groupResolution:params.groupResolution}):null,basePatch={updatedAt:Math.max(existing?.updatedAt??0,now),deliveryContext:normalized.deliveryContext,lastChannel:normalized.lastChannel,lastTo:normalized.lastTo,lastAccountId:normalized.lastAccountId,lastThreadId:normalized.lastThreadId},next=mergeSessionEntry(existing,metaPatch?{...basePatch,...metaPatch}:basePatch);return store[sessionKey]=next,await saveSessionStoreUnlocked(storePath,store,{activeSessionKey:sessionKey}),next})}function stripQuery(value){let noHash=value.split(`#`)[0]??value;return noHash.split(`?`)[0]??noHash}function extractFileNameFromMediaUrl(value){let trimmed=value.trim();if(!trimmed)return null;let cleaned=stripQuery(trimmed);try{let parsed=new URL(cleaned),base=path.basename(parsed.pathname);if(!base)return null;try{return decodeURIComponent(base)}catch{return base}}catch{let base=path.basename(cleaned);return!base||base===`/`||base===`.`?null:base}}function resolveMirroredTranscriptText(params){let mediaUrls=params.mediaUrls?.filter(url=>url&&url.trim())??[];if(mediaUrls.length>0){let names=mediaUrls.map(url=>extractFileNameFromMediaUrl(url)).filter(name=>!!(name&&name.trim()));return names.length>0?names.join(`, `):`media`}return(params.text??``).trim()||null}async function ensureSessionHeader(params){if(fs$1.existsSync(params.sessionFile))return;await fs$1.promises.mkdir(path.dirname(params.sessionFile),{recursive:!0});let header={type:`session`,version:CURRENT_SESSION_VERSION,id:params.sessionId,timestamp:new Date().toISOString(),cwd:process.cwd()};await fs$1.promises.writeFile(params.sessionFile,`${JSON.stringify(header)}\n`,{encoding:`utf-8`,mode:384})}async function appendAssistantMessageToSessionTranscript(params){let sessionKey=params.sessionKey.trim();if(!sessionKey)return{ok:!1,reason:`missing sessionKey`};let mirrorText=resolveMirroredTranscriptText({text:params.text,mediaUrls:params.mediaUrls});if(!mirrorText)return{ok:!1,reason:`empty text`};let storePath=params.storePath??resolveDefaultSessionStorePath(params.agentId),entry=loadSessionStore(storePath,{skipCache:!0})[sessionKey];if(!entry?.sessionId)return{ok:!1,reason:`unknown sessionKey: ${sessionKey}`};let sessionFile;try{sessionFile=resolveSessionFilePath(entry.sessionId,entry,{agentId:params.agentId,sessionsDir:path.dirname(storePath)})}catch(err){return{ok:!1,reason:err instanceof Error?err.message:String(err)}}return await ensureSessionHeader({sessionFile,sessionId:entry.sessionId}),SessionManager.open(sessionFile).appendMessage({role:`assistant`,content:[{type:`text`,text:mirrorText}],api:`openai-responses`,provider:`builderforce`,model:`delivery-mirror`,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:`stop`,timestamp:Date.now()}),(!entry.sessionFile||entry.sessionFile!==sessionFile)&&await updateSessionStore(storePath,current=>{current[sessionKey]={...entry,sessionFile}},{activeSessionKey:sessionKey}),emitSessionTranscriptUpdate(sessionFile),{ok:!0,sessionFile}}function parseSessionThreadInfo(sessionKey){if(!sessionKey)return{baseSessionKey:void 0,threadId:void 0};let topicIndex=sessionKey.lastIndexOf(`:topic:`),threadIndex=sessionKey.lastIndexOf(`:thread:`),markerIndex=Math.max(topicIndex,threadIndex),marker=topicIndex>threadIndex?`:topic:`:`:thread:`;return{baseSessionKey:markerIndex===-1?sessionKey:sessionKey.slice(0,markerIndex),threadId:(markerIndex===-1?void 0:sessionKey.slice(markerIndex+marker.length))?.trim()||void 0}}function extractDeliveryInfo(sessionKey){let{baseSessionKey,threadId}=parseSessionThreadInfo(sessionKey);if(!sessionKey||!baseSessionKey)return{deliveryContext:void 0,threadId};let deliveryContext;try{let store=loadSessionStore(resolveStorePath(loadConfig().session?.store)),entry=store[sessionKey];!entry?.deliveryContext&&baseSessionKey!==sessionKey&&(entry=store[baseSessionKey]),entry?.deliveryContext&&(deliveryContext={channel:entry.deliveryContext.channel,to:entry.deliveryContext.to,accountId:entry.deliveryContext.accountId})}catch{}return{deliveryContext,threadId}}function shouldSandboxSession(cfg,sessionKey,mainSessionKey){return cfg.mode===`off`?!1:cfg.mode===`all`?!0:sessionKey.trim()!==mainSessionKey.trim()}function resolveMainSessionKeyForSandbox(params){return params.cfg?.session?.scope===`global`?`global`:resolveAgentMainSessionKey({cfg:params.cfg,agentId:params.agentId})}function resolveComparableSessionKeyForSandbox(params){return canonicalizeMainSessionAlias({cfg:params.cfg,agentId:params.agentId,sessionKey:params.sessionKey})}function resolveSandboxRuntimeStatus(params){let sessionKey=params.sessionKey?.trim()??``,agentId=resolveSessionAgentId({sessionKey,config:params.cfg}),cfg=params.cfg,sandboxCfg=resolveSandboxConfigForAgent(cfg,agentId),mainSessionKey=resolveMainSessionKeyForSandbox({cfg,agentId}),sandboxed=sessionKey?shouldSandboxSession(sandboxCfg,resolveComparableSessionKeyForSandbox({cfg,agentId,sessionKey}),mainSessionKey):!1;return{agentId,sessionKey,mainSessionKey,mode:sandboxCfg.mode,sandboxed,toolPolicy:resolveSandboxToolPolicyForAgent(cfg,agentId)}}function formatSandboxToolPolicyBlockedMessage(params){let tool=params.toolName.trim().toLowerCase();if(!tool)return;let runtime=resolveSandboxRuntimeStatus({cfg:params.cfg,sessionKey:params.sessionKey});if(!runtime.sandboxed)return;let deny=new Set(expandToolGroups(runtime.toolPolicy.deny)),allow=expandToolGroups(runtime.toolPolicy.allow),allowSet=allow.length>0?new Set(allow):null,blockedByDeny=deny.has(tool),blockedByAllow=allowSet?!allowSet.has(tool):!1;if(!blockedByDeny&&!blockedByAllow)return;let reasons=[],fixes=[];blockedByDeny&&(reasons.push(`deny list`),fixes.push(`Remove "${tool}" from ${runtime.toolPolicy.sources.deny.key}.`)),blockedByAllow&&(reasons.push(`allow list`),fixes.push(`Add "${tool}" to ${runtime.toolPolicy.sources.allow.key} (or set it to [] to allow all).`));let lines=[];lines.push(`Tool "${tool}" blocked by sandbox tool policy (mode=${runtime.mode}).`),lines.push(`Session: ${runtime.sessionKey||`(unknown)`}`),lines.push(`Reason: ${reasons.join(` + `)}`),lines.push(`Fix:`),lines.push(`- agents.defaults.sandbox.mode=off (disable sandbox)`);for(let fix of fixes)lines.push(`- ${fix}`);return runtime.mode===`non-main`&&lines.push(`- Use main session key (direct): ${runtime.mainSessionKey}`),lines.push(`- See: ${formatCliCommand(`builderforce sandbox explain --session ${runtime.sessionKey}`)}`),lines.join(`
|
|
12
|
+
`));let fn=toStringOrEmpty(body.fn);if(!fn)return jsonError(res,400,`fn is required`);let ref=toStringOrEmpty(body.ref)||void 0,evalTimeoutMs=toNumber(body.timeoutMs),evalRequest={cdpUrl,targetId:tab.targetId,fn,ref,signal:req.signal};evalTimeoutMs!==void 0&&(evalRequest.timeoutMs=evalTimeoutMs);let result=await pw.evaluateViaPlaywright(evalRequest);return res.json({ok:!0,targetId:tab.targetId,url:tab.url,result})}case`close`:return await pw.closePageViaPlaywright({cdpUrl,targetId:tab.targetId}),res.json({ok:!0,targetId:tab.targetId});default:return jsonError(res,400,`unsupported kind`)}}})}),app.post(`/hooks/file-chooser`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),ref=toStringOrEmpty(body.ref)||void 0,inputRef=toStringOrEmpty(body.inputRef)||void 0,element=toStringOrEmpty(body.element)||void 0,paths=toStringArray(body.paths)??[],timeoutMs=toNumber(body.timeoutMs);if(!paths.length)return jsonError(res,400,`paths are required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`file chooser hook`,run:async({cdpUrl,tab,pw})=>{let uploadPathsResult=resolvePathsWithinRoot({rootDir:DEFAULT_UPLOAD_DIR,requestedPaths:paths,scopeLabel:`uploads directory (${DEFAULT_UPLOAD_DIR})`});if(!uploadPathsResult.ok){res.status(400).json({error:uploadPathsResult.error});return}let resolvedPaths=uploadPathsResult.paths;if(inputRef||element){if(ref)return jsonError(res,400,`ref cannot be combined with inputRef/element`);await pw.setInputFilesViaPlaywright({cdpUrl,targetId:tab.targetId,inputRef,element,paths:resolvedPaths})}else await pw.armFileUploadViaPlaywright({cdpUrl,targetId:tab.targetId,paths:resolvedPaths,timeoutMs:timeoutMs??void 0}),ref&&await pw.clickViaPlaywright({cdpUrl,targetId:tab.targetId,ref});res.json({ok:!0})}})}),app.post(`/hooks/dialog`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),accept=toBoolean(body.accept),promptText=toStringOrEmpty(body.promptText)||void 0,timeoutMs=toNumber(body.timeoutMs);if(accept===void 0)return jsonError(res,400,`accept is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`dialog hook`,run:async({cdpUrl,tab,pw})=>{await pw.armDialogViaPlaywright({cdpUrl,targetId:tab.targetId,accept,promptText,timeoutMs:timeoutMs??void 0}),res.json({ok:!0})}})}),app.post(`/wait/download`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),out=toStringOrEmpty(body.path)||``,timeoutMs=toNumber(body.timeoutMs);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`wait for download`,run:async({cdpUrl,tab,pw})=>{let downloadPath;if(out.trim()){let resolvedDownloadPath=resolveDownloadPathOrRespond(res,out);if(!resolvedDownloadPath)return;downloadPath=resolvedDownloadPath}let requestBase=buildDownloadRequestBase(cdpUrl,tab.targetId,timeoutMs),result=await pw.waitForDownloadViaPlaywright({...requestBase,path:downloadPath});respondWithDownloadResult(res,tab.targetId,result)}})}),app.post(`/download`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),ref=toStringOrEmpty(body.ref),out=toStringOrEmpty(body.path),timeoutMs=toNumber(body.timeoutMs);if(!ref)return jsonError(res,400,`ref is required`);if(!out)return jsonError(res,400,`path is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`download`,run:async({cdpUrl,tab,pw})=>{let downloadPath=resolveDownloadPathOrRespond(res,out);if(!downloadPath)return;let requestBase=buildDownloadRequestBase(cdpUrl,tab.targetId,timeoutMs),result=await pw.downloadViaPlaywright({...requestBase,ref,path:downloadPath});respondWithDownloadResult(res,tab.targetId,result)}})}),app.post(`/response/body`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),url=toStringOrEmpty(body.url),timeoutMs=toNumber(body.timeoutMs),maxChars=toNumber(body.maxChars);if(!url)return jsonError(res,400,`url is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`response body`,run:async({cdpUrl,tab,pw})=>{let result=await pw.responseBodyViaPlaywright({cdpUrl,targetId:tab.targetId,url,timeoutMs:timeoutMs??void 0,maxChars:maxChars??void 0});res.json({ok:!0,targetId:tab.targetId,response:result})}})}),app.post(`/highlight`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),ref=toStringOrEmpty(body.ref);if(!ref)return jsonError(res,400,`ref is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`highlight`,run:async({cdpUrl,tab,pw})=>{await pw.highlightViaPlaywright({cdpUrl,targetId:tab.targetId,ref}),res.json({ok:!0,targetId:tab.targetId})}})})}function registerBrowserAgentDebugRoutes(app,ctx){app.get(`/console`,async(req,res)=>{let targetId=resolveTargetIdFromQuery(req.query),level=typeof req.query.level==`string`?req.query.level:``;await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`console messages`,run:async({cdpUrl,tab,pw})=>{let messages=await pw.getConsoleMessagesViaPlaywright({cdpUrl,targetId:tab.targetId,level:level.trim()||void 0});res.json({ok:!0,messages,targetId:tab.targetId})}})}),app.get(`/errors`,async(req,res)=>{let targetId=resolveTargetIdFromQuery(req.query),clear=toBoolean(req.query.clear)??!1;await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`page errors`,run:async({cdpUrl,tab,pw})=>{let result=await pw.getPageErrorsViaPlaywright({cdpUrl,targetId:tab.targetId,clear});res.json({ok:!0,targetId:tab.targetId,...result})}})}),app.get(`/requests`,async(req,res)=>{let targetId=resolveTargetIdFromQuery(req.query),filter=typeof req.query.filter==`string`?req.query.filter:``,clear=toBoolean(req.query.clear)??!1;await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`network requests`,run:async({cdpUrl,tab,pw})=>{let result=await pw.getNetworkRequestsViaPlaywright({cdpUrl,targetId:tab.targetId,filter:filter.trim()||void 0,clear});res.json({ok:!0,targetId:tab.targetId,...result})}})}),app.post(`/trace/start`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),screenshots=toBoolean(body.screenshots)??void 0,snapshots=toBoolean(body.snapshots)??void 0,sources=toBoolean(body.sources)??void 0;await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`trace start`,run:async({cdpUrl,tab,pw})=>{await pw.traceStartViaPlaywright({cdpUrl,targetId:tab.targetId,screenshots,snapshots,sources}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/trace/stop`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),out=toStringOrEmpty(body.path)||``;await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`trace stop`,run:async({cdpUrl,tab,pw})=>{let id=crypto.randomUUID(),dir=DEFAULT_TRACE_DIR;await fs.mkdir(dir,{recursive:!0});let tracePathResult=resolvePathWithinRoot({rootDir:dir,requestedPath:out,scopeLabel:`trace directory`,defaultFileName:`browser-trace-${id}.zip`});if(!tracePathResult.ok){res.status(400).json({error:tracePathResult.error});return}let tracePath=tracePathResult.path;await pw.traceStopViaPlaywright({cdpUrl,targetId:tab.targetId,path:tracePath}),res.json({ok:!0,targetId:tab.targetId,path:path.resolve(tracePath)})}})})}const DEFAULT_BROWSER_SCREENSHOT_MAX_SIDE=2e3,DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES=5*1024*1024;async function normalizeBrowserScreenshot(buffer,opts){let maxSide=Math.max(1,Math.round(opts?.maxSide??DEFAULT_BROWSER_SCREENSHOT_MAX_SIDE)),maxBytes=Math.max(1,Math.round(opts?.maxBytes??DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES)),meta=await getImageMetadata(buffer),width=Number(meta?.width??0),height=Number(meta?.height??0),maxDim=Math.max(width,height);if(buffer.byteLength<=maxBytes&&(maxDim===0||width<=maxSide&&height<=maxSide))return{buffer};let sideGrid=buildImageResizeSideGrid(maxSide,maxDim>0?Math.min(maxSide,maxDim):maxSide),smallest=null;for(let side of sideGrid)for(let quality of IMAGE_REDUCE_QUALITY_STEPS){let out=await resizeToJpeg({buffer,maxSide:side,quality,withoutEnlargement:!0});if((!smallest||out.byteLength<smallest.size)&&(smallest={buffer:out,size:out.byteLength}),out.byteLength<=maxBytes)return{buffer:out,contentType:`image/jpeg`}}let best=smallest?.buffer??buffer;throw Error(`Browser screenshot could not be reduced below ${(maxBytes/(1024*1024)).toFixed(0)}MB (got ${(best.byteLength/(1024*1024)).toFixed(2)}MB)`)}async function saveBrowserMediaResponse(params){await ensureMediaDir();let saved=await saveMediaBuffer(params.buffer,params.contentType,`browser`,params.maxBytes);params.res.json({ok:!0,path:path.resolve(saved.path),targetId:params.targetId,url:params.url})}function registerBrowserAgentSnapshotRoutes(app,ctx){app.post(`/navigate`,async(req,res)=>{let body=readBody(req),url=toStringOrEmpty(body.url),targetId=toStringOrEmpty(body.targetId)||void 0;if(!url)return jsonError(res,400,`url is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`navigate`,run:async({cdpUrl,tab,pw})=>{let result=await pw.navigateViaPlaywright({cdpUrl,targetId:tab.targetId,url});res.json({ok:!0,targetId:tab.targetId,...result})}})}),app.post(`/pdf`,async(req,res)=>{await withPlaywrightRouteContext({req,res,ctx,targetId:toStringOrEmpty(readBody(req).targetId)||void 0,feature:`pdf`,run:async({cdpUrl,tab,pw})=>{let pdf=await pw.pdfViaPlaywright({cdpUrl,targetId:tab.targetId});await saveBrowserMediaResponse({res,buffer:pdf.buffer,contentType:`application/pdf`,maxBytes:pdf.buffer.byteLength,targetId:tab.targetId,url:tab.url})}})}),app.post(`/screenshot`,async(req,res)=>{let body=readBody(req),targetId=toStringOrEmpty(body.targetId)||void 0,fullPage=toBoolean(body.fullPage)??!1,ref=toStringOrEmpty(body.ref)||void 0,element=toStringOrEmpty(body.element)||void 0,type=body.type===`jpeg`?`jpeg`:`png`;if(fullPage&&(ref||element))return jsonError(res,400,`fullPage is not supported for element screenshots`);await withRouteTabContext({req,res,ctx,targetId,run:async({profileCtx,tab,cdpUrl})=>{let buffer;if(profileCtx.profile.driver===`extension`||!tab.wsUrl||ref||element){let pw=await requirePwAi(res,`screenshot`);if(!pw)return;buffer=(await pw.takeScreenshotViaPlaywright({cdpUrl,targetId:tab.targetId,ref,element,fullPage,type})).buffer}else buffer=await captureScreenshot({wsUrl:tab.wsUrl??``,fullPage,format:type,quality:type===`jpeg`?85:void 0});let normalized=await normalizeBrowserScreenshot(buffer,{maxSide:DEFAULT_BROWSER_SCREENSHOT_MAX_SIDE,maxBytes:DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES});await saveBrowserMediaResponse({res,buffer:normalized.buffer,contentType:normalized.contentType??`image/${type}`,maxBytes:DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES,targetId:tab.targetId,url:tab.url})}})}),app.get(`/snapshot`,async(req,res)=>{let profileCtx=resolveProfileContext(req,res,ctx);if(!profileCtx)return;let targetId=typeof req.query.targetId==`string`?req.query.targetId.trim():``,mode=req.query.mode===`efficient`?`efficient`:void 0,labels=toBoolean(req.query.labels)??void 0,format=(req.query.format===`aria`?`aria`:req.query.format===`ai`?`ai`:void 0)??(mode||await getPwAiModule()?`ai`:`aria`),limitRaw=typeof req.query.limit==`string`?Number(req.query.limit):void 0,hasMaxChars=Object.hasOwn(req.query,`maxChars`),maxCharsRaw=typeof req.query.maxChars==`string`?Number(req.query.maxChars):void 0,limit=Number.isFinite(limitRaw)?limitRaw:void 0,resolvedMaxChars=format===`ai`?hasMaxChars?typeof maxCharsRaw==`number`&&Number.isFinite(maxCharsRaw)&&maxCharsRaw>0?Math.floor(maxCharsRaw):void 0:mode===`efficient`?DEFAULT_AI_SNAPSHOT_EFFICIENT_MAX_CHARS:DEFAULT_AI_SNAPSHOT_MAX_CHARS:void 0,interactiveRaw=toBoolean(req.query.interactive),compactRaw=toBoolean(req.query.compact),depthRaw=toNumber(req.query.depth),refsModeRaw=toStringOrEmpty(req.query.refs).trim(),refsMode=refsModeRaw===`aria`?`aria`:refsModeRaw===`role`?`role`:void 0,interactive=interactiveRaw??(mode===`efficient`?!0:void 0),compact=compactRaw??(mode===`efficient`?!0:void 0),depth=depthRaw??(mode===`efficient`?DEFAULT_AI_SNAPSHOT_EFFICIENT_DEPTH:void 0),selector=toStringOrEmpty(req.query.selector),frameSelector=toStringOrEmpty(req.query.frame),selectorValue=selector.trim()||void 0,frameSelectorValue=frameSelector.trim()||void 0;try{let tab=await profileCtx.ensureTabAvailable(targetId||void 0);if((labels||mode===`efficient`)&&format===`aria`)return jsonError(res,400,`labels/mode=efficient require format=ai`);if(format===`ai`){let pw=await requirePwAi(res,`ai snapshot`);if(!pw)return;let wantsRoleSnapshot=labels===!0||mode===`efficient`||interactive===!0||compact===!0||depth!==void 0||!!selectorValue||!!frameSelectorValue,roleSnapshotArgs={cdpUrl:profileCtx.profile.cdpUrl,targetId:tab.targetId,selector:selectorValue,frameSelector:frameSelectorValue,refsMode,options:{interactive:interactive??void 0,compact:compact??void 0,maxDepth:depth??void 0}},snap=wantsRoleSnapshot?await pw.snapshotRoleViaPlaywright(roleSnapshotArgs):await pw.snapshotAiViaPlaywright({cdpUrl:profileCtx.profile.cdpUrl,targetId:tab.targetId,...typeof resolvedMaxChars==`number`?{maxChars:resolvedMaxChars}:{}}).catch(async err=>{if(String(err).toLowerCase().includes(`_snapshotforai`))return await pw.snapshotRoleViaPlaywright(roleSnapshotArgs);throw err});if(labels){let labeled=await pw.screenshotWithLabelsViaPlaywright({cdpUrl:profileCtx.profile.cdpUrl,targetId:tab.targetId,refs:`refs`in snap?snap.refs:{},type:`png`}),normalized=await normalizeBrowserScreenshot(labeled.buffer,{maxSide:DEFAULT_BROWSER_SCREENSHOT_MAX_SIDE,maxBytes:DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES});await ensureMediaDir();let saved=await saveMediaBuffer(normalized.buffer,normalized.contentType??`image/png`,`browser`,DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES),imageType=normalized.contentType?.includes(`jpeg`)?`jpeg`:`png`;return res.json({ok:!0,format,targetId:tab.targetId,url:tab.url,labels:!0,labelsCount:labeled.labels,labelsSkipped:labeled.skipped,imagePath:path.resolve(saved.path),imageType,...snap})}return res.json({ok:!0,format,targetId:tab.targetId,url:tab.url,...snap})}let snap=profileCtx.profile.driver===`extension`||!tab.wsUrl?requirePwAi(res,`aria snapshot`).then(async pw=>pw?await pw.snapshotAriaViaPlaywright({cdpUrl:profileCtx.profile.cdpUrl,targetId:tab.targetId,limit}):null):snapshotAria({wsUrl:tab.wsUrl??``,limit}),resolved=await Promise.resolve(snap);return resolved?res.json({ok:!0,format,targetId:tab.targetId,url:tab.url,...resolved}):void 0}catch(err){handleRouteError(ctx,res,err)}})}function parseStorageKind(raw){return raw===`local`||raw===`session`?raw:null}function parseStorageMutationRequest(kindParam,body){return{kind:parseStorageKind(toStringOrEmpty(kindParam)),targetId:resolveTargetIdFromBody(body)}}function parseRequiredStorageMutationRequest(kindParam,body){let parsed=parseStorageMutationRequest(kindParam,body);return parsed.kind?{kind:parsed.kind,targetId:parsed.targetId}:null}function parseStorageMutationOrRespond(res,kindParam,body){return parseRequiredStorageMutationRequest(kindParam,body)||(jsonError(res,400,`kind must be local|session`),null)}function parseStorageMutationFromRequest(req,res){let body=readBody(req),parsed=parseStorageMutationOrRespond(res,req.params.kind,body);return parsed?{body,parsed}:null}function registerBrowserAgentStorageRoutes(app,ctx){app.get(`/cookies`,async(req,res)=>{await withPlaywrightRouteContext({req,res,ctx,targetId:resolveTargetIdFromQuery(req.query),feature:`cookies`,run:async({cdpUrl,tab,pw})=>{let result=await pw.cookiesGetViaPlaywright({cdpUrl,targetId:tab.targetId});res.json({ok:!0,targetId:tab.targetId,...result})}})}),app.post(`/cookies/set`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),cookie=body.cookie&&typeof body.cookie==`object`&&!Array.isArray(body.cookie)?body.cookie:null;if(!cookie)return jsonError(res,400,`cookie is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`cookies set`,run:async({cdpUrl,tab,pw})=>{await pw.cookiesSetViaPlaywright({cdpUrl,targetId:tab.targetId,cookie:{name:toStringOrEmpty(cookie.name),value:toStringOrEmpty(cookie.value),url:toStringOrEmpty(cookie.url)||void 0,domain:toStringOrEmpty(cookie.domain)||void 0,path:toStringOrEmpty(cookie.path)||void 0,expires:toNumber(cookie.expires)??void 0,httpOnly:toBoolean(cookie.httpOnly)??void 0,secure:toBoolean(cookie.secure)??void 0,sameSite:cookie.sameSite===`Lax`||cookie.sameSite===`None`||cookie.sameSite===`Strict`?cookie.sameSite:void 0}}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/cookies/clear`,async(req,res)=>{await withPlaywrightRouteContext({req,res,ctx,targetId:resolveTargetIdFromBody(readBody(req)),feature:`cookies clear`,run:async({cdpUrl,tab,pw})=>{await pw.cookiesClearViaPlaywright({cdpUrl,targetId:tab.targetId}),res.json({ok:!0,targetId:tab.targetId})}})}),app.get(`/storage/:kind`,async(req,res)=>{let kind=parseStorageKind(toStringOrEmpty(req.params.kind));if(!kind)return jsonError(res,400,`kind must be local|session`);let targetId=resolveTargetIdFromQuery(req.query),key=toStringOrEmpty(req.query.key);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`storage get`,run:async({cdpUrl,tab,pw})=>{let result=await pw.storageGetViaPlaywright({cdpUrl,targetId:tab.targetId,kind,key:key.trim()||void 0});res.json({ok:!0,targetId:tab.targetId,...result})}})}),app.post(`/storage/:kind/set`,async(req,res)=>{let mutation=parseStorageMutationFromRequest(req,res);if(!mutation)return;let key=toStringOrEmpty(mutation.body.key);if(!key)return jsonError(res,400,`key is required`);let value=typeof mutation.body.value==`string`?mutation.body.value:``;await withPlaywrightRouteContext({req,res,ctx,targetId:mutation.parsed.targetId,feature:`storage set`,run:async({cdpUrl,tab,pw})=>{await pw.storageSetViaPlaywright({cdpUrl,targetId:tab.targetId,kind:mutation.parsed.kind,key,value}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/storage/:kind/clear`,async(req,res)=>{let mutation=parseStorageMutationFromRequest(req,res);mutation&&await withPlaywrightRouteContext({req,res,ctx,targetId:mutation.parsed.targetId,feature:`storage clear`,run:async({cdpUrl,tab,pw})=>{await pw.storageClearViaPlaywright({cdpUrl,targetId:tab.targetId,kind:mutation.parsed.kind}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/set/offline`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),offline=toBoolean(body.offline);if(offline===void 0)return jsonError(res,400,`offline is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`offline`,run:async({cdpUrl,tab,pw})=>{await pw.setOfflineViaPlaywright({cdpUrl,targetId:tab.targetId,offline}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/set/headers`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),headers=body.headers&&typeof body.headers==`object`&&!Array.isArray(body.headers)?body.headers:null;if(!headers)return jsonError(res,400,`headers is required`);let parsed={};for(let[k,v]of Object.entries(headers))typeof v==`string`&&(parsed[k]=v);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`headers`,run:async({cdpUrl,tab,pw})=>{await pw.setExtraHTTPHeadersViaPlaywright({cdpUrl,targetId:tab.targetId,headers:parsed}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/set/credentials`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),clear=toBoolean(body.clear)??!1,username=toStringOrEmpty(body.username)||void 0,password=typeof body.password==`string`?body.password:void 0;await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`http credentials`,run:async({cdpUrl,tab,pw})=>{await pw.setHttpCredentialsViaPlaywright({cdpUrl,targetId:tab.targetId,username,password,clear}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/set/geolocation`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),clear=toBoolean(body.clear)??!1,latitude=toNumber(body.latitude),longitude=toNumber(body.longitude),accuracy=toNumber(body.accuracy)??void 0,origin=toStringOrEmpty(body.origin)||void 0;await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`geolocation`,run:async({cdpUrl,tab,pw})=>{await pw.setGeolocationViaPlaywright({cdpUrl,targetId:tab.targetId,latitude,longitude,accuracy,origin,clear}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/set/media`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),schemeRaw=toStringOrEmpty(body.colorScheme),colorScheme=schemeRaw===`dark`||schemeRaw===`light`||schemeRaw===`no-preference`?schemeRaw:schemeRaw===`none`?null:void 0;if(colorScheme===void 0)return jsonError(res,400,`colorScheme must be dark|light|no-preference|none`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`media emulation`,run:async({cdpUrl,tab,pw})=>{await pw.emulateMediaViaPlaywright({cdpUrl,targetId:tab.targetId,colorScheme}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/set/timezone`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),timezoneId=toStringOrEmpty(body.timezoneId);if(!timezoneId)return jsonError(res,400,`timezoneId is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`timezone`,run:async({cdpUrl,tab,pw})=>{await pw.setTimezoneViaPlaywright({cdpUrl,targetId:tab.targetId,timezoneId}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/set/locale`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),locale=toStringOrEmpty(body.locale);if(!locale)return jsonError(res,400,`locale is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`locale`,run:async({cdpUrl,tab,pw})=>{await pw.setLocaleViaPlaywright({cdpUrl,targetId:tab.targetId,locale}),res.json({ok:!0,targetId:tab.targetId})}})}),app.post(`/set/device`,async(req,res)=>{let body=readBody(req),targetId=resolveTargetIdFromBody(body),name=toStringOrEmpty(body.name);if(!name)return jsonError(res,400,`name is required`);await withPlaywrightRouteContext({req,res,ctx,targetId,feature:`device emulation`,run:async({cdpUrl,tab,pw})=>{await pw.setDeviceViaPlaywright({cdpUrl,targetId:tab.targetId,name}),res.json({ok:!0,targetId:tab.targetId})}})})}function registerBrowserAgentRoutes(app,ctx){registerBrowserAgentSnapshotRoutes(app,ctx),registerBrowserAgentActRoutes(app,ctx),registerBrowserAgentDebugRoutes(app,ctx),registerBrowserAgentStorageRoutes(app,ctx)}function isValidPort(port){return Number.isFinite(port)&&port>0&&port<=65535}function clampPort(port,fallback){return isValidPort(port)?port:fallback}function derivePort(base,offset,fallback){return clampPort(base+offset,fallback)}const DEFAULT_BROWSER_CONTROL_PORT=18791,DEFAULT_BROWSER_CDP_PORT_RANGE_START=18800,DEFAULT_BROWSER_CDP_PORT_RANGE_END=18899;function deriveDefaultBrowserControlPort(gatewayPort){return derivePort(gatewayPort,2,DEFAULT_BROWSER_CONTROL_PORT)}function deriveDefaultBrowserCdpPortRange(browserControlPort){let start=derivePort(browserControlPort,9,DEFAULT_BROWSER_CDP_PORT_RANGE_START),end=clampPort(start+(DEFAULT_BROWSER_CDP_PORT_RANGE_END-DEFAULT_BROWSER_CDP_PORT_RANGE_START),DEFAULT_BROWSER_CDP_PORT_RANGE_END);return end<start?{start,end:start}:{start,end}}const CDP_PORT_RANGE_START=18800,PROFILE_NAME_REGEX=/^[a-z0-9][a-z0-9-]*$/;function isValidProfileName(name){return!name||name.length>64?!1:PROFILE_NAME_REGEX.test(name)}function allocateCdpPort(usedPorts,range){let start=range?.start??CDP_PORT_RANGE_START,end=range?.end??18899;if(!Number.isFinite(start)||!Number.isFinite(end)||start<=0||end<=0||start>end)return null;for(let port=start;port<=end;port++)if(!usedPorts.has(port))return port;return null}function getUsedPorts(profiles){if(!profiles)return new Set;let used=new Set;for(let profile of Object.values(profiles)){if(typeof profile.cdpPort==`number`){used.add(profile.cdpPort);continue}let rawUrl=profile.cdpUrl?.trim();if(rawUrl)try{let parsed=new URL(rawUrl),port=parsed.port&&Number.parseInt(parsed.port,10)>0?Number.parseInt(parsed.port,10):parsed.protocol===`https:`?443:80;!Number.isNaN(port)&&port>0&&port<=65535&&used.add(port)}catch{}}return used}const PROFILE_COLORS=[`#FF4500`,`#0066CC`,`#00AA00`,`#9933FF`,`#FF6699`,`#00CCCC`,`#FF9900`,`#6666FF`,`#CC3366`,`#339966`];function allocateColor(usedColors){for(let color of PROFILE_COLORS)if(!usedColors.has(color.toUpperCase()))return color;return PROFILE_COLORS[usedColors.size%PROFILE_COLORS.length]??PROFILE_COLORS[0]}function getUsedColors(profiles){return profiles?new Set(Object.values(profiles).map(p=>p.color.toUpperCase())):new Set}function normalizeHexColor(raw){let value=(raw??``).trim();if(!value)return DEFAULT_BUILDERFORCE_AGENTS_BROWSER_COLOR;let normalized=value.startsWith(`#`)?value:`#${value}`;return/^#[0-9a-fA-F]{6}$/.test(normalized)?normalized.toUpperCase():DEFAULT_BUILDERFORCE_AGENTS_BROWSER_COLOR}function normalizeTimeoutMs(raw,fallback){let value=typeof raw==`number`&&Number.isFinite(raw)?Math.floor(raw):fallback;return value<0?fallback:value}function parseHttpUrl(raw,label){let trimmed=raw.trim(),parsed=new URL(trimmed);if(parsed.protocol!==`http:`&&parsed.protocol!==`https:`)throw Error(`${label} must be http(s), got: ${parsed.protocol.replace(`:`,``)}`);let port=parsed.port&&Number.parseInt(parsed.port,10)>0?Number.parseInt(parsed.port,10):parsed.protocol===`https:`?443:80;if(Number.isNaN(port)||port<=0||port>65535)throw Error(`${label} has invalid port: ${parsed.port}`);return{parsed,port,normalized:parsed.toString().replace(/\/$/,``)}}function ensureDefaultProfile(profiles,defaultColor,legacyCdpPort,derivedDefaultCdpPort){let result={...profiles};return result[DEFAULT_BUILDERFORCE_AGENTS_BROWSER_PROFILE_NAME]||(result[DEFAULT_BUILDERFORCE_AGENTS_BROWSER_PROFILE_NAME]={cdpPort:legacyCdpPort??derivedDefaultCdpPort??CDP_PORT_RANGE_START,color:defaultColor}),result}function ensureDefaultChromeExtensionProfile(profiles,controlPort){let result={...profiles};if(result.chrome)return result;let relayPort=controlPort+1;return!Number.isFinite(relayPort)||relayPort<=0||relayPort>65535||getUsedPorts(result).has(relayPort)||(result.chrome={driver:`extension`,cdpUrl:`http://127.0.0.1:${relayPort}`,color:`#00AA00`}),result}function resolveBrowserConfig(cfg,rootConfig){let enabled=cfg?.enabled??DEFAULT_BUILDERFORCE_AGENTS_BROWSER_ENABLED,evaluateEnabled=cfg?.evaluateEnabled??DEFAULT_BROWSER_EVALUATE_ENABLED,controlPort=deriveDefaultBrowserControlPort(resolveGatewayPort(rootConfig)??DEFAULT_BROWSER_CONTROL_PORT),defaultColor=normalizeHexColor(cfg?.color),remoteCdpTimeoutMs=normalizeTimeoutMs(cfg?.remoteCdpTimeoutMs,1500),remoteCdpHandshakeTimeoutMs=normalizeTimeoutMs(cfg?.remoteCdpHandshakeTimeoutMs,Math.max(2e3,remoteCdpTimeoutMs*2)),derivedCdpRange=deriveDefaultBrowserCdpPortRange(controlPort),rawCdpUrl=(cfg?.cdpUrl??``).trim(),cdpInfo;if(rawCdpUrl)cdpInfo=parseHttpUrl(rawCdpUrl,`browser.cdpUrl`);else{let derivedPort=controlPort+1;if(derivedPort>65535)throw Error(`Derived CDP port (${derivedPort}) is too high; check gateway port configuration.`);let derived=new URL(`http://127.0.0.1:${derivedPort}`);cdpInfo={parsed:derived,port:derivedPort,normalized:derived.toString().replace(/\/$/,``)}}let headless=cfg?.headless===!0,noSandbox=cfg?.noSandbox===!0,attachOnly=cfg?.attachOnly===!0,executablePath=cfg?.executablePath?.trim()||void 0,defaultProfileFromConfig=cfg?.defaultProfile?.trim()||void 0,legacyCdpPort=rawCdpUrl?cdpInfo.port:void 0,profiles=ensureDefaultChromeExtensionProfile(ensureDefaultProfile(cfg?.profiles,defaultColor,legacyCdpPort,derivedCdpRange.start),controlPort),cdpProtocol=cdpInfo.parsed.protocol===`https:`?`https`:`http`,defaultProfile=defaultProfileFromConfig??(profiles[DEFAULT_BROWSER_DEFAULT_PROFILE_NAME]?DEFAULT_BROWSER_DEFAULT_PROFILE_NAME:DEFAULT_BUILDERFORCE_AGENTS_BROWSER_PROFILE_NAME),extraArgs=Array.isArray(cfg?.extraArgs)?cfg.extraArgs.filter(a=>typeof a==`string`&&a.trim().length>0):[];return{enabled,evaluateEnabled,controlPort,cdpProtocol,cdpHost:cdpInfo.parsed.hostname,cdpIsLoopback:isLoopbackHost(cdpInfo.parsed.hostname),remoteCdpTimeoutMs,remoteCdpHandshakeTimeoutMs,color:defaultColor,executablePath,headless,noSandbox,attachOnly,defaultProfile,profiles,extraArgs}}function resolveProfile(resolved,profileName){let profile=resolved.profiles[profileName];if(!profile)return null;let rawProfileUrl=profile.cdpUrl?.trim()??``,cdpHost=resolved.cdpHost,cdpPort=profile.cdpPort??0,cdpUrl=``,driver=profile.driver===`extension`?`extension`:`builderforce`;if(rawProfileUrl){let parsed=parseHttpUrl(rawProfileUrl,`browser.profiles.${profileName}.cdpUrl`);cdpHost=parsed.parsed.hostname,cdpPort=parsed.port,cdpUrl=parsed.normalized}else if(cdpPort)cdpUrl=`${resolved.cdpProtocol}://${resolved.cdpHost}:${cdpPort}`;else throw Error(`Profile "${profileName}" must define cdpPort or cdpUrl.`);return{name:profileName,cdpPort,cdpUrl,cdpHost,cdpIsLoopback:isLoopbackHost(cdpHost),color:profile.color,driver}}async function movePathToTrash(targetPath){try{return await runExec(`trash`,[targetPath],{timeoutMs:1e4}),targetPath}catch{let trashDir=path.join(os.homedir(),`.Trash`);fs$1.mkdirSync(trashDir,{recursive:!0});let base=path.basename(targetPath),dest=path.join(trashDir,`${base}-${Date.now()}`);return fs$1.existsSync(dest)&&(dest=path.join(trashDir,`${base}-${Date.now()}-${Math.random()}`)),fs$1.renameSync(targetPath,dest),dest}}const HEX_COLOR_RE=/^#[0-9A-Fa-f]{6}$/;function createBrowserProfilesService(ctx){return{listProfiles:async()=>await ctx.listProfiles(),createProfile:async params=>{let name=params.name.trim(),rawCdpUrl=params.cdpUrl?.trim()||void 0,driver=params.driver===`extension`?`extension`:void 0;if(!isValidProfileName(name))throw Error(`invalid profile name: use lowercase letters, numbers, and hyphens only`);let state=ctx.state(),resolvedProfiles=state.resolved.profiles;if(name in resolvedProfiles)throw Error(`profile "${name}" already exists`);let cfg=loadConfig(),rawProfiles=cfg.browser?.profiles??{};if(name in rawProfiles)throw Error(`profile "${name}" already exists`);let usedColors=getUsedColors(resolvedProfiles),profileColor=params.color&&HEX_COLOR_RE.test(params.color)?params.color:allocateColor(usedColors),profileConfig;if(rawCdpUrl)profileConfig={cdpUrl:parseHttpUrl(rawCdpUrl,`browser.profiles.cdpUrl`).normalized,...driver?{driver}:{},color:profileColor};else{let cdpPort=allocateCdpPort(getUsedPorts(resolvedProfiles),deriveDefaultBrowserCdpPortRange(state.resolved.controlPort));if(cdpPort===null)throw Error(`no available CDP ports in range`);profileConfig={cdpPort,...driver?{driver}:{},color:profileColor}}await writeConfigFile({...cfg,browser:{...cfg.browser,profiles:{...rawProfiles,[name]:profileConfig}}}),state.resolved.profiles[name]=profileConfig;let resolved=resolveProfile(state.resolved,name);if(!resolved)throw Error(`profile "${name}" not found after creation`);return{ok:!0,profile:name,cdpPort:resolved.cdpPort,cdpUrl:resolved.cdpUrl,color:resolved.color,isRemote:!resolved.cdpIsLoopback}},deleteProfile:async nameRaw=>{let name=nameRaw.trim();if(!name)throw Error(`profile name is required`);if(!isValidProfileName(name))throw Error(`invalid profile name`);let cfg=loadConfig(),profiles=cfg.browser?.profiles??{};if(!(name in profiles))throw Error(`profile "${name}" not found`);if(name===(cfg.browser?.defaultProfile??DEFAULT_BROWSER_DEFAULT_PROFILE_NAME))throw Error(`cannot delete the default profile "${name}"; change browser.defaultProfile first`);let deleted=!1,state=ctx.state();if(resolveProfile(state.resolved,name)?.cdpIsLoopback){try{await ctx.forProfile(name).stopRunningBrowser()}catch{}let userDataDir=resolveBuilderForceAgentsUserDataDir(name),profileDir=path.dirname(userDataDir);fs$1.existsSync(profileDir)&&(await movePathToTrash(profileDir),deleted=!0)}let{[name]:_removed,...remainingProfiles}=profiles;return await writeConfigFile({...cfg,browser:{...cfg.browser,profiles:remainingProfiles}}),delete state.resolved.profiles[name],state.profiles.delete(name),{ok:!0,profile:name,deleted}}}}async function withBasicProfileRoute(params){let profileCtx=resolveProfileContext(params.req,params.res,params.ctx);if(profileCtx)try{await params.run(profileCtx)}catch(err){jsonError(params.res,500,String(err))}}function registerBrowserBasicRoutes(app,ctx){app.get(`/profiles`,async(_req,res)=>{try{let profiles=await createBrowserProfilesService(ctx).listProfiles();res.json({profiles})}catch(err){jsonError(res,500,String(err))}}),app.get(`/`,async(req,res)=>{let current;try{current=ctx.state()}catch{return jsonError(res,503,`browser server not started`)}let profileCtx=getProfileContext(req,ctx);if(`error`in profileCtx)return jsonError(res,profileCtx.status,profileCtx.error);let[cdpHttp,cdpReady]=await Promise.all([profileCtx.isHttpReachable(300),profileCtx.isReachable(600)]),profileState=current.profiles.get(profileCtx.profile.name),detectedBrowser=null,detectedExecutablePath=null,detectError=null;try{let detected=resolveBrowserExecutableForPlatform(current.resolved,process.platform);detected&&(detectedBrowser=detected.kind,detectedExecutablePath=detected.path)}catch(err){detectError=String(err)}res.json({enabled:current.resolved.enabled,profile:profileCtx.profile.name,running:cdpReady,cdpReady,cdpHttp,pid:profileState?.running?.pid??null,cdpPort:profileCtx.profile.cdpPort,cdpUrl:profileCtx.profile.cdpUrl,chosenBrowser:profileState?.running?.exe.kind??null,detectedBrowser,detectedExecutablePath,detectError,userDataDir:profileState?.running?.userDataDir??null,color:profileCtx.profile.color,headless:current.resolved.headless,noSandbox:current.resolved.noSandbox,executablePath:current.resolved.executablePath??null,attachOnly:current.resolved.attachOnly})}),app.post(`/start`,async(req,res)=>{await withBasicProfileRoute({req,res,ctx,run:async profileCtx=>{await profileCtx.ensureBrowserAvailable(),res.json({ok:!0,profile:profileCtx.profile.name})}})}),app.post(`/stop`,async(req,res)=>{await withBasicProfileRoute({req,res,ctx,run:async profileCtx=>{let result=await profileCtx.stopRunningBrowser();res.json({ok:!0,stopped:result.stopped,profile:profileCtx.profile.name})}})}),app.post(`/reset-profile`,async(req,res)=>{await withBasicProfileRoute({req,res,ctx,run:async profileCtx=>{let result=await profileCtx.resetProfile();res.json({ok:!0,profile:profileCtx.profile.name,...result})}})}),app.post(`/profiles/create`,async(req,res)=>{let name=toStringOrEmpty(req.body?.name),color=toStringOrEmpty(req.body?.color),cdpUrl=toStringOrEmpty(req.body?.cdpUrl),driver=toStringOrEmpty(req.body?.driver);if(!name)return jsonError(res,400,`name is required`);try{let result=await createBrowserProfilesService(ctx).createProfile({name,color:color||void 0,cdpUrl:cdpUrl||void 0,driver:driver===`extension`?`extension`:void 0});res.json(result)}catch(err){let msg=String(err);if(msg.includes(`already exists`))return jsonError(res,409,msg);if(msg.includes(`invalid profile name`))return jsonError(res,400,msg);if(msg.includes(`no available CDP ports`))return jsonError(res,507,msg);if(msg.includes(`cdpUrl`))return jsonError(res,400,msg);jsonError(res,500,msg)}}),app.delete(`/profiles/:name`,async(req,res)=>{let name=toStringOrEmpty(req.params.name);if(!name)return jsonError(res,400,`profile name is required`);try{let result=await createBrowserProfilesService(ctx).deleteProfile(name);res.json(result)}catch(err){let msg=String(err);if(msg.includes(`invalid profile name`)||msg.includes(`default profile`))return jsonError(res,400,msg);if(msg.includes(`not found`))return jsonError(res,404,msg);jsonError(res,500,msg)}})}function resolveTabsProfileContext(req,res,ctx){let profileCtx=getProfileContext(req,ctx);return`error`in profileCtx?(jsonError(res,profileCtx.status,profileCtx.error),null):profileCtx}function handleTabsRouteError(ctx,res,err,opts){if(opts?.mapTabError){let mapped=ctx.mapTabError(err);if(mapped)return jsonError(res,mapped.status,mapped.message)}return jsonError(res,500,String(err))}async function withTabsProfileRoute(params){let profileCtx=resolveTabsProfileContext(params.req,params.res,params.ctx);if(profileCtx)try{await params.run(profileCtx)}catch(err){handleTabsRouteError(params.ctx,params.res,err,{mapTabError:params.mapTabError})}}async function ensureBrowserRunning(profileCtx,res){return await profileCtx.isReachable(300)?!0:(jsonError(res,409,`browser not running`),!1)}function resolveIndexedTab(tabs,index){return typeof index==`number`?tabs[index]:tabs.at(0)}function parseRequiredTargetId(res,rawTargetId){return toStringOrEmpty(rawTargetId)||(jsonError(res,400,`targetId is required`),null)}async function runTabTargetMutation(params){await withTabsProfileRoute({req:params.req,res:params.res,ctx:params.ctx,mapTabError:!0,run:async profileCtx=>{await ensureBrowserRunning(profileCtx,params.res)&&(await params.mutate(profileCtx,params.targetId),params.res.json({ok:!0}))}})}function registerBrowserTabRoutes(app,ctx){app.get(`/tabs`,async(req,res)=>{await withTabsProfileRoute({req,res,ctx,run:async profileCtx=>{if(!await profileCtx.isReachable(300))return res.json({running:!1,tabs:[]});let tabs=await profileCtx.listTabs();res.json({running:!0,tabs})}})}),app.post(`/tabs/open`,async(req,res)=>{let url=toStringOrEmpty(req.body?.url);if(!url)return jsonError(res,400,`url is required`);await withTabsProfileRoute({req,res,ctx,run:async profileCtx=>{await profileCtx.ensureBrowserAvailable();let tab=await profileCtx.openTab(url);res.json(tab)}})}),app.post(`/tabs/focus`,async(req,res)=>{let targetId=parseRequiredTargetId(res,req.body?.targetId);targetId&&await runTabTargetMutation({req,res,ctx,targetId,mutate:async(profileCtx,id)=>{await profileCtx.focusTab(id)}})}),app.delete(`/tabs/:targetId`,async(req,res)=>{let targetId=parseRequiredTargetId(res,req.params.targetId);targetId&&await runTabTargetMutation({req,res,ctx,targetId,mutate:async(profileCtx,id)=>{await profileCtx.closeTab(id)}})}),app.post(`/tabs/action`,async(req,res)=>{let action=toStringOrEmpty(req.body?.action),index=toNumber(req.body?.index);await withTabsProfileRoute({req,res,ctx,mapTabError:!0,run:async profileCtx=>{if(action===`list`){if(!await profileCtx.isReachable(300))return res.json({ok:!0,tabs:[]});let tabs=await profileCtx.listTabs();return res.json({ok:!0,tabs})}if(action===`new`){await profileCtx.ensureBrowserAvailable();let tab=await profileCtx.openTab(`about:blank`);return res.json({ok:!0,tab})}if(action===`close`){let target=resolveIndexedTab(await profileCtx.listTabs(),index);return target?(await profileCtx.closeTab(target.targetId),res.json({ok:!0,targetId:target.targetId})):jsonError(res,404,`tab not found`)}if(action===`select`){if(typeof index!=`number`)return jsonError(res,400,`index is required`);let target=(await profileCtx.listTabs())[index];return target?(await profileCtx.focusTab(target.targetId),res.json({ok:!0,targetId:target.targetId})):jsonError(res,404,`tab not found`)}return jsonError(res,400,`unknown tab action`)}})})}function registerBrowserRoutes(app,ctx){registerBrowserBasicRoutes(app,ctx),registerBrowserTabRoutes(app,ctx),registerBrowserAgentRoutes(app,ctx)}function applyResolvedConfig(current,freshResolved){current.resolved=freshResolved;for(let[name,runtime]of current.profiles){let nextProfile=resolveProfile(freshResolved,name);if(nextProfile){runtime.profile=nextProfile;continue}runtime.running||current.profiles.delete(name)}}function refreshResolvedBrowserConfigFromDisk(params){if(!params.refreshConfigFromDisk)return;let cfg=params.mode===`fresh`?createConfigIO().loadConfig():loadConfig(),freshResolved=resolveBrowserConfig(cfg.browser,cfg);applyResolvedConfig(params.current,freshResolved)}function resolveBrowserProfileWithHotReload(params){refreshResolvedBrowserConfigFromDisk({current:params.current,refreshConfigFromDisk:params.refreshConfigFromDisk,mode:`cached`});let profile=resolveProfile(params.current.resolved,params.name);return profile||(refreshResolvedBrowserConfigFromDisk({current:params.current,refreshConfigFromDisk:params.refreshConfigFromDisk,mode:`fresh`}),profile=resolveProfile(params.current.resolved,params.name),profile)}function resolveTargetIdFromTabs(input,tabs){let needle=input.trim();if(!needle)return{ok:!1,reason:`not_found`};let exact=tabs.find(t=>t.targetId===needle);if(exact)return{ok:!0,targetId:exact.targetId};let lower=needle.toLowerCase(),matches=tabs.map(t=>t.targetId).filter(id=>id.toLowerCase().startsWith(lower)),only=matches.length===1?matches[0]:void 0;return only?{ok:!0,targetId:only}:matches.length===0?{ok:!1,reason:`not_found`}:{ok:!1,reason:`ambiguous`,matches}}function normalizeWsUrl(raw,cdpBaseUrl){if(raw)try{return normalizeCdpWsUrl(raw,cdpBaseUrl)}catch{return raw}}function createProfileContext(opts,profile){let state=()=>{let current=opts.getState();if(!current)throw Error(`Browser server not started`);return current},getProfileState=()=>{let current=state(),profileState=current.profiles.get(profile.name);return profileState||(profileState={profile,running:null,lastTargetId:null},current.profiles.set(profile.name,profileState)),profileState},setProfileRunning=running=>{let profileState=getProfileState();profileState.running=running},listTabs=async()=>{if(!profile.cdpIsLoopback){let listPagesViaPlaywright=(await getPwAiModule$1({mode:`strict`}))?.listPagesViaPlaywright;if(typeof listPagesViaPlaywright==`function`)return(await listPagesViaPlaywright({cdpUrl:profile.cdpUrl})).map(p=>({targetId:p.targetId,title:p.title,url:p.url,type:p.type}))}return(await fetchJson(appendCdpPath(profile.cdpUrl,`/json/list`))).map(t=>({targetId:t.id??``,title:t.title??``,url:t.url??``,wsUrl:normalizeWsUrl(t.webSocketDebuggerUrl,profile.cdpUrl),type:t.type})).filter(t=>!!t.targetId)},openTab=async url=>{if(!profile.cdpIsLoopback){let createPageViaPlaywright=(await getPwAiModule$1({mode:`strict`}))?.createPageViaPlaywright;if(typeof createPageViaPlaywright==`function`){let page=await createPageViaPlaywright({cdpUrl:profile.cdpUrl,url}),profileState=getProfileState();return profileState.lastTargetId=page.targetId,{targetId:page.targetId,title:page.title,url:page.url,type:page.type}}}let createdViaCdp=await createTargetViaCdp({cdpUrl:profile.cdpUrl,url}).then(r=>r.targetId).catch(()=>null);if(createdViaCdp){let profileState=getProfileState();profileState.lastTargetId=createdViaCdp;let deadline=Date.now()+2e3;for(;Date.now()<deadline;){let found=(await listTabs().catch(()=>[])).find(t=>t.targetId===createdViaCdp);if(found)return found;await new Promise(r=>setTimeout(r,100))}return{targetId:createdViaCdp,title:``,url,type:`page`}}let encoded=encodeURIComponent(url),endpointUrl=new URL(appendCdpPath(profile.cdpUrl,`/json/new`)),endpoint=endpointUrl.search?(endpointUrl.searchParams.set(`url`,url),endpointUrl.toString()):`${endpointUrl.toString()}?${encoded}`,created=await fetchJson(endpoint,1500,{method:`PUT`}).catch(async err=>{if(String(err).includes(`HTTP 405`))return await fetchJson(endpoint,1500);throw err});if(!created.id)throw Error(`Failed to open tab (missing id)`);let profileState=getProfileState();return profileState.lastTargetId=created.id,{targetId:created.id,title:created.title??``,url:created.url??url,wsUrl:normalizeWsUrl(created.webSocketDebuggerUrl,profile.cdpUrl),type:created.type}},resolveRemoteHttpTimeout=timeoutMs=>{if(profile.cdpIsLoopback)return timeoutMs??300;let resolved=state().resolved;return typeof timeoutMs==`number`&&Number.isFinite(timeoutMs)?Math.max(Math.floor(timeoutMs),resolved.remoteCdpTimeoutMs):resolved.remoteCdpTimeoutMs},resolveRemoteWsTimeout=timeoutMs=>{if(profile.cdpIsLoopback){let base=timeoutMs??300;return Math.max(200,Math.min(2e3,base*2))}let resolved=state().resolved;return typeof timeoutMs==`number`&&Number.isFinite(timeoutMs)?Math.max(Math.floor(timeoutMs)*2,resolved.remoteCdpHandshakeTimeoutMs):resolved.remoteCdpHandshakeTimeoutMs},isReachable=async timeoutMs=>{let httpTimeout=resolveRemoteHttpTimeout(timeoutMs),wsTimeout=resolveRemoteWsTimeout(timeoutMs);return await isChromeCdpReady(profile.cdpUrl,httpTimeout,wsTimeout)},isHttpReachable=async timeoutMs=>{let httpTimeout=resolveRemoteHttpTimeout(timeoutMs);return await isChromeReachable(profile.cdpUrl,httpTimeout)},attachRunning=running=>{setProfileRunning(running),running.proc.on(`exit`,()=>{opts.getState()&&getProfileState().running?.pid===running.pid&&setProfileRunning(null)})},ensureBrowserAvailable=async()=>{let current=state(),remoteCdp=!profile.cdpIsLoopback,isExtension=profile.driver===`extension`,profileState=getProfileState(),httpReachable=await isHttpReachable();if(isExtension&&remoteCdp)throw Error(`Profile "${profile.name}" uses driver=extension but cdpUrl is not loopback (${profile.cdpUrl}).`);if(isExtension){if(!httpReachable&&(await ensureChromeExtensionRelayServer({cdpUrl:profile.cdpUrl}),!await isHttpReachable(1200)))throw Error(`Chrome extension relay for profile "${profile.name}" is not reachable at ${profile.cdpUrl}.`);if(await isReachable(600))return;throw Error(`Chrome extension relay is running, but no tab is connected. Click the BuilderForceAgents Chrome extension icon on a tab to attach it (profile "${profile.name}").`)}if(!httpReachable){if((current.resolved.attachOnly||remoteCdp)&&opts.onEnsureAttachTarget&&(await opts.onEnsureAttachTarget(profile),await isHttpReachable(1200)))return;if(current.resolved.attachOnly||remoteCdp)throw Error(remoteCdp?`Remote CDP for profile "${profile.name}" is not reachable at ${profile.cdpUrl}.`:`Browser attachOnly is enabled and profile "${profile.name}" is not running.`);attachRunning(await launchBuilderForceAgentsChrome(current.resolved,profile));return}if(!await isReachable()){if(!profileState.running)throw Error(`Port ${profile.cdpPort} is in use for profile "${profile.name}" but not by builderforce. Run action=reset-profile profile=${profile.name} to kill the process.`);if(current.resolved.attachOnly||remoteCdp){if(opts.onEnsureAttachTarget&&(await opts.onEnsureAttachTarget(profile),await isReachable(1200)))return;throw Error(remoteCdp?`Remote CDP websocket for profile "${profile.name}" is not reachable.`:`Browser attachOnly is enabled and CDP websocket for profile "${profile.name}" is not reachable.`)}if(await stopBuilderForceAgentsChrome(profileState.running),setProfileRunning(null),attachRunning(await launchBuilderForceAgentsChrome(current.resolved,profile)),!await isReachable(600))throw Error(`Chrome CDP websocket for profile "${profile.name}" is not reachable after restart.`)}},ensureTabAvailable=async targetId=>{await ensureBrowserAvailable();let profileState=getProfileState();if((await listTabs()).length===0){if(profile.driver===`extension`)throw Error(`tab not found (no attached Chrome tabs for profile "${profile.name}"). Click the BuilderForceAgents Browser Relay toolbar icon on the tab you want to control (badge ON).`);await openTab(`about:blank`)}let tabs=await listTabs(),candidates=profile.driver===`extension`||!profile.cdpIsLoopback?tabs:tabs.filter(t=>!!t.wsUrl),resolveById=raw=>{let resolved=resolveTargetIdFromTabs(raw,candidates);return resolved.ok?candidates.find(t=>t.targetId===resolved.targetId)??null:resolved.reason===`ambiguous`?`AMBIGUOUS`:null},chosen=targetId?resolveById(targetId):(()=>{let last=profileState.lastTargetId?.trim()||``,lastResolved=last?resolveById(last):null;return lastResolved&&lastResolved!==`AMBIGUOUS`?lastResolved:candidates.find(t=>(t.type??`page`)===`page`)??candidates.at(0)??null})();if(!chosen&&profile.driver===`extension`&&candidates.length===1&&(chosen=candidates[0]??null),chosen===`AMBIGUOUS`)throw Error(`ambiguous target id prefix`);if(!chosen)throw Error(`tab not found`);return profileState.lastTargetId=chosen.targetId,chosen},focusTab=async targetId=>{let resolved=resolveTargetIdFromTabs(targetId,await listTabs());if(!resolved.ok)throw resolved.reason===`ambiguous`?Error(`ambiguous target id prefix`):Error(`tab not found`);if(!profile.cdpIsLoopback){let focusPageByTargetIdViaPlaywright=(await getPwAiModule$1({mode:`strict`}))?.focusPageByTargetIdViaPlaywright;if(typeof focusPageByTargetIdViaPlaywright==`function`){await focusPageByTargetIdViaPlaywright({cdpUrl:profile.cdpUrl,targetId:resolved.targetId});let profileState=getProfileState();profileState.lastTargetId=resolved.targetId;return}}await fetchOk(appendCdpPath(profile.cdpUrl,`/json/activate/${resolved.targetId}`));let profileState=getProfileState();profileState.lastTargetId=resolved.targetId},closeTab=async targetId=>{let resolved=resolveTargetIdFromTabs(targetId,await listTabs());if(!resolved.ok)throw resolved.reason===`ambiguous`?Error(`ambiguous target id prefix`):Error(`tab not found`);if(!profile.cdpIsLoopback){let closePageByTargetIdViaPlaywright=(await getPwAiModule$1({mode:`strict`}))?.closePageByTargetIdViaPlaywright;if(typeof closePageByTargetIdViaPlaywright==`function`){await closePageByTargetIdViaPlaywright({cdpUrl:profile.cdpUrl,targetId:resolved.targetId});return}}await fetchOk(appendCdpPath(profile.cdpUrl,`/json/close/${resolved.targetId}`))},stopRunningBrowser=async()=>{if(profile.driver===`extension`)return{stopped:await stopChromeExtensionRelayServer({cdpUrl:profile.cdpUrl})};let profileState=getProfileState();return profileState.running?(await stopBuilderForceAgentsChrome(profileState.running),setProfileRunning(null),{stopped:!0}):{stopped:!1}};return{profile,ensureBrowserAvailable,ensureTabAvailable,isHttpReachable,isReachable,listTabs,openTab,focusTab,closeTab,stopRunningBrowser,resetProfile:async()=>{if(profile.driver===`extension`)return await stopChromeExtensionRelayServer({cdpUrl:profile.cdpUrl}).catch(()=>{}),{moved:!1,from:profile.cdpUrl};if(!profile.cdpIsLoopback)throw Error(`reset-profile is only supported for local profiles (profile "${profile.name}" is remote).`);let userDataDir=resolveBuilderForceAgentsUserDataDir(profile.name),profileState=getProfileState();if(await isHttpReachable(300)&&!profileState.running)try{await(await import(`./pw-ai-CsJmJcNk.js`)).closePlaywrightBrowserConnection()}catch{}profileState.running&&await stopRunningBrowser();try{await(await import(`./pw-ai-CsJmJcNk.js`)).closePlaywrightBrowserConnection()}catch{}return fs$1.existsSync(userDataDir)?{moved:!0,from:userDataDir,to:await movePathToTrash(userDataDir)}:{moved:!1,from:userDataDir}}}}function createBrowserRouteContext(opts){let refreshConfigFromDisk=opts.refreshConfigFromDisk===!0,state=()=>{let current=opts.getState();if(!current)throw Error(`Browser server not started`);return current},forProfile=profileName=>{let current=state(),name=profileName??current.resolved.defaultProfile,profile=resolveBrowserProfileWithHotReload({current,refreshConfigFromDisk,name});if(!profile){let available=Object.keys(current.resolved.profiles).join(`, `);throw Error(`Profile "${name}" not found. Available profiles: ${available||`(none)`}`)}return createProfileContext(opts,profile)},listProfiles=async()=>{let current=state();refreshResolvedBrowserConfigFromDisk({current,refreshConfigFromDisk,mode:`cached`});let result=[];for(let name of Object.keys(current.resolved.profiles)){let profileState=current.profiles.get(name),profile=resolveProfile(current.resolved,name);if(!profile)continue;let tabCount=0,running=!1;if(profileState?.running){running=!0;try{tabCount=(await createProfileContext(opts,profile).listTabs()).filter(t=>t.type===`page`).length}catch{}}else try{await isChromeReachable(profile.cdpUrl,200)&&(running=!0,tabCount=(await createProfileContext(opts,profile).listTabs().catch(()=>[])).filter(t=>t.type===`page`).length)}catch{}result.push({name,cdpPort:profile.cdpPort,cdpUrl:profile.cdpUrl,color:profile.color,running,tabCount,isDefault:name===current.resolved.defaultProfile,isRemote:!profile.cdpIsLoopback})}return result},getDefaultContext=()=>forProfile();return{state,forProfile,listProfiles,ensureBrowserAvailable:()=>getDefaultContext().ensureBrowserAvailable(),ensureTabAvailable:targetId=>getDefaultContext().ensureTabAvailable(targetId),isHttpReachable:timeoutMs=>getDefaultContext().isHttpReachable(timeoutMs),isReachable:timeoutMs=>getDefaultContext().isReachable(timeoutMs),listTabs:()=>getDefaultContext().listTabs(),openTab:url=>getDefaultContext().openTab(url),focusTab:targetId=>getDefaultContext().focusTab(targetId),closeTab:targetId=>getDefaultContext().closeTab(targetId),stopRunningBrowser:()=>getDefaultContext().stopRunningBrowser(),resetProfile:()=>getDefaultContext().resetProfile(),mapTabError:err=>{let msg=String(err);return msg.includes(`ambiguous target id prefix`)?{status:409,message:`ambiguous target id prefix`}:msg.includes(`tab not found`)||msg.includes(`not found`)?{status:404,message:msg}:null}}}function firstHeader(value){return Array.isArray(value)?value[0]??``:value??``}function isMutatingMethod(method){let m=(method||``).trim().toUpperCase();return m===`POST`||m===`PUT`||m===`PATCH`||m===`DELETE`}function isLoopbackUrl(value){let v=value.trim();if(!v||v===`null`)return!1;try{return isLoopbackHost(new URL(v).hostname)}catch{return!1}}function shouldRejectBrowserMutation(params){if(!isMutatingMethod(params.method))return!1;if((params.secFetchSite??``).trim().toLowerCase()===`cross-site`)return!0;let origin=(params.origin??``).trim();if(origin)return!isLoopbackUrl(origin);let referer=(params.referer??``).trim();return referer?!isLoopbackUrl(referer):!1}function browserMutationGuardMiddleware(){return(req,res,next)=>{let method=(req.method||``).trim().toUpperCase();if(method===`OPTIONS`)return next();if(shouldRejectBrowserMutation({method,origin:firstHeader(req.headers.origin),referer:firstHeader(req.headers.referer),secFetchSite:firstHeader(req.headers[`sec-fetch-site`])})){res.status(403).send(`Forbidden`);return}next()}}function firstHeaderValue(value){return Array.isArray(value)?value[0]??``:value??``}function parseBearerToken(authorization){if(!(!authorization||!authorization.toLowerCase().startsWith(`bearer `)))return authorization.slice(7).trim()||void 0}function parseBasicPassword(authorization){if(!authorization||!authorization.toLowerCase().startsWith(`basic `))return;let encoded=authorization.slice(6).trim();if(encoded)try{let decoded=Buffer.from(encoded,`base64`).toString(`utf8`),sep=decoded.indexOf(`:`);return sep<0?void 0:decoded.slice(sep+1).trim()||void 0}catch{return}}function isAuthorizedBrowserRequest(req,auth){let authorization=firstHeaderValue(req.headers.authorization).trim();if(auth.token){let bearer=parseBearerToken(authorization);if(bearer&&safeEqualSecret(bearer,auth.token))return!0}if(auth.password){let passwordHeader=(firstHeaderValue(req.headers[`x-builderforce-password`])||firstHeaderValue(req.headers[`x-builderforce-password`])).trim();if(passwordHeader&&safeEqualSecret(passwordHeader,auth.password))return!0;let basicPassword=parseBasicPassword(authorization);if(basicPassword&&safeEqualSecret(basicPassword,auth.password))return!0}return!1}function installBrowserCommonMiddleware(app){app.use((req,res,next)=>{let ctrl=new AbortController,abort=()=>ctrl.abort(Error(`request aborted`));req.once(`aborted`,abort),res.once(`close`,()=>{res.writableEnded||abort()}),Object.defineProperty(req,`signal`,{value:ctrl.signal,configurable:!0,enumerable:!0,writable:!0}),next()}),app.use(express.json({limit:`1mb`})),app.use(browserMutationGuardMiddleware())}function installBrowserAuthMiddleware(app,auth){!auth.token&&!auth.password||app.use((req,res,next)=>{if(isAuthorizedBrowserRequest(req,auth))return next();res.status(401).send(`Unauthorized`)})}async function startBrowserBridgeServer(params){let host=params.host??`127.0.0.1`;if(!isLoopbackHost(host))throw Error(`bridge server must bind to loopback host (got ${host})`);let port=params.port??0,app=express();installBrowserCommonMiddleware(app);let authToken=params.authToken?.trim()||void 0,authPassword=params.authPassword?.trim()||void 0;if(!authToken&&!authPassword)throw Error(`bridge server requires auth (authToken/authPassword missing)`);installBrowserAuthMiddleware(app,{token:authToken,password:authPassword});let state={server:null,port,resolved:params.resolved,profiles:new Map};registerBrowserRoutes(app,createBrowserRouteContext({getState:()=>state,onEnsureAttachTarget:params.onEnsureAttachTarget}));let server=await new Promise((resolve,reject)=>{let s=app.listen(port,host,()=>resolve(s));s.once(`error`,reject)}),resolvedPort=server.address()?.port??port;return state.server=server,state.port=resolvedPort,state.resolved.controlPort=resolvedPort,setBridgeAuthForPort(resolvedPort,{token:authToken,password:authPassword}),{server,port:resolvedPort,baseUrl:`http://${host}:${resolvedPort}`,state}}async function stopBrowserBridgeServer(server){try{let address=server.address();address?.port&&deleteBridgeAuthForPort(address.port)}catch{}await new Promise(resolve=>{server.close(()=>resolve())})}const BROWSER_BRIDGES=new Map;function hashTextSha256(value){return crypto.createHash(`sha256`).update(value).digest(`hex`)}function normalizeForHash(value){if(value!==void 0){if(Array.isArray(value))return value.map(normalizeForHash).filter(item=>item!==void 0);if(value&&typeof value==`object`){let entries=Object.entries(value).toSorted(([a],[b])=>a.localeCompare(b)),normalized={};for(let[key,entryValue]of entries){let next=normalizeForHash(entryValue);next!==void 0&&(normalized[key]=next)}return normalized}return value}}function computeSandboxConfigHash(input){return computeHash(input)}function computeSandboxBrowserConfigHash(input){return computeHash(input)}function computeHash(input){let payload=normalizeForHash(input);return hashTextSha256(JSON.stringify(payload))}const BLOCKED_ENV_VAR_PATTERNS=[/^ANTHROPIC_API_KEY$/i,/^OPENAI_API_KEY$/i,/^GEMINI_API_KEY$/i,/^OPENROUTER_API_KEY$/i,/^MINIMAX_API_KEY$/i,/^ELEVENLABS_API_KEY$/i,/^SYNTHETIC_API_KEY$/i,/^TELEGRAM_BOT_TOKEN$/i,/^DISCORD_BOT_TOKEN$/i,/^SLACK_(BOT|APP)_TOKEN$/i,/^LINE_CHANNEL_SECRET$/i,/^LINE_CHANNEL_ACCESS_TOKEN$/i,/^BUILDERFORCE_AGENTS_GATEWAY_(TOKEN|PASSWORD)$/i,/^AWS_(SECRET_ACCESS_KEY|SECRET_KEY|SESSION_TOKEN)$/i,/^(GH|GITHUB)_TOKEN$/i,/^(AZURE|AZURE_OPENAI|COHERE|AI_GATEWAY|OPENROUTER)_API_KEY$/i,/_?(API_KEY|TOKEN|PASSWORD|PRIVATE_KEY|SECRET)$/i],ALLOWED_ENV_VAR_PATTERNS=[/^LANG$/,/^LC_.*$/i,/^PATH$/i,/^HOME$/i,/^USER$/i,/^SHELL$/i,/^TERM$/i,/^TZ$/i,/^NODE_ENV$/i];function validateEnvVarValue(value){if(value.includes(`\0`))return`Contains null bytes`;if(value.length>32768)return`Value exceeds maximum length`;if(/^[A-Za-z0-9+/=]{80,}$/.test(value))return`Value looks like base64-encoded credential data`}function matchesAnyPattern(value,patterns){return patterns.some(pattern=>pattern.test(value))}function sanitizeEnvVars(envVars,options={}){let allowed={},blocked=[],warnings=[],blockedPatterns=[...BLOCKED_ENV_VAR_PATTERNS,...options.customBlockedPatterns??[]],allowedPatterns=[...ALLOWED_ENV_VAR_PATTERNS,...options.customAllowedPatterns??[]];for(let[rawKey,value]of Object.entries(envVars)){let key=rawKey.trim();if(!key)continue;if(matchesAnyPattern(key,blockedPatterns)){blocked.push(key);continue}if(options.strictMode&&!matchesAnyPattern(key,allowedPatterns)){blocked.push(key);continue}let warning=validateEnvVarValue(value);if(warning){if(warning===`Contains null bytes`){blocked.push(key);continue}warnings.push(`${key}: ${warning}`)}allowed[key]=value}return{allowed,blocked,warnings}}const CLEANUP_SIGNALS=[`SIGINT`,`SIGTERM`,`SIGQUIT`,`SIGABRT`],CLEANUP_STATE_KEY=Symbol.for(`builderforce.sessionWriteLockCleanupState`),HELD_LOCKS_KEY=Symbol.for(`builderforce.sessionWriteLockHeldLocks`),WATCHDOG_STATE_KEY=Symbol.for(`builderforce.sessionWriteLockWatchdogState`),DEFAULT_MAX_HOLD_MS=300*1e3,DEFAULT_WATCHDOG_INTERVAL_MS=6e4,MAX_LOCK_HOLD_MS=2147e6,HELD_LOCKS=resolveProcessScopedMap(HELD_LOCKS_KEY);function resolveCleanupState(){let proc=process;return proc[CLEANUP_STATE_KEY]||(proc[CLEANUP_STATE_KEY]={registered:!1,cleanupHandlers:new Map}),proc[CLEANUP_STATE_KEY]}function resolveWatchdogState(){let proc=process;return proc[WATCHDOG_STATE_KEY]||(proc[WATCHDOG_STATE_KEY]={started:!1,intervalMs:DEFAULT_WATCHDOG_INTERVAL_MS}),proc[WATCHDOG_STATE_KEY]}function resolvePositiveMs(value,fallback,opts={}){return typeof value!=`number`||Number.isNaN(value)||value<=0?fallback:value===1/0?opts.allowInfinity?value:fallback:Number.isFinite(value)?value:fallback}function resolveSessionLockMaxHoldFromTimeout(params){let minMs=resolvePositiveMs(params.minMs,DEFAULT_MAX_HOLD_MS),timeoutMs=resolvePositiveMs(params.timeoutMs,minMs,{allowInfinity:!0});if(timeoutMs===1/0)return MAX_LOCK_HOLD_MS;let graceMs=resolvePositiveMs(params.graceMs,12e4);return Math.min(MAX_LOCK_HOLD_MS,Math.max(minMs,timeoutMs+graceMs))}async function releaseHeldLock(normalizedSessionFile,held,opts={}){if(HELD_LOCKS.get(normalizedSessionFile)!==held)return!1;if(opts.force)held.count=0;else if(--held.count,held.count>0)return!1;if(held.releasePromise)return await held.releasePromise.catch(()=>void 0),!0;HELD_LOCKS.delete(normalizedSessionFile),held.releasePromise=(async()=>{try{await held.handle.close()}catch{}try{await fs.rm(held.lockPath,{force:!0})}catch{}})();try{return await held.releasePromise,!0}finally{held.releasePromise=void 0}}function releaseAllLocksSync(){for(let[sessionFile,held]of HELD_LOCKS){try{typeof held.handle.close==`function`&&held.handle.close().catch(()=>{})}catch{}try{fs$1.rmSync(held.lockPath,{force:!0})}catch{}HELD_LOCKS.delete(sessionFile)}}async function runLockWatchdogCheck(nowMs=Date.now()){let released=0;for(let[sessionFile,held]of HELD_LOCKS.entries()){let heldForMs=nowMs-held.acquiredAt;heldForMs<=held.maxHoldMs||(console.warn(`[session-write-lock] releasing lock held for ${heldForMs}ms (max=${held.maxHoldMs}ms): ${held.lockPath}`),await releaseHeldLock(sessionFile,held,{force:!0})&&(released+=1))}return released}function ensureWatchdogStarted(intervalMs){let watchdogState=resolveWatchdogState();watchdogState.started||(watchdogState.started=!0,watchdogState.intervalMs=intervalMs,watchdogState.timer=setInterval(()=>{runLockWatchdogCheck().catch(()=>{})},intervalMs),watchdogState.timer.unref?.())}function handleTerminationSignal(signal){releaseAllLocksSync();let cleanupState=resolveCleanupState();if(process.listenerCount(signal)===1){let handler=cleanupState.cleanupHandlers.get(signal);handler&&(process.off(signal,handler),cleanupState.cleanupHandlers.delete(signal));try{process.kill(process.pid,signal)}catch{}}}function registerCleanupHandlers(){let cleanupState=resolveCleanupState();cleanupState.registered||(cleanupState.registered=!0,process.on(`exit`,()=>{releaseAllLocksSync()})),ensureWatchdogStarted(DEFAULT_WATCHDOG_INTERVAL_MS);for(let signal of CLEANUP_SIGNALS)if(!cleanupState.cleanupHandlers.has(signal))try{let handler=()=>handleTerminationSignal(signal);cleanupState.cleanupHandlers.set(signal,handler),process.on(signal,handler)}catch{}}async function readLockPayload(lockPath){try{let raw=await fs.readFile(lockPath,`utf8`),parsed=JSON.parse(raw),payload={};return typeof parsed.pid==`number`&&(payload.pid=parsed.pid),typeof parsed.createdAt==`string`&&(payload.createdAt=parsed.createdAt),payload}catch{return null}}function inspectLockPayload(payload,staleMs,nowMs){let pid=typeof payload?.pid==`number`?payload.pid:null,pidAlive=pid===null?!1:isPidAlive(pid),createdAt=typeof payload?.createdAt==`string`?payload.createdAt:null,createdAtMs=createdAt?Date.parse(createdAt):NaN,ageMs=Number.isFinite(createdAtMs)?Math.max(0,nowMs-createdAtMs):null,staleReasons=[];return pid===null?staleReasons.push(`missing-pid`):pidAlive||staleReasons.push(`dead-pid`),ageMs===null?staleReasons.push(`invalid-createdAt`):ageMs>staleMs&&staleReasons.push(`too-old`),{pid,pidAlive,createdAt,ageMs,stale:staleReasons.length>0,staleReasons}}async function acquireSessionWriteLock(params){registerCleanupHandlers();let timeoutMs=resolvePositiveMs(params.timeoutMs,1e4,{allowInfinity:!0}),staleMs=resolvePositiveMs(params.staleMs,18e5),maxHoldMs=resolvePositiveMs(params.maxHoldMs,DEFAULT_MAX_HOLD_MS),sessionFile=path.resolve(params.sessionFile),sessionDir=path.dirname(sessionFile);await fs.mkdir(sessionDir,{recursive:!0});let normalizedDir=sessionDir;try{normalizedDir=await fs.realpath(sessionDir)}catch{}let normalizedSessionFile=path.join(normalizedDir,path.basename(sessionFile)),lockPath=`${normalizedSessionFile}.lock`,allowReentrant=params.allowReentrant??!0,held=HELD_LOCKS.get(normalizedSessionFile);if(allowReentrant&&held)return held.count+=1,{release:async()=>{await releaseHeldLock(normalizedSessionFile,held)}};let startedAt=Date.now(),attempt=0;for(;Date.now()-startedAt<timeoutMs;){attempt+=1;try{let handle=await fs.open(lockPath,`wx`),createdAt=new Date().toISOString();await handle.writeFile(JSON.stringify({pid:process.pid,createdAt},null,2),`utf8`);let createdHeld={count:1,handle,lockPath,acquiredAt:Date.now(),maxHoldMs};return HELD_LOCKS.set(normalizedSessionFile,createdHeld),{release:async()=>{await releaseHeldLock(normalizedSessionFile,createdHeld)}}}catch(err){if(err.code!==`EEXIST`)throw err;if(inspectLockPayload(await readLockPayload(lockPath),staleMs,Date.now()).stale){await fs.rm(lockPath,{force:!0});continue}let delay=Math.min(1e3,50*attempt);await new Promise(r=>setTimeout(r,delay))}}let payload=await readLockPayload(lockPath),owner=typeof payload?.pid==`number`?`pid=${payload.pid}`:`unknown`;throw Error(`session file locked (timeout ${timeoutMs}ms): ${owner} ${lockPath}`)}[...CLEANUP_SIGNALS];function isRecord(value){return!!value&&typeof value==`object`}function isRegistryEntry(value){return isRecord(value)&&typeof value.containerName==`string`}function isRegistryFile(value){if(!isRecord(value))return!1;let maybeEntries=value.entries;return Array.isArray(maybeEntries)&&maybeEntries.every(isRegistryEntry)}async function withRegistryLock(registryPath,fn){let lock=await acquireSessionWriteLock({sessionFile:registryPath,allowReentrant:!1});try{return await fn()}finally{await lock.release()}}async function readRegistryFromFile(registryPath,mode){try{let raw=await fs.readFile(registryPath,`utf-8`),parsed=JSON.parse(raw);if(isRegistryFile(parsed))return parsed;if(mode===`fallback`)return{entries:[]};throw Error(`Invalid sandbox registry format: ${registryPath}`)}catch(error){if(error?.code===`ENOENT`||mode===`fallback`)return{entries:[]};throw error instanceof Error?error:Error(`Failed to read sandbox registry file: ${registryPath}`,{cause:error})}}async function writeRegistryFile(registryPath,registry){await fs.mkdir(SANDBOX_STATE_DIR,{recursive:!0});let payload=`${JSON.stringify(registry,null,2)}\n`,registryDir=path.dirname(registryPath),tempPath=path.join(registryDir,`${path.basename(registryPath)}.${crypto.randomUUID()}.tmp`);await fs.writeFile(tempPath,payload,`utf-8`);try{await fs.rename(tempPath,registryPath)}catch(error){throw await fs.rm(tempPath,{force:!0}),error}}async function readRegistry(){return await readRegistryFromFile(SANDBOX_REGISTRY_PATH,`fallback`)}function upsertEntry(entries,entry){let existing=entries.find(item=>item.containerName===entry.containerName),next=entries.filter(item=>item.containerName!==entry.containerName);return next.push({...entry,createdAtMs:existing?.createdAtMs??entry.createdAtMs,image:existing?.image??entry.image,configHash:entry.configHash??existing?.configHash}),next}function removeEntry(entries,containerName){return entries.filter(entry=>entry.containerName!==containerName)}async function withRegistryMutation(registryPath,mutate){await withRegistryLock(registryPath,async()=>{let next=mutate((await readRegistryFromFile(registryPath,`strict`)).entries);next!==null&&await writeRegistryFile(registryPath,{entries:next})})}async function updateRegistry(entry){await withRegistryMutation(SANDBOX_REGISTRY_PATH,entries=>upsertEntry(entries,entry))}async function removeRegistryEntry(containerName){await withRegistryMutation(SANDBOX_REGISTRY_PATH,entries=>{let next=removeEntry(entries,containerName);return next.length===entries.length?null:next})}async function readBrowserRegistry(){return await readRegistryFromFile(SANDBOX_BROWSER_REGISTRY_PATH,`fallback`)}async function updateBrowserRegistry(entry){await withRegistryMutation(SANDBOX_BROWSER_REGISTRY_PATH,entries=>upsertEntry(entries,entry))}async function removeBrowserRegistryEntry(containerName){await withRegistryMutation(SANDBOX_BROWSER_REGISTRY_PATH,entries=>{let next=removeEntry(entries,containerName);return next.length===entries.length?null:next})}function slugifySessionKey(value){let trimmed=value.trim()||`session`,hash=hashTextSha256(trimmed).slice(0,8);return`${trimmed.toLowerCase().replace(/[^a-z0-9._-]+/g,`-`).replace(/^-+|-+$/g,``).slice(0,32)||`session`}-${hash}`}function resolveSandboxWorkspaceDir(root,sessionKey){let resolvedRoot=resolveUserPath(root),slug=slugifySessionKey(sessionKey);return path.join(resolvedRoot,slug)}function resolveSandboxScopeKey(scope,sessionKey){let trimmed=sessionKey.trim()||`main`;return scope===`shared`?`shared`:scope===`session`?trimmed:`agent:${resolveAgentIdFromSessionKey(trimmed)}`}function resolveSandboxAgentId(scopeKey){let trimmed=scopeKey.trim();if(!trimmed||trimmed===`shared`)return;let parts=trimmed.split(`:`).filter(Boolean);return parts[0]===`agent`&&parts[1]?normalizeAgentId(parts[1]):resolveAgentIdFromSessionKey(trimmed)}const BLOCKED_HOST_PATHS=[`/etc`,`/private/etc`,`/proc`,`/sys`,`/dev`,`/root`,`/boot`,`/run`,`/var/run`,`/private/var/run`,`/var/run/docker.sock`,`/private/var/run/docker.sock`,`/run/docker.sock`],BLOCKED_NETWORK_MODES=new Set([`host`]),BLOCKED_SECCOMP_PROFILES=new Set([`unconfined`]),BLOCKED_APPARMOR_PROFILES=new Set([`unconfined`]);function parseBindSourcePath(bind){let trimmed=bind.trim(),firstColon=trimmed.indexOf(`:`);return firstColon<=0?trimmed:trimmed.slice(0,firstColon)}function normalizeHostPath(raw){let trimmed=raw.trim();return normalizeBaseUrl$1(posix.normalize(trimmed))||`/`}function getBlockedBindReason(bind){let sourceRaw=parseBindSourcePath(bind);return sourceRaw.startsWith(`/`)?getBlockedReasonForSourcePath(normalizeHostPath(sourceRaw)):{kind:`non_absolute`,sourcePath:sourceRaw}}function getBlockedReasonForSourcePath(sourceNormalized){if(sourceNormalized===`/`)return{kind:`covers`,blockedPath:`/`};for(let blocked of BLOCKED_HOST_PATHS)if(sourceNormalized===blocked||sourceNormalized.startsWith(blocked+`/`))return{kind:`targets`,blockedPath:blocked};return null}function tryRealpathAbsolute(path){if(!path.startsWith(`/`)||!existsSync(path))return path;try{return normalizeHostPath(realpathSync.native(path))}catch{return path}}function formatBindBlockedError(params){if(params.reason.kind===`non_absolute`)return Error(`Sandbox security: bind mount "${params.bind}" uses a non-absolute source path "${params.reason.sourcePath}". Only absolute POSIX paths are supported for sandbox binds.`);let verb=params.reason.kind===`covers`?`covers`:`targets`;return Error(`Sandbox security: bind mount "${params.bind}" ${verb} blocked path "${params.reason.blockedPath}". Mounting system directories (or Docker socket paths) into sandbox containers is not allowed. Use project-specific paths instead (e.g. /home/user/myproject).`)}function validateBindMounts(binds){if(binds?.length)for(let rawBind of binds){let bind=rawBind.trim();if(!bind)continue;let blocked=getBlockedBindReason(bind);if(blocked)throw formatBindBlockedError({bind,reason:blocked});let sourceNormalized=normalizeHostPath(parseBindSourcePath(bind)),sourceReal=tryRealpathAbsolute(sourceNormalized);if(sourceReal!==sourceNormalized){let reason=getBlockedReasonForSourcePath(sourceReal);if(reason)throw formatBindBlockedError({bind,reason})}}}function validateNetworkMode(network){if(network&&BLOCKED_NETWORK_MODES.has(network.trim().toLowerCase()))throw Error(`Sandbox security: network mode "${network}" is blocked. Network "host" mode bypasses container network isolation. Use "bridge" or "none" instead.`)}function validateSeccompProfile(profile){if(profile&&BLOCKED_SECCOMP_PROFILES.has(profile.trim().toLowerCase()))throw Error(`Sandbox security: seccomp profile "${profile}" is blocked. Disabling seccomp removes syscall filtering and weakens sandbox isolation. Use a custom seccomp profile file or omit this setting.`)}function validateApparmorProfile(profile){if(profile&&BLOCKED_APPARMOR_PROFILES.has(profile.trim().toLowerCase()))throw Error(`Sandbox security: apparmor profile "${profile}" is blocked. Disabling AppArmor removes mandatory access controls and weakens sandbox isolation. Use a named AppArmor profile or omit this setting.`)}function validateSandboxSecurity(cfg){validateBindMounts(cfg.binds),validateNetworkMode(cfg.network),validateSeccompProfile(cfg.seccompProfile),validateApparmorProfile(cfg.apparmorProfile)}function createAbortError(){let err=Error(`Aborted`);return err.name=`AbortError`,err}function execDockerRaw(args,opts){return new Promise((resolve,reject)=>{let child=spawn(`docker`,args,{stdio:[`pipe`,`pipe`,`pipe`]}),stdoutChunks=[],stderrChunks=[],aborted=!1,signal=opts?.signal,handleAbort=()=>{aborted||(aborted=!0,child.kill(`SIGTERM`))};signal&&(signal.aborted?handleAbort():signal.addEventListener(`abort`,handleAbort,{once:!0})),child.stdout?.on(`data`,chunk=>{stdoutChunks.push(Buffer.isBuffer(chunk)?chunk:Buffer.from(chunk))}),child.stderr?.on(`data`,chunk=>{stderrChunks.push(Buffer.isBuffer(chunk)?chunk:Buffer.from(chunk))}),child.on(`error`,error=>{signal&&signal.removeEventListener(`abort`,handleAbort),reject(error)}),child.on(`close`,code=>{signal&&signal.removeEventListener(`abort`,handleAbort);let stdout=Buffer.concat(stdoutChunks),stderr=Buffer.concat(stderrChunks);if(aborted||signal?.aborted){reject(createAbortError());return}let exitCode=code??0;if(exitCode!==0&&!opts?.allowFailure){let message=stderr.length>0?stderr.toString(`utf8`).trim():``;reject(Object.assign(Error(message||`docker ${args.join(` `)} failed`),{code:exitCode,stdout,stderr}));return}resolve({stdout,stderr,code:exitCode})});let stdin=child.stdin;stdin&&(opts?.input===void 0?stdin.end():stdin.end(opts.input))})}async function execDocker(args,opts){let result=await execDockerRaw(args,opts);return{stdout:result.stdout.toString(`utf8`),stderr:result.stderr.toString(`utf8`),code:result.code}}async function readDockerContainerLabel(containerName,label){let result=await execDocker([`inspect`,`-f`,`{{ index .Config.Labels "${label}" }}`,containerName],{allowFailure:!0});if(result.code!==0)return null;let raw=result.stdout.trim();return!raw||raw===`<no value>`?null:raw}async function readDockerPort(containerName,port){let result=await execDocker([`port`,containerName,`${port}/tcp`],{allowFailure:!0});if(result.code!==0)return null;let match=(result.stdout.trim().split(/\r?\n/)[0]??``).match(/:(\d+)\s*$/);if(!match)return null;let mapped=Number.parseInt(match[1]??``,10);return Number.isFinite(mapped)?mapped:null}async function dockerImageExists(image){let result=await execDocker([`image`,`inspect`,image],{allowFailure:!0});if(result.code===0)return!0;let stderr=result.stderr.trim();if(stderr.includes(`No such image`))return!1;throw Error(`Failed to inspect sandbox image: ${stderr}`)}async function ensureDockerImage(image){if(!await dockerImageExists(image)){if(image===DEFAULT_SANDBOX_IMAGE){await execDocker([`pull`,`debian:bookworm-slim`]),await execDocker([`tag`,`debian:bookworm-slim`,DEFAULT_SANDBOX_IMAGE]);return}throw Error(`Sandbox image not found: ${image}. Build or pull it first.`)}}async function dockerContainerState(name){let result=await execDocker([`inspect`,`-f`,`{{.State.Running}}`,name],{allowFailure:!0});return result.code===0?{exists:!0,running:result.stdout.trim()===`true`}:{exists:!1,running:!1}}function normalizeDockerLimit(value){return value==null?void 0:typeof value==`number`?Number.isFinite(value)?String(value):void 0:value.trim()||void 0}function formatUlimitValue(name,value){if(!name.trim())return null;if(typeof value==`number`||typeof value==`string`){let raw=String(value).trim();return raw?`${name}=${raw}`:null}let soft=typeof value.soft==`number`?Math.max(0,value.soft):void 0,hard=typeof value.hard==`number`?Math.max(0,value.hard):void 0;return soft===void 0&&hard===void 0?null:soft===void 0?`${name}=${hard}`:hard===void 0?`${name}=${soft}`:`${name}=${soft}:${hard}`}function buildSandboxCreateArgs(params){validateSandboxSecurity(params.cfg);let createdAtMs=params.createdAtMs??Date.now(),args=[`create`,`--name`,params.name];args.push(`--label`,`builderforce.sandbox=1`),args.push(`--label`,`builderforce.sessionKey=${params.scopeKey}`),args.push(`--label`,`builderforce.createdAtMs=${createdAtMs}`),params.configHash&&args.push(`--label`,`builderforce.configHash=${params.configHash}`);for(let[key,value]of Object.entries(params.labels??{}))key&&value&&args.push(`--label`,`${key}=${value}`);params.cfg.readOnlyRoot&&args.push(`--read-only`);for(let entry of params.cfg.tmpfs)args.push(`--tmpfs`,entry);params.cfg.network&&args.push(`--network`,params.cfg.network),params.cfg.user&&args.push(`--user`,params.cfg.user);let envSanitization=sanitizeEnvVars(params.cfg.env??{});envSanitization.blocked.length>0&&console.warn(`[Security] Blocked sensitive environment variables:`,envSanitization.blocked.join(`, `)),envSanitization.warnings.length>0&&console.warn(`[Security] Suspicious environment variables:`,envSanitization.warnings);for(let[key,value]of Object.entries(envSanitization.allowed))args.push(`--env`,`${key}=${value}`);for(let cap of params.cfg.capDrop)args.push(`--cap-drop`,cap);args.push(`--security-opt`,`no-new-privileges`),params.cfg.seccompProfile&&args.push(`--security-opt`,`seccomp=${params.cfg.seccompProfile}`),params.cfg.apparmorProfile&&args.push(`--security-opt`,`apparmor=${params.cfg.apparmorProfile}`);for(let entry of params.cfg.dns??[])entry.trim()&&args.push(`--dns`,entry);for(let entry of params.cfg.extraHosts??[])entry.trim()&&args.push(`--add-host`,entry);typeof params.cfg.pidsLimit==`number`&¶ms.cfg.pidsLimit>0&&args.push(`--pids-limit`,String(params.cfg.pidsLimit));let memory=normalizeDockerLimit(params.cfg.memory);memory&&args.push(`--memory`,memory);let memorySwap=normalizeDockerLimit(params.cfg.memorySwap);memorySwap&&args.push(`--memory-swap`,memorySwap),typeof params.cfg.cpus==`number`&¶ms.cfg.cpus>0&&args.push(`--cpus`,String(params.cfg.cpus));for(let[name,value]of Object.entries(params.cfg.ulimits??{})){let formatted=formatUlimitValue(name,value);formatted&&args.push(`--ulimit`,formatted)}if(params.cfg.binds?.length)for(let bind of params.cfg.binds)args.push(`-v`,bind);return args}async function createSandboxContainer(params){let{name,cfg,workspaceDir,scopeKey}=params;await ensureDockerImage(cfg.image);let args=buildSandboxCreateArgs({name,cfg,scopeKey,configHash:params.configHash});args.push(`--workdir`,cfg.workdir);let mainMountSuffix=params.workspaceAccess===`ro`&&workspaceDir===params.agentWorkspaceDir?`:ro`:``;if(args.push(`-v`,`${workspaceDir}:${cfg.workdir}${mainMountSuffix}`),params.workspaceAccess!==`none`&&workspaceDir!==params.agentWorkspaceDir){let agentMountSuffix=params.workspaceAccess===`ro`?`:ro`:``;args.push(`-v`,`${params.agentWorkspaceDir}:${SANDBOX_AGENT_WORKSPACE_MOUNT}${agentMountSuffix}`)}args.push(cfg.image,`sleep`,`infinity`),await execDocker(args),await execDocker([`start`,name]),cfg.setupCommand?.trim()&&await execDocker([`exec`,`-i`,name,`sh`,`-lc`,cfg.setupCommand])}async function readContainerConfigHash(containerName){return await readDockerContainerLabel(containerName,`builderforce.configHash`)}function formatSandboxRecreateHint(params){return params.scope===`session`?formatCliCommand(`builderforce sandbox recreate --session ${params.sessionKey}`):params.scope===`agent`?formatCliCommand(`builderforce sandbox recreate --agent ${resolveSandboxAgentId(params.sessionKey)??`main`}`):formatCliCommand(`builderforce sandbox recreate --all`)}async function ensureSandboxContainer(params){let scopeKey=resolveSandboxScopeKey(params.cfg.scope,params.sessionKey),slug=params.cfg.scope===`shared`?`shared`:slugifySessionKey(scopeKey),containerName=`${params.cfg.docker.containerPrefix}${slug}`.slice(0,63),expectedHash=computeSandboxConfigHash({docker:params.cfg.docker,workspaceAccess:params.cfg.workspaceAccess,workspaceDir:params.workspaceDir,agentWorkspaceDir:params.agentWorkspaceDir}),now=Date.now(),state=await dockerContainerState(containerName),hasContainer=state.exists,running=state.running,currentHash=null,hashMismatch=!1,registryEntry;if(hasContainer&&(registryEntry=(await readRegistry()).entries.find(entry=>entry.containerName===containerName),currentHash=await readContainerConfigHash(containerName),currentHash||=registryEntry?.configHash??null,hashMismatch=!currentHash||currentHash!==expectedHash,hashMismatch)){let lastUsedAtMs=registryEntry?.lastUsedAtMs;if(running&&(typeof lastUsedAtMs!=`number`||now-lastUsedAtMs<3e5)){let hint=formatSandboxRecreateHint({scope:params.cfg.scope,sessionKey:scopeKey});defaultRuntime.log(`Sandbox config changed for ${containerName} (recently used). Recreate to apply: ${hint}`)}else await execDocker([`rm`,`-f`,containerName],{allowFailure:!0}),hasContainer=!1,running=!1}return hasContainer?running||await execDocker([`start`,containerName]):await createSandboxContainer({name:containerName,cfg:params.cfg.docker,workspaceDir:params.workspaceDir,workspaceAccess:params.cfg.workspaceAccess,agentWorkspaceDir:params.agentWorkspaceDir,scopeKey,configHash:expectedHash}),await updateRegistry({containerName,sessionKey:scopeKey,createdAtMs:now,lastUsedAtMs:now,image:params.cfg.docker.image,configHash:hashMismatch&&running?currentHash??void 0:expectedHash}),containerName}async function waitForSandboxCdp(params){let deadline=Date.now()+Math.max(0,params.timeoutMs),url=`http://127.0.0.1:${params.cdpPort}/json/version`;for(;Date.now()<deadline;){try{let ctrl=new AbortController,t=setTimeout(ctrl.abort.bind(ctrl),1e3);try{if((await fetch(url,{signal:ctrl.signal})).ok)return!0}finally{clearTimeout(t)}}catch{}await new Promise(r=>setTimeout(r,150))}return!1}function buildSandboxBrowserResolvedConfig(params){return{enabled:!0,evaluateEnabled:params.evaluateEnabled,controlPort:params.controlPort,cdpProtocol:`http`,cdpHost:`127.0.0.1`,cdpIsLoopback:!0,remoteCdpTimeoutMs:1500,remoteCdpHandshakeTimeoutMs:3e3,color:DEFAULT_BUILDERFORCE_AGENTS_BROWSER_COLOR,executablePath:void 0,headless:params.headless,noSandbox:!1,attachOnly:!0,defaultProfile:DEFAULT_BUILDERFORCE_AGENTS_BROWSER_PROFILE_NAME,extraArgs:[],profiles:{[DEFAULT_BUILDERFORCE_AGENTS_BROWSER_PROFILE_NAME]:{cdpPort:params.cdpPort,color:DEFAULT_BUILDERFORCE_AGENTS_BROWSER_COLOR}}}}async function ensureSandboxBrowserImage(image){if((await execDocker([`image`,`inspect`,image],{allowFailure:!0})).code!==0)throw Error(`Sandbox browser image not found: ${image}. Build it with scripts/sandbox-browser-setup.sh.`)}async function ensureSandboxBrowser(params){if(!params.cfg.browser.enabled||!isToolAllowed(params.cfg.tools,`browser`))return null;let slug=params.cfg.scope===`shared`?`shared`:slugifySessionKey(params.scopeKey),containerName=`${params.cfg.browser.containerPrefix}${slug}`.slice(0,63),state=await dockerContainerState(containerName),browserImage=params.cfg.browser.image??DEFAULT_SANDBOX_BROWSER_IMAGE,browserDockerCfg=resolveSandboxBrowserDockerCreateConfig({docker:params.cfg.docker,browser:{...params.cfg.browser,image:browserImage}}),expectedHash=computeSandboxBrowserConfigHash({docker:browserDockerCfg,browser:{cdpPort:params.cfg.browser.cdpPort,vncPort:params.cfg.browser.vncPort,noVncPort:params.cfg.browser.noVncPort,headless:params.cfg.browser.headless,enableNoVnc:params.cfg.browser.enableNoVnc},workspaceAccess:params.cfg.workspaceAccess,workspaceDir:params.workspaceDir,agentWorkspaceDir:params.agentWorkspaceDir}),now=Date.now(),hasContainer=state.exists,running=state.running,currentHash=null,hashMismatch=!1;if(hasContainer){let registryEntry=(await readBrowserRegistry()).entries.find(entry=>entry.containerName===containerName);if(currentHash=await readDockerContainerLabel(containerName,`builderforce.configHash`),hashMismatch=!currentHash||currentHash!==expectedHash,currentHash||(currentHash=registryEntry?.configHash??null,hashMismatch=!currentHash||currentHash!==expectedHash),hashMismatch){let lastUsedAtMs=registryEntry?.lastUsedAtMs;if(running&&(typeof lastUsedAtMs!=`number`||now-lastUsedAtMs<3e5)){let hint=params.cfg.scope===`session`?`builderforce sandbox recreate --browser --session ${params.scopeKey}`:params.cfg.scope===`agent`?`builderforce sandbox recreate --browser --agent ${resolveSandboxAgentId(params.scopeKey)??`main`}`:`builderforce sandbox recreate --browser --all`;defaultRuntime.log(`Sandbox browser config changed for ${containerName} (recently used). Recreate to apply: ${hint}`)}else await execDocker([`rm`,`-f`,containerName],{allowFailure:!0}),hasContainer=!1,running=!1}}if(hasContainer)running||await execDocker([`start`,containerName]);else{await ensureSandboxBrowserImage(browserImage);let args=buildSandboxCreateArgs({name:containerName,cfg:browserDockerCfg,scopeKey:params.scopeKey,labels:{"builderforce.sandboxBrowser":`1`},configHash:expectedHash}),mainMountSuffix=params.cfg.workspaceAccess===`ro`&¶ms.workspaceDir===params.agentWorkspaceDir?`:ro`:``;if(args.push(`-v`,`${params.workspaceDir}:${params.cfg.docker.workdir}${mainMountSuffix}`),params.cfg.workspaceAccess!==`none`&¶ms.workspaceDir!==params.agentWorkspaceDir){let agentMountSuffix=params.cfg.workspaceAccess===`ro`?`:ro`:``;args.push(`-v`,`${params.agentWorkspaceDir}:${SANDBOX_AGENT_WORKSPACE_MOUNT}${agentMountSuffix}`)}args.push(`-p`,`127.0.0.1::${params.cfg.browser.cdpPort}`),params.cfg.browser.enableNoVnc&&!params.cfg.browser.headless&&args.push(`-p`,`127.0.0.1::${params.cfg.browser.noVncPort}`),args.push(`-e`,`BUILDERFORCE_AGENTS_BROWSER_HEADLESS=${params.cfg.browser.headless?`1`:`0`}`),args.push(`-e`,`BUILDERFORCE_AGENTS_BROWSER_ENABLE_NOVNC=${params.cfg.browser.enableNoVnc?`1`:`0`}`),args.push(`-e`,`BUILDERFORCE_AGENTS_BROWSER_CDP_PORT=${params.cfg.browser.cdpPort}`),args.push(`-e`,`BUILDERFORCE_AGENTS_BROWSER_VNC_PORT=${params.cfg.browser.vncPort}`),args.push(`-e`,`BUILDERFORCE_AGENTS_BROWSER_NOVNC_PORT=${params.cfg.browser.noVncPort}`),args.push(browserImage),await execDocker(args),await execDocker([`start`,containerName])}let mappedCdp=await readDockerPort(containerName,params.cfg.browser.cdpPort);if(!mappedCdp)throw Error(`Failed to resolve CDP port mapping for ${containerName}.`);let mappedNoVnc=params.cfg.browser.enableNoVnc&&!params.cfg.browser.headless?await readDockerPort(containerName,params.cfg.browser.noVncPort):null,existing=BROWSER_BRIDGES.get(params.scopeKey),existingProfile=existing?resolveProfile(existing.bridge.state.resolved,DEFAULT_BUILDERFORCE_AGENTS_BROWSER_PROFILE_NAME):null,desiredAuthToken=params.bridgeAuth?.token?.trim()||void 0,desiredAuthPassword=params.bridgeAuth?.password?.trim()||void 0;!desiredAuthToken&&!desiredAuthPassword&&(desiredAuthToken=existing?.authToken,desiredAuthPassword=existing?.authPassword,!desiredAuthToken&&!desiredAuthPassword&&(desiredAuthToken=crypto.randomBytes(24).toString(`hex`)));let shouldReuse=existing&&existing.containerName===containerName&&existingProfile?.cdpPort===mappedCdp,authMatches=!existing||existing.authToken===desiredAuthToken&&existing.authPassword===desiredAuthPassword;existing&&!shouldReuse&&(await stopBrowserBridgeServer(existing.bridge.server).catch(()=>void 0),BROWSER_BRIDGES.delete(params.scopeKey)),existing&&shouldReuse&&!authMatches&&(await stopBrowserBridgeServer(existing.bridge.server).catch(()=>void 0),BROWSER_BRIDGES.delete(params.scopeKey));let bridge=shouldReuse&&authMatches&&existing?existing.bridge:null,resolvedBridge=await(async()=>{if(bridge)return bridge;let onEnsureAttachTarget=params.cfg.browser.autoStart?async()=>{let state=await dockerContainerState(containerName);if(state.exists&&!state.running&&await execDocker([`start`,containerName]),!await waitForSandboxCdp({cdpPort:mappedCdp,timeoutMs:params.cfg.browser.autoStartTimeoutMs}))throw Error(`Sandbox browser CDP did not become reachable on 127.0.0.1:${mappedCdp} within ${params.cfg.browser.autoStartTimeoutMs}ms.`)}:void 0;return await startBrowserBridgeServer({resolved:buildSandboxBrowserResolvedConfig({controlPort:0,cdpPort:mappedCdp,headless:params.cfg.browser.headless,evaluateEnabled:params.evaluateEnabled??DEFAULT_BROWSER_EVALUATE_ENABLED}),authToken:desiredAuthToken,authPassword:desiredAuthPassword,onEnsureAttachTarget})})();(!shouldReuse||!authMatches)&&BROWSER_BRIDGES.set(params.scopeKey,{bridge:resolvedBridge,containerName,authToken:desiredAuthToken,authPassword:desiredAuthPassword}),await updateBrowserRegistry({containerName,sessionKey:params.scopeKey,createdAtMs:now,lastUsedAtMs:now,image:browserImage,configHash:hashMismatch&&running?currentHash??void 0:expectedHash,cdpPort:mappedCdp,noVncPort:mappedNoVnc??void 0});let noVncUrl=mappedNoVnc&¶ms.cfg.browser.enableNoVnc&&!params.cfg.browser.headless?`http://127.0.0.1:${mappedNoVnc}/vnc.html?autoconnect=1&resize=remote`:void 0;return{bridgeUrl:resolvedBridge.baseUrl,noVncUrl,containerName}}function parseSandboxBindMount(spec){let trimmed=spec.trim();if(!trimmed)return null;let parsed=splitBindSpec(trimmed);if(!parsed)return null;let hostToken=parsed.host.trim(),containerToken=parsed.container.trim();if(!hostToken||!containerToken||!path.posix.isAbsolute(containerToken))return null;let optionsToken=parsed.options.trim().toLowerCase(),writable=!(optionsToken?optionsToken.split(`,`).map(entry=>entry.trim()).filter(Boolean):[]).includes(`ro`);return{hostRoot:path.resolve(hostToken),containerRoot:normalizeContainerPath(containerToken),writable}}function splitBindSpec(spec){let separator=getHostContainerSeparatorIndex(spec);if(separator===-1)return null;let host=spec.slice(0,separator),rest=spec.slice(separator+1),optionsStart=rest.indexOf(`:`);return optionsStart===-1?{host,container:rest,options:``}:{host,container:rest.slice(0,optionsStart),options:rest.slice(optionsStart+1)}}function getHostContainerSeparatorIndex(spec){let hasDriveLetterPrefix=/^[A-Za-z]:[\\/]/.test(spec);for(let i=hasDriveLetterPrefix?2:0;i<spec.length;i+=1)if(spec[i]===`:`)return i;return-1}function buildSandboxFsMounts(sandbox){let mounts=[{hostRoot:path.resolve(sandbox.workspaceDir),containerRoot:normalizeContainerPath(sandbox.containerWorkdir),writable:sandbox.workspaceAccess===`rw`,source:`workspace`}];sandbox.workspaceAccess!==`none`&&path.resolve(sandbox.agentWorkspaceDir)!==path.resolve(sandbox.workspaceDir)&&mounts.push({hostRoot:path.resolve(sandbox.agentWorkspaceDir),containerRoot:SANDBOX_AGENT_WORKSPACE_MOUNT,writable:sandbox.workspaceAccess===`rw`,source:`agent`});for(let bind of sandbox.docker.binds??[]){let parsed=parseSandboxBindMount(bind);parsed&&mounts.push({hostRoot:parsed.hostRoot,containerRoot:parsed.containerRoot,writable:parsed.writable,source:`bind`})}return dedupeMounts(mounts)}function resolveSandboxFsPathWithMounts(params){let mountsByContainer=[...params.mounts].toSorted((a,b)=>b.containerRoot.length-a.containerRoot.length),mountsByHost=[...params.mounts].toSorted((a,b)=>b.hostRoot.length-a.hostRoot.length),input=params.filePath,inputPosix=normalizePosixInput(input);if(path.posix.isAbsolute(inputPosix)){let containerMount=findMountByContainerPath(mountsByContainer,inputPosix);if(containerMount){let rel=path.posix.relative(containerMount.containerRoot,inputPosix);return{hostPath:rel?path.resolve(containerMount.hostRoot,...toHostSegments(rel)):containerMount.hostRoot,containerPath:rel?path.posix.join(containerMount.containerRoot,rel):containerMount.containerRoot,relativePath:toDisplayRelative({containerPath:rel?path.posix.join(containerMount.containerRoot,rel):containerMount.containerRoot,defaultContainerRoot:params.defaultContainerRoot}),writable:containerMount.writable}}}let hostResolved=resolveSandboxInputPath(input,params.cwd),hostMount=findMountByHostPath(mountsByHost,hostResolved);if(hostMount){let relHost=path.relative(hostMount.hostRoot,hostResolved),relPosix=relHost?relHost.split(path.sep).join(path.posix.sep):``,containerPath=relPosix?path.posix.join(hostMount.containerRoot,relPosix):hostMount.containerRoot;return{hostPath:hostResolved,containerPath,relativePath:toDisplayRelative({containerPath,defaultContainerRoot:params.defaultContainerRoot}),writable:hostMount.writable}}throw resolveSandboxPath({filePath:input,cwd:params.cwd,root:params.defaultWorkspaceRoot}),Error(`Path escapes sandbox root (${params.defaultWorkspaceRoot}): ${input}`)}function dedupeMounts(mounts){let seen=new Set,deduped=[];for(let mount of mounts){let key=`${mount.hostRoot}=>${mount.containerRoot}`;seen.has(key)||(seen.add(key),deduped.push(mount))}return deduped}function findMountByContainerPath(mounts,target){for(let mount of mounts)if(isPathInsidePosix(mount.containerRoot,target))return mount;return null}function findMountByHostPath(mounts,target){for(let mount of mounts)if(isPathInsideHost(mount.hostRoot,target))return mount;return null}function isPathInsidePosix(root,target){let rel=path.posix.relative(root,target);return rel?!(rel.startsWith(`..`)||path.posix.isAbsolute(rel)):!0}function isPathInsideHost(root,target){let rel=path.relative(root,target);return rel?!(rel.startsWith(`..`)||path.isAbsolute(rel)):!0}function toHostSegments(relativePosix){return relativePosix.split(`/`).filter(Boolean)}function toDisplayRelative(params){let rel=path.posix.relative(params.defaultContainerRoot,params.containerPath);return rel?!rel.startsWith(`..`)&&!path.posix.isAbsolute(rel)?rel:params.containerPath:``}function normalizeContainerPath(value){let normalized=path.posix.normalize(value);return normalized===`.`?`/`:normalized}function normalizePosixInput(value){return value.replace(/\\/g,`/`).trim()}function createSandboxFsBridge(params){return new SandboxFsBridgeImpl(params.sandbox)}var SandboxFsBridgeImpl=class{constructor(sandbox){this.sandbox=sandbox,this.mounts=buildSandboxFsMounts(sandbox)}resolvePath(params){let target=this.resolveResolvedPath(params);return{hostPath:target.hostPath,relativePath:target.relativePath,containerPath:target.containerPath}}async readFile(params){let target=this.resolveResolvedPath(params);return(await this.runCommand(`set -eu; cat -- "$1"`,{args:[target.containerPath],signal:params.signal})).stdout}async writeFile(params){let target=this.resolveResolvedPath(params);this.ensureWriteAccess(target,`write files`);let buffer=Buffer.isBuffer(params.data)?params.data:Buffer.from(params.data,params.encoding??`utf8`),script=params.mkdir===!1?`set -eu; cat >"$1"`:`set -eu; dir=$(dirname -- "$1"); if [ "$dir" != "." ]; then mkdir -p -- "$dir"; fi; cat >"$1"`;await this.runCommand(script,{args:[target.containerPath],stdin:buffer,signal:params.signal})}async mkdirp(params){let target=this.resolveResolvedPath(params);this.ensureWriteAccess(target,`create directories`),await this.runCommand(`set -eu; mkdir -p -- "$1"`,{args:[target.containerPath],signal:params.signal})}async remove(params){let target=this.resolveResolvedPath(params);this.ensureWriteAccess(target,`remove files`);let flags=[params.force===!1?``:`-f`,params.recursive?`-r`:``].filter(Boolean),rmCommand=flags.length>0?`rm ${flags.join(` `)}`:`rm`;await this.runCommand(`set -eu; ${rmCommand} -- "$1"`,{args:[target.containerPath],signal:params.signal})}async rename(params){let from=this.resolveResolvedPath({filePath:params.from,cwd:params.cwd}),to=this.resolveResolvedPath({filePath:params.to,cwd:params.cwd});this.ensureWriteAccess(from,`rename files`),this.ensureWriteAccess(to,`rename files`),await this.runCommand(`set -eu; dir=$(dirname -- "$2"); if [ "$dir" != "." ]; then mkdir -p -- "$dir"; fi; mv -- "$1" "$2"`,{args:[from.containerPath,to.containerPath],signal:params.signal})}async stat(params){let target=this.resolveResolvedPath(params),result=await this.runCommand(`set -eu; stat -c "%F|%s|%Y" -- "$1"`,{args:[target.containerPath],signal:params.signal,allowFailure:!0});if(result.code!==0){let stderr=result.stderr.toString(`utf8`);if(stderr.includes(`No such file or directory`))return null;let message=stderr.trim()||`stat failed with code ${result.code}`;throw Error(`stat failed for ${target.containerPath}: ${message}`)}let[typeRaw,sizeRaw,mtimeRaw]=result.stdout.toString(`utf8`).trim().split(`|`),size=Number.parseInt(sizeRaw??`0`,10),mtime=Number.parseInt(mtimeRaw??`0`,10)*1e3;return{type:coerceStatType(typeRaw),size:Number.isFinite(size)?size:0,mtimeMs:Number.isFinite(mtime)?mtime:0}}async runCommand(script,options={}){let dockerArgs=[`exec`,`-i`,this.sandbox.containerName,`sh`,`-c`,script,`builderforce-sandbox-fs`];return options.args?.length&&dockerArgs.push(...options.args),execDockerRaw(dockerArgs,{input:options.stdin,allowFailure:options.allowFailure,signal:options.signal})}ensureWriteAccess(target,action){if(!allowsWrites(this.sandbox.workspaceAccess)||!target.writable)throw Error(`Sandbox path is read-only; cannot ${action}: ${target.containerPath}`)}resolveResolvedPath(params){return resolveSandboxFsPathWithMounts({filePath:params.filePath,cwd:params.cwd??this.sandbox.workspaceDir,defaultWorkspaceRoot:this.sandbox.workspaceDir,defaultContainerRoot:this.sandbox.containerWorkdir,mounts:this.mounts})}};function allowsWrites(access){return access===`rw`}function coerceStatType(typeRaw){if(!typeRaw)return`other`;let normalized=typeRaw.trim().toLowerCase();return normalized.includes(`directory`)?`directory`:normalized.includes(`file`)?`file`:`other`}let lastPruneAtMs=0;function shouldPruneSandboxEntry(cfg,now,entry){let idleHours=cfg.prune.idleHours,maxAgeDays=cfg.prune.maxAgeDays;if(idleHours===0&&maxAgeDays===0)return!1;let idleMs=now-entry.lastUsedAtMs,ageMs=now-entry.createdAtMs;return idleHours>0&&idleMs>idleHours*60*60*1e3||maxAgeDays>0&&ageMs>maxAgeDays*24*60*60*1e3}async function pruneSandboxRegistryEntries(params){let now=Date.now();if(params.cfg.prune.idleHours===0&¶ms.cfg.prune.maxAgeDays===0)return;let registry=await params.read();for(let entry of registry.entries)if(shouldPruneSandboxEntry(params.cfg,now,entry))try{await execDocker([`rm`,`-f`,entry.containerName],{allowFailure:!0})}catch{}finally{await params.remove(entry.containerName),await params.onRemoved?.(entry)}}async function pruneSandboxContainers(cfg){await pruneSandboxRegistryEntries({cfg,read:readRegistry,remove:removeRegistryEntry})}async function pruneSandboxBrowsers(cfg){await pruneSandboxRegistryEntries({cfg,read:readBrowserRegistry,remove:removeBrowserRegistryEntry,onRemoved:async entry=>{let bridge=BROWSER_BRIDGES.get(entry.sessionKey);bridge?.containerName===entry.containerName&&(await stopBrowserBridgeServer(bridge.bridge.server).catch(()=>void 0),BROWSER_BRIDGES.delete(entry.sessionKey))}})}async function maybePruneSandboxes(cfg){let now=Date.now();if(!(now-lastPruneAtMs<300*1e3)){lastPruneAtMs=now;try{await pruneSandboxContainers(cfg),await pruneSandboxBrowsers(cfg)}catch(error){let message=error instanceof Error?error.message:typeof error==`string`?error:JSON.stringify(error);defaultRuntime.error?.(`Sandbox prune failed: ${message??`unknown error`}`)}}}function normalizeStringEntries(list){return(list??[]).map(entry=>String(entry).trim()).filter(Boolean)}function normalizeStringEntriesLower(list){return normalizeStringEntries(list).map(entry=>entry.toLowerCase())}function normalizeHyphenSlug(raw){let trimmed=raw?.trim().toLowerCase()??``;return trimmed?trimmed.replace(/\s+/g,`-`).replace(/[^a-z0-9#@._+-]+/g,`-`).replace(/-{2,}/g,`-`).replace(/^[-.]+|[-.]+$/g,``):``}function normalizeAtHashSlug(raw){let trimmed=raw?.trim().toLowerCase()??``;return trimmed?trimmed.replace(/^[@#]+/,``).replace(/[\s_]+/g,`-`).replace(/[^a-z0-9-]+/g,`-`).replace(/-{2,}/g,`-`).replace(/^-+|-+$/g,``):``}const getGroupSurfaces=()=>new Set([...listDeliverableMessageChannels(),`webchat`]);function normalizeGroupLabel(raw){return normalizeHyphenSlug(raw)}function shortenGroupId(value){let trimmed=value?.trim()??``;return trimmed?trimmed.length<=14?trimmed:`${trimmed.slice(0,6)}...${trimmed.slice(-4)}`:``}function buildGroupDisplayName(params){let providerKey=(params.provider?.trim().toLowerCase()||`group`).trim(),groupChannel=params.groupChannel?.trim(),space=params.space?.trim(),subject=params.subject?.trim(),detail=(groupChannel&&space?`${space}${groupChannel.startsWith(`#`)?``:`#`}${groupChannel}`:groupChannel||subject||space||``)||``,fallbackId=params.id?.trim()||params.key,rawLabel=detail||fallbackId,token=normalizeGroupLabel(rawLabel);return token||=normalizeGroupLabel(shortenGroupId(rawLabel)),!params.groupChannel&&token.startsWith(`#`)&&(token=token.replace(/^#+/,``)),token&&!/^[@#]/.test(token)&&!token.startsWith(`g-`)&&!token.includes(`#`)&&(token=`g-${token}`),token?`${providerKey}:${token}`:providerKey}function resolveGroupSessionKey(ctx){let from=typeof ctx.From==`string`?ctx.From.trim():``,chatType=ctx.ChatType?.trim().toLowerCase(),normalizedChatType=chatType===`channel`?`channel`:chatType===`group`?`group`:void 0,isWhatsAppGroupId=from.toLowerCase().endsWith(`@g.us`);if(!(normalizedChatType===`group`||normalizedChatType===`channel`||from.includes(`:group:`)||from.includes(`:channel:`)||isWhatsAppGroupId))return null;let providerHint=ctx.Provider?.trim().toLowerCase(),parts=from.split(`:`).filter(Boolean),head=parts[0]?.trim().toLowerCase()??``,headIsSurface=head?getGroupSurfaces().has(head):!1,provider=headIsSurface?head:providerHint??(isWhatsAppGroupId?`whatsapp`:void 0);if(!provider)return null;let second=parts[1]?.trim().toLowerCase(),secondIsKind=second===`group`||second===`channel`,kind=secondIsKind?second:from.includes(`:channel:`)||normalizedChatType===`channel`?`channel`:`group`,finalId=(headIsSurface?secondIsKind?parts.slice(2).join(`:`):parts.slice(1).join(`:`):from).trim().toLowerCase();return finalId?{key:`${provider}:${kind}:${finalId}`,channel:provider,id:finalId,chatType:kind===`channel`?`channel`:`group`}:null}function resolveChannelGroupConfig(groups,groupId,caseInsensitive=!1){if(!groups)return;let direct=groups[groupId];if(direct)return direct;if(!caseInsensitive)return;let target=groupId.toLowerCase(),matchedKey=Object.keys(groups).find(key=>key!==`*`&&key.toLowerCase()===target);if(matchedKey)return groups[matchedKey]}function normalizeSenderKey(value){let trimmed=value.trim();return trimmed?(trimmed.startsWith(`@`)?trimmed.slice(1):trimmed).toLowerCase():``}function resolveToolsBySender(params){let toolsBySender=params.toolsBySender;if(!toolsBySender)return;let entries=Object.entries(toolsBySender);if(entries.length===0)return;let normalized=new Map,wildcard;for(let[rawKey,policy]of entries){if(!policy)continue;let key=normalizeSenderKey(rawKey);if(key){if(key===`*`){wildcard=policy;continue}normalized.has(key)||normalized.set(key,policy)}}let candidates=[],pushCandidate=value=>{let trimmed=value?.trim();trimmed&&candidates.push(trimmed)};pushCandidate(params.senderId),pushCandidate(params.senderE164),pushCandidate(params.senderUsername),pushCandidate(params.senderName);for(let candidate of candidates){let key=normalizeSenderKey(candidate);if(!key)continue;let match=normalized.get(key);if(match)return match}return wildcard}function resolveChannelGroups(cfg,channel,accountId){let normalizedAccountId=normalizeAccountId$1(accountId),channelConfig=cfg.channels?.[channel];if(channelConfig)return channelConfig.accounts?.[normalizedAccountId]?.groups??channelConfig.accounts?.[Object.keys(channelConfig.accounts??{}).find(key=>key.toLowerCase()===normalizedAccountId.toLowerCase())??``]?.groups??channelConfig.groups}function resolveChannelGroupPolicy(params){let{cfg,channel}=params,groups=resolveChannelGroups(cfg,channel,params.accountId),allowlistEnabled=!!(groups&&Object.keys(groups).length>0),normalizedId=params.groupId?.trim(),groupConfig=normalizedId?resolveChannelGroupConfig(groups,normalizedId,params.groupIdCaseInsensitive):void 0,defaultConfig=groups?.[`*`];return{allowlistEnabled,allowed:!allowlistEnabled||allowlistEnabled&&!!(groups&&Object.hasOwn(groups,`*`))||!!groupConfig,groupConfig,defaultConfig}}function resolveChannelGroupRequireMention(params){let{requireMentionOverride,overrideOrder=`after-config`}=params,{groupConfig,defaultConfig}=resolveChannelGroupPolicy(params),configMention=typeof groupConfig?.requireMention==`boolean`?groupConfig.requireMention:typeof defaultConfig?.requireMention==`boolean`?defaultConfig.requireMention:void 0;return overrideOrder===`before-config`&&typeof requireMentionOverride==`boolean`?requireMentionOverride:typeof configMention==`boolean`?configMention:overrideOrder!==`before-config`&&typeof requireMentionOverride==`boolean`?requireMentionOverride:!0}function resolveChannelGroupToolsPolicy(params){let{groupConfig,defaultConfig}=resolveChannelGroupPolicy(params),groupSenderPolicy=resolveToolsBySender({toolsBySender:groupConfig?.toolsBySender,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164});if(groupSenderPolicy)return groupSenderPolicy;if(groupConfig?.tools)return groupConfig.tools;let defaultSenderPolicy=resolveToolsBySender({toolsBySender:defaultConfig?.toolsBySender,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164});if(defaultSenderPolicy)return defaultSenderPolicy;if(defaultConfig?.tools)return defaultConfig.tools}function buildSlackThreadingToolContext(params){let configuredReplyToMode=resolveSlackReplyToMode(resolveSlackAccount({cfg:params.cfg,accountId:params.accountId}),params.context.ChatType),effectiveReplyToMode=params.context.ThreadLabel?`all`:configuredReplyToMode,threadId=params.context.MessageThreadId??params.context.ReplyToId;return{currentChannelId:params.context.To?.startsWith(`channel:`)?params.context.To.slice(8):void 0,currentThreadTs:threadId==null?void 0:String(threadId),replyToMode:effectiveReplyToMode,hasRepliedRef:params.hasRepliedRef}}function normalizeDiscordSlug(value){return normalizeAtHashSlug(value)}function parseTelegramGroupId(value){let raw=value?.trim()??``;if(!raw)return{chatId:void 0,topicId:void 0};let parts=raw.split(`:`).filter(Boolean);return parts.length>=3&&parts[1]===`topic`&&/^-?\d+$/.test(parts[0])&&/^\d+$/.test(parts[2])?{chatId:parts[0],topicId:parts[2]}:parts.length>=2&&/^-?\d+$/.test(parts[0])&&/^\d+$/.test(parts[1])?{chatId:parts[0],topicId:parts[1]}:{chatId:raw,topicId:void 0}}function resolveTelegramRequireMention(params){let{cfg,chatId,topicId}=params;if(!chatId)return;let groupConfig=cfg.channels?.telegram?.groups?.[chatId],groupDefault=cfg.channels?.telegram?.groups?.[`*`],topicConfig=topicId&&groupConfig?.topics?groupConfig.topics[topicId]:void 0,defaultTopicConfig=topicId&&groupDefault?.topics?groupDefault.topics[topicId]:void 0;if(typeof topicConfig?.requireMention==`boolean`)return topicConfig.requireMention;if(typeof defaultTopicConfig?.requireMention==`boolean`)return defaultTopicConfig.requireMention;if(typeof groupConfig?.requireMention==`boolean`)return groupConfig.requireMention;if(typeof groupDefault?.requireMention==`boolean`)return groupDefault.requireMention}function resolveDiscordGuildEntry(guilds,groupSpace){if(!guilds||Object.keys(guilds).length===0)return null;let space=groupSpace?.trim()??``;if(space&&guilds[space])return guilds[space];let normalized=normalizeDiscordSlug(space);if(normalized&&guilds[normalized])return guilds[normalized];if(normalized){let match=Object.values(guilds).find(entry=>normalizeDiscordSlug(entry?.slug??void 0)===normalized);if(match)return match}return guilds[`*`]??null}function resolveDiscordChannelEntry(channelEntries,params){if(!channelEntries||Object.keys(channelEntries).length===0)return;let groupChannel=params.groupChannel,channelSlug=normalizeDiscordSlug(groupChannel);return(params.groupId?channelEntries[params.groupId]:void 0)??(channelSlug?channelEntries[channelSlug]??channelEntries[`#${channelSlug}`]:void 0)??(groupChannel?channelEntries[normalizeDiscordSlug(groupChannel)]:void 0)}function resolveSlackChannelPolicyEntry(params){let channels=resolveSlackAccount({cfg:params.cfg,accountId:params.accountId}).channels??{};if(Object.keys(channels).length===0)return;let channelId=params.groupId?.trim(),channelName=params.groupChannel?.replace(/^#/,``),normalizedName=normalizeHyphenSlug(channelName),candidates=[channelId??``,channelName?`#${channelName}`:``,channelName??``,normalizedName].filter(Boolean);for(let candidate of candidates)if(candidate&&channels[candidate])return channels[candidate];return channels[`*`]}function resolveTelegramGroupRequireMention(params){let{chatId,topicId}=parseTelegramGroupId(params.groupId),requireMention=resolveTelegramRequireMention({cfg:params.cfg,chatId,topicId});return typeof requireMention==`boolean`?requireMention:resolveChannelGroupRequireMention({cfg:params.cfg,channel:`telegram`,groupId:chatId??params.groupId,accountId:params.accountId})}function resolveWhatsAppGroupRequireMention(params){return resolveChannelGroupRequireMention({cfg:params.cfg,channel:`whatsapp`,groupId:params.groupId,accountId:params.accountId})}function resolveIMessageGroupRequireMention(params){return resolveChannelGroupRequireMention({cfg:params.cfg,channel:`imessage`,groupId:params.groupId,accountId:params.accountId})}function resolveDiscordGroupRequireMention(params){let guildEntry=resolveDiscordGuildEntry(params.cfg.channels?.discord?.guilds,params.groupSpace),channelEntries=guildEntry?.channels;if(channelEntries&&Object.keys(channelEntries).length>0){let entry=resolveDiscordChannelEntry(channelEntries,params);if(entry&&typeof entry.requireMention==`boolean`)return entry.requireMention}return typeof guildEntry?.requireMention==`boolean`?guildEntry.requireMention:!0}function resolveGoogleChatGroupRequireMention(params){return resolveChannelGroupRequireMention({cfg:params.cfg,channel:`googlechat`,groupId:params.groupId,accountId:params.accountId})}function resolveGoogleChatGroupToolPolicy(params){return resolveChannelGroupToolsPolicy({cfg:params.cfg,channel:`googlechat`,groupId:params.groupId,accountId:params.accountId,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164})}function resolveSlackGroupRequireMention(params){let resolved=resolveSlackChannelPolicyEntry(params);return typeof resolved?.requireMention==`boolean`?resolved.requireMention:!0}function resolveTelegramGroupToolPolicy(params){let{chatId}=parseTelegramGroupId(params.groupId);return resolveChannelGroupToolsPolicy({cfg:params.cfg,channel:`telegram`,groupId:chatId??params.groupId,accountId:params.accountId,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164})}function resolveWhatsAppGroupToolPolicy(params){return resolveChannelGroupToolsPolicy({cfg:params.cfg,channel:`whatsapp`,groupId:params.groupId,accountId:params.accountId,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164})}function resolveIMessageGroupToolPolicy(params){return resolveChannelGroupToolsPolicy({cfg:params.cfg,channel:`imessage`,groupId:params.groupId,accountId:params.accountId,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164})}function resolveDiscordGroupToolPolicy(params){let guildEntry=resolveDiscordGuildEntry(params.cfg.channels?.discord?.guilds,params.groupSpace),channelEntries=guildEntry?.channels;if(channelEntries&&Object.keys(channelEntries).length>0){let entry=resolveDiscordChannelEntry(channelEntries,params),senderPolicy=resolveToolsBySender({toolsBySender:entry?.toolsBySender,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164});if(senderPolicy)return senderPolicy;if(entry?.tools)return entry.tools}let guildSenderPolicy=resolveToolsBySender({toolsBySender:guildEntry?.toolsBySender,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164});if(guildSenderPolicy)return guildSenderPolicy;if(guildEntry?.tools)return guildEntry.tools}function resolveSlackGroupToolPolicy(params){let resolved=resolveSlackChannelPolicyEntry(params);if(!resolved)return;let senderPolicy=resolveToolsBySender({toolsBySender:resolved?.toolsBySender,senderId:params.senderId,senderName:params.senderName,senderUsername:params.senderUsername,senderE164:params.senderE164});if(senderPolicy)return senderPolicy;if(resolved?.tools)return resolved.tools}function normalizeSignalMessagingTarget(raw){let trimmed=raw.trim();if(!trimmed)return;let normalized=trimmed;if(normalized.toLowerCase().startsWith(`signal:`)&&(normalized=normalized.slice(7).trim()),!normalized)return;let lower=normalized.toLowerCase();if(lower.startsWith(`group:`)){let id=normalized.slice(6).trim();return id?`group:${id}`:void 0}if(lower.startsWith(`username:`)){let id=normalized.slice(9).trim();return id?`username:${id}`.toLowerCase():void 0}if(lower.startsWith(`u:`)){let id=normalized.slice(2).trim();return id?`username:${id}`.toLowerCase():void 0}if(lower.startsWith(`uuid:`)){let id=normalized.slice(5).trim();return id?id.toLowerCase():void 0}return normalized.toLowerCase()}const formatLower=allowFrom=>allowFrom.map(entry=>String(entry).trim()).filter(Boolean).map(entry=>entry.toLowerCase()),formatDiscordAllowFrom=allowFrom=>allowFrom.map(entry=>String(entry).trim().replace(/^<@!?/,``).replace(/>$/,``).replace(/^discord:/i,``).replace(/^user:/i,``).replace(/^pk:/i,``).trim().toLowerCase()).filter(Boolean);function resolveDirectOrGroupChannelId(context){return(context.ChatType?.toLowerCase()===`direct`?context.From??context.To:context.To)?.trim()||void 0}function buildSignalThreadToolContext(params){let currentChannelIdRaw=resolveDirectOrGroupChannelId(params.context);return{currentChannelId:currentChannelIdRaw?normalizeSignalMessagingTarget(currentChannelIdRaw)??currentChannelIdRaw.trim():void 0,currentThreadTs:params.context.ReplyToId,hasRepliedRef:params.hasRepliedRef}}function buildIMessageThreadToolContext(params){return{currentChannelId:resolveDirectOrGroupChannelId(params.context),currentThreadTs:params.context.ReplyToId,hasRepliedRef:params.hasRepliedRef}}function resolveCaseInsensitiveAccount(accounts,accountId){if(!accounts)return;let normalized=normalizeAccountId$1(accountId);return accounts[normalized]??accounts[Object.keys(accounts).find(key=>key.toLowerCase()===normalized.toLowerCase())??``]}const DOCKS={telegram:{id:`telegram`,capabilities:{chatTypes:[`direct`,`group`,`channel`,`thread`],nativeCommands:!0,blockStreaming:!0},outbound:{textChunkLimit:4e3},config:{resolveAllowFrom:({cfg,accountId})=>(resolveTelegramAccount({cfg,accountId}).config.allowFrom??[]).map(entry=>String(entry)),formatAllowFrom:({allowFrom})=>allowFrom.map(entry=>String(entry).trim()).filter(Boolean).map(entry=>entry.replace(/^(telegram|tg):/i,``)).map(entry=>entry.toLowerCase())},groups:{resolveRequireMention:resolveTelegramGroupRequireMention,resolveToolPolicy:resolveTelegramGroupToolPolicy},threading:{resolveReplyToMode:({cfg})=>cfg.channels?.telegram?.replyToMode??`off`,buildToolContext:({context,hasRepliedRef})=>{let threadId=context.MessageThreadId??context.ReplyToId;return{currentChannelId:context.To?.trim()||void 0,currentThreadTs:threadId==null?void 0:String(threadId),hasRepliedRef}}}},whatsapp:{id:`whatsapp`,capabilities:{chatTypes:[`direct`,`group`],polls:!0,reactions:!0,media:!0},commands:{enforceOwnerForCommands:!0,skipWhenConfigEmpty:!0},outbound:{textChunkLimit:4e3},config:{resolveAllowFrom:({cfg,accountId})=>resolveWhatsAppAccount({cfg,accountId}).allowFrom??[],formatAllowFrom:({allowFrom})=>allowFrom.map(entry=>String(entry).trim()).filter(entry=>!!entry).map(entry=>entry===`*`?entry:normalizeWhatsAppTarget(entry)).filter(entry=>!!entry)},groups:{resolveRequireMention:resolveWhatsAppGroupRequireMention,resolveToolPolicy:resolveWhatsAppGroupToolPolicy,resolveGroupIntroHint:()=>`WhatsApp IDs: SenderId is the participant JID (group participant id).`},mentions:{stripPatterns:({ctx})=>{let selfE164=(ctx.To??``).replace(/^whatsapp:/,``);if(!selfE164)return[];let escaped=escapeRegExp(selfE164);return[escaped,`@${escaped}`]}},threading:{buildToolContext:({context,hasRepliedRef})=>({currentChannelId:context.From?.trim()||context.To?.trim()||void 0,currentThreadTs:context.ReplyToId,hasRepliedRef})}},discord:{id:`discord`,capabilities:{chatTypes:[`direct`,`channel`,`thread`],polls:!0,reactions:!0,media:!0,nativeCommands:!0,threads:!0},outbound:{textChunkLimit:2e3},streaming:{blockStreamingCoalesceDefaults:{minChars:1500,idleMs:1e3}},elevated:{allowFromFallback:({cfg})=>cfg.channels?.discord?.allowFrom??cfg.channels?.discord?.dm?.allowFrom},config:{resolveAllowFrom:({cfg,accountId})=>{let account=resolveDiscordAccount({cfg,accountId});return(account.config.allowFrom??account.config.dm?.allowFrom??[]).map(entry=>String(entry))},formatAllowFrom:({allowFrom})=>formatDiscordAllowFrom(allowFrom)},groups:{resolveRequireMention:resolveDiscordGroupRequireMention,resolveToolPolicy:resolveDiscordGroupToolPolicy},mentions:{stripPatterns:()=>[`<@!?\\d+>`]},threading:{resolveReplyToMode:({cfg})=>cfg.channels?.discord?.replyToMode??`off`,buildToolContext:({context,hasRepliedRef})=>({currentChannelId:context.To?.trim()||void 0,currentThreadTs:context.ReplyToId,hasRepliedRef})}},irc:{id:`irc`,capabilities:{chatTypes:[`direct`,`group`],media:!0,blockStreaming:!0},outbound:{textChunkLimit:350},streaming:{blockStreamingCoalesceDefaults:{minChars:300,idleMs:1e3}},config:{resolveAllowFrom:({cfg,accountId})=>{let channel=cfg.channels?.irc;return(resolveCaseInsensitiveAccount(channel?.accounts,accountId)?.allowFrom??channel?.allowFrom??[]).map(entry=>String(entry))},formatAllowFrom:({allowFrom})=>allowFrom.map(entry=>String(entry).trim()).filter(Boolean).map(entry=>entry.replace(/^irc:/i,``).replace(/^user:/i,``).toLowerCase())},groups:{resolveRequireMention:({cfg,accountId,groupId})=>groupId?resolveChannelGroupRequireMention({cfg,channel:`irc`,groupId,accountId,groupIdCaseInsensitive:!0}):!0,resolveToolPolicy:({cfg,accountId,groupId,senderId,senderName,senderUsername})=>{if(groupId)return resolveChannelGroupToolsPolicy({cfg,channel:`irc`,groupId,accountId,groupIdCaseInsensitive:!0,senderId,senderName,senderUsername})}}},googlechat:{id:`googlechat`,capabilities:{chatTypes:[`direct`,`group`,`thread`],reactions:!0,media:!0,threads:!0,blockStreaming:!0},outbound:{textChunkLimit:4e3},config:{resolveAllowFrom:({cfg,accountId})=>{let channel=cfg.channels?.googlechat;return(resolveCaseInsensitiveAccount(channel?.accounts,accountId)?.dm?.allowFrom??channel?.dm?.allowFrom??[]).map(entry=>String(entry))},formatAllowFrom:({allowFrom})=>allowFrom.map(entry=>String(entry).trim()).filter(Boolean).map(entry=>entry.replace(/^(googlechat|google-chat|gchat):/i,``).replace(/^user:/i,``).replace(/^users\//i,``).toLowerCase())},groups:{resolveRequireMention:resolveGoogleChatGroupRequireMention,resolveToolPolicy:resolveGoogleChatGroupToolPolicy},threading:{resolveReplyToMode:({cfg})=>cfg.channels?.googlechat?.replyToMode??`off`,buildToolContext:({context,hasRepliedRef})=>{let threadId=context.MessageThreadId??context.ReplyToId;return{currentChannelId:context.To?.trim()||void 0,currentThreadTs:threadId==null?void 0:String(threadId),hasRepliedRef}}}},slack:{id:`slack`,capabilities:{chatTypes:[`direct`,`channel`,`thread`],reactions:!0,media:!0,nativeCommands:!0,threads:!0},outbound:{textChunkLimit:4e3},streaming:{blockStreamingCoalesceDefaults:{minChars:1500,idleMs:1e3}},config:{resolveAllowFrom:({cfg,accountId})=>{let account=resolveSlackAccount({cfg,accountId});return(account.config.allowFrom??account.dm?.allowFrom??[]).map(entry=>String(entry))},formatAllowFrom:({allowFrom})=>formatLower(allowFrom)},groups:{resolveRequireMention:resolveSlackGroupRequireMention,resolveToolPolicy:resolveSlackGroupToolPolicy},mentions:{stripPatterns:()=>[`<@[^>]+>`]},threading:{resolveReplyToMode:({cfg,accountId,chatType})=>resolveSlackReplyToMode(resolveSlackAccount({cfg,accountId}),chatType),allowExplicitReplyTagsWhenOff:!0,buildToolContext:params=>buildSlackThreadingToolContext(params)}},signal:{id:`signal`,capabilities:{chatTypes:[`direct`,`group`],reactions:!0,media:!0},outbound:{textChunkLimit:4e3},streaming:{blockStreamingCoalesceDefaults:{minChars:1500,idleMs:1e3}},config:{resolveAllowFrom:({cfg,accountId})=>(resolveSignalAccount({cfg,accountId}).config.allowFrom??[]).map(entry=>String(entry)),formatAllowFrom:({allowFrom})=>allowFrom.map(entry=>String(entry).trim()).filter(Boolean).map(entry=>entry===`*`?`*`:normalizeE164(entry.replace(/^signal:/i,``))).filter(Boolean)},threading:{buildToolContext:({context,hasRepliedRef})=>buildSignalThreadToolContext({context,hasRepliedRef})}},imessage:{id:`imessage`,capabilities:{chatTypes:[`direct`,`group`],reactions:!0,media:!0},outbound:{textChunkLimit:4e3},config:{resolveAllowFrom:({cfg,accountId})=>(resolveIMessageAccount({cfg,accountId}).config.allowFrom??[]).map(entry=>String(entry)),formatAllowFrom:({allowFrom})=>allowFrom.map(entry=>String(entry).trim()).filter(Boolean)},groups:{resolveRequireMention:resolveIMessageGroupRequireMention,resolveToolPolicy:resolveIMessageGroupToolPolicy},threading:{buildToolContext:({context,hasRepliedRef})=>buildIMessageThreadToolContext({context,hasRepliedRef})}}};function buildDockFromPlugin(plugin){return{id:plugin.id,capabilities:plugin.capabilities,commands:plugin.commands,outbound:plugin.outbound?.textChunkLimit?{textChunkLimit:plugin.outbound.textChunkLimit}:void 0,streaming:plugin.streaming?{blockStreamingCoalesceDefaults:plugin.streaming.blockStreamingCoalesceDefaults}:void 0,elevated:plugin.elevated,config:plugin.config?{resolveAllowFrom:plugin.config.resolveAllowFrom,formatAllowFrom:plugin.config.formatAllowFrom}:void 0,groups:plugin.groups,mentions:plugin.mentions,threading:plugin.threading,agentPrompt:plugin.agentPrompt}}function listPluginDockEntries(){let registry=requireActivePluginRegistry(),entries=[],seen=new Set;for(let entry of registry.channels){let plugin=entry.plugin,id=String(plugin.id).trim();if(!id||seen.has(id)||(seen.add(id),CHAT_CHANNEL_ORDER.includes(plugin.id)))continue;let dock=entry.dock??buildDockFromPlugin(plugin);entries.push({id:plugin.id,dock,order:plugin.meta.order})}return entries}function listChannelDocks(){let baseEntries=CHAT_CHANNEL_ORDER.map(id=>({id,dock:DOCKS[id],order:getChatChannelMeta(id).order})),pluginEntries=listPluginDockEntries(),combined=[...baseEntries,...pluginEntries];return combined.sort((a,b)=>{let indexA=CHAT_CHANNEL_ORDER.indexOf(a.id),indexB=CHAT_CHANNEL_ORDER.indexOf(b.id),orderA=a.order??(indexA===-1?999:indexA),orderB=b.order??(indexB===-1?999:indexB);return orderA===orderB?String(a.id).localeCompare(String(b.id)):orderA-orderB}),combined.map(entry=>entry.dock)}function getChannelDock(id){let core=DOCKS[id];if(core)return core;let pluginEntry=requireActivePluginRegistry().channels.find(entry=>entry.plugin.id===id);if(pluginEntry)return pluginEntry.dock??buildDockFromPlugin(pluginEntry.plugin)}const mergeOrigin=(existing,next)=>{if(!existing&&!next)return;let merged=existing?{...existing}:{};return next?.label&&(merged.label=next.label),next?.provider&&(merged.provider=next.provider),next?.surface&&(merged.surface=next.surface),next?.chatType&&(merged.chatType=next.chatType),next?.from&&(merged.from=next.from),next?.to&&(merged.to=next.to),next?.accountId&&(merged.accountId=next.accountId),next?.threadId!=null&&next.threadId!==``&&(merged.threadId=next.threadId),Object.keys(merged).length>0?merged:void 0};function deriveSessionOrigin(ctx){let label=resolveConversationLabel(ctx)?.trim(),provider=normalizeMessageChannel(typeof ctx.OriginatingChannel==`string`&&ctx.OriginatingChannel||ctx.Surface||ctx.Provider),surface=ctx.Surface?.trim().toLowerCase(),chatType=normalizeChatType(ctx.ChatType)??void 0,from=ctx.From?.trim(),to=(typeof ctx.OriginatingTo==`string`?ctx.OriginatingTo:ctx.To)?.trim()??void 0,accountId=ctx.AccountId?.trim(),threadId=ctx.MessageThreadId??void 0,origin={};return label&&(origin.label=label),provider&&(origin.provider=provider),surface&&(origin.surface=surface),chatType&&(origin.chatType=chatType),from&&(origin.from=from),to&&(origin.to=to),accountId&&(origin.accountId=accountId),threadId!=null&&threadId!==``&&(origin.threadId=threadId),Object.keys(origin).length>0?origin:void 0}function deriveGroupSessionPatch(params){let resolution=params.groupResolution??resolveGroupSessionKey(params.ctx);if(!resolution?.channel)return null;let channel=resolution.channel,subject=params.ctx.GroupSubject?.trim(),space=params.ctx.GroupSpace?.trim(),explicitChannel=params.ctx.GroupChannel?.trim(),normalizedChannel=normalizeChannelId(channel),isChannelProvider=!!(normalizedChannel&&getChannelDock(normalizedChannel)?.capabilities.chatTypes.includes(`channel`)),nextGroupChannel=explicitChannel??((resolution.chatType===`channel`||isChannelProvider)&&subject&&subject.startsWith(`#`)?subject:void 0),nextSubject=nextGroupChannel?void 0:subject,patch={chatType:resolution.chatType??`group`,channel,groupId:resolution.id};nextSubject&&(patch.subject=nextSubject),nextGroupChannel&&(patch.groupChannel=nextGroupChannel),space&&(patch.space=space);let displayName=buildGroupDisplayName({provider:channel,subject:nextSubject??params.existing?.subject,groupChannel:nextGroupChannel??params.existing?.groupChannel,space:space??params.existing?.space,id:resolution.id,key:params.sessionKey});return displayName&&(patch.displayName=displayName),patch}function deriveSessionMetaPatch(params){let groupPatch=deriveGroupSessionPatch(params),origin=deriveSessionOrigin(params.ctx);if(!groupPatch&&!origin)return null;let patch=groupPatch?{...groupPatch}:{},mergedOrigin=mergeOrigin(params.existing?.origin,origin);return mergedOrigin&&(patch.origin=mergedOrigin),Object.keys(patch).length>0?patch:null}function resolveMainSessionKey(cfg){if(cfg?.session?.scope===`global`)return`global`;let agents=cfg?.agents?.list??[];return buildAgentMainSessionKey({agentId:normalizeAgentId(agents.find(agent=>agent?.default)?.id??agents[0]?.id??DEFAULT_AGENT_ID),mainKey:normalizeMainKey(cfg?.session?.mainKey)})}function resolveAgentMainSessionKey(params){let mainKey=normalizeMainKey(params.cfg?.session?.mainKey);return buildAgentMainSessionKey({agentId:params.agentId,mainKey})}function resolveExplicitAgentSessionKey(params){let agentId=params.agentId?.trim();if(agentId)return resolveAgentMainSessionKey({cfg:params.cfg,agentId})}function canonicalizeMainSessionAlias(params){let raw=params.sessionKey.trim();if(!raw)return raw;let agentId=normalizeAgentId(params.agentId),mainKey=normalizeMainKey(params.cfg?.session?.mainKey),agentMainSessionKey=buildAgentMainSessionKey({agentId,mainKey}),agentMainAliasKey=buildAgentMainSessionKey({agentId,mainKey:`main`}),isMainAlias=raw===`main`||raw===mainKey||raw===agentMainSessionKey||raw===agentMainAliasKey;return params.cfg?.session?.scope===`global`&&isMainAlias?`global`:isMainAlias?agentMainSessionKey:raw}function mergeSessionEntry(existing,patch){let sessionId=patch.sessionId??existing?.sessionId??crypto.randomUUID(),updatedAt=Math.max(existing?.updatedAt??0,patch.updatedAt??0,Date.now());return existing?{...existing,...patch,sessionId,updatedAt}:{...patch,sessionId,updatedAt}}function resolveFreshSessionTotalTokens(entry){let total=entry?.totalTokens;if(!(typeof total!=`number`||!Number.isFinite(total)||total<0)&&entry?.totalTokensFresh!==!1)return total}const DEFAULT_RESET_TRIGGERS=[`/new`,`/reset`],THREAD_SESSION_MARKERS=[`:thread:`,`:topic:`],GROUP_SESSION_MARKERS=[`:group:`,`:channel:`];function isThreadSessionKey(sessionKey){let normalized=(sessionKey??``).toLowerCase();return normalized?THREAD_SESSION_MARKERS.some(marker=>normalized.includes(marker)):!1}function resolveSessionResetType(params){if(params.isThread||isThreadSessionKey(params.sessionKey))return`thread`;if(params.isGroup)return`group`;let normalized=(params.sessionKey??``).toLowerCase();return GROUP_SESSION_MARKERS.some(marker=>normalized.includes(marker))?`group`:`direct`}function resolveThreadFlag(params){return params.messageThreadId!=null||params.threadLabel?.trim()||params.threadStarterBody?.trim()||params.parentSessionKey?.trim()?!0:isThreadSessionKey(params.sessionKey)}function resolveDailyResetAtMs(now,atHour){let normalizedAtHour=normalizeResetAtHour(atHour),resetAt=new Date(now);return resetAt.setHours(normalizedAtHour,0,0,0),now<resetAt.getTime()&&resetAt.setDate(resetAt.getDate()-1),resetAt.getTime()}function resolveSessionResetPolicy(params){let sessionCfg=params.sessionCfg,baseReset=params.resetOverride??sessionCfg?.reset,typeReset=params.resetOverride?void 0:sessionCfg?.resetByType?.[params.resetType]??(params.resetType===`direct`?sessionCfg?.resetByType?.dm:void 0),hasExplicitReset=!!(baseReset||sessionCfg?.resetByType),legacyIdleMinutes=params.resetOverride?void 0:sessionCfg?.idleMinutes,mode=typeReset?.mode??baseReset?.mode??(!hasExplicitReset&&legacyIdleMinutes!=null?`idle`:`daily`),atHour=normalizeResetAtHour(typeReset?.atHour??baseReset?.atHour??4),idleMinutesRaw=typeReset?.idleMinutes??baseReset?.idleMinutes??legacyIdleMinutes,idleMinutes;if(idleMinutesRaw!=null){let normalized=Math.floor(idleMinutesRaw);Number.isFinite(normalized)&&(idleMinutes=Math.max(normalized,1))}else mode===`idle`&&(idleMinutes=60);return{mode,atHour,idleMinutes}}function resolveChannelResetConfig(params){let resetByChannel=params.sessionCfg?.resetByChannel;if(!resetByChannel)return;let normalized=normalizeMessageChannel(params.channel),fallback=params.channel?.trim().toLowerCase(),key=normalized??fallback;if(key)return resetByChannel[key]??resetByChannel[key.toLowerCase()]}function evaluateSessionFreshness(params){let dailyResetAt=params.policy.mode===`daily`?resolveDailyResetAtMs(params.now,params.policy.atHour):void 0,idleExpiresAt=params.policy.idleMinutes==null?void 0:params.updatedAt+params.policy.idleMinutes*6e4,staleDaily=dailyResetAt!=null&¶ms.updatedAt<dailyResetAt,staleIdle=idleExpiresAt!=null&¶ms.now>idleExpiresAt;return{fresh:!(staleDaily||staleIdle),dailyResetAt,idleExpiresAt}}function normalizeResetAtHour(value){if(typeof value!=`number`||!Number.isFinite(value))return 4;let normalized=Math.floor(value);return Number.isFinite(normalized)?normalized<0?0:normalized>23?23:normalized:4}function deriveSessionKey(scope,ctx){if(scope===`global`)return`global`;let resolvedGroup=resolveGroupSessionKey(ctx);return resolvedGroup?resolvedGroup.key:(ctx.From?normalizeE164(ctx.From):``)||`unknown`}function resolveSessionKey(scope,ctx,mainKey){let explicit=ctx.SessionKey?.trim();if(explicit)return explicit.toLowerCase();let raw=deriveSessionKey(scope,ctx);if(scope===`global`)return raw;let canonical=buildAgentMainSessionKey({agentId:DEFAULT_AGENT_ID,mainKey:normalizeMainKey(mainKey)});return raw.includes(`:group:`)||raw.includes(`:channel:`)?`agent:${DEFAULT_AGENT_ID}:${raw}`:canonical}const INPUT_PROVENANCE_KIND_VALUES=[`external_user`,`inter_session`,`internal_system`];function normalizeOptionalString(value){return typeof value==`string`&&value.trim()||void 0}function isInputProvenanceKind(value){return typeof value==`string`&&INPUT_PROVENANCE_KIND_VALUES.includes(value)}function normalizeInputProvenance(value){if(!value||typeof value!=`object`)return;let record=value;if(isInputProvenanceKind(record.kind))return{kind:record.kind,sourceSessionKey:normalizeOptionalString(record.sourceSessionKey),sourceChannel:normalizeOptionalString(record.sourceChannel),sourceTool:normalizeOptionalString(record.sourceTool)}}function applyInputProvenanceToUserMessage(message,inputProvenance){return!inputProvenance||message.role!==`user`||normalizeInputProvenance(message.provenance)?message:{...message,provenance:inputProvenance}}function isInterSessionInputProvenance(value){return normalizeInputProvenance(value)?.kind===`inter_session`}function hasInterSessionUserProvenance(message){return!message||message.role!==`user`?!1:isInterSessionInputProvenance(message.provenance)}const TOOL_CALL_TYPES$1=new Set([`tool_use`,`toolcall`,`tool_call`]),TOOL_RESULT_TYPES=new Set([`tool_result`,`tool_result_error`]),normalizeType=value=>typeof value==`string`?value.trim().toLowerCase():``,extractToolCallNames=message=>{let names=new Set,toolNameRaw=message.toolName??message.tool_name;typeof toolNameRaw==`string`&&toolNameRaw.trim()&&names.add(toolNameRaw.trim());let content=message.content;if(!Array.isArray(content))return Array.from(names);for(let entry of content){if(!entry||typeof entry!=`object`)continue;let block=entry,type=normalizeType(block.type);if(!TOOL_CALL_TYPES$1.has(type))continue;let name=block.name;typeof name==`string`&&name.trim()&&names.add(name.trim())}return Array.from(names)},countToolResults=message=>{let content=message.content;if(!Array.isArray(content))return{total:0,errors:0};let total=0,errors=0;for(let entry of content){if(!entry||typeof entry!=`object`)continue;let block=entry,type=normalizeType(block.type);TOOL_RESULT_TYPES.has(type)&&(total+=1,block.is_error===!0&&(errors+=1))}return{total,errors}};function resolveSessionTranscriptCandidates(sessionId,storePath,sessionFile,agentId){let candidates=[],pushCandidate=resolve=>{try{candidates.push(resolve())}catch{}};if(storePath){let sessionsDir=path.dirname(storePath);sessionFile&&pushCandidate(()=>resolveSessionFilePath(sessionId,{sessionFile},{sessionsDir,agentId})),pushCandidate(()=>resolveSessionTranscriptPathInDir(sessionId,sessionsDir))}else if(sessionFile)if(agentId)pushCandidate(()=>resolveSessionFilePath(sessionId,{sessionFile},{agentId}));else{let trimmed=sessionFile.trim();trimmed&&candidates.push(path.resolve(trimmed))}agentId&&pushCandidate(()=>resolveSessionTranscriptPath(sessionId,agentId));let home=resolveRequiredHomeDir(process.env,os.homedir),legacyDir=path.join(home,`.builderforce`,`sessions`);return pushCandidate(()=>resolveSessionTranscriptPathInDir(sessionId,legacyDir)),Array.from(new Set(candidates))}function archiveFileOnDisk(filePath,reason){let archived=`${filePath}.${reason}.${new Date().toISOString().replaceAll(`:`,`-`)}`;return fs$1.renameSync(filePath,archived),archived}function archiveSessionTranscripts(opts){let archived=[];for(let candidate of resolveSessionTranscriptCandidates(opts.sessionId,opts.storePath,opts.sessionFile,opts.agentId))if(fs$1.existsSync(candidate))try{archived.push(archiveFileOnDisk(candidate,opts.reason))}catch{}return archived}function restoreArchiveTimestamp(raw){let[datePart,timePart]=raw.split(`T`);return!datePart||!timePart?raw:`${datePart}T${timePart.replace(/-/g,`:`)}`}function parseArchivedTimestamp(fileName,reason){let marker=`.${reason}.`,index=fileName.lastIndexOf(marker);if(index<0)return null;let raw=fileName.slice(index+marker.length);if(!raw)return null;let timestamp=Date.parse(restoreArchiveTimestamp(raw));return Number.isNaN(timestamp)?null:timestamp}async function cleanupArchivedSessionTranscripts(opts){if(!Number.isFinite(opts.olderThanMs)||opts.olderThanMs<0)return{removed:0,scanned:0};let now=opts.nowMs??Date.now(),reason=opts.reason??`deleted`,directories=Array.from(new Set(opts.directories.map(dir=>path.resolve(dir)))),removed=0,scanned=0;for(let dir of directories){let entries=await fs$1.promises.readdir(dir).catch(()=>[]);for(let entry of entries){let timestamp=parseArchivedTimestamp(entry,reason);if(timestamp==null||(scanned+=1,now-timestamp<=opts.olderThanMs))continue;let fullPath=path.join(dir,entry);(await fs$1.promises.stat(fullPath).catch(()=>null))?.isFile()&&(await fs$1.promises.rm(fullPath).catch(()=>void 0),removed+=1)}}return{removed,scanned}}function jsonUtf8Bytes(value){try{return Buffer.byteLength(JSON.stringify(value),`utf8`)}catch{return Buffer.byteLength(String(value),`utf8`)}}function capArrayByJsonBytes(items,maxBytes){if(items.length===0)return{items,bytes:2};let parts=items.map(item=>jsonUtf8Bytes(item)),bytes=2+parts.reduce((a,b)=>a+b,0)+(items.length-1),start=0;for(;bytes>maxBytes&&start<items.length-1;)bytes-=parts[start]+1,start+=1;return{items:start>0?items.slice(start):items,bytes}}function normalizeAccountId(value){if(typeof value==`string`)return value.trim()||void 0}function normalizeDeliveryContext(context){if(!context)return;let channel=typeof context.channel==`string`?normalizeMessageChannel(context.channel)??context.channel.trim():void 0,to=typeof context.to==`string`?context.to.trim():void 0,accountId=normalizeAccountId(context.accountId),threadId=typeof context.threadId==`number`&&Number.isFinite(context.threadId)?Math.trunc(context.threadId):typeof context.threadId==`string`?context.threadId.trim():void 0,normalizedThreadId=typeof threadId==`string`?threadId||void 0:threadId;if(!channel&&!to&&!accountId&&normalizedThreadId==null)return;let normalized={channel:channel||void 0,to:to||void 0,accountId};return normalizedThreadId!=null&&(normalized.threadId=normalizedThreadId),normalized}function normalizeSessionDeliveryFields(source){if(!source)return{deliveryContext:void 0,lastChannel:void 0,lastTo:void 0,lastAccountId:void 0,lastThreadId:void 0};let merged=mergeDeliveryContext(normalizeDeliveryContext({channel:source.lastChannel??source.channel,to:source.lastTo,accountId:source.lastAccountId,threadId:source.lastThreadId}),normalizeDeliveryContext(source.deliveryContext));return merged?{deliveryContext:merged,lastChannel:merged.channel,lastTo:merged.to,lastAccountId:merged.accountId,lastThreadId:merged.threadId}:{deliveryContext:void 0,lastChannel:void 0,lastTo:void 0,lastAccountId:void 0,lastThreadId:void 0}}function deliveryContextFromSession(entry){if(entry)return normalizeSessionDeliveryFields({channel:entry.channel,lastChannel:entry.lastChannel,lastTo:entry.lastTo,lastAccountId:entry.lastAccountId,lastThreadId:entry.lastThreadId??entry.deliveryContext?.threadId??entry.origin?.threadId,deliveryContext:entry.deliveryContext}).deliveryContext}function mergeDeliveryContext(primary,fallback){let normalizedPrimary=normalizeDeliveryContext(primary),normalizedFallback=normalizeDeliveryContext(fallback);if(!(!normalizedPrimary&&!normalizedFallback))return normalizeDeliveryContext({channel:normalizedPrimary?.channel??normalizedFallback?.channel,to:normalizedPrimary?.to??normalizedFallback?.to,accountId:normalizedPrimary?.accountId??normalizedFallback?.accountId,threadId:normalizedPrimary?.threadId??normalizedFallback?.threadId})}function deliveryContextKey(context){let normalized=normalizeDeliveryContext(context);if(!normalized?.channel||!normalized?.to)return;let threadId=normalized.threadId!=null&&normalized.threadId!==``?String(normalized.threadId):``;return`${normalized.channel}|${normalized.to}|${normalized.accountId??``}|${threadId}`}function resolveCacheTtlMs(params){let{envValue,defaultTtlMs}=params;if(envValue){let parsed=Number.parseInt(envValue,10);if(Number.isFinite(parsed)&&parsed>=0)return parsed}return defaultTtlMs}function isCacheEnabled(ttlMs){return ttlMs>0}function getFileMtimeMs(filePath){try{return fs$1.statSync(filePath).mtimeMs}catch{return}}const log=createSubsystemLogger(`sessions/store`),SESSION_STORE_CACHE=new Map;function isSessionStoreRecord(value){return!!value&&typeof value==`object`&&!Array.isArray(value)}function getSessionStoreTtl(){return resolveCacheTtlMs({envValue:process.env.BUILDERFORCE_AGENTS_SESSION_CACHE_TTL_MS,defaultTtlMs:45e3})}function isSessionStoreCacheEnabled(){return isCacheEnabled(getSessionStoreTtl())}function isSessionStoreCacheValid(entry){let now=Date.now(),ttl=getSessionStoreTtl();return now-entry.loadedAt<=ttl}function invalidateSessionStoreCache(storePath){SESSION_STORE_CACHE.delete(storePath)}function normalizeSessionEntryDelivery(entry){let normalized=normalizeSessionDeliveryFields({channel:entry.channel,lastChannel:entry.lastChannel,lastTo:entry.lastTo,lastAccountId:entry.lastAccountId,lastThreadId:entry.lastThreadId??entry.deliveryContext?.threadId??entry.origin?.threadId,deliveryContext:entry.deliveryContext}),nextDelivery=normalized.deliveryContext,sameDelivery=(entry.deliveryContext?.channel??void 0)===nextDelivery?.channel&&(entry.deliveryContext?.to??void 0)===nextDelivery?.to&&(entry.deliveryContext?.accountId??void 0)===nextDelivery?.accountId&&(entry.deliveryContext?.threadId??void 0)===nextDelivery?.threadId,sameLast=entry.lastChannel===normalized.lastChannel&&entry.lastTo===normalized.lastTo&&entry.lastAccountId===normalized.lastAccountId&&entry.lastThreadId===normalized.lastThreadId;return sameDelivery&&sameLast?entry:{...entry,deliveryContext:nextDelivery,lastChannel:normalized.lastChannel,lastTo:normalized.lastTo,lastAccountId:normalized.lastAccountId,lastThreadId:normalized.lastThreadId}}function removeThreadFromDeliveryContext(context){if(!context||context.threadId==null)return context;let next={...context};return delete next.threadId,next}function normalizeSessionStore(store){for(let[key,entry]of Object.entries(store)){if(!entry)continue;let normalized=normalizeSessionEntryDelivery(entry);normalized!==entry&&(store[key]=normalized)}}function loadSessionStore(storePath,opts={}){if(!opts.skipCache&&isSessionStoreCacheEnabled()){let cached=SESSION_STORE_CACHE.get(storePath);if(cached&&isSessionStoreCacheValid(cached)){if(getFileMtimeMs(storePath)===cached.mtimeMs)return structuredClone(cached.store);invalidateSessionStoreCache(storePath)}}let store={},mtimeMs=getFileMtimeMs(storePath),maxReadAttempts=process.platform===`win32`?3:1,retryBuf=maxReadAttempts>1?new Int32Array(new SharedArrayBuffer(4)):void 0;for(let attempt=0;attempt<maxReadAttempts;attempt++)try{let raw=fs$1.readFileSync(storePath,`utf-8`);if(raw.length===0&&attempt<maxReadAttempts-1){Atomics.wait(retryBuf,0,0,50);continue}let parsed=JSON.parse(raw);isSessionStoreRecord(parsed)&&(store=parsed),mtimeMs=getFileMtimeMs(storePath)??mtimeMs;break}catch{if(attempt<maxReadAttempts-1){Atomics.wait(retryBuf,0,0,50);continue}}for(let entry of Object.values(store)){if(!entry||typeof entry!=`object`)continue;let rec=entry;typeof rec.channel!=`string`&&typeof rec.provider==`string`&&(rec.channel=rec.provider,delete rec.provider),typeof rec.lastChannel!=`string`&&typeof rec.lastProvider==`string`&&(rec.lastChannel=rec.lastProvider,delete rec.lastProvider),typeof rec.groupChannel!=`string`&&typeof rec.room==`string`?(rec.groupChannel=rec.room,delete rec.room):`room`in rec&&delete rec.room}return!opts.skipCache&&isSessionStoreCacheEnabled()&&SESSION_STORE_CACHE.set(storePath,{store:structuredClone(store),loadedAt:Date.now(),storePath,mtimeMs}),structuredClone(store)}function readSessionUpdatedAt(params){try{return loadSessionStore(params.storePath)[params.sessionKey]?.updatedAt}catch{return}}const DEFAULT_SESSION_PRUNE_AFTER_MS=720*60*60*1e3,DEFAULT_SESSION_ROTATE_BYTES=10485760;function resolvePruneAfterMs(maintenance){let raw=maintenance?.pruneAfter??maintenance?.pruneDays;if(raw==null||raw===``)return DEFAULT_SESSION_PRUNE_AFTER_MS;try{return parseDurationMs(String(raw).trim(),{defaultUnit:`d`})}catch{return DEFAULT_SESSION_PRUNE_AFTER_MS}}function resolveRotateBytes(maintenance){let raw=maintenance?.rotateBytes;if(raw==null||raw===``)return DEFAULT_SESSION_ROTATE_BYTES;try{return parseByteSize(String(raw).trim(),{defaultUnit:`b`})}catch{return DEFAULT_SESSION_ROTATE_BYTES}}function resolveMaintenanceConfig(){let maintenance;try{maintenance=loadConfig().session?.maintenance}catch{}return{mode:maintenance?.mode??`warn`,pruneAfterMs:resolvePruneAfterMs(maintenance),maxEntries:maintenance?.maxEntries??500,rotateBytes:resolveRotateBytes(maintenance)}}function pruneStaleEntries(store,overrideMaxAgeMs,opts={}){let maxAgeMs=overrideMaxAgeMs??resolveMaintenanceConfig().pruneAfterMs,cutoffMs=Date.now()-maxAgeMs,pruned=0;for(let[key,entry]of Object.entries(store))entry?.updatedAt!=null&&entry.updatedAt<cutoffMs&&(opts.onPruned?.({key,entry}),delete store[key],pruned++);return pruned>0&&opts.log!==!1&&log.info(`pruned stale session entries`,{pruned,maxAgeMs}),pruned}function getEntryUpdatedAt(entry){return entry?.updatedAt??-1/0}function getActiveSessionMaintenanceWarning(params){let activeSessionKey=params.activeSessionKey.trim();if(!activeSessionKey)return null;let activeEntry=params.store[activeSessionKey];if(!activeEntry)return null;let cutoffMs=(params.nowMs??Date.now())-params.pruneAfterMs,wouldPrune=activeEntry.updatedAt==null?!1:activeEntry.updatedAt<cutoffMs,keys=Object.keys(params.store),wouldCap=keys.length>params.maxEntries&&keys.toSorted((a,b)=>getEntryUpdatedAt(params.store[b])-getEntryUpdatedAt(params.store[a])).slice(params.maxEntries).includes(activeSessionKey);return!wouldPrune&&!wouldCap?null:{activeSessionKey,activeUpdatedAt:activeEntry.updatedAt,totalEntries:keys.length,pruneAfterMs:params.pruneAfterMs,maxEntries:params.maxEntries,wouldPrune,wouldCap}}function capEntryCount(store,overrideMax,opts={}){let maxEntries=overrideMax??resolveMaintenanceConfig().maxEntries,keys=Object.keys(store);if(keys.length<=maxEntries)return 0;let toRemove=keys.toSorted((a,b)=>{let aTime=getEntryUpdatedAt(store[a]);return getEntryUpdatedAt(store[b])-aTime}).slice(maxEntries);for(let key of toRemove)delete store[key];return opts.log!==!1&&log.info(`capped session entry count`,{removed:toRemove.length,maxEntries}),toRemove.length}async function getSessionFileSize(storePath){try{return(await fs$1.promises.stat(storePath)).size}catch{return null}}async function rotateSessionFile(storePath,overrideBytes){let maxBytes=overrideBytes??resolveMaintenanceConfig().rotateBytes,fileSize=await getSessionFileSize(storePath);if(fileSize==null||fileSize<=maxBytes)return!1;let backupPath=`${storePath}.bak.${Date.now()}`;try{await fs$1.promises.rename(storePath,backupPath),log.info(`rotated session store file`,{backupPath:path.basename(backupPath),sizeBytes:fileSize})}catch{return!1}try{let dir=path.dirname(storePath),baseName=path.basename(storePath),backups=(await fs$1.promises.readdir(dir)).filter(f=>f.startsWith(`${baseName}.bak.`)).toSorted().toReversed();if(backups.length>3){let toDelete=backups.slice(3);for(let old of toDelete)await fs$1.promises.unlink(path.join(dir,old)).catch(()=>void 0);log.info(`cleaned up old session store backups`,{deleted:toDelete.length})}}catch{}return!0}async function saveSessionStoreUnlocked(storePath,store,opts){if(invalidateSessionStoreCache(storePath),normalizeSessionStore(store),!opts?.skipMaintenance){let maintenance=resolveMaintenanceConfig();if(maintenance.mode===`warn`){let activeSessionKey=opts?.activeSessionKey?.trim();if(activeSessionKey){let warning=getActiveSessionMaintenanceWarning({store,activeSessionKey,pruneAfterMs:maintenance.pruneAfterMs,maxEntries:maintenance.maxEntries});warning&&(log.warn(`session maintenance would evict active session; skipping enforcement`,{activeSessionKey:warning.activeSessionKey,wouldPrune:warning.wouldPrune,wouldCap:warning.wouldCap,pruneAfterMs:warning.pruneAfterMs,maxEntries:warning.maxEntries}),await opts?.onWarn?.(warning))}}else{let prunedSessionFiles=new Map;pruneStaleEntries(store,maintenance.pruneAfterMs,{onPruned:({entry})=>{(!prunedSessionFiles.has(entry.sessionId)||entry.sessionFile)&&prunedSessionFiles.set(entry.sessionId,entry.sessionFile)}}),capEntryCount(store,maintenance.maxEntries);let archivedDirs=new Set;for(let[sessionId,sessionFile]of prunedSessionFiles){let archived=archiveSessionTranscripts({sessionId,storePath,sessionFile,reason:`deleted`});for(let archivedPath of archived)archivedDirs.add(path.dirname(archivedPath))}archivedDirs.size>0&&await cleanupArchivedSessionTranscripts({directories:[...archivedDirs],olderThanMs:maintenance.pruneAfterMs,reason:`deleted`}),await rotateSessionFile(storePath,maintenance.rotateBytes)}}await fs$1.promises.mkdir(path.dirname(storePath),{recursive:!0});let json=JSON.stringify(store,null,2);if(process.platform===`win32`){let tmp=`${storePath}.${process.pid}.${crypto.randomUUID()}.tmp`;try{await fs$1.promises.writeFile(tmp,json,`utf-8`);for(let i=0;i<5;i++)try{await fs$1.promises.rename(tmp,storePath);break}catch{i<4&&await new Promise(r=>setTimeout(r,50*(i+1))),i===4&&console.warn(`[session-store] rename failed after 5 attempts: ${storePath}`)}}catch(err){if((err&&typeof err==`object`&&`code`in err?String(err.code):null)===`ENOENT`)return;throw err}finally{await fs$1.promises.rm(tmp,{force:!0}).catch(()=>void 0)}return}let tmp=`${storePath}.${process.pid}.${crypto.randomUUID()}.tmp`;try{await fs$1.promises.writeFile(tmp,json,{mode:384,encoding:`utf-8`}),await fs$1.promises.rename(tmp,storePath),await fs$1.promises.chmod(storePath,384)}catch(err){if((err&&typeof err==`object`&&`code`in err?String(err.code):null)===`ENOENT`){try{await fs$1.promises.mkdir(path.dirname(storePath),{recursive:!0}),await fs$1.promises.writeFile(storePath,json,{mode:384,encoding:`utf-8`}),await fs$1.promises.chmod(storePath,384)}catch(err2){if((err2&&typeof err2==`object`&&`code`in err2?String(err2.code):null)===`ENOENT`)return;throw err2}return}throw err}finally{await fs$1.promises.rm(tmp,{force:!0})}}async function updateSessionStore(storePath,mutator,opts){return await withSessionStoreLock(storePath,async()=>{let store=loadSessionStore(storePath,{skipCache:!0}),result=await mutator(store);return await saveSessionStoreUnlocked(storePath,store,opts),result})}const LOCK_QUEUES=new Map;function lockTimeoutError(storePath){return Error(`timeout waiting for session store lock: ${storePath}`)}function getOrCreateLockQueue(storePath){let existing=LOCK_QUEUES.get(storePath);if(existing)return existing;let created={running:!1,pending:[]};return LOCK_QUEUES.set(storePath,created),created}async function drainSessionStoreLockQueue(storePath){let queue=LOCK_QUEUES.get(storePath);if(!(!queue||queue.running)){queue.running=!0;try{for(;queue.pending.length>0;){let task=queue.pending.shift();if(!task)continue;let remainingTimeoutMs=task.timeoutMs??1/0;if(task.timeoutMs!=null&&remainingTimeoutMs<=0){task.reject(lockTimeoutError(storePath));continue}let lock,result,failed,hasFailure=!1;try{lock=await acquireSessionWriteLock({sessionFile:storePath,timeoutMs:remainingTimeoutMs,staleMs:task.staleMs}),result=await task.fn()}catch(err){hasFailure=!0,failed=err}finally{await lock?.release().catch(()=>void 0)}if(hasFailure){task.reject(failed);continue}task.resolve(result)}}finally{queue.running=!1,queue.pending.length===0?LOCK_QUEUES.delete(storePath):queueMicrotask(()=>{drainSessionStoreLockQueue(storePath)})}}}async function withSessionStoreLock(storePath,fn,opts={}){if(!storePath||typeof storePath!=`string`)throw Error(`withSessionStoreLock: storePath must be a non-empty string, got ${JSON.stringify(storePath)}`);let timeoutMs=opts.timeoutMs??1e4,staleMs=opts.staleMs??3e4;opts.pollIntervalMs;let hasTimeout=timeoutMs>0&&Number.isFinite(timeoutMs),queue=getOrCreateLockQueue(storePath);return await new Promise((resolve,reject)=>{let task={fn:async()=>await fn(),resolve:value=>resolve(value),reject,timeoutMs:hasTimeout?timeoutMs:void 0,staleMs};queue.pending.push(task),drainSessionStoreLockQueue(storePath)})}async function updateSessionStoreEntry(params){let{storePath,sessionKey,update}=params;return await withSessionStoreLock(storePath,async()=>{let store=loadSessionStore(storePath),existing=store[sessionKey];if(!existing)return null;let patch=await update(existing);if(!patch)return existing;let next=mergeSessionEntry(existing,patch);return store[sessionKey]=next,await saveSessionStoreUnlocked(storePath,store,{activeSessionKey:sessionKey}),next})}async function recordSessionMetaFromInbound(params){let{storePath,sessionKey,ctx}=params,createIfMissing=params.createIfMissing??!0;return await updateSessionStore(storePath,store=>{let existing=store[sessionKey],patch=deriveSessionMetaPatch({ctx,sessionKey,existing,groupResolution:params.groupResolution});if(!patch)return existing??null;if(!existing&&!createIfMissing)return null;let next=mergeSessionEntry(existing,patch);return store[sessionKey]=next,next},{activeSessionKey:sessionKey})}async function updateLastRoute(params){let{storePath,sessionKey,channel,to,accountId,threadId,ctx}=params;return await withSessionStoreLock(storePath,async()=>{let store=loadSessionStore(storePath),existing=store[sessionKey],now=Date.now(),explicitContext=normalizeDeliveryContext(params.deliveryContext),inlineContext=normalizeDeliveryContext({channel,to,accountId,threadId}),mergedInput=mergeDeliveryContext(explicitContext,inlineContext),explicitDeliveryContext=params.deliveryContext,explicitThreadValue=(explicitDeliveryContext!=null&&Object.prototype.hasOwnProperty.call(explicitDeliveryContext,`threadId`)?explicitDeliveryContext.threadId:void 0)??(threadId!=null&&threadId!==``?threadId:void 0),merged=mergeDeliveryContext(mergedInput,(explicitContext?.channel||explicitContext?.to||inlineContext?.channel||inlineContext?.to)&&explicitThreadValue==null?removeThreadFromDeliveryContext(deliveryContextFromSession(existing)):deliveryContextFromSession(existing)),normalized=normalizeSessionDeliveryFields({deliveryContext:{channel:merged?.channel,to:merged?.to,accountId:merged?.accountId,threadId:merged?.threadId}}),metaPatch=ctx?deriveSessionMetaPatch({ctx,sessionKey,existing,groupResolution:params.groupResolution}):null,basePatch={updatedAt:Math.max(existing?.updatedAt??0,now),deliveryContext:normalized.deliveryContext,lastChannel:normalized.lastChannel,lastTo:normalized.lastTo,lastAccountId:normalized.lastAccountId,lastThreadId:normalized.lastThreadId},next=mergeSessionEntry(existing,metaPatch?{...basePatch,...metaPatch}:basePatch);return store[sessionKey]=next,await saveSessionStoreUnlocked(storePath,store,{activeSessionKey:sessionKey}),next})}function stripQuery(value){let noHash=value.split(`#`)[0]??value;return noHash.split(`?`)[0]??noHash}function extractFileNameFromMediaUrl(value){let trimmed=value.trim();if(!trimmed)return null;let cleaned=stripQuery(trimmed);try{let parsed=new URL(cleaned),base=path.basename(parsed.pathname);if(!base)return null;try{return decodeURIComponent(base)}catch{return base}}catch{let base=path.basename(cleaned);return!base||base===`/`||base===`.`?null:base}}function resolveMirroredTranscriptText(params){let mediaUrls=params.mediaUrls?.filter(url=>url&&url.trim())??[];if(mediaUrls.length>0){let names=mediaUrls.map(url=>extractFileNameFromMediaUrl(url)).filter(name=>!!(name&&name.trim()));return names.length>0?names.join(`, `):`media`}return(params.text??``).trim()||null}async function ensureSessionHeader(params){if(fs$1.existsSync(params.sessionFile))return;await fs$1.promises.mkdir(path.dirname(params.sessionFile),{recursive:!0});let header={type:`session`,version:CURRENT_SESSION_VERSION,id:params.sessionId,timestamp:new Date().toISOString(),cwd:process.cwd()};await fs$1.promises.writeFile(params.sessionFile,`${JSON.stringify(header)}\n`,{encoding:`utf-8`,mode:384})}async function appendAssistantMessageToSessionTranscript(params){let sessionKey=params.sessionKey.trim();if(!sessionKey)return{ok:!1,reason:`missing sessionKey`};let mirrorText=resolveMirroredTranscriptText({text:params.text,mediaUrls:params.mediaUrls});if(!mirrorText)return{ok:!1,reason:`empty text`};let storePath=params.storePath??resolveDefaultSessionStorePath(params.agentId),entry=loadSessionStore(storePath,{skipCache:!0})[sessionKey];if(!entry?.sessionId)return{ok:!1,reason:`unknown sessionKey: ${sessionKey}`};let sessionFile;try{sessionFile=resolveSessionFilePath(entry.sessionId,entry,{agentId:params.agentId,sessionsDir:path.dirname(storePath)})}catch(err){return{ok:!1,reason:err instanceof Error?err.message:String(err)}}return await ensureSessionHeader({sessionFile,sessionId:entry.sessionId}),SessionManager.open(sessionFile).appendMessage({role:`assistant`,content:[{type:`text`,text:mirrorText}],api:`openai-responses`,provider:`builderforce`,model:`delivery-mirror`,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:`stop`,timestamp:Date.now()}),(!entry.sessionFile||entry.sessionFile!==sessionFile)&&await updateSessionStore(storePath,current=>{current[sessionKey]={...entry,sessionFile}},{activeSessionKey:sessionKey}),emitSessionTranscriptUpdate(sessionFile),{ok:!0,sessionFile}}function parseSessionThreadInfo(sessionKey){if(!sessionKey)return{baseSessionKey:void 0,threadId:void 0};let topicIndex=sessionKey.lastIndexOf(`:topic:`),threadIndex=sessionKey.lastIndexOf(`:thread:`),markerIndex=Math.max(topicIndex,threadIndex),marker=topicIndex>threadIndex?`:topic:`:`:thread:`;return{baseSessionKey:markerIndex===-1?sessionKey:sessionKey.slice(0,markerIndex),threadId:(markerIndex===-1?void 0:sessionKey.slice(markerIndex+marker.length))?.trim()||void 0}}function extractDeliveryInfo(sessionKey){let{baseSessionKey,threadId}=parseSessionThreadInfo(sessionKey);if(!sessionKey||!baseSessionKey)return{deliveryContext:void 0,threadId};let deliveryContext;try{let store=loadSessionStore(resolveStorePath(loadConfig().session?.store)),entry=store[sessionKey];!entry?.deliveryContext&&baseSessionKey!==sessionKey&&(entry=store[baseSessionKey]),entry?.deliveryContext&&(deliveryContext={channel:entry.deliveryContext.channel,to:entry.deliveryContext.to,accountId:entry.deliveryContext.accountId})}catch{}return{deliveryContext,threadId}}function shouldSandboxSession(cfg,sessionKey,mainSessionKey){return cfg.mode===`off`?!1:cfg.mode===`all`?!0:sessionKey.trim()!==mainSessionKey.trim()}function resolveMainSessionKeyForSandbox(params){return params.cfg?.session?.scope===`global`?`global`:resolveAgentMainSessionKey({cfg:params.cfg,agentId:params.agentId})}function resolveComparableSessionKeyForSandbox(params){return canonicalizeMainSessionAlias({cfg:params.cfg,agentId:params.agentId,sessionKey:params.sessionKey})}function resolveSandboxRuntimeStatus(params){let sessionKey=params.sessionKey?.trim()??``,agentId=resolveSessionAgentId({sessionKey,config:params.cfg}),cfg=params.cfg,sandboxCfg=resolveSandboxConfigForAgent(cfg,agentId),mainSessionKey=resolveMainSessionKeyForSandbox({cfg,agentId}),sandboxed=sessionKey?shouldSandboxSession(sandboxCfg,resolveComparableSessionKeyForSandbox({cfg,agentId,sessionKey}),mainSessionKey):!1;return{agentId,sessionKey,mainSessionKey,mode:sandboxCfg.mode,sandboxed,toolPolicy:resolveSandboxToolPolicyForAgent(cfg,agentId)}}function formatSandboxToolPolicyBlockedMessage(params){let tool=params.toolName.trim().toLowerCase();if(!tool)return;let runtime=resolveSandboxRuntimeStatus({cfg:params.cfg,sessionKey:params.sessionKey});if(!runtime.sandboxed)return;let deny=new Set(expandToolGroups(runtime.toolPolicy.deny)),allow=expandToolGroups(runtime.toolPolicy.allow),allowSet=allow.length>0?new Set(allow):null,blockedByDeny=deny.has(tool),blockedByAllow=allowSet?!allowSet.has(tool):!1;if(!blockedByDeny&&!blockedByAllow)return;let reasons=[],fixes=[];blockedByDeny&&(reasons.push(`deny list`),fixes.push(`Remove "${tool}" from ${runtime.toolPolicy.sources.deny.key}.`)),blockedByAllow&&(reasons.push(`allow list`),fixes.push(`Add "${tool}" to ${runtime.toolPolicy.sources.allow.key} (or set it to [] to allow all).`));let lines=[];lines.push(`Tool "${tool}" blocked by sandbox tool policy (mode=${runtime.mode}).`),lines.push(`Session: ${runtime.sessionKey||`(unknown)`}`),lines.push(`Reason: ${reasons.join(` + `)}`),lines.push(`Fix:`),lines.push(`- agents.defaults.sandbox.mode=off (disable sandbox)`);for(let fix of fixes)lines.push(`- ${fix}`);return runtime.mode===`non-main`&&lines.push(`- Use main session key (direct): ${runtime.mainSessionKey}`),lines.push(`- See: ${formatCliCommand(`builderforce sandbox explain --session ${runtime.sessionKey}`)}`),lines.join(`
|
|
13
13
|
`)}async function ensureSandboxWorkspace(workspaceDir,seedFrom,skipBootstrap){if(await fs.mkdir(workspaceDir,{recursive:!0}),seedFrom){let seed=resolveUserPath(seedFrom),files=[DEFAULT_AGENTS_FILENAME,DEFAULT_SOUL_FILENAME,DEFAULT_TOOLS_FILENAME,DEFAULT_IDENTITY_FILENAME,DEFAULT_USER_FILENAME,DEFAULT_BOOTSTRAP_FILENAME,DEFAULT_HEARTBEAT_FILENAME];for(let name of files){let src=path.join(seed,name),dest=path.join(workspaceDir,name);try{await fs.access(dest)}catch{try{let content=await fs.readFile(src,`utf-8`);await fs.writeFile(dest,content,{encoding:`utf-8`,flag:`wx`})}catch{}}}}await ensureAgentWorkspace({dir:workspaceDir,ensureBootstrapFiles:!skipBootstrap})}async function ensureSandboxWorkspaceLayout(params){let{cfg,rawSessionKey}=params,agentWorkspaceDir=resolveUserPath(params.workspaceDir?.trim()||DEFAULT_AGENT_WORKSPACE_DIR),workspaceRoot=resolveUserPath(cfg.workspaceRoot),scopeKey=resolveSandboxScopeKey(cfg.scope,rawSessionKey),sandboxWorkspaceDir=cfg.scope===`shared`?workspaceRoot:resolveSandboxWorkspaceDir(workspaceRoot,scopeKey),workspaceDir=cfg.workspaceAccess===`rw`?agentWorkspaceDir:sandboxWorkspaceDir;if(workspaceDir===sandboxWorkspaceDir){if(await ensureSandboxWorkspace(sandboxWorkspaceDir,agentWorkspaceDir,params.config?.agents?.defaults?.skipBootstrap),cfg.workspaceAccess!==`rw`)try{await syncSkillsToWorkspace({sourceWorkspaceDir:agentWorkspaceDir,targetWorkspaceDir:sandboxWorkspaceDir,config:params.config})}catch(error){let message=error instanceof Error?error.message:JSON.stringify(error);defaultRuntime.error?.(`Sandbox skill sync failed: ${message}`)}}else await fs.mkdir(workspaceDir,{recursive:!0});return{agentWorkspaceDir,scopeKey,sandboxWorkspaceDir,workspaceDir}}function resolveSandboxSession(params){let rawSessionKey=params.sessionKey?.trim();if(!rawSessionKey)return null;let runtime=resolveSandboxRuntimeStatus({cfg:params.config,sessionKey:rawSessionKey});return runtime.sandboxed?{rawSessionKey,runtime,cfg:resolveSandboxConfigForAgent(params.config,runtime.agentId)}:null}async function resolveSandboxContext(params){let resolved=resolveSandboxSession(params);if(!resolved)return null;let{rawSessionKey,cfg}=resolved;await maybePruneSandboxes(cfg);let{agentWorkspaceDir,scopeKey,workspaceDir}=await ensureSandboxWorkspaceLayout({cfg,rawSessionKey,config:params.config,workspaceDir:params.workspaceDir}),containerName=await ensureSandboxContainer({sessionKey:rawSessionKey,workspaceDir,agentWorkspaceDir,cfg}),browser=await ensureSandboxBrowser({scopeKey,workspaceDir,agentWorkspaceDir,cfg,evaluateEnabled:params.config?.browser?.evaluateEnabled??DEFAULT_BROWSER_EVALUATE_ENABLED,bridgeAuth:cfg.browser.enabled?await(async()=>{let cfgForAuth=params.config??loadConfig(),browserAuth=resolveBrowserControlAuth(cfgForAuth);try{browserAuth=(await ensureBrowserControlAuth({cfg:cfgForAuth})).auth}catch(error){let message=error instanceof Error?error.message:JSON.stringify(error);defaultRuntime.error?.(`Sandbox browser auth ensure failed: ${message}`)}return browserAuth})():void 0}),sandboxContext={enabled:!0,sessionKey:rawSessionKey,workspaceDir,agentWorkspaceDir,workspaceAccess:cfg.workspaceAccess,containerName,containerWorkdir:cfg.docker.workdir,docker:cfg.docker,tools:cfg.tools,browserAllowHostControl:cfg.browser.allowHostControl,browser:browser??void 0};return sandboxContext.fsBridge=createSandboxFsBridge({sandbox:sandboxContext}),sandboxContext}async function ensureSandboxWorkspaceForSession(params){let resolved=resolveSandboxSession(params);if(!resolved)return null;let{rawSessionKey,cfg}=resolved,{workspaceDir}=await ensureSandboxWorkspaceLayout({cfg,rawSessionKey,config:params.config,workspaceDir:params.workspaceDir});return{workspaceDir,containerWorkdir:cfg.docker.workdir}}function formatBillingErrorMessage(provider,model){let providerName=provider?.trim(),modelName=model?.trim(),providerLabel=providerName&&modelName?`${providerName} (${modelName})`:providerName||void 0;return providerLabel?`⚠️ ${providerLabel} returned a billing error — your API key has run out of credits or has an insufficient balance. Check your ${providerName} billing dashboard and top up or switch to a different API key.`:`⚠️ API provider returned a billing error — your API key has run out of credits or has an insufficient balance. Check your provider's billing dashboard and top up or switch to a different API key.`}const BILLING_ERROR_USER_MESSAGE=formatBillingErrorMessage();function formatRateLimitOrOverloadedErrorCopy(raw){if(isRateLimitErrorMessage(raw))return`⚠️ API rate limit reached. Please try again later.`;if(isOverloadedErrorMessage(raw))return`The AI service is temporarily overloaded. Please try again in a moment.`}function isContextOverflowError(errorMessage){if(!errorMessage)return!1;let lower=errorMessage.toLowerCase(),hasRequestSizeExceeds=lower.includes(`request size exceeds`),hasContextWindow=lower.includes(`context window`)||lower.includes(`context length`)||lower.includes(`maximum context length`);return lower.includes(`request_too_large`)||lower.includes(`request exceeds the maximum size`)||lower.includes(`context length exceeded`)||lower.includes(`maximum context length`)||lower.includes(`prompt is too long`)||lower.includes(`exceeds model context window`)||hasRequestSizeExceeds&&hasContextWindow||lower.includes(`context overflow:`)||lower.includes(`413`)&&lower.includes(`too large`)}const CONTEXT_WINDOW_TOO_SMALL_RE=/context window.*(too small|minimum is)/i,CONTEXT_OVERFLOW_HINT_RE=/context.*overflow|context window.*(too (?:large|long)|exceed|over|limit|max(?:imum)?|requested|sent|tokens)|prompt.*(too (?:large|long)|exceed|over|limit|max(?:imum)?)|(?:request|input).*(?:context|window|length|token).*(too (?:large|long)|exceed|over|limit|max(?:imum)?)/i,RATE_LIMIT_HINT_RE=/rate limit|too many requests|requests per (?:minute|hour|day)|quota|throttl|429\b/i;function isLikelyContextOverflowError(errorMessage){return!errorMessage||CONTEXT_WINDOW_TOO_SMALL_RE.test(errorMessage)||isRateLimitErrorMessage(errorMessage)?!1:isContextOverflowError(errorMessage)?!0:RATE_LIMIT_HINT_RE.test(errorMessage)?!1:CONTEXT_OVERFLOW_HINT_RE.test(errorMessage)}function isCompactionFailureError(errorMessage){if(!errorMessage)return!1;let lower=errorMessage.toLowerCase();return lower.includes(`summarization failed`)||lower.includes(`auto-compaction`)||lower.includes(`compaction failed`)||lower.includes(`compaction`)?isLikelyContextOverflowError(errorMessage)?!0:lower.includes(`context overflow`):!1}const ERROR_PAYLOAD_PREFIX_RE=/^(?:error|api\s*error|apierror|openai\s*error|anthropic\s*error|gateway\s*error)[:\s-]+/i,FINAL_TAG_RE=/<\s*\/?\s*final\s*>/gi,ERROR_PREFIX_RE=/^(?:error|api\s*error|openai\s*error|anthropic\s*error|gateway\s*error|request failed|failed|exception)[:\s-]+/i,CONTEXT_OVERFLOW_ERROR_HEAD_RE=/^(?:context overflow:|request_too_large\b|request size exceeds\b|request exceeds the maximum size\b|context length exceeded\b|maximum context length\b|prompt is too long\b|exceeds model context window\b)/i,BILLING_ERROR_HEAD_RE=/^(?:error[:\s-]+)?billing(?:\s+error)?(?:[:\s-]+|$)|^(?:error[:\s-]+)?(?:credit balance|insufficient credits?|payment required|http\s*402\b)/i,HTTP_STATUS_PREFIX_RE=/^(?:http\s*)?(\d{3})\s+(.+)$/i,HTTP_STATUS_CODE_PREFIX_RE=/^(?:http\s*)?(\d{3})(?:\s+([\s\S]+))?$/i,HTML_ERROR_PREFIX_RE=/^\s*(?:<!doctype\s+html\b|<html\b)/i,CLOUDFLARE_HTML_ERROR_CODES=new Set([521,522,523,524,525,526,530]),TRANSIENT_HTTP_ERROR_CODES=new Set([500,502,503,521,522,523,524,529]),HTTP_ERROR_HINTS=[`error`,`bad request`,`not found`,`unauthorized`,`forbidden`,`internal server`,`service unavailable`,`gateway`,`rate limit`,`overloaded`,`timeout`,`timed out`,`invalid`,`too many requests`,`permission`];function extractLeadingHttpStatus(raw){let match=raw.match(HTTP_STATUS_CODE_PREFIX_RE);if(!match)return null;let code=Number(match[1]);return Number.isFinite(code)?{code,rest:(match[2]??``).trim()}:null}function isCloudflareOrHtmlErrorPage(raw){let trimmed=raw.trim();if(!trimmed)return!1;let status=extractLeadingHttpStatus(trimmed);return!status||status.code<500?!1:CLOUDFLARE_HTML_ERROR_CODES.has(status.code)?!0:status.code<600&&HTML_ERROR_PREFIX_RE.test(status.rest)&&/<\/html>/i.test(status.rest)}function isTransientHttpError(raw){let trimmed=raw.trim();if(!trimmed)return!1;let status=extractLeadingHttpStatus(trimmed);return status?TRANSIENT_HTTP_ERROR_CODES.has(status.code):!1}function stripFinalTagsFromText(text){return text&&text.replace(FINAL_TAG_RE,``)}function collapseConsecutiveDuplicateBlocks(text){let trimmed=text.trim();if(!trimmed)return text;let blocks=trimmed.split(/\n{2,}/);if(blocks.length<2)return text;let normalizeBlock=value=>value.trim().replace(/\s+/g,` `),result=[],lastNormalized=null;for(let block of blocks){let normalized=normalizeBlock(block);lastNormalized&&normalized===lastNormalized||(result.push(block.trim()),lastNormalized=normalized)}return result.length===blocks.length?text:result.join(`
|
|
14
14
|
|
|
15
15
|
`)}function isLikelyHttpErrorText(raw){if(isCloudflareOrHtmlErrorPage(raw))return!0;let match=raw.match(HTTP_STATUS_PREFIX_RE);if(!match)return!1;let code=Number(match[1]);if(!Number.isFinite(code)||code<400)return!1;let message=match[2].toLowerCase();return HTTP_ERROR_HINTS.some(hint=>message.includes(hint))}function shouldRewriteContextOverflowText(raw){return isContextOverflowError(raw)?isRawApiErrorPayload(raw)||isLikelyHttpErrorText(raw)||ERROR_PREFIX_RE.test(raw)||CONTEXT_OVERFLOW_ERROR_HEAD_RE.test(raw):!1}function shouldRewriteBillingText(raw){return isBillingErrorMessage(raw)?isRawApiErrorPayload(raw)||isLikelyHttpErrorText(raw)||ERROR_PREFIX_RE.test(raw)||BILLING_ERROR_HEAD_RE.test(raw):!1}function isErrorPayloadObject(payload){if(!payload||typeof payload!=`object`||Array.isArray(payload))return!1;let record=payload;if(record.type===`error`||typeof record.request_id==`string`||typeof record.requestId==`string`)return!0;if(`error`in record){let err=record.error;if(err&&typeof err==`object`&&!Array.isArray(err)){let errRecord=err;if(typeof errRecord.message==`string`||typeof errRecord.type==`string`||typeof errRecord.code==`string`)return!0}}return!1}function parseApiErrorPayload(raw){if(!raw)return null;let trimmed=raw.trim();if(!trimmed)return null;let candidates=[trimmed];ERROR_PAYLOAD_PREFIX_RE.test(trimmed)&&candidates.push(trimmed.replace(ERROR_PAYLOAD_PREFIX_RE,``).trim());for(let candidate of candidates)if(!(!candidate.startsWith(`{`)||!candidate.endsWith(`}`)))try{let parsed=JSON.parse(candidate);if(isErrorPayloadObject(parsed))return parsed}catch{}return null}function getApiErrorPayloadFingerprint(raw){if(!raw)return null;let payload=parseApiErrorPayload(raw);return payload?stableStringify(payload):null}function isRawApiErrorPayload(raw){return getApiErrorPayloadFingerprint(raw)!==null}function parseApiErrorInfo(raw){if(!raw)return null;let trimmed=raw.trim();if(!trimmed)return null;let httpCode,candidate=trimmed,httpPrefixMatch=candidate.match(/^(\d{3})\s+(.+)$/s);httpPrefixMatch&&(httpCode=httpPrefixMatch[1],candidate=httpPrefixMatch[2].trim());let payload=parseApiErrorPayload(candidate);if(!payload)return null;let requestId=typeof payload.request_id==`string`?payload.request_id:typeof payload.requestId==`string`?payload.requestId:void 0,topType=typeof payload.type==`string`?payload.type:void 0,topMessage=typeof payload.message==`string`?payload.message:void 0,errType,errMessage;if(payload.error&&typeof payload.error==`object`&&!Array.isArray(payload.error)){let err=payload.error;typeof err.type==`string`&&(errType=err.type),typeof err.code==`string`&&!errType&&(errType=err.code),typeof err.message==`string`&&(errMessage=err.message)}return{httpCode,type:errType??topType,message:errMessage??topMessage,requestId}}function formatRawAssistantErrorForUi(raw){let trimmed=(raw??``).trim();if(!trimmed)return`LLM request failed with an unknown error.`;let leadingStatus=extractLeadingHttpStatus(trimmed);if(leadingStatus&&isCloudflareOrHtmlErrorPage(trimmed))return`The AI service is temporarily unavailable (HTTP ${leadingStatus.code}). Please try again in a moment.`;let httpMatch=trimmed.match(HTTP_STATUS_PREFIX_RE);if(httpMatch){let rest=httpMatch[2].trim();if(!rest.startsWith(`{`))return`HTTP ${httpMatch[1]}: ${rest}`}let info=parseApiErrorInfo(trimmed);if(info?.message){let prefix=info.httpCode?`HTTP ${info.httpCode}`:`LLM error`,type=info.type?` ${info.type}`:``,requestId=info.requestId?` (request_id: ${info.requestId})`:``;return`${prefix}${type}: ${info.message}${requestId}`}return trimmed.length>600?`${trimmed.slice(0,600)}…`:trimmed}function formatAssistantErrorText(msg,opts){let raw=(msg.errorMessage??``).trim();if(msg.stopReason!==`error`&&!raw)return;if(!raw)return`LLM request failed with an unknown error.`;let unknownTool=raw.match(/unknown tool[:\s]+["']?([a-z0-9_-]+)["']?/i)??raw.match(/tool\s+["']?([a-z0-9_-]+)["']?\s+(?:not found|is not available)/i);if(unknownTool?.[1]){let rewritten=formatSandboxToolPolicyBlockedMessage({cfg:opts?.cfg,sessionKey:opts?.sessionKey,toolName:unknownTool[1]});if(rewritten)return rewritten}if(isContextOverflowError(raw))return`Context overflow: prompt too large for the model. Try /reset (or /new) to start a fresh session, or use a larger-context model.`;if(/incorrect role information|roles must alternate|400.*role|"message".*role.*information/i.test(raw))return`Message ordering conflict - please try again. If this persists, use /new to start a fresh session.`;if(isMissingToolCallInputError(raw))return`Session history looks corrupted (tool call input missing). Use /new to start a fresh session. If this keeps happening, reset the session or delete the corrupted session transcript.`;let invalidRequest=raw.match(/"type":"invalid_request_error".*?"message":"([^"]+)"/);return invalidRequest?.[1]?`LLM request rejected: ${invalidRequest[1]}`:formatRateLimitOrOverloadedErrorCopy(raw)||(isTimeoutErrorMessage(raw)?`LLM request timed out.`:isBillingErrorMessage(raw)?formatBillingErrorMessage(opts?.provider,opts?.model??msg.model):isLikelyHttpErrorText(raw)||isRawApiErrorPayload(raw)?formatRawAssistantErrorForUi(raw):(raw.length>600&&console.warn(`[formatAssistantErrorText] Long error truncated:`,raw.slice(0,200)),raw.length>600?`${raw.slice(0,600)}…`:raw))}function sanitizeUserFacingText(text,opts){if(!text)return text;let errorContext=opts?.errorContext??!1,stripped=stripFinalTagsFromText(text),trimmed=stripped.trim();if(!trimmed)return``;if(errorContext){if(/incorrect role information|roles must alternate/i.test(trimmed))return`Message ordering conflict - please try again. If this persists, use /new to start a fresh session.`;if(shouldRewriteContextOverflowText(trimmed))return`Context overflow: prompt too large for the model. Try /reset (or /new) to start a fresh session, or use a larger-context model.`;if(isBillingErrorMessage(trimmed))return BILLING_ERROR_USER_MESSAGE;if(isRawApiErrorPayload(trimmed)||isLikelyHttpErrorText(trimmed))return formatRawAssistantErrorForUi(trimmed);if(ERROR_PREFIX_RE.test(trimmed))return formatRateLimitOrOverloadedErrorCopy(trimmed)||(isTimeoutErrorMessage(trimmed)?`LLM request timed out.`:formatRawAssistantErrorForUi(trimmed))}return shouldRewriteBillingText(trimmed)?BILLING_ERROR_USER_MESSAGE:collapseConsecutiveDuplicateBlocks(stripped.replace(/^(?:[ \t]*\r?\n)+/,``))}function isRateLimitAssistantError(msg){return!msg||msg.stopReason!==`error`?!1:isRateLimitErrorMessage(msg.errorMessage??``)}const ERROR_PATTERNS={rateLimit:[/rate[_ ]limit|too many requests|429/,`exceeded your current quota`,`resource has been exhausted`,`quota exceeded`,`resource_exhausted`,`usage limit`],overloaded:[/overloaded_error|"type"\s*:\s*"overloaded_error"/i,`overloaded`],timeout:[`timeout`,`timed out`,`deadline exceeded`,`context deadline exceeded`,`error from model endpoint`,`upstream error from openinference`,/without sending (?:any )?chunks?/i,/\bstop reason:\s*abort\b/i,/\breason:\s*abort\b/i,/\bunhandled stop reason:\s*abort\b/i],billing:[/["']?(?:status|code)["']?\s*[:=]\s*402\b|\bhttp\s*402\b|\berror(?:\s+code)?\s*[:=]?\s*402\b|\b(?:got|returned|received)\s+(?:a\s+)?402\b|^\s*402\s+payment/i,`payment required`,`insufficient credits`,`credit balance`,`plans & billing`,`insufficient balance`],auth:[/invalid[_ ]?api[_ ]?key/,`incorrect api key`,`invalid token`,`authentication`,`re-authenticate`,`oauth token refresh failed`,`unauthorized`,`forbidden`,`access denied`,`expired`,`token has expired`,/\b401\b/,/\b403\b/,`no credentials found`,`no api key found`],format:[`string should match pattern`,`tool_use.id`,`tool_use_id`,`messages.1.content.1.tool_use.id`,`invalid request format`]},TOOL_CALL_INPUT_MISSING_RE=/tool_(?:use|call)\.(?:input|arguments).*?(?:field required|required)/i,TOOL_CALL_INPUT_PATH_RE=/messages\.\d+\.content\.\d+\.tool_(?:use|call)\.(?:input|arguments)/i,IMAGE_DIMENSION_ERROR_RE=/image dimensions exceed max allowed size for many-image requests:\s*(\d+)\s*pixels/i,IMAGE_DIMENSION_PATH_RE=/messages\.(\d+)\.content\.(\d+)\.image/i,IMAGE_SIZE_ERROR_RE=/image exceeds\s*(\d+(?:\.\d+)?)\s*mb/i;function matchesErrorPatterns(raw,patterns){if(!raw)return!1;let value=raw.toLowerCase();return patterns.some(pattern=>pattern instanceof RegExp?pattern.test(value):value.includes(pattern))}function isRateLimitErrorMessage(raw){return matchesErrorPatterns(raw,ERROR_PATTERNS.rateLimit)}function isTimeoutErrorMessage(raw){return matchesErrorPatterns(raw,ERROR_PATTERNS.timeout)}function isBillingErrorMessage(raw){let value=raw.toLowerCase();return value?matchesErrorPatterns(value,ERROR_PATTERNS.billing)?!0:BILLING_ERROR_HEAD_RE.test(raw)?value.includes(`upgrade`)||value.includes(`credits`)||value.includes(`payment`)||value.includes(`plan`):!1:!1}function isMissingToolCallInputError(raw){return raw?TOOL_CALL_INPUT_MISSING_RE.test(raw)||TOOL_CALL_INPUT_PATH_RE.test(raw):!1}function isBillingAssistantError(msg){return!msg||msg.stopReason!==`error`?!1:isBillingErrorMessage(msg.errorMessage??``)}function isAuthErrorMessage(raw){return matchesErrorPatterns(raw,ERROR_PATTERNS.auth)}function isOverloadedErrorMessage(raw){return matchesErrorPatterns(raw,ERROR_PATTERNS.overloaded)}function parseImageDimensionError(raw){if(!raw||!raw.toLowerCase().includes(`image dimensions exceed max allowed size`))return null;let limitMatch=raw.match(IMAGE_DIMENSION_ERROR_RE),pathMatch=raw.match(IMAGE_DIMENSION_PATH_RE);return{maxDimensionPx:limitMatch?.[1]?Number.parseInt(limitMatch[1],10):void 0,messageIndex:pathMatch?.[1]?Number.parseInt(pathMatch[1],10):void 0,contentIndex:pathMatch?.[2]?Number.parseInt(pathMatch[2],10):void 0,raw}}function isImageDimensionErrorMessage(raw){return!!parseImageDimensionError(raw)}function parseImageSizeError(raw){if(!raw)return null;let lower=raw.toLowerCase();if(!lower.includes(`image exceeds`)||!lower.includes(`mb`))return null;let match=raw.match(IMAGE_SIZE_ERROR_RE);return{maxMb:match?.[1]?Number.parseFloat(match[1]):void 0,raw}}function isImageSizeError(errorMessage){return errorMessage?!!parseImageSizeError(errorMessage):!1}function isCloudCodeAssistFormatError(raw){return!isImageDimensionErrorMessage(raw)&&matchesErrorPatterns(raw,ERROR_PATTERNS.format)}function isAuthAssistantError(msg){return!msg||msg.stopReason!==`error`?!1:isAuthErrorMessage(msg.errorMessage??``)}function classifyFailoverReason(raw){return isImageDimensionErrorMessage(raw)||isImageSizeError(raw)?null:isTransientHttpError(raw)?`timeout`:isRateLimitErrorMessage(raw)||isOverloadedErrorMessage(raw)?`rate_limit`:isCloudCodeAssistFormatError(raw)?`format`:isBillingErrorMessage(raw)?`billing`:isTimeoutErrorMessage(raw)?`timeout`:isAuthErrorMessage(raw)?`auth`:null}function isFailoverErrorMessage(raw){return classifyFailoverReason(raw)!==null}function isFailoverAssistantError(msg){return!msg||msg.stopReason!==`error`?!1:isFailoverErrorMessage(msg.errorMessage??``)}function isGoogleModelApi(api){return api===`google-gemini-cli`||api===`google-generative-ai`||api===`google-antigravity`}function isAntigravityClaude(params){let provider=params.provider?.toLowerCase(),api=params.api?.toLowerCase();return provider!==`google-antigravity`&&api!==`google-antigravity`?!1:params.modelId?.toLowerCase().includes(`claude`)??!1}function parseOpenAIReasoningSignature(value){if(!value)return null;let candidate=null;if(typeof value==`string`){let trimmed=value.trim();if(!trimmed.startsWith(`{`)||!trimmed.endsWith(`}`))return null;try{candidate=JSON.parse(trimmed)}catch{return null}}else typeof value==`object`&&(candidate=value);if(!candidate)return null;let id=typeof candidate.id==`string`?candidate.id:``,type=typeof candidate.type==`string`?candidate.type:``;return id.startsWith(`rs_`)&&(type===`reasoning`||type.startsWith(`reasoning.`))?{id,type}:null}function hasFollowingNonThinkingBlock(content,index){for(let i=index+1;i<content.length;i++){let block=content[i];if(!block||typeof block!=`object`||block.type!==`thinking`)return!0}return!1}function downgradeOpenAIReasoningBlocks(messages){let out=[];for(let msg of messages){if(!msg||typeof msg!=`object`){out.push(msg);continue}if(msg.role!==`assistant`){out.push(msg);continue}let assistantMsg=msg;if(!Array.isArray(assistantMsg.content)){out.push(msg);continue}let changed=!1,nextContent=[];for(let i=0;i<assistantMsg.content.length;i++){let block=assistantMsg.content[i];if(!block||typeof block!=`object`){nextContent.push(block);continue}let record=block;if(record.type!==`thinking`){nextContent.push(block);continue}if(!parseOpenAIReasoningSignature(record.thinkingSignature)){nextContent.push(block);continue}if(hasFollowingNonThinkingBlock(assistantMsg.content,i)){nextContent.push(block);continue}changed=!0}if(!changed){out.push(msg);continue}nextContent.length!==0&&out.push({...assistantMsg,content:nextContent})}return out}const TOOL_CALL_TYPES=new Set([`toolCall`,`toolUse`,`functionCall`]);function sanitizeToolCallId(id,mode=`strict`){if(!id||typeof id!=`string`)return mode===`strict9`?`defaultid`:`defaulttoolid`;if(mode===`strict9`){let alphanumericOnly=id.replace(/[^a-zA-Z0-9]/g,``);return alphanumericOnly.length>=9?alphanumericOnly.slice(0,9):alphanumericOnly.length>0?shortHash(alphanumericOnly,9):shortHash(`sanitized`,9)}let alphanumericOnly=id.replace(/[^a-zA-Z0-9]/g,``);return alphanumericOnly.length>0?alphanumericOnly:`sanitizedtoolid`}function extractToolCallsFromAssistant(msg){let content=msg.content;if(!Array.isArray(content))return[];let toolCalls=[];for(let block of content){if(!block||typeof block!=`object`)continue;let rec=block;typeof rec.id!=`string`||!rec.id||typeof rec.type==`string`&&TOOL_CALL_TYPES.has(rec.type)&&toolCalls.push({id:rec.id,name:typeof rec.name==`string`?rec.name:void 0})}return toolCalls}function extractToolResultId(msg){let toolCallId=msg.toolCallId;if(typeof toolCallId==`string`&&toolCallId)return toolCallId;let toolUseId=msg.toolUseId;return typeof toolUseId==`string`&&toolUseId?toolUseId:null}function shortHash(text,length=8){return createHash(`sha1`).update(text).digest(`hex`).slice(0,length)}function makeUniqueToolId(params){if(params.mode===`strict9`){let base=sanitizeToolCallId(params.id,params.mode),candidate=base.length>=9?base.slice(0,9):``;if(candidate&&!params.used.has(candidate))return candidate;for(let i=0;i<1e3;i+=1){let hashed=shortHash(`${params.id}:${i}`,9);if(!params.used.has(hashed))return hashed}return shortHash(`${params.id}:${Date.now()}`,9)}let base=sanitizeToolCallId(params.id,params.mode).slice(0,40);if(!params.used.has(base))return base;let hash=shortHash(params.id),separator=params.mode===`strict`?``:`_`,maxBaseLen=40-separator.length-hash.length,candidate=`${base.length>maxBaseLen?base.slice(0,maxBaseLen):base}${separator}${hash}`;if(!params.used.has(candidate))return candidate;for(let i=2;i<1e3;i+=1){let suffix=params.mode===`strict`?`x${i}`:`_${i}`,next=`${candidate.slice(0,40-suffix.length)}${suffix}`;if(!params.used.has(next))return next}let ts=params.mode===`strict`?`t${Date.now()}`:`_${Date.now()}`;return`${candidate.slice(0,40-ts.length)}${ts}`}function rewriteAssistantToolCallIds(params){let content=params.message.content;if(!Array.isArray(content))return params.message;let changed=!1,next=content.map(block=>{if(!block||typeof block!=`object`)return block;let rec=block,type=rec.type,id=rec.id;if(type!==`functionCall`&&type!==`toolUse`&&type!==`toolCall`||typeof id!=`string`||!id)return block;let nextId=params.resolve(id);return nextId===id?block:(changed=!0,{...block,id:nextId})});return changed?{...params.message,content:next}:params.message}function rewriteToolResultIds(params){let toolCallId=typeof params.message.toolCallId==`string`&¶ms.message.toolCallId?params.message.toolCallId:void 0,toolUseId=params.message.toolUseId,toolUseIdStr=typeof toolUseId==`string`&&toolUseId?toolUseId:void 0,nextToolCallId=toolCallId?params.resolve(toolCallId):void 0,nextToolUseId=toolUseIdStr?params.resolve(toolUseIdStr):void 0;return nextToolCallId===toolCallId&&nextToolUseId===toolUseIdStr?params.message:{...params.message,...nextToolCallId&&{toolCallId:nextToolCallId},...nextToolUseId&&{toolUseId:nextToolUseId}}}function sanitizeToolCallIdsForCloudCodeAssist(messages,mode=`strict`){let map=new Map,used=new Set,resolve=id=>{let existing=map.get(id);if(existing)return existing;let next=makeUniqueToolId({id,used,mode});return map.set(id,next),used.add(next),next},changed=!1,out=messages.map(msg=>{if(!msg||typeof msg!=`object`)return msg;let role=msg.role;if(role===`assistant`){let next=rewriteAssistantToolCallIds({message:msg,resolve});return next!==msg&&(changed=!0),next}if(role===`toolResult`){let next=rewriteToolResultIds({message:msg,resolve});return next!==msg&&(changed=!0),next}return msg});return changed?out:messages}async function sanitizeSessionMessagesImages(messages,label,options){let allowNonImageSanitization=(options?.sanitizeMode??`full`)===`full`,imageSanitization={maxDimensionPx:options?.maxDimensionPx,maxBytes:options?.maxBytes},sanitizedIds=allowNonImageSanitization&&options?.sanitizeToolCallIds?sanitizeToolCallIdsForCloudCodeAssist(messages,options.toolCallIdMode):messages,out=[];for(let msg of sanitizedIds){if(!msg||typeof msg!=`object`){out.push(msg);continue}let role=msg.role;if(role===`toolResult`){let toolMsg=msg,nextContent=await sanitizeContentBlocksImages(Array.isArray(toolMsg.content)?toolMsg.content:[],label,imageSanitization);out.push({...toolMsg,content:nextContent});continue}if(role===`user`){let userMsg=msg,content=userMsg.content;if(Array.isArray(content)){let nextContent=await sanitizeContentBlocksImages(content,label,imageSanitization);out.push({...userMsg,content:nextContent});continue}}if(role===`assistant`){let assistantMsg=msg;if(assistantMsg.stopReason===`error`){let content=assistantMsg.content;if(Array.isArray(content)){let nextContent=await sanitizeContentBlocksImages(content,label,imageSanitization);out.push({...assistantMsg,content:nextContent})}else out.push(assistantMsg);continue}let content=assistantMsg.content;if(Array.isArray(content)){if(!allowNonImageSanitization){let nextContent=await sanitizeContentBlocksImages(content,label,imageSanitization);out.push({...assistantMsg,content:nextContent});continue}let finalContent=await sanitizeContentBlocksImages((options?.preserveSignatures?content:stripThoughtSignatures(content,options?.sanitizeThoughtSignatures)).filter(block=>{if(!block||typeof block!=`object`)return!0;let rec=block;return rec.type!==`text`||typeof rec.text!=`string`?!0:rec.text.trim().length>0}),label,imageSanitization);if(finalContent.length===0)continue;out.push({...assistantMsg,content:finalContent});continue}}out.push(msg)}return out}function normalizeTextForComparison(text){return text.trim().toLowerCase().replace(/\p{Emoji_Presentation}|\p{Extended_Pictographic}/gu,``).replace(/\s+/g,` `).trim()}function isMessagingToolDuplicateNormalized(normalized,normalizedSentTexts){return normalizedSentTexts.length===0||!normalized||normalized.length<10?!1:normalizedSentTexts.some(normalizedSent=>!normalizedSent||normalizedSent.length<10?!1:normalized.includes(normalizedSent)||normalizedSent.includes(normalized))}function isMessagingToolDuplicate(text,sentTexts){if(sentTexts.length===0)return!1;let normalized=normalizeTextForComparison(text);return!normalized||normalized.length<10?!1:isMessagingToolDuplicateNormalized(normalized,sentTexts.map(normalizeTextForComparison))}function normalizeProviderId(provider){if(!provider)return``;let normalized=provider.trim().toLowerCase();return normalized===`z.ai`||normalized===`z-ai`?`zai`:normalized}function isBinaryThinkingProvider(provider){return normalizeProviderId(provider)===`zai`}const XHIGH_MODEL_REFS=[`openai/gpt-5.2`,`openai-codex/gpt-5.3-codex`,`openai-codex/gpt-5.3-codex-spark`,`openai-codex/gpt-5.2-codex`,`openai-codex/gpt-5.1-codex`,`github-copilot/gpt-5.2-codex`,`github-copilot/gpt-5.2`],XHIGH_MODEL_SET=new Set(XHIGH_MODEL_REFS.map(entry=>entry.toLowerCase())),XHIGH_MODEL_IDS=new Set(XHIGH_MODEL_REFS.map(entry=>entry.split(`/`)[1]?.toLowerCase()).filter(entry=>!!entry));function normalizeThinkLevel(raw){if(!raw)return;let key=raw.trim().toLowerCase(),collapsed=key.replace(/[\s_-]+/g,``);if(collapsed===`xhigh`||collapsed===`extrahigh`)return`xhigh`;if([`off`].includes(key))return`off`;if([`on`,`enable`,`enabled`].includes(key))return`low`;if([`min`,`minimal`].includes(key))return`minimal`;if([`low`,`thinkhard`,`think-hard`,`think_hard`].includes(key))return`low`;if([`mid`,`med`,`medium`,`thinkharder`,`think-harder`,`harder`].includes(key))return`medium`;if([`high`,`ultra`,`ultrathink`,`think-hard`,`thinkhardest`,`highest`,`max`].includes(key))return`high`;if([`think`].includes(key))return`minimal`}function supportsXHighThinking(provider,model){let modelKey=model?.trim().toLowerCase();if(!modelKey)return!1;let providerKey=provider?.trim().toLowerCase();return providerKey?XHIGH_MODEL_SET.has(`${providerKey}/${modelKey}`):XHIGH_MODEL_IDS.has(modelKey)}function listThinkingLevels(provider,model){let levels=[`off`,`minimal`,`low`,`medium`,`high`];return supportsXHighThinking(provider,model)&&levels.push(`xhigh`),levels}function listThinkingLevelLabels(provider,model){return isBinaryThinkingProvider(provider)?[`off`,`on`]:listThinkingLevels(provider,model)}function formatThinkingLevels(provider,model,separator=`, `){return listThinkingLevelLabels(provider,model).join(separator)}function formatXHighModelHint(){let refs=[...XHIGH_MODEL_REFS];return refs.length===0?`unknown model`:refs.length===1?refs[0]:refs.length===2?`${refs[0]} or ${refs[1]}`:`${refs.slice(0,-1).join(`, `)} or ${refs[refs.length-1]}`}function normalizeOnOffFullLevel(raw){if(!raw)return;let key=raw.toLowerCase();if([`off`,`false`,`no`,`0`].includes(key))return`off`;if([`full`,`all`,`everything`].includes(key))return`full`;if([`on`,`minimal`,`true`,`yes`,`1`].includes(key))return`on`}function normalizeVerboseLevel(raw){return normalizeOnOffFullLevel(raw)}function normalizeUsageDisplay(raw){if(!raw)return;let key=raw.toLowerCase();if([`off`,`false`,`no`,`0`,`disable`,`disabled`].includes(key))return`off`;if([`on`,`true`,`yes`,`1`,`enable`,`enabled`].includes(key)||[`tokens`,`token`,`tok`,`minimal`,`min`].includes(key))return`tokens`;if([`full`,`session`].includes(key))return`full`}function resolveResponseUsageMode(raw){return normalizeUsageDisplay(raw)??`off`}function normalizeElevatedLevel(raw){if(!raw)return;let key=raw.toLowerCase();if([`off`,`false`,`no`,`0`].includes(key))return`off`;if([`full`,`auto`,`auto-approve`,`autoapprove`].includes(key))return`full`;if([`ask`,`prompt`,`approval`,`approve`].includes(key))return`ask`;if([`on`,`true`,`yes`,`1`].includes(key))return`on`}function normalizeReasoningLevel(raw){if(!raw)return;let key=raw.toLowerCase();if([`off`,`false`,`no`,`0`,`hide`,`hidden`,`disable`,`disabled`].includes(key))return`off`;if([`on`,`true`,`yes`,`1`,`show`,`visible`,`enable`,`enabled`].includes(key))return`on`;if([`stream`,`streaming`,`draft`,`live`].includes(key))return`stream`}function extractSupportedValues(raw){let match=raw.match(/supported values are:\s*([^\n.]+)/i)??raw.match(/supported values:\s*([^\n.]+)/i);if(!match?.[1])return[];let fragment=match[1],quoted=Array.from(fragment.matchAll(/['"]([^'"]+)['"]/g)).map(entry=>entry[1]?.trim());return quoted.length>0?quoted.filter(entry=>!!entry):fragment.split(/,|\band\b/gi).map(entry=>entry.replace(/^[^a-zA-Z]+|[^a-zA-Z]+$/g,``).trim()).filter(Boolean)}function pickFallbackThinkingLevel(params){let raw=params.message?.trim();if(!raw)return;let supported=extractSupportedValues(raw);if(supported.length!==0)for(let entry of supported){let normalized=normalizeThinkLevel(entry);if(normalized&&!params.attempted.has(normalized))return normalized}}function validateTurnsWithConsecutiveMerge(params){let{messages,role,merge}=params;if(!Array.isArray(messages)||messages.length===0)return messages;let result=[],lastRole;for(let msg of messages){if(!msg||typeof msg!=`object`){result.push(msg);continue}let msgRole=msg.role;if(!msgRole){result.push(msg);continue}if(msgRole===lastRole&&lastRole===role){let lastMsg=result[result.length-1],currentMsg=msg;if(lastMsg&&typeof lastMsg==`object`){let lastTyped=lastMsg;result[result.length-1]=merge(lastTyped,currentMsg);continue}}result.push(msg),lastRole=msgRole}return result}function mergeConsecutiveAssistantTurns(previous,current){let mergedContent=[...Array.isArray(previous.content)?previous.content:[],...Array.isArray(current.content)?current.content:[]];return{...previous,content:mergedContent,...current.usage&&{usage:current.usage},...current.stopReason&&{stopReason:current.stopReason},...current.errorMessage&&{errorMessage:current.errorMessage}}}function validateGeminiTurns(messages){return validateTurnsWithConsecutiveMerge({messages,role:`assistant`,merge:mergeConsecutiveAssistantTurns})}function mergeConsecutiveUserTurns(previous,current){let mergedContent=[...Array.isArray(previous.content)?previous.content:[],...Array.isArray(current.content)?current.content:[]];return{...current,content:mergedContent,timestamp:current.timestamp??previous.timestamp}}function validateAnthropicTurns(messages){return validateTurnsWithConsecutiveMerge({messages,role:`user`,merge:mergeConsecutiveUserTurns})}function extractTextFromChatContent(content,opts){let normalize=opts?.normalizeText??(text=>text.replace(/\s+/g,` `).trim()),joinWith=opts?.joinWith??` `;if(typeof content==`string`)return normalize(opts?.sanitizeText?opts.sanitizeText(content):content)||null;if(!Array.isArray(content))return null;let chunks=[];for(let block of content){if(!block||typeof block!=`object`||block.type!==`text`)continue;let text=block.text;if(typeof text!=`string`)continue;let value=opts?.sanitizeText?opts.sanitizeText(text):text;value.trim()&&chunks.push(value)}return normalize(chunks.join(joinWith))||null}function asRecord(args){return args&&typeof args==`object`?args:void 0}function normalizeToolName(name){return(name??`tool`).trim()}function defaultTitle(name){let cleaned=name.replace(/_/g,` `).trim();return cleaned?cleaned.split(/\s+/).map(part=>part.length<=2&&part.toUpperCase()===part?part:`${part.at(0)?.toUpperCase()??``}${part.slice(1)}`).join(` `):`Tool`}function normalizeVerb(value){let trimmed=value?.trim();if(trimmed)return trimmed.replace(/_/g,` `)}function coerceDisplayValue(value,opts={}){let maxStringChars=opts.maxStringChars??160,maxArrayEntries=opts.maxArrayEntries??3;if(value!=null){if(typeof value==`string`){let trimmed=value.trim();if(!trimmed)return;let firstLine=trimmed.split(/\r?\n/)[0]?.trim()??``;return firstLine?firstLine.length>maxStringChars?`${firstLine.slice(0,Math.max(0,maxStringChars-3))}…`:firstLine:void 0}if(typeof value==`boolean`)return!value&&!opts.includeFalse?void 0:value?`true`:`false`;if(typeof value==`number`)return Number.isFinite(value)?value===0&&!opts.includeZero?void 0:String(value):opts.includeNonFinite?String(value):void 0;if(Array.isArray(value)){let values=value.map(item=>coerceDisplayValue(item,opts)).filter(item=>!!item);if(values.length===0)return;let preview=values.slice(0,maxArrayEntries).join(`, `);return values.length>maxArrayEntries?`${preview}…`:preview}}}function lookupValueByPath(args,path){if(!args||typeof args!=`object`)return;let current=args;for(let segment of path.split(`.`)){if(!segment||!current||typeof current!=`object`)return;current=current[segment]}return current}function formatDetailKey(raw,overrides={}){let last=raw.split(`.`).filter(Boolean).at(-1)??raw;return overrides[last]||last.replace(/_/g,` `).replace(/-/g,` `).replace(/([a-z0-9])([A-Z])/g,`$1 $2`).trim().toLowerCase()||last.toLowerCase()}function resolvePathArg(args){let record=asRecord(args);if(record)for(let candidate of[record.path,record.file_path,record.filePath]){if(typeof candidate!=`string`)continue;let trimmed=candidate.trim();if(trimmed)return trimmed}}function resolveReadDetail(args){let record=asRecord(args);if(!record)return;let path=resolvePathArg(record);if(!path)return;let offsetRaw=typeof record.offset==`number`&&Number.isFinite(record.offset)?Math.floor(record.offset):void 0,limitRaw=typeof record.limit==`number`&&Number.isFinite(record.limit)?Math.floor(record.limit):void 0,offset=offsetRaw===void 0?void 0:Math.max(1,offsetRaw),limit=limitRaw===void 0?void 0:Math.max(1,limitRaw);return offset!==void 0&&limit!==void 0?`${limit===1?`line`:`lines`} ${offset}-${offset+limit-1} from ${path}`:offset===void 0?limit===void 0?`from ${path}`:`first ${limit} ${limit===1?`line`:`lines`} of ${path}`:`from line ${offset} in ${path}`}function resolveWriteDetail(toolKey,args){let record=asRecord(args);if(!record)return;let path=resolvePathArg(record)??(typeof record.url==`string`?record.url.trim():void 0);if(!path)return;if(toolKey===`attach`)return`from ${path}`;let destinationPrefix=toolKey===`edit`?`in`:`to`,content=typeof record.content==`string`?record.content:typeof record.newText==`string`?record.newText:typeof record.new_string==`string`?record.new_string:void 0;return content&&content.length>0?`${destinationPrefix} ${path} (${content.length} chars)`:`${destinationPrefix} ${path}`}function resolveWebSearchDetail(args){let record=asRecord(args);if(!record)return;let query=typeof record.query==`string`?record.query.trim():void 0,count=typeof record.count==`number`&&Number.isFinite(record.count)&&record.count>0?Math.floor(record.count):void 0;if(query)return count===void 0?`for "${query}"`:`for "${query}" (top ${count})`}function resolveWebFetchDetail(args){let record=asRecord(args);if(!record)return;let url=typeof record.url==`string`?record.url.trim():void 0;if(!url)return;let mode=typeof record.extractMode==`string`?record.extractMode.trim():void 0,maxChars=typeof record.maxChars==`number`&&Number.isFinite(record.maxChars)&&record.maxChars>0?Math.floor(record.maxChars):void 0,suffix=[mode?`mode ${mode}`:void 0,maxChars===void 0?void 0:`max ${maxChars} chars`].filter(value=>!!value).join(`, `);return suffix?`from ${url} (${suffix})`:`from ${url}`}function stripOuterQuotes(value){if(!value)return value;let trimmed=value.trim();return trimmed.length>=2&&(trimmed.startsWith(`"`)&&trimmed.endsWith(`"`)||trimmed.startsWith(`'`)&&trimmed.endsWith(`'`))?trimmed.slice(1,-1).trim():trimmed}function splitShellWords(input,maxWords=48){if(!input)return[];let words=[],current=``,quote,escaped=!1;for(let i=0;i<input.length;i+=1){let char=input[i];if(escaped){current+=char,escaped=!1;continue}if(char===`\\`){escaped=!0;continue}if(quote){char===quote?quote=void 0:current+=char;continue}if(char===`"`||char===`'`){quote=char;continue}if(/\s/.test(char)){if(!current)continue;if(words.push(current),words.length>=maxWords)return words;current=``;continue}current+=char}return current&&words.push(current),words}function binaryName(token){if(!token)return;let cleaned=stripOuterQuotes(token)??token;return(cleaned.split(/[/]/).at(-1)??cleaned).trim().toLowerCase()}function optionValue(words,names){let lookup=new Set(names);for(let i=0;i<words.length;i+=1){let token=words[i];if(token){if(lookup.has(token)){let value=words[i+1];if(value&&!value.startsWith(`-`))return value;continue}for(let name of names)if(name.startsWith(`--`)&&token.startsWith(`${name}=`))return token.slice(name.length+1)}}}function positionalArgs(words,from=1,optionsWithValue=[]){let args=[],takesValue=new Set(optionsWithValue);for(let i=from;i<words.length;i+=1){let token=words[i];if(token){if(token===`--`){for(let j=i+1;j<words.length;j+=1){let candidate=words[j];candidate&&args.push(candidate)}break}if(token.startsWith(`--`)){if(token.includes(`=`))continue;takesValue.has(token)&&(i+=1);continue}if(token.startsWith(`-`)){takesValue.has(token)&&(i+=1);continue}args.push(token)}}return args}function firstPositional(words,from=1,optionsWithValue=[]){return positionalArgs(words,from,optionsWithValue)[0]}function trimLeadingEnv(words){if(words.length===0)return words;let index=0;if(binaryName(words[0])===`env`){for(index=1;index<words.length;){let token=words[index];if(!token)break;if(token.startsWith(`-`)){index+=1;continue}if(/^[A-Za-z_][A-Za-z0-9_]*=/.test(token)){index+=1;continue}break}return words.slice(index)}for(;index<words.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(words[index]);)index+=1;return words.slice(index)}function unwrapShellWrapper(command){let words=splitShellWords(command,10);if(words.length<3)return command;let bin=binaryName(words[0]);if(!(bin===`bash`||bin===`sh`||bin===`zsh`||bin===`fish`))return command;let flagIndex=words.findIndex((token,index)=>index>0&&(token===`-c`||token===`-lc`||token===`-ic`));if(flagIndex===-1)return command;let inner=words.slice(flagIndex+1).join(` `).trim();return inner?stripOuterQuotes(inner)??command:command}function scanTopLevelChars(command,visit){let quote,escaped=!1;for(let i=0;i<command.length;i+=1){let char=command[i];if(escaped){escaped=!1;continue}if(char===`\\`){escaped=!0;continue}if(quote){char===quote&&(quote=void 0);continue}if(char===`"`||char===`'`){quote=char;continue}if(visit(char,i)===!1)return}}function firstTopLevelStage(command){let splitIndex=-1;return scanTopLevelChars(command,(char,index)=>char===`;`||(char===`&`||char===`|`)&&command[index+1]===char?(splitIndex=index,!1):!0),splitIndex>=0?command.slice(0,splitIndex):command}function splitTopLevelPipes(command){let parts=[],start=0;return scanTopLevelChars(command,(char,index)=>(char===`|`&&command[index-1]!==`|`&&command[index+1]!==`|`&&(parts.push(command.slice(start,index)),start=index+1),!0)),parts.push(command.slice(start)),parts.map(part=>part.trim()).filter(part=>part.length>0)}function stripShellPreamble(command){let rest=command.trim();for(let i=0;i<4;i+=1){let andIndex=rest.indexOf(`&&`),semicolonIndex=rest.indexOf(`;`),newlineIndex=rest.indexOf(`
|