beeos-claw 0.6.55-staging.0 → 0.6.55-staging.2

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.
@@ -1 +1 @@
1
- import{randomUUID,createHash}from"node:crypto";import{mkdir,readFile,rm,stat,readdir,rename,cp}from"node:fs/promises";import path from"node:path";import{join,resolve,relative,sep}from"node:path";import{homedir}from"node:os";import AdmZip from"adm-zip";import{sanitizePromptPayload}from"../message-sanitization/prompt-payload-sanitizer.js";import{previewSanitizedText,stripTransportMetadata}from"../message-sanitization/transport-metadata-sanitizer.js";import{isHeartbeatPromptText,isCronPromptText,extractCronJobId,classifyHeartbeatFilter}from"../message-sanitization/message-classifier.js";import{createCronOverridesStore,createInMemoryCronOverridesStore}from"../cron-overrides.js";import{CRON_DURATION_FLOOR_MS}from"../config.js";import{CANVAS_TERM_MAPPING,CANVAS_UPDATE_HINT,buildActiveSurfacesContext}from"../canvas/tools.js";import{parseMentionsParam,buildMentionInstruction}from"../delegation/mention-resolver.js";import{EXTERNAL_PROTOCOL_TAGS}from"./external-protocol-tags.js";import{extractPromptBlocks,toGatewayPrompt,resolveSlashCommandName as resolveSlashCmd}from"./prompt-converter.js";import{extractBeeosUserTextFromHistoryContent}from"./beeos-user-envelope.js";import{loadHistory,loadHistoryPage,groupIntoTurns,splitUserReplayBlocks}from"./history-replay.js";import{classifyHistoricalAssistantTurnCompletion}from"./history-turn-completion-policy.js";import{LocalSessionHistory}from"./local-session-history.js";import{reconcileMissingArtifacts}from"./missing-artifacts-reconciler.js";import{AcpRunOutputChannel}from"./acp-run-output-channel.js";import{buildResolutionPlan,resolveResources,applyResolutions}from"./prompt-resource-resolver.js";import{readEnrichmentCache,readUploadToolResult}from"../upload/result-cache.js";import{buildToolResultSessionContentBlocks}from"./tool-result-blocks.js";import{prefersChatFileArtifacts}from"./file-artifact-presenter.js";import{buildToolCallPayload,buildToolCallUpdatePayload}from"./tool-call-payload-builder.js";import{Errors}from"../errors/index.js";import{classifyAgentRuntimeFailure,INTERRUPTED_PUBLIC_MESSAGE}from"../runtime/agent-runtime-failure-classifier.js";import{isPlaceholderTitle}from"../runtime/conversation-title.js";import{SESSION_RECOVERY_REQUIRED_CODE,SESSION_RECOVERY_REQUIRED_MESSAGE}from"../runtime/session-mutation-coordinator.js";import{observeLifecyclePromise}from"../runtime/lifecycle-promise-observer.js";import{RunOutputRouter}from"../runtime/run-output-router.js";import{AgentMappingRegistry}from"../runtime/agent-mapping-registry.js";import{RuntimeSessionNewRejectedError}from"../runtime/runtime-rpc-consumer.js";import{normalizeRuntimeAgentLocalKey,OpenClawRuntimeCommandDispatcher,RuntimeCommandError}from"../runtime/runtime-command-dispatcher.js";import{FileRuntimeAgentIdentityRegistry}from"../runtime/runtime-agent-identity-registry.js";import{FileRuntimeLeasePort}from"../runtime/runtime-operation-lease.js";import{FileRuntimeCommandJournal,InMemoryRuntimeCommandJournal}from"../runtime/runtime-command-journal.js";import{OpenClawRuntimeCapabilities}from"../runtime/openclaw-runtime-capabilities.js";import{RuntimeCanvasService}from"../canvas/runtime-canvas-service.js";import{RuntimeSkillsService}from"../runtime/runtime-skills-service.js";import{RuntimeCronService,encodeBeeosChannelTag}from"../runtime/runtime-cron-service.js";export{BEEOS_CHANNEL_TAG_RE,decodeBeeosChannelId,encodeBeeosChannelTag,stripBeeosChannelTag}from"../runtime/runtime-cron-service.js";import{openClawKeyMatchesConversation,openClawSessionKeysFromList,resolveLiveOpenClawSessionKey}from"../runtime/openclaw-session-key-resolution.js";import{LocalConversationHistoryBoundary,OpenClawGatewaySessionMetadataPort,OpenClawSessionMetadataService,RuntimeSessionControlService}from"../runtime/runtime-session-control-service.js";import{pageRuntimeSessions,parseRuntimeSessionListPage}from"../runtime/runtime-session-list-policy.js";import{assertTrustedCoreExecutionAttempt,createAcpCoreExecutionAttempt}from"../runtime/core-execution-attempt-context.js";import{CompositeActiveRunPort}from"../runtime/composite-active-run-port.js";import{resolveChannelFromSessionKey}from"../channel-detection.js";import{resolveTurnId,recordTurnIdForRole,recordToolCallTurnForUpdate}from"./gateway-events.js";import{emitToolResultProjectionForTurn}from"./tool-result-emitter.js";import{extractPersistedToolResultEnvelope,findPersistedToolResultEnvelopeByMessageId}from"./tool-result-transcript.js";import{projectPersistedToolResultForReplay}from"./tool-result-projection.js";import{openRunOutputRoute}from"./run-output-route.js";import{McpService}from"./mcp-service.js";import{McpDesiredStateStore,normalizeMcpPublicServerConfig}from"./mcp-desired-state-store.js";import{requireSecretFreeMcpServerName}from"./mcp-public-config-security.js";import{McpDesiredStateController}from"./mcp-desired-state-controller.js";import{CredentialReferenceRuntimeResolver}from"./mcp-credential-reference-port.js";import{DEFAULT_PROMPT_TIMEOUT_MS}from"./types.js";import{ACP_PROTOCOL_VERSION}from"@beeos-ai/beeos-types/acp";import{canonicalizeJcs}from"@beeos-ai/beeos-types/runtime";import{MAX_ZIP_BYTES,parseAndValidateTemplatePackage,TemplatePackageError}from"./agent-template-package.js";const DEFAULT_AGENT_ID="main";export function normalizeAgentId(e){return normalizeRuntimeAgentLocalKey(e)}function narrowContentBlock(e){switch(e.type){case"text":return"string"==typeof e.text?{type:"text",text:e.text}:void 0;case"resource_link":return"string"==typeof e.uri?{type:"resource_link",uri:e.uri,...void 0!==e.name?{name:e.name}:{},...void 0!==e.mimeType?{mimeType:e.mimeType}:{}}:void 0;case"file":return{type:"file",...void 0!==e.text?{text:e.text}:{},...void 0!==e.uri?{uri:e.uri}:{},...void 0!==e.mimeType?{mimeType:e.mimeType}:{}};case"image":return{type:"image",...void 0!==e.uri?{uri:e.uri}:{},...void 0!==e.mimeType?{mimeType:e.mimeType}:{}};default:return}}const DEFAULT_MODE={id:"default",name:"Default",description:"Default agent mode"},USER_MESSAGE_PREFIX="User Message From BeeOS:",USER_MESSAGE_PREFIX_WITH_NEWLINE=`${USER_MESSAGE_PREFIX}\n`,SLASH_COMMAND_NAME_RE=/^[a-z0-9_-]+$/i,HISTORY_PAGE_DEFAULT_LIMIT=100,HISTORY_PAGE_MAX_LIMIT=1e3;function withUserMessagePrefix(e){return e.startsWith(USER_MESSAGE_PREFIX)?e:`${USER_MESSAGE_PREFIX_WITH_NEWLINE}${e}`}const BACKGROUND_SESSION_PATTERNS=[":cron:",":hook:","node-",...EXTERNAL_PROTOCOL_TAGS];function isBackgroundSessionKey(e){return BACKGROUND_SESSION_PATTERNS.some(t=>e.includes(t))}function isSystemDefaultSessionKey(e){const t=e.split(":");if(t.length<3||"agent"!==t[0])return!1;const s=t.slice(2).join(":");return"main"===s||s===t[1]}function cleanGatewayDerivedTitle(e){if(!e)return;let t=extractBeeosUserTextFromHistoryContent(e).text;return/^\[System\b/.test(t)?void 0:(t=t.trim(),t||void 0)}function cleanHistoryUserContent(e){return extractBeeosUserTextFromHistoryContent(e).text.trim()}function mergePersistedToolResultEnvelopes(e,t){if(e||t)return{toolName:e?.toolName||t?.toolName||"tool",...e?.toolCallId||t?.toolCallId?{toolCallId:e?.toolCallId??t?.toolCallId}:{},...e?.rawText??t?.rawText?{rawText:e?.rawText??t?.rawText}:{},...void 0!==e?.rawResult||void 0!==t?.rawResult?{rawResult:e?.rawResult??t?.rawResult}:{},...e?.timestamp||t?.timestamp?{timestamp:e?.timestamp??t?.timestamp}:{}}}function resolveStandaloneSlashCommandName(e,t){if(0===e.length)return;if(e.some(e=>"text"!==e.type))return;const s=t.trim();if(!s||s.includes("\n")||!s.startsWith("/"))return;const i=s.match(/^\/([^\s:]+)(?::|\s|$)/)?.[1]?.trim();return i&&!i.includes("/")&&SLASH_COMMAND_NAME_RE.test(i)?i.toLowerCase():void 0}class AcpRuntimeCommandResourceStore{fetchPackage;parsePackage;isValidationError;content=new Map;artifacts=new Map;mcpDefinitions=new Map;constructor(e,t,s){this.fetchPackage=e,this.parsePackage=t,this.isValidationError=s}registerContent(e){const t=`acp-content:sha256:${createHash("sha256").update(e).digest("hex")}`;return this.content.set(t,e),t}registerMcpDefinition(e,t){this.mcpDefinitions.set(JSON.stringify(e),structuredClone(t))}async resolveMcpDefinition(e){const t=this.mcpDefinitions.get(JSON.stringify(e));if(!t)throw new Error("ACP MCP definition was not verified for this request");return structuredClone(t)}async registerArtifact(e,t){try{const s=await this.fetchPackage(e);if(!s.ok)throw new TemplatePackageError(`failed to download package: HTTP ${s.status}`);const i=Buffer.from(await s.arrayBuffer());if(i.length>MAX_ZIP_BYTES)throw new TemplatePackageError(`package too large: ${i.length} bytes (max ${MAX_ZIP_BYTES})`);const n=createHash("sha256").update(i).digest("hex");if(t?.trim()&&t.trim().toLowerCase()!==n)throw new TemplatePackageError("package sha256 mismatch");const r=this.parsePackage(i),o=`acp-artifact:sha256:${n}`;return this.artifacts.set(o,r),{artifactRef:o,version:r.manifest?.version??"0.0.0",sha256:n,sizeBytes:String(i.length),source:"acp-verified"}}catch(e){if(e instanceof TemplatePackageError||this.isValidationError(e))throw new RuntimeCommandError("INVALID_PARAMS",`Invalid template package: ${e instanceof Error?e.message:String(e)}`);throw new RuntimeCommandError("EXECUTION_FAILED",`Apply template failed: ${e instanceof Error?e.message:String(e)}`)}}registerParsedArtifact(e){const t=createHash("sha256");for(const[s,i]of[...e.workspaceFiles].sort(([e],[t])=>e.localeCompare(t)))t.update(s).update("\0").update(i).update("\0");const s=t.digest("hex"),i=`acp-artifact:sha256:${s}`;this.artifacts.set(i,e);const n=[...e.workspaceFiles.values()].reduce((e,t)=>e+t.byteLength,0);return{artifactRef:i,version:e.manifest?.version??"0.0.0",sha256:s,sizeBytes:String(n),source:"acp-verified"}}async readContent(e){const t=this.content.get(e);if(void 0===t)throw new Error(`unknown contentRef: ${e}`);return t}async readVerifiedArtifact(e){const t=this.artifacts.get(e.artifactRef);if(!t)throw new Error(`unknown verified artifact: ${e.artifactRef}`);return t}}export const lateBoundRuntimeAgentProjectionPort=e=>({prepareCommittedMutation:async t=>{const s=e();if(!s)throw new Error("agent projection production is unavailable");return await s.prepareCommittedMutation(t)},applyCommittedMutation:async t=>{const s=e();if(!s)throw new Error("agent projection production is unavailable");return await s.applyCommittedMutation(t)},compactCommittedMutation:async t=>{const s=e();if(!s)throw new Error("agent projection production is unavailable");await s.compactCommittedMutation(t)}});function deriveSessionId(e,t){const s=createHash("sha256").update(`${t}:${e}`).digest("hex").substring(0,32);return`session:${s.slice(0,8)}-${s.slice(8,12)}-${s.slice(12,16)}-${s.slice(16,20)}-${s.slice(20,32)}`}function parseAgentIdFromKey(e){const t=e.split(":");return(t.length>=3&&"agent"===t[0]?t[1]:"")||"main"}function createSessionObject(e){const t=parseAgentIdFromKey(e.gatewayKey);return{sessionId:deriveSessionId(e.gatewayKey,e.instanceId),gatewaySessionKey:e.gatewayKey,agentId:t,platformAgentId:e.resolvePlatformAgentId?.(t),createdAt:Date.now(),chatReplayDedupKeys:new Set,cwd:e.cwd,title:e.title,channelType:resolveChannelFromSessionKey(e.gatewayKey,e.thirdPartyChannels)}}class HistoryUpdateRecorder{transport;updates=[];constructor(e){this.transport=e}sendSessionUpdate(e,t,s){const i=this.transport.materializeSessionUpdate(t,s);return null!==i&&this.updates.push(i),!0}}export class AcpGatewayBridgeCore{state;transport;config;logger;writeObs;getGateway;thirdPartyChannels;beeosFileResolver;agentFetch;agentGatewayUrl;triggerAgentSync;sessionMutationCoordinator;runtimeCommands;runtimeAgentIdentities;runtimeLeases;runtimeCommandResources;localHistoryBoundary;activeRuns;markAgentProjectionDirty;projectionObservationTimeoutMs;cronChannelCache=new Map;static CRON_CHANNEL_CACHE_MAX=256;localSessionHistory;mcp;runtimeTerminalService;runOutputRouter;agentMappings;initialized=!1;get agentMapping(){return this.agentMappings.snapshot()}cronIsolatedSessionKeys=new Set;cronOverrides;cronReady;sessionPreloadInFlight=null;sessionPreloadInFlightGeneration=null;sessionPreloadReady=Promise.resolve();resolveSessionPreloadReady=null;rejectSessionPreloadReady=null;sessionPreloadGeneration=0;sessionPreloadBarrierActive=!1;sessionPreloadFailed=!1;async setAgentMapping(e){await this.agentMappings.replace(e)}async applyAgentMapping(e){for(const t of this.state.sessions.values())!t.platformAgentId&&t.agentId&&(t.platformAgentId=e.get(t.agentId)?.id);this.sessionPreloadFailed&&this.beginGatewaySessionPreload(),this.sessionPreloadBarrierActive&&await this.preloadSessionsFromGateway()}beginGatewaySessionPreload(){this.sessionPreloadBarrierActive&&this.sessionPreloadReady.catch(()=>{}),this.sessionPreloadGeneration+=1,this.sessionPreloadBarrierActive=!0,this.sessionPreloadFailed=!1,this.sessionPreloadReady=new Promise((e,t)=>{this.resolveSessionPreloadReady=e,this.rejectSessionPreloadReady=t}),this.sessionPreloadReady.catch(()=>{})}failGatewaySessionPreload(){this.sessionPreloadBarrierActive&&(this.sessionPreloadBarrierActive=!1,this.sessionPreloadFailed=!0,this.rejectSessionPreloadReady?.(new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway disconnected before session hydration completed")),this.resolveSessionPreloadReady=null,this.rejectSessionPreloadReady=null)}async waitForGatewaySessionPreload(){await this.sessionPreloadReady}onceAgentMappingReady(e){this.agentMappings.onceReady(e)}onAgentMappingUpdated(e){const t=this.agentMappings.snapshot();t&&t.size>0&&e(new Map(t)),this.agentMappings.subscribe(t=>e(new Map(t)))}markAgentSyncDone(){this.agentMappings.markSynchronized()}waitForAgentMapping(e=15e3){return this.agentMappings.waitForSynchronization(e)}handleTerminalRequest=null;handleTerminalNotification=null;onRunComplete=null;onRunFailed=null;onRunEnded=null;handleSlashCommand=null;onPromptStart=null;instanceId;getAuthoritativeInstanceId;mcpCredentialReferences;constructor(e){this.state=e.state,this.transport=e.transport,this.config=e.config,this.logger=e.logger,this.writeObs=e.writeObs,this.getGateway=e.getGateway,this.instanceId=e.instanceId||"unknown",this.getAuthoritativeInstanceId=e.getAuthoritativeInstanceId,this.mcpCredentialReferences=e.mcpCredentialReferences,this.thirdPartyChannels=e.thirdPartyChannels??new Set,this.beeosFileResolver=e.beeosFileResolver,this.agentFetch=e.agentFetch,this.agentGatewayUrl=e.agentGatewayUrl,this.triggerAgentSync=e.triggerAgentSync,this.markAgentProjectionDirty=e.markAgentProjectionDirty,this.projectionObservationTimeoutMs=e.projectionObservationTimeoutMs??5e3,this.sessionMutationCoordinator=e.sessionMutationCoordinator,this.agentMappings=e.agentMappings??new AgentMappingRegistry,this.agentMappings.subscribe(e=>this.applyAgentMapping(e)),this.runOutputRouter=e.runOutputRouter??new RunOutputRouter([new AcpRunOutputChannel({state:this.state,transport:this.transport})],{lifecycle:{terminal:(e,t)=>this.commitRunOutputTerminal(e,t)}}),this.runtimeAgentIdentities=e.runtimeAgentIdentities??new FileRuntimeAgentIdentityRegistry(join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos","runtime-agent-identities.json")),this.runtimeLeases=e.runtimeLeases??new FileRuntimeLeasePort(join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos","runtime-operation-leases.json")),this.runtimeCommandResources=e.runtimeCommandResources??new AcpRuntimeCommandResourceStore(e.agentFetch??((e,t)=>fetch(e,t)),e.runtimeTemplatePackageParser?.parse??parseAndValidateTemplatePackage,e.runtimeTemplatePackageParser?.isValidationError??(e=>e instanceof TemplatePackageError));const t=e.runtimeCommandJournal??(e.runtimeLeases||process.env.VITEST?new InMemoryRuntimeCommandJournal:new FileRuntimeCommandJournal(join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos","runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:e=>this.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=threshold error=${e instanceof Error?e.message:String(e)}`)}));this.localSessionHistory=new LocalSessionHistory({logger:this.logger}),this.cronOverrides=e.cronOverrides??(process.env.VITEST?createInMemoryCronOverridesStore():createCronOverridesStore(this.logger)),this.cronReady=this.cronOverrides.initialize(),this.cronReady.catch(()=>{});const s=e.mcpCredentialReferences?new CredentialReferenceRuntimeResolver(e.mcpCredentialReferences):void 0,i=e.mcpProjection?new McpDesiredStateController(new McpDesiredStateStore(e.mcpDesiredStatePath??join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos","mcp-desired-state.json")),e.mcpProjection,s,e.mcpCredentialReferences,t,e.runtimeReceiptHistory,this.logger):void 0;this.mcp=new McpService({transport:this.transport,logger:this.logger,state:this.state,...i?{desiredState:i}:{}}),this.localHistoryBoundary=new LocalConversationHistoryBoundary(e=>{const t=this.state.sessions.get(e);t&&(t.chatReplayDedupKeys.clear(),this.onSessionCleared?.(t.sessionId,t.gatewaySessionKey))}),this.activeRuns=e.activeRuns??new CompositeActiveRunPort;const n=new RuntimeCronService({getGateway:e.getGateway,overrides:this.cronOverrides,durationFloorMs:CRON_DURATION_FLOOR_MS,durationCapMs:this.config.cronMaxDurationCapMs,channels:{allocate:async e=>await this.allocateCronChannel(e),lookup:e=>this.cronChannelCache.get(e),remember:(e,t)=>this.setCronChannelCache(e,t),forget:e=>{this.cronChannelCache.delete(e)}}}),r=new RuntimeSessionControlService({convergeRun:async({runtimeSessionKey:e})=>{const t=this.state.sessionsByGatewayKey.get(e),s=(new Date).toISOString();if(!t||t.promptDone&&void 0===t.pendingPromptId&&void 0===t.currentRunId)return{state:"no_active_run",quiescence:{state:"verified",observedAt:s}};const i=this.getGateway();if(!i?.isConnected)return{state:"recovery_required",reason:"gateway_disconnected"};if(!t.writeGateToken||!t.currentRunId)return{state:"recovery_required",reason:"run_conflict",runtimeRunId:t.currentRunId??"unknown"};const n=t.currentRunId,r=await this.sessionMutationCoordinator.requestCancel(t.writeGateToken,i);if(!r.ok)return{state:"recovery_required",reason:"terminal_not_observed",runtimeRunId:n};const o=await r.terminalObservation;return o.ok?{state:"terminal_observed",runtimeRunId:n,abortObservation:r.acknowledged?{state:"target_acknowledged",aborted:!0,runIds:[n],targetRunAcknowledged:!0}:{state:"already_stopped",aborted:!1,runIds:[]},terminalState:"cancelled",quiescence:{state:"verified",observedAt:(new Date).toISOString()}}:{state:"recovery_required",reason:"quiescence_failed"===o.recovery.reason?"quiescence_failed":"quiescence_timeout"===o.recovery.reason?"quiescence_timeout":"terminal_not_observed",runtimeRunId:n}},reset:async e=>{const t=this.getGateway();if(!t?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");await t.sessionsReset(e)}},new OpenClawSessionMetadataService(new OpenClawGatewaySessionMetadataPort(e.getGateway,(e,t)=>{const s=this.state.sessionsByGatewayKey.get(e);if(!s)throw new RuntimeCommandError("INVALID_PARAMS",`setModel projection requires a live local session for key=${e}`);void 0===t.modelOverrideId?delete s.modelOverrideId:s.modelOverrideId=t.modelOverrideId,s.effectiveModelId=t.effectiveModelId,void 0===t.modelProvider?delete s.modelProvider:s.modelProvider=t.modelProvider},{resolveLiveKey:e=>resolveLiveOpenClawSessionKey(e,this.state.sessions.values()),waitForHydration:async()=>await this.waitForGatewaySessionPreload(),reconcileMissingLiveKey:async(e,t)=>await this.reconcileMissingGatewaySession(e,t),materializeMissingModelKey:async(e,t)=>await this.materializeMissingModelSessionKey(e,t),registerMaterializedModelKey:e=>this.registerMaterializedModelSession(e)})),this.activeRuns),o=e.getAgentProjection?lateBoundRuntimeAgentProjectionPort(e.getAgentProjection):void 0,a=e.runtimeCommandDispatcher??new OpenClawRuntimeCommandDispatcher({getGateway:e.getGateway,resources:this.runtimeCommandResources,identities:this.runtimeAgentIdentities,leases:this.runtimeLeases,journal:t,logger:e.logger,...o?{projection:o}:{},...e.templateOriginReporter?{templateOriginReporter:e.templateOriginReporter}:{}});this.runtimeCommands=a,a instanceof OpenClawRuntimeCommandDispatcher&&a.setCapabilities(new OpenClawRuntimeCapabilities({getGateway:e.getGateway,identities:this.runtimeAgentIdentities,sessions:r,skills:new RuntimeSkillsService({getGateway:e.getGateway,resources:this.runtimeCommandResources,getOpenclawHome:()=>this.getOpenclawHome()}),mcp:this.mcp,terminal:{execute:async(e,t)=>{if(!this.runtimeTerminalService)throw new RuntimeCommandError("INVALID_PARAMS","terminal canonical delegate is not configured");return await this.runtimeTerminalService.execute(e,t)}},canvas:new RuntimeCanvasService({apply:async(e,t)=>{await this.applyCanonicalCanvasControl(e,t)}}),cron:n})),this.transport.setSessionUpdateObserver((e,t,s)=>{const i=s?.turnId;if(!i)return;const n=this.state.sessions.get(e);n&&(recordTurnIdForRole(n,t,i),recordToolCallTurnForUpdate(n,t,i))})}setRuntimeTerminalService(e){this.runtimeTerminalService=e}setRuntimeReceiptHistoryPort(e){this.mcp.setRuntimeReceiptHistoryPort(e)}getRuntimeCommandDispatcher(){return this.runtimeCommands}async startMcp(){await this.mcp.start()}async stopMcp(){await this.mcp.stop()}nudgeMcpNativeProjection(){this.mcp.nudgeNativeProjection()}observeActiveRun(e){const t=e.currentRunId,s=e.writeGateToken,i=this.sessionMutationCoordinator.snapshot(e.gatewaySessionKey),n=void 0===e.pendingPromptId?e.gatewayRequestId??t:String(e.pendingPromptId);if(!t||!i||!n)return;const r={runtimeConversationUid:e.sessionId,runtimeSessionKey:e.gatewaySessionKey,historyGeneration:this.localHistoryBoundary.currentGeneration(e.sessionId),targetMessageId:n,runtimeRunId:t,activeRunRegistrationId:`acp:${i.operationId}`};return this.activeRuns.register(r,async()=>{const n=this.getGateway();if(!n?.isConnected||e.currentRunId!==t)return"recovery_required";const r=s?.operationId===i.operationId?await this.sessionMutationCoordinator.requestCancel(s,n):await this.sessionMutationCoordinator.requestCancelForSession(e.gatewaySessionKey,n);return r.ok?"accepted":!("reason"in r)||"terminal_already_observed"!==r.reason&&"no_active_mutation"!==r.reason?"recovery_required":"already_terminal"}),e.activeRunBinding=r,r}getActiveRunAuthority(){return{activeRuns:this.activeRuns,historyGeneration:e=>this.localHistoryBoundary.currentGeneration(e)}}resolvePlatformAgentId(e){return this.agentMapping?.get(e)?.id}resolveLocalAgentId(e){if(this.agentMapping&&e)for(const[t,s]of this.agentMapping)if(s.id===e)return t}getDefaultAgentId(){return this.agentMapping&&this.agentMapping.size>0?this.agentMapping.keys().next().value??"main":"main"}registerSession(e,t={}){const{ensureVerbose:s=!0}=t;this.state.sessions.has(e.sessionId);this.state.sessions.set(e.sessionId,e),this.state.sessionsByGatewayKey.set(e.gatewaySessionKey,e),s&&this.ensureVerboseFull(e),this.subscribeSessionMessages(e)}async ensureVerboseFull(e){if(e.verbosePatched)return;const t=this.getGateway();if(t?.isConnected){e.verbosePatched=!0;try{await t.sessionsPatch(e.gatewaySessionKey,{verboseLevel:"full"})}catch(t){e.verbosePatched=!1,this.logger.debug?.(`[bridge-core] ensureVerboseFull failed: ${t instanceof Error?t.message:t}`)}}}ensureSessionForKey(e){const t=this.state.findSessionByKey(e);if(t)return t;const s=createSessionObject({gatewayKey:e,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels});return this.registerSession(s),this.logger.debug?.(`[bridge-core] ensureSessionForKey: lazily created session sid=${s.sessionId} key=${e}`),this.onSessionLoaded?.(s.sessionId,s.gatewaySessionKey,s.platformAgentId),s}async preloadSessionsFromGateway(){const e=this.sessionPreloadGeneration;if(this.sessionPreloadInFlight&&this.sessionPreloadInFlightGeneration===e)return await this.sessionPreloadInFlight;const t=this.preloadGatewaySessionIndex(e);this.sessionPreloadInFlight=t,this.sessionPreloadInFlightGeneration=e;try{await t,e===this.sessionPreloadGeneration&&this.sessionPreloadBarrierActive&&(this.sessionPreloadBarrierActive=!1,this.resolveSessionPreloadReady?.(),this.resolveSessionPreloadReady=null,this.rejectSessionPreloadReady=null)}catch(t){throw e===this.sessionPreloadGeneration&&this.sessionPreloadBarrierActive&&(this.sessionPreloadBarrierActive=!1,this.sessionPreloadFailed=!0,this.rejectSessionPreloadReady?.(t instanceof Error?t:new Error("session preload failed")),this.resolveSessionPreloadReady=null,this.rejectSessionPreloadReady=null),t}finally{this.sessionPreloadInFlight===t&&(this.sessionPreloadInFlight=null,this.sessionPreloadInFlightGeneration=null)}}async preloadGatewaySessionIndex(e){const t=this.getGateway();if(this.logSessionAuthority("session_preload_start",{generation:e,reason:t?.isConnected?"gateway_connected":"gateway_not_connected"}),!t?.isConnected)throw this.logSessionAuthority("session_preload_failure",{generation:e,reason:"gateway_not_connected"}),new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected for session hydration");try{const s=await t.sessionsList(),i=this.mergeGatewaySessions(s);this.logSessionAuthority("session_preload_success",{generation:e,loaded:i,reason:"authoritative_sessions_list"})}catch(t){throw this.logSessionAuthority("session_preload_failure",{generation:e,reason:t instanceof RuntimeCommandError?t.code:"sessions_list_failed"}),t}}mergeGatewaySessions(e,t){if(!e||"object"!=typeof e)return 0;const s=e.sessions;if(!Array.isArray(s))return 0;let i=0;for(const e of s){if(!e||"object"!=typeof e||Array.isArray(e))continue;const s=e,n=s.sessionKey??s.key??s.id??s.name;if("string"!=typeof n||!n.trim()||n.trim()!==n||t&&!t(n)||this.state.findSessionByKey(n))continue;const r=s.label||s.derivedTitle||s.title||void 0,o=r&&!isPlaceholderTitle(r)?r:void 0,a=createSessionObject({gatewayKey:n,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels,title:o});this.registerSession(a,{ensureVerbose:!1}),this.onSessionLoaded?.(a.sessionId,a.gatewaySessionKey,a.platformAgentId),i+=1}return i}async reconcileMissingGatewaySession(e,t){await this.assertSessionReconcileFence(t);const s=this.getGateway();if(!s?.isConnected)throw this.logSessionAuthority("session_reconcile_decision",{reason:"gateway_not_connected",runtimeSessionKeyHash:this.sessionKeyHash(e)}),new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected for session reconciliation");const i=await s.sessionsList();await this.assertSessionReconcileFence(t);const n=!e.startsWith("agent:"),r=[...openClawSessionKeysFromList(i)].filter(t=>t===e?!n:openClawKeyMatchesConversation(t,e));if(this.logSessionAuthority("session_reconcile_decision",{reason:r.length?"exact_candidates":"no_exact_candidate",runtimeSessionKeyHash:this.sessionKeyHash(e),candidateHashes:r.map(e=>this.sessionKeyHash(e))}),!r.length)return;const o=new Set(r),a=this.mergeGatewaySessions(i,e=>o.has(e));this.logSessionAuthority("session_reconcile_decision",{reason:"reconciled_exact_candidates",runtimeSessionKeyHash:this.sessionKeyHash(e),loaded:a,candidateHashes:r.map(e=>this.sessionKeyHash(e))})}async materializeMissingModelSessionKey(e,t){await this.assertSessionReconcileFence(t);const s=t.attempt,i=s.verifiedMessageGrant?.claims,n=i?.command;if("service"!==s.origin||!i||"conversation"!==i.scope||"session/set_model"!==i.method||"session.setModel"!==n?.name||"conversation"!==n.context.scope||i.runtimeSessionKey!==e||i.runtimeConversationUid!==e||n.context.conversation.runtimeSessionKey!==e||n.context.conversation.runtimeConversationUid!==i.runtimeConversationUid)throw this.logSessionAuthority("model_intent_materialization_rejected",{reason:"verified_service_target_required",runtimeSessionKeyHash:this.sessionKeyHash(e)}),new RuntimeCommandError("INVALID_PARAMS","model-intent session materialization requires an exact verified Service target");if(!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e))throw new RuntimeCommandError("INVALID_PARAMS","model-intent session materialization requires a canonical conversation UUID");let r;try{if(!this.runtimeAgentIdentities.resolveActiveLocalKey)throw new Error("identity resolver unavailable");r=await this.runtimeAgentIdentities.resolveActiveLocalKey(i.runtimeAgentUid)}catch{throw new RuntimeCommandError("INVALID_PARAMS","verified model-intent runtime agent is not active")}if(this.resolveLocalAgentId(i.platformAgentId)!==r)throw new RuntimeCommandError("INVALID_PARAMS","verified model-intent agent does not match the active platform projection");const o=`agent:${r}:beeos-conversation:ctx-${e}`,a=this.getGateway();if(!a?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected for model-intent session materialization");const l=await a.sessionsList();await this.assertSessionReconcileFence(t);const d=[...openClawSessionKeysFromList(l)].filter(t=>openClawKeyMatchesConversation(t,e));if(d.length){if(1===d.length&&d[0]===o)return this.mergeGatewaySessions(l,e=>e===o),o;throw new RuntimeCommandError("INVALID_PARAMS","verified model-intent target conflicts with an existing OpenClaw session")}return this.logSessionAuthority("model_intent_materialization_allowed",{runtimeSessionKeyHash:this.sessionKeyHash(e),openClawSessionKeyHash:this.sessionKeyHash(o)}),o}registerMaterializedModelSession(e){this.state.findSessionByKey(e)||(this.registerSession(createSessionObject({gatewayKey:e,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels}),{ensureVerbose:!1}),this.logSessionAuthority("model_intent_materialization_committed",{openClawSessionKeyHash:this.sessionKeyHash(e)}))}async assertSessionReconcileFence(e){if(e.lease.operationId!==e.attempt.operationId||e.lease.runtimeEpoch!==e.attempt.runtimeEpoch||e.attempt.abortSignal?.aborted)throw this.logSessionAuthority("session_reconcile_decision",{reason:"operation_fence_mismatch"}),new RuntimeCommandError("INVALID_PARAMS","session reconciliation is fenced");try{await this.runtimeLeases.renew(e.lease)}catch{throw this.logSessionAuthority("session_reconcile_decision",{reason:"lease_epoch_mismatch"}),new RuntimeCommandError("INVALID_PARAMS","session reconciliation is fenced")}if(e.attempt.abortSignal?.aborted)throw this.logSessionAuthority("session_reconcile_decision",{reason:"operation_aborted"}),new RuntimeCommandError("INVALID_PARAMS","session reconciliation is fenced")}sessionKeyHash(e){return createHash("sha256").update(e).digest("hex").slice(0,16)}logSessionAuthority(e,t){this.logger.info?.(`[bridge-core] ${JSON.stringify({event:e,...t})}`)}async handleRequest(e){this.writeObs({component:"bridge-core",domain:"forward",name:`rpc.request.${e.method}`,severity:"debug",payload:{id:e.id,method:e.method}});const t=e;try{switch(t.method){case"initialize":await this.handleInitialize(t);break;case"ping":this.transport.sendResult(t.id,{pong:!0});break;case"session/new":await this.handleSessionNew(t);break;case"session/load":await this.handleSessionLoad(t);break;case"session/list":await this.handleSessionList(t);break;case"session/rename":await this.handleSessionRename(t);break;case"session/delete":await this.handleSessionDelete(t);break;case"session/clear":await this.handleSessionClear(t);break;case"session/prompt":await this.handleSessionPrompt(t);break;case"session/set_mode":await this.handleSessionSetMode(t);break;case"session/set_model":await this.handleSessionSetModel(t);break;case"agent/create":await this.handleAgentCreate(t);break;case"agent/update":await this.handleAgentUpdate(t);break;case"agent/delete":await this.handleAgentDelete(t);break;case"agent/applyTemplate":await this.handleApplyTemplate(t);break;case"skills/list":await this.handleSkillsList(t);break;case"skills/install":await this.handleSkillsInstall(t);break;case"skills/uninstall":await this.handleSkillsUninstall(t);break;case"skills/update":await this.handleSkillsUpdate(t);break;case"models/list":await this.handleModelsList(t);break;case"session/cancel":await this.handleSessionCancel(t.id,t.params);break;case"cron/list":case"cron/status":case"cron/add":case"cron/update":case"cron/remove":case"cron/run":case"cron/runs":await this.handleCronRequest(t);break;default:"agent/list"===e.method?await this.handleAgentList(e):"_beeos/session/history"===e.method?await this.handleSessionHistoryPage(e):"mcp/list"===e.method?await this.handleMcpList(e):"mcp/set"===e.method?await this.handleMcpSet(e):"mcp/unset"===e.method?await this.handleMcpUnset(e):e.method.startsWith("terminal/")&&this.handleTerminalRequest?await this.handleTerminalRequest(e):this.transport.sendAcpError(e.id,Errors.MethodNotFound,{message:`Method not found: ${e.method}`})}}catch(t){this.transport.sendAcpError(e.id,Errors.InternalError,{message:t instanceof Error?t.message:"Internal error"})}}handleCanvasNotification=null;getCanvasState=null;onSessionLoaded=null;onSkillInstalled=null;onSessionDeleted=null;onSessionCleared=null;handleNotification(e){switch(e.method){case"session/cancel":this.observeLifecycle(this.handleSessionCancel(void 0,e.params),"session_cancel_notification");break;case"canvas/toggle":case"canvas/clear":case"canvas/reference":case"canvas/dimensions":this.observeLifecycle(this.dispatchLegacyCanvasNotification(e.method,e.params),`canvas_notification_${e.method.replace("/","_")}`);break;default:e.method.startsWith("terminal/")&&this.handleTerminalNotification?this.handleTerminalNotification(e.method,e.params):this.logger.debug?.(`[bridge-core] Unknown notification: ${e.method}`)}}async handleInitialize(e){await this.cronReady,this.initialized=!0;const t=this.transport.forwardControl;!1===e.params?.forwardThinking&&(t.forwardThinking=!1),!1===e.params?.forwardToolCalls&&(t.forwardToolCalls=!1),this.transport.sendResult(e.id,{protocolVersion:ACP_PROTOCOL_VERSION,agentCapabilities:{loadSession:!0,skills:!0,multipleSessions:!0,sessionCapabilities:{"web-ssh":!!this.config.bridge.shell.enabled,canvas:!!this.config.canvasEnabled},promptCapabilities:{image:!0,audio:!1,embeddedContext:!0},mcp:{}},agentInfo:{name:"beeos-claw",title:"BeeOS Agent Bridge",version:"0.1.0"},_meta:{},authMethods:[]})}async createRuntimeSession(e,t){const s=e.operationId.trim(),i=e.platformAgentId.trim();if(!s)throw new Error("session/new operationId is required");if(assertTrustedCoreExecutionAttempt(t,{operationId:s,runtimeEpoch:t.runtimeEpoch}),"service"!==t.origin)throw new Error("session/new runtime allocation requires Service authority");const n=this.resolveLocalAgentId(i);if(!i||!n)throw new RuntimeSessionNewRejectedError("session/new target agent is unknown");const r=`agent:${n}:service:${s}`;return{sessionId:this.allocateSession({agentId:n,gatewaySessionKey:r,cwd:e.cwd??".",...void 0===e.title?{}:{title:e.title}}).sessionId,gatewaySessionKey:r,agentId:n,platformAgentId:i}}async handleSessionNew(e){const t="string"==typeof e.params?.agentId&&e.params.agentId||this.config.gateway.agentId||this.getDefaultAgentId(),s=`agent:${t}:${randomUUID()}`,i="string"==typeof e.params?.cwd?e.params.cwd:".",n=this.allocateSession({agentId:t,gatewaySessionKey:s,cwd:i});this.transport.sendResult(e.id,{sessionId:n.sessionId,modes:{availableModes:[{...DEFAULT_MODE}],currentModeId:DEFAULT_MODE.id},_meta:{sessionKey:s,agentId:n.agentId,platformAgentId:n.platformAgentId??null}}),this.fetchHistoryInBackground(s)}allocateSession(e){const t=this.state.findSessionByKey(e.gatewaySessionKey);if(t)return t;const s=createSessionObject({gatewayKey:e.gatewaySessionKey,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels,...void 0===e.cwd?{}:{cwd:e.cwd},...void 0===e.title?{}:{title:e.title}});if(s.agentId!==e.agentId)throw new Error("session/new gateway key agent mismatch");return this.registerSession(s,{ensureVerbose:!1}),s}fetchHistoryInBackground(e){const t=this.getGateway();t?.isConnected&&loadHistory(t,e,50).catch(e=>{this.logger.debug?.(`[bridge-core] Background history fetch failed: ${e instanceof Error?e.message:e}`)})}async subscribeSessionMessages(e){if(e.messageSubscriptionActive)return;const t=this.getGateway();if(t?.isConnected)try{await t.sessionsMessagesSubscribe(e.gatewaySessionKey),e.messageSubscriptionActive=!0,this.logger.debug?.(`[bridge-core] session.message subscribed sessionKey=${e.gatewaySessionKey}`)}catch(t){this.logger.debug?.(`[bridge-core] session.message subscribe failed key=${e.gatewaySessionKey}: ${t instanceof Error?t.message:t}`)}}async resubscribeAllSessionMessages(){for(const e of this.state.sessions.values())e.messageSubscriptionActive=!1,await this.subscribeSessionMessages(e)}handleSessionMessage(e){const t=e?.sessionKey,s=e?.messageId;if(!t||!s)return;const i=this.state.sessionsByGatewayKey.get(t);if(!i)return;const n=e.message?.role;if("toolResult"===n||"tool"===n)return void this.handlePersistedToolResultMessage(i,e).catch(e=>{this.logger.warn?.(`[bridge-core] persisted toolResult handling failed sessionKey=${t} messageId=${s}: ${e instanceof Error?e.message:String(e)}`)});const r="user"===n?"user":"system"===n?"system":"assistant",o=i.lastTurnIdByRole?.[r];if(!o)return void this.logger.debug?.(`[bridge-core] session.message skipped: no lastTurnId role=${r} sessionKey=${t} messageId=${s}`);const a=e.message?.timestamp,l="number"==typeof a&&a>0?new Date(a).toISOString():void 0,d={turnId:o};"number"==typeof e.messageSeq&&(d.seq=e.messageSeq),this.transport.sendSessionUpdate(i.sessionId,{sessionUpdate:"message_id_bind",role:r,messageId:s,...l?{timestamp:l}:{}},d),i.lastTurnIdByRole&&(i.lastTurnIdByRole[r]=void 0),"assistant"===r&&reconcileMissingArtifacts({localHistory:this.localSessionHistory,runOutputRouter:this.runOutputRouter,config:this.config,logger:this.logger,writeObs:this.writeObs,getCanvasState:()=>this.getCanvasState?.()??null,isSessionAlive:e=>this.state.sessions.has(e)},{session:i,turnId:o,messageId:s,runId:o})}async handlePersistedToolResultMessage(e,t){const s=t.messageId;if(!s)return;const i=extractPersistedToolResultEnvelope(t.message),n=this.extractPersistedToolResultFromHistory(e,s),r=mergePersistedToolResultEnvelopes(n,i);if(!r?.toolCallId)return void this.writeObs({component:"bridge-core",domain:"tool_result",name:"tool_result.skipped_no_tool_call_id",severity:"warn",payload:{sessionId:e.sessionId,messageId:s}});n&&this.writeObs({component:"bridge-core",domain:"tool_result",name:"tool_result.hydrated_from_history",severity:"debug",payload:{sessionId:e.sessionId,messageId:s,toolCallId:r.toolCallId,toolName:r.toolName}});const o=e.toolCallTurns?.get(r.toolCallId),a=o?.turnId??resolveTurnId(e);if(!a)return void this.writeObs({component:"bridge-core",domain:"tool_result",name:"tool_result.skipped_no_turn",severity:"warn",payload:{sessionId:e.sessionId,messageId:t.messageId,toolCallId:r.toolCallId,toolName:r.toolName}});const l=projectPersistedToolResultForReplay({toolCallId:r.toolCallId,toolName:r.toolName||o?.toolName||"tool",...r.rawText?{rawText:r.rawText}:{},...r.rawResult?{rawResult:r.rawResult}:{},...r.timestamp?{timestamp:r.timestamp}:{}});await emitToolResultProjectionForTurn({session:e,projection:l,toolCallId:r.toolCallId,turnId:a,config:this.config,logger:this.logger,writeObs:this.writeObs,getCanvasState:()=>this.getCanvasState?.()??null,sendUpdate:s=>(a&&void 0===s._turnId&&(s._turnId=a),this.transport.sendSessionUpdate(e.sessionId,s,{turnId:a,..."number"==typeof t.messageSeq?{seq:t.messageSeq}:{}}))})}extractPersistedToolResultFromHistory(e,t){if(!e.agentId)return;const s=this.localSessionHistory.readEntries({agentId:e.agentId,sessionKey:e.gatewaySessionKey,sessionId:e.sessionId});return findPersistedToolResultEnvelopeByMessageId(s,t)}async recoverSessionFromGateway(e){const t=this.getGateway();if(t?.isConnected)try{const s=await t.sessionsList();if(!s||"object"!=typeof s)return;const i=s.sessions;if(!Array.isArray(i))return;for(const t of i){const s=t,i=s.sessionKey??s.key??s.id??s.name;if(i&&"string"==typeof i&&deriveSessionId(i,this.instanceId)===e){const e=s.label||s.derivedTitle||s.title||void 0,t=e&&!isPlaceholderTitle(e)?e:void 0,n=createSessionObject({gatewayKey:i,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels,title:t});return this.registerSession(n),n}}}catch(e){this.logger.debug?.("[bridge-core] recoverSessionFromGateway failed:",e)}}async resolveOrCreateSession(e){let t=this.state.sessions.get(e);if(!t&&e.startsWith("agent:")&&(t=this.state.findSessionByKey(e)),t||e.startsWith("agent:")||(t=await this.recoverSessionFromGateway(e)),t)return{session:t,created:!1};const s=e.startsWith("agent:"),i=this.config.gateway.agentId||this.getDefaultAgentId();return t=createSessionObject({gatewayKey:s?e:`agent:${i}:${e}`,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels}),this.registerSession(t),{session:t,created:!0}}async handleSessionLoad(e){const t=e.params?.sessionId;if(!t)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId"});const{session:s}=await this.resolveOrCreateSession(t),i=s.sessionId;this.state.lastActiveSessionId=i,s.chatReplayDedupKeys.clear(),s.sessionLoadInProgress=!0;try{const e=await this.emitHistoryTurns(s);this.emitCurrentStreamSnapshot(s,e)}finally{s.sessionLoadInProgress=!1}this.transport.sendResult(e.id,{sessionId:i}),this.onSessionLoaded?.(i,s.gatewaySessionKey,s.platformAgentId),this.pushSkillsList().catch(e=>{this.logger.warn?.(`[bridge-core] pushSkillsList failed: ${e instanceof Error?e.message:e}`)})}async emitHistoryTurns(e){const t=new Set,s=e.sessionId;try{const i=this.getGateway(),n=e.assistantMq&&!e.assistantMq.closed,r=n?null:i,o=await loadHistory(r,e.gatewaySessionKey,100,{localHistory:this.localSessionHistory,agentId:e.agentId,sessionId:e.sessionId,logger:this.logger,emitObs:e=>{this.writeObs({component:"history-replay",domain:"lifecycle",name:e.name,severity:e.severity,payload:e.payload})}}),a=groupIntoTurns(o),l=a.filter(e=>"user"===e.role).length,d=a.filter(e=>"assistant"===e.role).length,c=a.filter(e=>e.toolCalls?.length).length,m=a.reduce((e,t)=>e+(t.toolCalls?.length??0),0);if(this.logger.debug?.(`[bridge-core] handleSessionLoad sid=${s} messages=${o.length} turns=${a.length} userTurns=${l} asstTurns=${d} turnsWithToolCalls=${c} totalToolCalls=${m}`),l>0&&e.pendingUserTurnUpdates&&e.pendingUserTurnUpdates.length>0&&(e.pendingUserTurnUpdates=[]),a.length>0&&isPlaceholderTitle(e.title)&&"manual"!==e.titleSource){const t=a.find(e=>{if("user"!==e.role)return!1;if("internal_system"===e.provenance?.kind)return!1;if(isCronPromptText(e.content))return!1;const t=cleanHistoryUserContent(e.content);return t.length>0&&!/^Sender\s*\(untrusted/i.test(t)});if(t?.content){const i=cleanHistoryUserContent(t.content).slice(0,64).replace(/\n/g," ").trim();if(i){e.title=i,e.titleSource="truncated";const t=this.getGateway();t?.isConnected&&t.sessionsPatch(e.gatewaySessionKey,{label:i}).catch(()=>{}),this.transport.sendSessionUpdate(s,{sessionUpdate:"title_update",title:i}),this.tryGenerateTitleFromHistory(e,s,a)}}}const u=t=>`history:${e.gatewaySessionKey}:${t}`;let p=-1;if(n)for(let e=a.length-1;e>=0;e--)if("assistant"===a[e].role){p=e;break}const{replayedToolCallIds:h,streamCompleteCount:g}=await this.replayHistoryTurnsToSink(e,a,this.transport,{mqActive:Boolean(n),lastAsstIdx:p,turnIdFor:(e,t)=>u(t)});for(const e of h)t.add(e);this.logger.debug?.(`[bridge-core] emitHistoryTurns DONE sid=${s} historyStreamCompleteEmitted=${g} replayedToolCallIds=${t.size}`)}catch(e){this.logger.warn?.("[bridge-core] Failed to load session history:",e)}return t}async replayHistoryTurnsToSink(e,t,s,i){const n=e.sessionId,r=new Set;let o=0;for(let a=0;a<t.length;a++){const l=t[a],d=i.turnIdFor(l,a);if(i.mqActive&&a===i.lastAsstIdx){const e=classifyHistoricalAssistantTurnCompletion(l,{mqActive:i.mqActive,isSkippedForLiveMq:!0});this.logger.debug?.(`[bridge-core] SKIP history_turn[${a}] assistant — ${"live"===e.kind?e.reason:"mq active"}, deferring to emitCurrentStreamSnapshot`);continue}if("user"===l.role&&"internal_system"===l.provenance?.kind){this.logger.debug?.(`[bridge-core] SKIP history_turn[${a}] internal_system user message`);continue}if("user"===l.role&&/^\s*system\s*\(untrusted[^)]*\)/i.test(l.content)){this.logger.debug?.(`[bridge-core] SKIP history_turn[${a}] system-injected user message`);continue}if("user"===l.role&&isHeartbeatPromptText(l.content)){this.logger.info?.(`[bridge-core] SKIP history_turn[${a}] heartbeat cron prompt sessionId=${e.sessionId} contentLen=${l.content.length} preview="${previewSanitizedText(l.content)}"`);continue}if("user"===l.role&&isCronPromptText(l.content)){this.logger.info?.(`[bridge-core] SKIP history_turn[${a}] cron-triggered user message sessionId=${e.sessionId} contentLen=${l.content.length} preview="${previewSanitizedText(l.content)}"`);continue}if("assistant"===l.role){const t=classifyHeartbeatFilter(l.content);if(t.filtered){this.logger.info?.(`[bridge-core] SKIP history_turn[${a}] heartbeat assistant reply reason=${t.reason} sessionId=${e.sessionId} contentLen=${l.content.length} preview="${previewSanitizedText(l.content)}"`);continue}}const c="assistant"===l.role?classifyHistoricalAssistantTurnCompletion(l,{mqActive:i.mqActive,isSkippedForLiveMq:!1}):void 0;if("error"===c?.kind||"interrupted"===c?.kind&&c.failure)s.sendSessionUpdate(n,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:""},...l.timestamp?{timestamp:l.timestamp}:{}},{turnId:d,...void 0!==l._seq?{seq:l._seq}:{}}),s.sendSessionUpdate(n,{sessionUpdate:"stream_complete",stopReason:c.stopReason,error:c.message,errorCode:c.errorCode},{turnId:d}),this.logger.info?.(`[bridge-core] history_runtime_failure_terminalized session_id=${n} turn_id=${d} error_code=${c.errorCode} stop_reason=${c.stopReason} source=history_replay`),o+=1;else if("system"!==l.role)if(this.logger.debug?.(`[bridge-core] SENDING history_turn[${a}] role=${l.role} contentLen=${l.content.length} content=${JSON.stringify(l.content.slice(0,80))} tcCount=${l.toolCalls?.length??0}`),"user"===l.role)this.replayUserTurnAsChunks(s,n,l.content,l.metadata,l.timestamp,d,l._seq);else{if(await this.replayAssistantTurnAsStream(s,n,l,d),l.toolCalls)for(const e of l.toolCalls)e.id&&r.add(e.id);const e=c??classifyHistoricalAssistantTurnCompletion(l,{mqActive:i.mqActive,isSkippedForLiveMq:!1});if("interrupted"===e.kind){const t=new Set(e.incompleteToolCallIds);for(const i of l.toolCalls??[])t.has(i.id)&&s.sendSessionUpdate(n,buildToolCallUpdatePayload({toolCallId:i.id,toolName:i.name,outputText:e.message,kind:i.kind,status:"failed",timestamp:l.timestamp}),{turnId:d});this.logger.info?.(`[bridge-core] history_orphan_turn_terminalized session_id=${n} turn_id=${d} tool_call_ids=${e.incompleteToolCallIds.join(",")} stop_reason=${e.stopReason} source=history_replay`),s.sendSessionUpdate(n,{sessionUpdate:"stream_complete",stopReason:e.stopReason,error:e.message,errorCode:e.errorCode},{turnId:d}),o+=1;continue}if("completed"!==e.kind)continue;s.sendSessionUpdate(n,{sessionUpdate:"stream_complete",stopReason:e.stopReason},{turnId:d}),o+=1}else this.logger.debug?.(`[bridge-core] SENDING system_message[${a}] contentLen=${l.content.length}`),s.sendSessionUpdate(n,{sessionUpdate:"system_message_chunk",content:{type:"text",text:l.content},metadata:l.metadata,...l.timestamp?{timestamp:l.timestamp}:{}},{turnId:d,...void 0!==l._seq?{seq:l._seq}:{}})}return{replayedToolCallIds:r,streamCompleteCount:o}}async handleSessionHistoryPage(e){const t=e.params??{},s="string"==typeof t.sessionId?t.sessionId:"";if(!s)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId"});const i="string"==typeof t.cursor&&t.cursor.length>0?t.cursor:void 0,n="number"==typeof t.limit&&Number.isFinite(t.limit)&&t.limit>0?Math.min(1e3,Math.floor(t.limit)):100,{session:r}=await this.resolveOrCreateSession(s),o=this.getGateway(),a=await loadHistoryPage(o,r.gatewaySessionKey,{...i?{cursor:i}:{},limit:n,logger:this.logger}),l=groupIntoTurns(a.messages),d=new HistoryUpdateRecorder(this.transport);await this.replayHistoryTurnsToSink(r,l,d,{mqActive:!1,lastAsstIdx:-1,turnIdFor:(e,t)=>void 0!==e._seq?`history:${r.gatewaySessionKey}:seq:${e._seq}`:`history:${r.gatewaySessionKey}:seq:none:${i??"head"}:${t}`}),this.logger.debug?.(`[bridge-core] _beeos/session/history sid=${r.sessionId} cursor=${i??"<head>"} limit=${n} turns=${l.length} updates=${d.updates.length} hasMore=${a.hasMore} nextCursor=${a.nextCursor??"<none>"}`),this.transport.sendResult(e.id,{updates:d.updates,...a.nextCursor?{nextCursor:a.nextCursor}:{},hasMore:a.hasMore})}emitCurrentStreamSnapshot(e,t){const s=e.pendingUserTurnUpdates;if(s&&s.length>0)for(const t of s)this.transport.sendSessionUpdate(e.sessionId,t);const i=e.assistantMq;if(i&&!i.closed){const s=i.peek(0);let n=0;if(t&&t.size>0)for(let e=0;e<s.length;e++){const i=s[e],r=i.sessionUpdate,o=i.toolCallId;if("tool_call"!==r&&"tool_call_update"!==r||!o||!t.has(o)){if("agent_message_chunk"===r){if(""===(i.content?.text??"").trim()&&e<s.length-1){const i=s[e+1];if(("tool_call"===i.sessionUpdate||"tool_call_update"===i.sessionUpdate)&&i.toolCallId&&t.has(i.toolCallId)){n=e+1;continue}}break}break}n=e+1}const r=resolveTurnId(e),o=r?{turnId:r}:void 0;for(let t=n;t<s.length;t++)this.transport.sendSessionUpdate(e.sessionId,s[t],o)}}async handleSessionList(e){let t;try{t=parseRuntimeSessionListPage(e.params)}catch(t){return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:t instanceof Error?t.message:"Invalid session/list params"})}const s=[];for(const[,e]of this.state.sessions)isBackgroundSessionKey(e.gatewaySessionKey)||this.cronIsolatedSessionKeys.has(e.gatewaySessionKey)||!t.includeDeleted&&this.state.deletedGatewaySessionKeys.has(e.gatewaySessionKey)||s.push({sessionId:e.sessionId,title:e.title||"New chat",cwd:e.cwd,updatedAt:e.lastStreamActivityTs??e.createdAt,agentId:e.agentId,platformAgentId:e.platformAgentId,deletable:!isSystemDefaultSessionKey(e.gatewaySessionKey)});const i=this.getGateway();if(i?.isConnected)try{const e=await i.sessionsList();if(e&&"object"==typeof e&&Array.isArray(e.sessions)){const i=e.sessions;for(const e of i){const i=e,n=i.sessionKey??i.key??i.id??i.name;if(!n||"string"!=typeof n)continue;if(isBackgroundSessionKey(n))continue;if(this.cronIsolatedSessionKeys.has(n))continue;const r=deriveSessionId(n,this.instanceId),o=s.find(e=>e.sessionId===n||e.sessionId===r||this.state.sessions.get(e.sessionId)?.gatewaySessionKey===n);if(!0===i.deleted){if(this.state.deletedGatewaySessionKeys.add(n),!t.includeDeleted)continue}else this.state.deletedGatewaySessionKeys.delete(n);const a=i.label||cleanGatewayDerivedTitle(i.derivedTitle)||i.title||void 0,l=a&&!isPlaceholderTitle(a)?a:void 0,d=i.model,c=i.modelProvider;if(o){if(l&&isPlaceholderTitle(o.title)){o.title=l;const e=this.state.sessions.get(o.sessionId);e&&isPlaceholderTitle(e.title)&&(e.title=l)}const e=i.updatedAt;e&&(o.updatedAt=e),d&&(o.model=d),c&&(o.modelProvider=c)}else{const e=createSessionObject({gatewayKey:n,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels,cwd:i.cwd,title:l});this.registerSession(e,{ensureVerbose:!1}),s.push({sessionId:e.sessionId,title:l||"",cwd:i.cwd,updatedAt:i.updatedAt,agentId:e.agentId,platformAgentId:e.platformAgentId,deletable:!isSystemDefaultSessionKey(n),model:d,modelProvider:c})}}}}catch(e){this.logger.debug?.("[bridge-core] sessions.list enrichment failed:",e)}const n=t.includeDeleted?s:s.filter(e=>{const t=this.state.sessions.get(e.sessionId)?.gatewaySessionKey;return!t||!this.state.deletedGatewaySessionKeys.has(t)});n.sort((e,t)=>(t.updatedAt??0)-(e.updatedAt??0)||e.sessionId.localeCompare(t.sessionId)),this.transport.sendResult(e.id,{sessions:pageRuntimeSessions(n,t)})}async handleSessionRename(e){const t=e.params?.sessionId,s=e.params?.title;if(!t||!s)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId, title"});const i=s.slice(0,64),n=this.state.sessions.get(t);if(n)try{const s=await this.dispatchLegacyConversationCapability("session.rename",n,{title:i});n.title=s.title,n.titleSource="manual",this.transport.sendResult(e.id,{sessionId:t,title:s.title})}catch(s){this.logger.warn?.(`[bridge-core] ACP rename projection deferred: ${s instanceof Error?s.message:String(s)}`),n.title=i,n.titleSource="manual",this.transport.sendResult(e.id,{sessionId:t,title:i})}else this.transport.sendAcpError(e.id,Errors.SessionNotFound)}static TITLE_TEMPLATES=[{match:["今日日报","日报"],zh:"今日工作日报",en:"Daily Work Log"},{match:["周报","周报总结","本周周报"],zh:"本周工作周报",en:"Weekly Work Report"},{match:["月报","月度汇报","月度总结"],zh:"本月工作月报",en:"Monthly Work Report"},{match:["工作日志","日志"],zh:"工作日志整理",en:"Work Log Review"},{match:["晨会","今日晨会"],zh:"今日晨会纪要",en:"Daily Standup Notes"},{match:["复盘","今日复盘"],zh:"今日工作复盘",en:"Daily Retrospective"}];static TITLE_SYSTEM_PROMPT='You are a conversation title generator. Your task is to generate a concise, human-readable title summarizing the topic of the user\'s question together with the assistant\'s answer.\n\n### Rules:\n- Length: 3-6 words in English, or 6-20 characters in Chinese/Japanese/Korean (up to 24 if needed to keep all key domain terms intact).\n- Language: Match the user\'s input language exactly. Mixed zh/en input → use the dominant language. Do NOT switch languages. (IM path injects Client UI locale via conversation-title.ts — keep ACP in sync with that module when changing examples.)\n- Extract the core topic (noun-centric) of the Q&A exchange, not the action verb.\n- Use the assistant\'s answer to disambiguate or enrich the topic when the question alone is vague (e.g. "帮我看下" + an answer about x402 → "x402 支付集成").\n- No punctuation, no quotation marks, no emojis, no markdown.\n- No prefix like "Title:" or "Here is". Output the title only.\n- Avoid generic words: "help", "question", "chat", "problem", "task".\n- Greetings and self-introductions ARE concrete topics (e.g. "自我介绍", "自己紹介", "Self Introduction") — never treat them as vague.\n- NEVER return placeholder / empty-chat titles such as "新会话", "新對話", "New chat", "New Conversation", "新しいチャット", "새 채팅", "แชตใหม่". Always invent a concrete noun-centric topic from whatever words are present.\n\n### Input Format:\nThe user message will contain both the question and the assistant\'s answer, delimited like this:\n[User Question]\n...question text...\n\n[Assistant Answer]\n...answer text (may be truncated with "..." in the middle)...\n\n### Output Format:\nRespond ONLY with valid JSON, no other text:\n{"title": "<your title here>"}\n\n### Examples:\nInput:\n[User Question]\n帮我写一个关于BeeOS设备接入的PRD\n\n[Assistant Answer]\n好的,这是 BeeOS 设备接入 PRD 的草稿:1. 目标 ...\nOutput: {"title": "BeeOS 设备接入 PRD"}\n\nInput:\n[User Question]\n帮我看下\n\n[Assistant Answer]\n你说的 x402 支付协议是一个基于 HTTP 402 的链上支付标准 ...\nOutput: {"title": "x402 支付协议解析"}\n\nInput:\n[User Question]\nWhat\'s the best way to integrate x402 payment protocol?\n\n[Assistant Answer]\nYou have three options: 1. Use the official SDK ...\nOutput: {"title": "x402 Payment Integration"}\n\nInput:\n[User Question]\n用一句话介绍一下你自己。\n\n[Assistant Answer]\n我是 BeeOS 智能助手,可以帮助你完成设备控制与任务协作。\nOutput: {"title": "自我介绍"}\n\nInput:\n[User Question]\nhi\n\n[Assistant Answer]\nHello! How can I help you today?\nOutput: {"title": "Greeting"}';matchTitleTemplate(e){const t=e.trim().toLowerCase();for(const s of AcpGatewayBridgeCore.TITLE_TEMPLATES)if(s.match.some(e=>e.toLowerCase()===t))return/[\u4e00-\u9fff]/.test(e)?s.zh:s.en}isGuardMessage(e){const t=e.trim();return[...t].length<3||[...t].every(e=>/[\p{P}\p{S}\s]/u.test(e))}truncateQuestionForTitle(e){return e.length>2e3?e.slice(0,1500):e}truncateAnswerForTitle(e){return e.length<=1e3?e:`${e.slice(0,500)}\n...\n${e.slice(-500)}`}generateTitleFromQA(e,t,s,i){if(this.isGuardMessage(s))return void this.logger.info?.(`[bridge-core] title-gen SKIP guard message sid=${t}`);const n=this.matchTitleTemplate(s);if(n){e.title=n,e.titleSource="template";const s=this.getGateway();return s?.isConnected&&s.sessionsPatch(e.gatewaySessionKey,{label:n}).catch(()=>{}),void this.transport.sendSessionUpdate(t,{sessionUpdate:"title_update",title:n})}const r=(process.env.AROUTER_BASE_URL||"").replace(/\/$/,""),o=process.env.AROUTER_API_KEY||"";if(!r||!o)return void this.logger.info?.(`[bridge-core] title-gen SKIP sid=${t}: no AROUTER env vars`);const a=`[User Question]\n${this.truncateQuestionForTitle(s)}\n\n[Assistant Answer]\n${this.truncateAnswerForTitle(i)}`;this.logger.info?.(`[bridge-core] title-gen CALLING LLM sid=${t} q="${s.slice(0,60)}" aLen=${i.length}`),fetch(`${r}/v1/chat/completions`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({model:"openai/gpt-4o-mini",max_tokens:32,temperature:.3,messages:[{role:"system",content:AcpGatewayBridgeCore.TITLE_SYSTEM_PROMPT},{role:"user",content:a}]})}).then(async s=>{if(!s.ok){const e=await s.text().catch(()=>"");return void this.logger.info?.(`[bridge-core] title-gen FAILED sid=${t} status=${s.status} body=${e.slice(0,300)}`)}const i=await s.json(),n=i.choices?.[0]?.message?.content?.trim();if(!n)return;const r=n.replace(/^```json\s*/,"").replace(/```$/,"").trim();let o;try{o=JSON.parse(r).title?.trim()??""}catch{return void this.logger.debug?.(`[bridge-core] title-gen JSON parse failed: ${r}`)}if(!o)return;if(isPlaceholderTitle(o))return void this.logger.debug?.("[bridge-core] title-gen discarded: placeholder LLM title");if("manual"===e.titleSource)return void this.logger.debug?.("[bridge-core] title-gen discarded: user already renamed");this.logger.info?.(`[bridge-core] title-gen OK sid=${t} title="${o}"`),e.title=o,e.titleSource="auto";const a=this.getGateway();a?.isConnected&&a.sessionsPatch(e.gatewaySessionKey,{label:o}).catch(()=>{}),this.transport.sendSessionUpdate(t,{sessionUpdate:"title_update",title:o})}).catch(e=>{this.logger.info?.(`[bridge-core] title-gen ERROR sid=${t}: ${e instanceof Error?e.message:e}`)})}async tryGenerateTitleFromHistory(e,t,s){if("manual"===e.titleSource||"auto"===e.titleSource)return;const i=this.getGateway();if(!i?.isConnected)return;let n=s;if(!n)try{const t=await loadHistory(i,e.gatewaySessionKey);n=groupIntoTurns(t)}catch(e){return void this.logger.debug?.(`[bridge-core] title-gen loadHistory failed sid=${t}: ${e instanceof Error?e.message:e}`)}const r=n.find(e=>{if("user"!==e.role)return!1;if(!e.content?.trim())return!1;if("internal_system"===e.provenance?.kind)return!1;if(isCronPromptText(e.content))return!1;const t=cleanHistoryUserContent(e.content);return!!t&&!/^Sender\s*\(untrusted/i.test(t)});if(!r)return;const o=n.indexOf(r),a=n.slice(o+1).find(e=>"assistant"===e.role&&!!e.content?.trim());if(!a)return;const l=cleanHistoryUserContent(r.content),d=a.content.trim();l&&d&&this.generateTitleFromQA(e,t,l,d)}async handleSessionDelete(e){const t=e.params?.sessionId;if(!t)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId"});const s=this.state.sessions.get(t);if(!s)return this.logger.info?.(`[bridge-core] session/delete sessionId=${t} NOT FOUND in local state`),void this.transport.sendAcpError(e.id,Errors.SessionNotFound);const i=s.gatewaySessionKey,n=this.sessionMutationCoordinator.tryStartExclusive({sessionKey:i,label:"session_delete"},"acp");if(!n.ok)return void this.sendSessionBusy(s,e.id,"active_session_mutation",n.queueDepth);const r=n.token;try{if(this.hasActiveRun(s)){const t=this.sessionMutationCoordinator.gateSnapshot(i);return void this.sendSessionBusy(s,e.id,"active_session_mutation",t.queueDepth)}this.logger.info?.(`[bridge-core] session/delete sessionId=${t} gatewayKey=${i} gwConnected=${!!this.getGateway()?.isConnected}`);let n=!1;const r=this.getGateway();if(r?.isConnected&&i)try{await r.sessionsDelete(i),n=!0,this.logger.info?.(`[bridge-core] Gateway sessions.delete SUCCESS for key=${i}`)}catch(t){const s=t instanceof Error?t.message:String(t);if(this.logger.info?.(`[bridge-core] Gateway sessions.delete FAILED for key=${i}: ${s}`),!s.includes("Cannot delete the main session"))return void this.transport.sendResult(e.id,{deleted:!1});try{await r.sessionsReset(i),n=!0,this.logger.info?.(`[bridge-core] Gateway sessions.reset SUCCESS for key=${i}`)}catch(t){return this.logger.warn?.(`[bridge-core] Gateway sessions.reset also failed for key=${i}: ${t instanceof Error?t.message:t}`),void this.transport.sendResult(e.id,{deleted:!1})}}else i&&this.logger.warn?.(`[bridge-core] Gateway not connected, proceeding with local-only delete for key=${i}`);this.state.clearPromptTimeout(s),this.state.clearStreamIdleTimer(s),s.assistantMq?.close(),s.assistantMq=void 0,s.currentRunId&&this.state.promptsByRunId.delete(s.currentRunId);const o=new Set;for(const[e,s]of this.state.toolCallToSessionMap)s===t&&(this.state.toolCallToSessionMap.delete(e),o.add(e));for(const[e,t]of this.state.toolCallIdMap)o.has(t)&&this.state.toolCallIdMap.delete(e);this.state.sessions.delete(t),this.state.sessionsByGatewayKey.delete(i),this.state.deletedGatewaySessionKeys.add(i),this.transport.resetSessionUpdateCount(t),n&&this.onSessionDeleted?.(i),this.logger.info?.(`[bridge-core] session/delete DONE sessionId=${t} gatewayKey=${i} deleted=true`),this.transport.sendResult(e.id,{deleted:!0})}finally{this.sessionMutationCoordinator.releaseBeforeRuntime(r)}}async handleSessionClear(e){this.logger.info?.(`[bridge-core] handleSessionClear called, params=${JSON.stringify(e.params)}`);const t=e.params?.sessionId;if(!t)return this.logger.warn?.("[bridge-core] session/clear missing sessionId"),void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId"});const s=this.state.sessions.get(t);if(!s)return this.logger.warn?.(`[bridge-core] session/clear sessionId=${t} not found in state`),void this.transport.sendAcpError(e.id,Errors.SessionNotFound);this.logger.info?.(`[bridge-core] session/clear sessionId=${t} gatewayKey=${s.gatewaySessionKey}`);const i=this.getGateway(),n=i?.isConnected;if(!s.promptDone&&(void 0!==s.pendingPromptId||void 0!==s.currentRunId)){this.logger.info?.(`[bridge-core] session/clear: cancelling active run sid=${t} runId=${s.currentRunId}`);const r=s.writeGateToken;let o=!1;if(n&&r){const e=await this.sessionMutationCoordinator.requestCancel(r,i);o="recovery_required"===await this.handleCancellationResult(s,e)}else n&&await i.chatAbort(s.gatewaySessionKey,s.currentRunId);return void this.transport.sendError(e.id,Errors.AgentError.rpcCode,o?SESSION_RECOVERY_REQUIRED_MESSAGE:"Cancellation is still converging",o?{code:SESSION_RECOVERY_REQUIRED_CODE,stopReason:"interrupted"}:{code:"session_cancellation_pending",stopReason:"cancelled"})}try{await this.dispatchLegacyConversationCapability("session.clear",s,{}),this.transport.sendResult(e.id,{cleared:!0}),this.logger.info?.("[bridge-core] session/clear DONE, sent cleared=true")}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleSessionSetMode(e){const t=e.params?.sessionId,s=e.params?.mode,i=t?this.state.sessions.get(t):void 0;if(t&&s)if(i)try{const t=await this.dispatchLegacyConversationCapability("session.setMode",i,{mode:s});this.transport.sendResult(e.id,t)}catch(t){this.sendRuntimeCommandError(e.id,t)}else this.transport.sendAcpError(e.id,Errors.SessionNotFound);else this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId, mode"})}async handleSessionSetModel(e){const t=e.params,s=null===t||"object"!=typeof t||Array.isArray(t)?void 0:t,i="string"==typeof s?.sessionId?s.sessionId:void 0;if(!i||!s||!Object.prototype.hasOwnProperty.call(s,"model"))return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId, model"});const n=s.model;if(null!==n&&("string"!=typeof n||0===n.length||n.trim()!==n))return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"model must be a non-empty string or null"});const r=i?this.state.sessions.get(i):void 0;if(r)try{const t=await this.dispatchLegacyConversationCapability("session.setModel",r,{modelOverrideId:n});this.transport.sendResult(e.id,{model:t.effectiveModelId,modelProvider:t.modelProvider??null})}catch(t){this.sendRuntimeCommandError(e.id,t)}else this.transport.sendAcpError(e.id,Errors.SessionNotFound)}sendRuntimeCommandError(e,t){if(t instanceof RuntimeCommandError)switch(t.code){case"MISSING_PARAM":return void this.transport.sendAcpError(e,Errors.MissingParam,{...t.param?{param:t.param}:{}});case"INVALID_PARAMS":return void this.transport.sendAcpError(e,Errors.InvalidParams,{message:t.message});case"GATEWAY_NOT_CONNECTED":return void this.transport.sendAcpError(e,Errors.GatewayNotConnected);case"EXECUTION_FAILED":case"AGENT_DEFAULT_MODEL_NOT_CONFIGURED":case"OUTCOME_UNKNOWN":case"INSTANCE_EPOCH_MISMATCH":case"COMMAND_EXPIRED":case"IDEMPOTENCY_CONFLICT":case"UNAUTHORIZED_TARGET":return void this.transport.sendAcpError(e,Errors.InternalError,{message:t.message})}else this.transport.sendAcpError(e,Errors.InternalError,{message:t instanceof Error?t.message:"Internal error"})}createOperationId(){const e=randomUUID();return`${e.slice(0,14)}7${e.slice(15)}`}canonicalContextBase(e,t=this.createOperationId(),s="0"){const i=Date.now(),n=createHash("sha256").update(JSON.stringify(e)).digest("hex");return{operationId:t,runtimeInstanceUid:this.instanceId,dispatchEpoch:s,paramsHash:n,idempotencyKeyHash:createHash("sha256").update(t).digest("hex"),claimDeadline:new Date(i+3e4).toISOString(),executionDeadline:new Date(i+3e5).toISOString()}}acpExecutionAttempt(e){return createAcpCoreExecutionAttempt({operationId:e.context.operationId,runtimeEpoch:e.context.dispatchEpoch,resources:this.runtimeCommandResources,historyBoundary:this.localHistoryBoundary})}instanceCommand(e,t,s){return{name:e,params:t,context:{...this.canonicalContextBase(t,s.operationId,s.runtimeEpoch),scope:"instance"}}}async agentCommand(e,t,s,i){return{name:e,params:s,context:{...this.canonicalContextBase(s,i.operationId,i.runtimeEpoch),scope:"agent",agent:{runtimeLocalKey:t,runtimeAgentUid:await this.runtimeAgentIdentities.resolveOrCreate(t)}}}}async dispatchLegacyAgentCapability(e,t,s){const i=normalizeRuntimeAgentLocalKey(t),n=await this.runtimeLeases.claim(this.createOperationId());let r=!1;try{const t={name:e,params:s,context:{...this.canonicalContextBase(s,n.operationId,n.runtimeEpoch),scope:"agent",agent:{runtimeLocalKey:i,runtimeAgentUid:await this.runtimeAgentIdentities.resolveOrCreate(i)}}},o=this.runtimeCommands.dispatch(t,this.acpExecutionAttempt(t));return r=!0,await o.completion}finally{r||await this.runtimeLeases.release(n)}}async dispatchLegacyInstanceCapability(e,t){const s=await this.runtimeLeases.claim(this.createOperationId());let i=!1;try{const n={name:e,params:t,context:{...this.canonicalContextBase(t,s.operationId,s.runtimeEpoch),scope:"instance"}},r=this.runtimeCommands.dispatch(n,this.acpExecutionAttempt(n));return i=!0,await r.completion}finally{i||await this.runtimeLeases.release(s)}}async handleMcpList(e){const t=await this.dispatchLegacyInstanceCapability("mcp.list",{});this.transport.sendResult(e.id,{servers:Object.fromEntries(t.servers.map(e=>[e.name,{transport:e.transport,...e.url?{url:e.url}:{},...e.command?{command:e.command}:{},...e.args?{args:[...e.args]}:{},...e.cwd?{cwd:e.cwd}:{},...e.connectionTimeoutMs?{connectionTimeoutMs:e.connectionTimeoutMs}:{}}]))})}async handleMcpSet(e){const t=e.params&&"object"==typeof e.params?e.params:{},s="string"==typeof t.name?t.name:"";let i;try{if(requireSecretFreeMcpServerName(s),!t.server||"object"!=typeof t.server||Array.isArray(t.server))throw new Error("mcp/set requires name and server");const e=t.server,n=extractMcpCredentialMaterial(e),r={...e};delete r.headers,delete r.env;const o=e.transport??("string"==typeof e.command?"stdio":"string"==typeof e.url?"streamable-http":void 0),a=normalizeMcpPublicServerConfig({...r,transport:o}),l=createHash("sha256").update(canonicalizeJcs(a)).digest("hex");i={definition:{definitionRef:`acp-mcp:sha256:${l}`,version:"local-v1",sha256:l,...n?{credentialRef:await requireCredentialReferencePort(this.mcpCredentialReferences).put({serverName:s,material:n})}:{}},config:structuredClone(a)}}catch(t){return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:t instanceof Error?t.message:"Invalid MCP server config"})}this.runtimeCommandResources.registerMcpDefinition?.(i.definition,i.config);try{await this.dispatchLegacyInstanceCapability("mcp.set",{name:s,definition:i.definition})}catch(e){if(i.definition.credentialRef){!await this.mcp.removeCredentialIfUnreferenced(i.definition.credentialRef)&&this.mcpCredentialReferences?.owns(i.definition.credentialRef)&&await this.mcpCredentialReferences.remove(i.definition.credentialRef)}throw e}this.transport.sendResult(e.id,{ok:!0,restarting:!1})}async handleMcpUnset(e){const t=e.params&&"object"==typeof e.params?e.params:{};let s;try{s=requireSecretFreeMcpServerName(t.name)}catch{return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"mcp/unset requires name"})}await this.dispatchLegacyInstanceCapability("mcp.unset",{name:s}),this.transport.sendResult(e.id,{ok:!0,restarting:!1})}async dispatchLegacyConversationCapability(e,t,s){const i=await this.runtimeLeases.claim(this.createOperationId());let n=!1;try{const r={name:e,params:s,context:{...this.canonicalContextBase(s,i.operationId,i.runtimeEpoch),scope:"conversation",conversation:{runtimeConversationUid:t.sessionId,runtimeSessionKey:t.gatewaySessionKey}}},o=this.runtimeCommands.dispatch(r,this.acpExecutionAttempt(r));return n=!0,await o.completion}finally{n||await this.runtimeLeases.release(i)}}async dispatchLegacyTerminalCapability(e,t,s,i){const n=await this.runtimeLeases.claim(this.createOperationId());let r=!1;try{const o={name:e,params:i,context:{...this.canonicalContextBase(i,n.operationId,n.runtimeEpoch),scope:"terminal",terminal:{terminalId:t,clientId:s}}},a=this.runtimeCommands.dispatch(o,this.acpExecutionAttempt(o));return r=!0,await a.completion}finally{r||await this.runtimeLeases.release(n)}}async dispatchLegacyCanvasNotification(e,t){const s=t&&"object"==typeof t?t:{},i=e.replace("/","."),n="canvas.toggle"===i?{visible:!0===s.visible||!0===s.enabled}:"canvas.reference"===i?{surfaceId:"string"==typeof s.surfaceId?s.surfaceId:""}:"canvas.dimensions"===i?{width:Number(s.width),height:Number(s.height),..."number"==typeof s.devicePixelRatio?{devicePixelRatio:s.devicePixelRatio}:{}}:{},r="string"==typeof s.sessionId?s.sessionId:"acp",o="string"==typeof s.canvasId?s.canvasId:r,a=await this.runtimeLeases.claim(this.createOperationId());let l=!1;try{const e={name:i,params:n,context:{...this.canonicalContextBase(n,a.operationId,a.runtimeEpoch),scope:"canvas",canvas:{canvasId:o,sessionId:r}}},t=this.runtimeCommands.dispatch(e,this.acpExecutionAttempt(e));l=!0,await t.completion}finally{l||await this.runtimeLeases.release(a)}}async applyCanonicalCanvasControl(e,t){if("canvas/reference"===e){const e=this.state.resolveSessionFromParams(t);if(!e)throw new RuntimeCommandError("INVALID_PARAMS","canvas session not found");const s="string"==typeof t.surfaceId?t.surfaceId:void 0;return void(e.referencedSurfaceId=s||void 0)}if(!this.handleCanvasNotification)throw new RuntimeCommandError("INVALID_PARAMS","canvas control delegate is not configured");await this.handleCanvasNotification(e,t)}async dispatchLegacyCronCapability(e,t){const s=normalizeRuntimeAgentLocalKey(this.config.gateway.agentId),i=await this.runtimeLeases.claim(this.createOperationId());let n=!1;try{const r="cron.add"===e?{...t,runtimeCronJobUid:`runtime-cron:${i.operationId}`}:"cron.run"===e?{...t,runToken:`runtime-run:${i.operationId}`}:t,o={name:e,params:r,context:{...this.canonicalContextBase(r,i.operationId,i.runtimeEpoch),scope:"agent",agent:{runtimeLocalKey:s,runtimeAgentUid:await this.runtimeAgentIdentities.resolveOrCreate(s)}}},a=this.runtimeCommands.dispatch(o,this.acpExecutionAttempt(o));return n=!0,await a.completion}finally{n||await this.runtimeLeases.release(i)}}validateConnectedGateway(){if(!this.getGateway()?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected")}async finishClaimedLegacyAgentMutation(e,t){let s=!1;try{const e=await t(),i=this.runtimeCommands.dispatch(e,this.acpExecutionAttempt(e));s=!0;const n=await i.completion;return queueMicrotask(()=>{this.observeAgentProjection()}),n}finally{s||await this.runtimeLeases.release(e)}}async observeAgentProjection(){await(async(e,t)=>{if(!t)return;let s;try{await Promise.race([Promise.resolve().then(t),new Promise((t,i)=>{s=setTimeout(()=>i(new Error(`${e} timed out`)),this.projectionObservationTimeoutMs)})])}catch(t){this.logger.warn?.(`[acp] ${e} failed: ${t instanceof Error?t.message:String(t)}`)}finally{s&&clearTimeout(s)}})("agent projection sync",this.triggerAgentSync)}async handleAgentCreate(e){try{const t=e.params?.name?.trim()??"";if(!t)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");this.validateConnectedGateway();const s=this.createOperationId(),i=await this.runtimeLeases.claim(s),n=await this.finishClaimedLegacyAgentMutation(i,async()=>{const s=await this.runtimeAgentIdentities.allocateCreate(i,"agent.create"),n=Object.entries(e.params?.files??{}).filter(e=>"string"==typeof e[1]).map(([e,t])=>({name:e,contentRef:this.runtimeCommandResources.registerContent(t)}));return this.instanceCommand("agent.create",{runtimeAgentUid:s.runtimeAgentUid,name:t,...e.params?.identity?{identity:e.params.identity}:{},...e.params?.model?{defaultModelId:e.params.model}:{},...n.length?{bootstrapFiles:n}:{}},i)});this.transport.sendResult(e.id,{agentId:n.runtimeLocalKey,platformAgentId:this.resolvePlatformAgentId(n.runtimeLocalKey)??"",workspace:n.workspacePath})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleApplyTemplate(e){try{const t=e.params?.name?.trim()??"";if(!t)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");const s=e.params?.packageUrl?.trim()??"";if(!s)throw new RuntimeCommandError("MISSING_PARAM","Missing packageUrl","packageUrl");this.validateConnectedGateway();const i=await this.runtimeCommandResources.registerArtifact(s,e.params?.sha256),n=this.createOperationId(),r=await this.runtimeLeases.claim(n),o=await this.finishClaimedLegacyAgentMutation(r,async()=>{const s=await this.runtimeAgentIdentities.allocateCreate(r,"agent.applyTemplate");return this.instanceCommand("agent.applyTemplate",{runtimeAgentUid:s.runtimeAgentUid,name:t,artifact:i,...e.params?.identity?{identity:e.params.identity}:{},...e.params?.model?{defaultModelId:e.params.model}:{}},r)});this.transport.sendResult(e.id,{agentId:o.runtimeLocalKey,platformAgentId:this.resolvePlatformAgentId(o.runtimeLocalKey)??"",workspace:o.workspacePath})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleAgentUpdate(e){try{const t=e.params?.agentId?.trim()??"";if(!t)throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");const s={...void 0!==e.params?.name?{name:e.params.name}:{},...void 0!==e.params?.model?{defaultModelId:e.params.model}:{},...void 0!==e.params?.identity?{identity:e.params.identity}:{}};if(0===Object.keys(s).length)throw new RuntimeCommandError("INVALID_PARAMS","No updatable fields provided (name/model/identity)");this.validateConnectedGateway();const i=await this.runtimeLeases.claim(this.createOperationId()),n=await this.finishClaimedLegacyAgentMutation(i,()=>this.agentCommand("agent.update",t,s,i));this.transport.sendResult(e.id,{agentId:n.runtimeLocalKey})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleAgentDelete(e){try{const t=e.params?.agentId?.trim()??"";if(!t)throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");if("main"===normalizeRuntimeAgentLocalKey(t))throw new RuntimeCommandError("INVALID_PARAMS",'"main" cannot be deleted; delete the instance instead');this.validateConnectedGateway();const s=await this.runtimeLeases.claim(this.createOperationId()),i=await this.finishClaimedLegacyAgentMutation(s,()=>this.agentCommand("agent.delete",t,void 0===e.params?.deleteFiles?{}:{deleteFiles:e.params.deleteFiles},s));this.transport.sendResult(e.id,{agentId:i.runtimeLocalKey,...void 0===i.removedBindings?{}:{removedBindings:i.removedBindings}})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleAgentList(e){try{const t=await this.dispatchLegacyInstanceCapability("agent.listProjection",{}),s=t.agents.map(e=>({agentId:e.runtimeLocalKey,name:e.name,...e.emoji?{emoji:e.emoji}:{},...e.avatarUrl?{avatarUrl:e.avatarUrl}:{}})),i={...t.defaultRuntimeLocalKey?{defaultId:t.defaultRuntimeLocalKey}:{},agents:s};this.transport.sendResult(e.id,i)}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleModelsList(e){try{this.transport.sendResult(e.id,await this.dispatchLegacyInstanceCapability("models.list",{}))}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleSkillsList(e){try{const t=await this.dispatchLegacyAgentCapability("skills.list",e.params?.agentId,{});this.transport.sendResult(e.id,{skills:t.skills.map(e=>({name:e.name,description:e.description??"",...e.emoji?{emoji:e.emoji}:{},...e.always?{always:!0}:{},...e.bundled?{bundled:!0}:{},...e.enabled?{}:{disabled:!0},skillKey:e.skillKey,...e.iconUrl?{iconUrl:e.iconUrl}:{}}))})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async fetchSimplifiedSkills(e){const t=this.getGateway();if(!t?.isConnected)return[];try{const s=await t.skillsStatus(e);if(!s?.skills)return[];const i=s.skills.filter(e=>!1!==e.eligible||!0===e.disabled).sort((e,t)=>e.always&&!t.always?-1:!e.always&&t.always?1:e.name.localeCompare(t.name)).map(e=>({name:e.name,description:e.description,emoji:e.emoji,always:e.always||void 0,bundled:e.bundled||void 0,disabled:e.disabled||void 0,skillKey:e.skillKey||void 0,iconUrl:void 0})),n=await this.getSkillsDir(e);for(const e of i){const t=e.skillKey??e.name;try{const s=await readFile(join(n,t,".beeos-meta.json"),"utf8"),i=JSON.parse(s);i?.iconUrl&&(e.iconUrl=i.iconUrl)}catch{}}return i}catch(e){return this.logger.warn?.(`[bridge-core] Failed to fetch skills: ${e instanceof Error?e.message:e}`),[]}}async pushSkillsList(e){const t=await this.fetchSimplifiedSkills(e);this.transport.sendNotification("session/update",{sessionId:"_system",update:{sessionUpdate:"available_commands_update",commands:t}}),this.writeObs({component:"bridge-core",domain:"lifecycle",name:"skills.pushed",severity:"debug",payload:{count:t.length}})}getOpenclawHome(){return process.env.OPENCLAW_HOME||join(homedir(),".openclaw")}async getSkillsDir(e){const t=this.getOpenclawHome();if(!e||"main"===e)return join(t,"skills");const s=await this.resolveAgentWorkspaceDir(e);return join(s??join(t,`workspace-${e}`),"skills")}async resolveAgentWorkspaceDir(e){const t=this.getGateway();if(!t?.isConnected)return null;try{const s=await t.request("agents.list",{}),i=normalizeAgentId(e),n=s?.agents?.find(e=>normalizeAgentId(e.id)===i);return n?.workspace??null}catch(t){return this.logger.warn?.(`[bridge-core] resolveAgentWorkspaceDir("${e}") failed: ${t instanceof Error?t.message:t}`),null}}async handleSkillsInstall(e){const t=e.params,s=t?.zipUrl,i=t?.files,n=t?.agentId;let r;try{r=validateSkillSlug(t?.slug||t?.name||"")}catch{return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"Invalid or missing skill slug"})}if(i?.length||s)try{let o;if(s){o=parseSkillZip(await fetchWithRetry(s,{maxRetries:4,logger:this.logger}))}else{if(!i?.length)throw new RuntimeCommandError("INVALID_PARAMS","Missing skill files or zipUrl");o=new Map;for(const e of i){const t=normalizeSkillArtifactPath(e.path),s=await fetchWithRetry(e.downloadUrl,{maxRetries:4,logger:this.logger});o.set(t,new Uint8Array(s))}}if(t?.iconUrl&&o.set(".beeos-meta.json",(new TextEncoder).encode(JSON.stringify({iconUrl:t.iconUrl}))),!this.runtimeCommandResources.registerParsedArtifact)throw new RuntimeCommandError("EXECUTION_FAILED","ACP skill artifact ingress is not configured");const a=this.runtimeCommandResources.registerParsedArtifact({manifest:{slug:r,version:"0.0.0"},workspaceFiles:o});await this.dispatchLegacyAgentCapability("skills.install",n,{artifact:a}),this.transport.sendResult(e.id,{success:!0}),this.onSkillInstalled?.(r,resolveSkillInstallDir(await this.getSkillsDir(n),r)),await this.pushSkillsList(n)}catch(t){this.logger.error?.(`[bridge-core] Failed to install skill "${r}": ${t instanceof Error?t.message:t}`),this.transport.sendAcpError(e.id,Errors.InternalError,{message:`Install failed: ${t instanceof Error?t.message:String(t)}`})}else this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"Missing skill files or zipUrl"})}async handleSkillsUninstall(e){const t=e.params,s=t?.agentId;let i;try{i=validateSkillSlug(t?.slug||t?.name||"")}catch{return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"Invalid or missing skill name"})}try{await this.dispatchLegacyAgentCapability("skills.uninstall",s,{skillKey:i}),this.transport.sendResult(e.id,{success:!0}),await this.pushSkillsList(s)}catch(t){this.logger.error?.(`[bridge-core] Failed to uninstall skill "${i}": ${t instanceof Error?t.message:t}`),this.transport.sendAcpError(e.id,Errors.InternalError,{message:`Uninstall failed: ${t instanceof Error?t.message:String(t)}`})}}async handleSkillsUpdate(e){const t=e.params,s=t?.skillKey,i=t?.enabled,n=t?.agentId;if(s&&"boolean"==typeof i)try{await this.dispatchLegacyAgentCapability("skills.setEnabled",n,{skillKey:s,enabled:i}),this.logger.info?.(`[bridge-core] Updated skill "${s}" enabled=${i}${n?` for agent "${n}"`:""}`),this.transport.sendResult(e.id,{ok:!0,skillKey:s,enabled:i}),await this.pushSkillsList(n)}catch(t){this.logger.error?.(`[bridge-core] Failed to update skill "${s}": ${t instanceof Error?t.message:t}`),this.transport.sendAcpError(e.id,Errors.InternalError,{message:`Update failed: ${t instanceof Error?t.message:String(t)}`})}else this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"Missing skillKey or enabled"})}async handleSessionPrompt(e){const t=e.params?.sessionId,s=e.params?.prompt;if(!t||!s)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId, prompt"});const{session:i,created:n}=await this.resolveOrCreateSession(t);n&&this.onSessionLoaded?.(i.sessionId,i.gatewaySessionKey,i.platformAgentId);const r=i.sessionId;this.state.lastActiveSessionId=r,this.onPromptStart?.();const o=this.getGateway();if(!o?.isConnected)return void this.transport.sendAcpError(e.id,Errors.GatewayNotConnected);if(this.hasActiveRun(i)){const t=this.sessionMutationCoordinator.gateSnapshot(i.gatewaySessionKey);return void this.sendSessionBusy(i,e.id,"active_run",t.queueDepth)}try{await this.ensureVerboseFull(i)}catch(t){return this.releaseSessionWriteGate(i),void this.transport.sendAcpError(e.id,Errors.InternalError,{message:`Failed to prepare agent context: ${t instanceof Error?t.message:String(t)}`})}let a=extractPromptBlocks(sanitizePromptPayload(s));const l=a.filter(e=>"text"===e.type&&"text"in e&&"string"==typeof e.text).map(e=>e.text).join("\n");if(i.fileArtifactDisplayMode=prefersChatFileArtifacts(l)?"chat":"auto",!i.title||isPlaceholderTitle(i.title)){const e=a.find(e=>"text"===e.type&&e.text);if(e&&"text"in e&&e.text){const t=stripTransportMetadata(e.text).trim();if(this.logger.info?.(`[bridge-core] title-gen sid=${r} existing="${i.title}" cleanText="${t.slice(0,80)}" isPlaceholder=${isPlaceholderTitle(t)}`),t&&!isPlaceholderTitle(t)){const e=t.slice(0,64).replace(/\n/g," ").trim();i.title=e,i.titleSource="truncated",o?.isConnected&&e&&o.sessionsPatch(i.gatewaySessionKey,{label:e}).catch(e=>{this.logger.debug?.(`[bridge-core] sessionsPatch (title) failed: ${e instanceof Error?e.message:e}`)}),this.transport.sendSessionUpdate(r,{sessionUpdate:"title_update",title:e})}}}const d=buildResolutionPlan(a);if(d.refs.length>0)try{const e=await resolveResources(d,this.writeObs);a=applyResolutions(a,e)}catch(e){this.logger.warn?.(`[bridge-core] Resource pre-resolution failed: ${e instanceof Error?e.message:e}`)}let c;if(this.beeosFileResolver){const e=this.beeosFileResolver.buildResolutionPlan(a);if(e.refs.length>0)try{c=await this.beeosFileResolver.resolveMetadataForPrompt(e)}catch(e){this.logger.warn?.(`[bridge-core] BeeOS file resolution failed: ${e instanceof Error?e.message:e}`)}}const m=toGatewayPrompt(a,{beeosFileResolutionsByUri:c});if("error"in m)return this.releaseSessionWriteGate(i),void this.transport.sendAcpError(e.id,Errors.PromptConvertFailed,{message:m.error});const{message:u,attachments:p}=m,h=resolveStandaloneSlashCommandName(a,u)??resolveSlashCmd(a,u),g="string"==typeof h;if(h&&this.handleSlashCommand){const t=this;if(await(async()=>{try{return await t.handleSlashCommand(h)}catch(e){return t.logger.warn?.(`[bridge-core] /${h} handler failed: ${e instanceof Error?e.message:e}`),!1}})())return this.emitPromptAsUserSessionUpdates(i,a,resolveTurnId(i)),this.transport.sendSessionUpdate(r,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:`Loading ${h}...`},timestamp:(new Date).toISOString()},{turnId:resolveTurnId(i)}),this.transport.sendResult(e.id,{stopReason:"end_turn"}),void this.releaseSessionWriteGate(i)}const y="chat.send";let f=g?u:withUserMessagePrefix(u);const w=this.config.canvasEnabled&&!g&&!0===i.canvasActive&&!i.channelType;if(w){const e=i.promptsSinceCanvasInjection??0;!i.canvasPromptInjected||e>=10?(f=`${CANVAS_TERM_MAPPING}\n\n${f}`,i.canvasPromptInjected=!0,i.promptsSinceCanvasInjection=0,this.logger.debug?.("[bridge-core] Canvas term mapping injected (re-inject cycle)")):i.promptsSinceCanvasInjection=e+1}if(w){const e=this.getCanvasState?.();if(e){const t=e.resolveCanvasForSession(i.gatewaySessionKey);f=`${buildActiveSurfacesContext(e,t)??"[Canvas] Canvas panel is available. Use render_ui for structured/visual content."}\n\n${f}`}const t=i.referencedSurfaceId;t&&(f=`${CANVAS_UPDATE_HINT}\n[IMPORTANT: The user selected canvas surface "${t}" for editing. You MUST call render_ui with id="${t}" to UPDATE this existing surface. Do NOT create a new surface with a different id.]\n${f}`,i.referencedSurfaceId=void 0)}if(!g){const t=parseMentionsParam(e.params?.mentions);if(t.length>0){i.activeMentions=t;f=`${buildMentionInstruction(t)}\n${f}`,this.logger.debug?.(`[bridge-core] delegation mention instruction injected for ${t.length} agent(s)`)}else i.activeMentions=void 0}if(g&&this.logger.info?.(`[bridge-core] slash command passthrough sessionId=${r} command=/${h}`),this.hasActiveRun(i)){const t=this.sessionMutationCoordinator.gateSnapshot(i.gatewaySessionKey);return void this.sendSessionBusy(i,e.id,"active_run_after_prepare",t.queueDepth)}const I=this.sessionMutationCoordinator.tryStartInteractive({sessionKey:i.gatewaySessionKey,label:"session/prompt"},"acp");if(!I.ok)return void this.sendSessionBusy(i,e.id,I.reason,I.queueDepth);i.writeGateToken=I.token,this.state.resetForNewRun(i,r,this.transport),i.pendingPromptId=e.id,i.gatewayMethod=y;const v=resolveTurnId(i);this.emitPromptAsUserSessionUpdates(i,a,v),this.transport.sendSessionUpdate(r,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:""},timestamp:(new Date).toISOString()},v?{turnId:v}:void 0);const S=extractCronJobId(u);S&&(i.cronJobId=S),this.schedulePromptTimeout(i);const C=p.length>0?p.map(e=>({type:e.type,content:e.content,...e.mimeType?{mimeType:e.mimeType}:{},...e.fileName?{fileName:e.fileName}:{}})):void 0,R=`req_${randomUUID().replace(/-/g,"")}`,A={sessionKey:i.gatewaySessionKey,message:f,deliver:!1,idempotencyKey:randomUUID()};C?.length&&(A.attachments=C);if(!o.sendFrame({type:"req",id:R,method:y,params:A}))return this.state.clearPromptTimeout(i),i.pendingPromptId=void 0,i.assistantMq?.close(),i.assistantMq=void 0,i.pendingUserTurnUpdates=void 0,this.releaseSessionWriteGate(i),void this.transport.sendAcpError(e.id,Errors.SendFailed,{message:"failed to send prompt to gateway"});i.gatewayRequestId=R,this.state.promptsByGatewayRequestId.set(R,i)}replayUserTurnAsChunks(e,t,s,i,n,r,o){const a=splitUserReplayBlocks(s),l=r||void 0!==o?{...r?{turnId:r}:{},...void 0!==o?{seq:o}:{}}:void 0;for(const s of a){let r;if("text"===s.type){if(!s.text)continue;r={type:"text",text:s.text}}else r="resource_link"===s.type?{type:"resource_link",uri:s.uri??"",...s.fileName?{name:s.fileName}:{},...s.mimeType?{mimeType:s.mimeType}:{}}:{type:s.type,...s.mimeType?{mimeType:s.mimeType}:{},...s.fileName?{fileName:s.fileName}:{}};e.sendSessionUpdate(t,{sessionUpdate:"user_message_chunk",content:r,...i?{metadata:i}:{},...n?{timestamp:n}:{}},l)}}async replayAssistantTurnAsStream(e,t,s,i){const n=e=>{if(i||void 0!==e)return{...i?{turnId:i}:{},...void 0!==e?{seq:e}:{}}},r=new Set,o=(s,i,o)=>{if(!s?.length)return;const a=n(o);for(const n of s)n.uri&&!r.has(n.uri)&&(r.add(n.uri),e.sendSessionUpdate(t,{sessionUpdate:"agent_message_chunk",content:{type:"resource_link",uri:n.uri,name:n.name??"file",...n.mimeType?{mimeType:n.mimeType}:{}},...i?{timestamp:i}:{}},a))},a=n(s._seq);if(!s.sourceMessages?.length){const i={sessionUpdate:"history_turn",role:s.role,content:s.content,reasoning:s.reasoning,toolCalls:s.toolCalls,...s.timestamp?{timestamp:s.timestamp}:{}};return s.metadata&&(i.metadata=s.metadata),e.sendSessionUpdate(t,i,a),void o(s.files,s.timestamp,s._seq)}for(const i of s.sourceMessages){const r=i.updateType??"agent_message_chunk",a=n(i._seq);switch(r){case"agent_message_chunk":e.sendSessionUpdate(t,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:i.content},...i.timestamp?{timestamp:i.timestamp}:{},...i.metadata?{metadata:i.metadata}:{}},a);break;case"agent_thought_chunk":e.sendSessionUpdate(t,{sessionUpdate:"agent_thought_chunk",content:{type:"text",text:i.content},...i.timestamp?{timestamp:i.timestamp}:{}},a);break;case"tool_call":{const n=i.toolCallId??"",r=s.toolCalls?.find(e=>e.id===n),o=i.toolName??"tool",l=i.content??"";let d={};if(l)try{d=JSON.parse(l)}catch{}e.sendSessionUpdate(t,buildToolCallPayload({toolCallId:n,toolName:o,args:d,kind:r?.kind,status:r?.status??"in_progress",timestamp:i.timestamp}),a);break}case"tool_call_update":{const n=i.toolCallId??"",r=s.toolCalls?.find(e=>e.id===n),l=i.toolName??r?.name??"tool",d=i.content??r?.output??"",c=await this.resolveReplayResourceLinks(n,l,i.files);e.sendSessionUpdate(t,buildToolCallUpdatePayload({toolCallId:n,toolName:l,outputText:d,kind:r?.kind,status:r?.status??"completed",resourceLinks:c,timestamp:i.timestamp}),a),o(c,i.timestamp,i._seq);break}}}o(s.files,s.timestamp,s._seq)}async resolveReplayResourceLinks(e,t,s){let i=s?.map(e=>({uri:e.uri,name:e.name??"file",...e.mimeType?{mimeType:e.mimeType}:{}}));if(i?.length)return i;if(i=await readEnrichmentCache(e).catch(()=>{}),i?.length)return i;if("beeos_upload_file"!==t)return;const n=await readUploadToolResult(e).catch(()=>{});if(!n)return;const r=buildToolResultSessionContentBlocks(t,n).filter(e=>"resource_link"===e.content?.type&&e.content.uri).map(e=>({uri:e.content.uri,name:e.content.name??"file",...e.content.mimeType?{mimeType:e.content.mimeType}:{}}));return r.length>0?r:void 0}emitPromptAsUserSessionUpdates(e,t,s){const i=(new Date).toISOString(),n=s?{turnId:s}:void 0;for(const r of t){const t=this.toUserSessionUpdateContent(r);if(!t)continue;const o={sessionUpdate:"user_message_chunk",content:t,timestamp:i};s&&void 0===o._turnId&&(o._turnId=s),this.transport.sendSessionUpdate(e.sessionId,o,n),e.pendingUserTurnUpdates?.push(o)}}toUserSessionUpdateContent(e){if("text"===e.type){if(!e.text)return;return{type:"text",text:e.text}}if("image"===e.type){const t=e.mimeType??e.mime_type??"application/octet-stream",s=e.fileName??e.file_name??e.name;if(!e.data&&!e.uri)return;return{type:"image",...e.data?{data:e.data}:{},...e.uri?{uri:e.uri}:{},...t?{mimeType:t}:{},...s?{fileName:s}:{}}}if("resource_link"===e.type){if(!e.uri)return;return{type:"resource_link",uri:e.uri,...e.title?{title:e.title}:{},...e.name?{name:e.name}:{},...e.mimeType??e.mime_type?{mimeType:e.mimeType??e.mime_type}:{}}}if("file"===e.type){const t=e.mimeType??e.mime_type??"application/octet-stream",s=e.fileName??e.file_name??e.filename??e.name;if(!e.data&&!e.text&&!e.uri)return;return{type:"file",...e.data?{data:e.data}:{},...e.text?{text:e.text}:{},...e.uri?{uri:e.uri}:{},...t?{mimeType:t}:{},...s?{fileName:s}:{}}}if("resource"===e.type){const t=e.resource??{},s=t.uri??e.uri,i=t.mimeType??t.mime_type??e.mimeType??e.mime_type,n=t.text??e.text,r=t.data??e.data,o=t.fileName??t.file_name??t.filename??t.name??e.fileName??e.file_name??e.filename??e.name;if(!s&&!n&&!r)return;return{type:"resource",resource:{...s?{uri:s}:{},...i?{mimeType:i}:{},...n?{text:n}:{},...r?{data:r}:{},...o?{fileName:o}:{}}}}}async handleSessionCancel(e,t){const s=t&&"object"==typeof t&&"sessionId"in t?t.sessionId:void 0;if(!s)return void(void 0!==e&&this.transport.sendAcpError(e,Errors.MissingParam,{param:"sessionId"}));const i=this.state.sessions.get(s);if(!i)return void(void 0!==e&&this.transport.sendAcpError(e,Errors.SessionNotFound));const n=i.activeRunBinding??this.observeActiveRun(i);if(!n){const t=this.sessionMutationCoordinator.snapshot(i.gatewaySessionKey);if("exclusive"===t?.attachmentPolicy&&void 0!==e){const t=this.sessionMutationCoordinator.gateSnapshot(i.gatewaySessionKey);return void this.sendSessionBusy(i,e,"exclusive_session_mutation",t.queueDepth)}return void(void 0!==e&&this.transport.sendResult(e,{}))}const r=await this.runtimeLeases.claim(this.createOperationId());let o=!1;try{const s=t&&"object"==typeof t&&"reason"in t&&"string"==typeof t.reason?t.reason:void 0,i={targetMessageId:n.targetMessageId,...s?{reason:s}:{}},a={name:"session.cancel",params:i,context:{...this.canonicalContextBase(i,r.operationId,r.runtimeEpoch),scope:"conversation",historyGeneration:n.historyGeneration,conversation:{runtimeConversationUid:n.runtimeConversationUid,runtimeSessionKey:n.runtimeSessionKey}}},l=this.runtimeCommands.dispatch(a,this.acpExecutionAttempt(a));o=!0,await l.completion,void 0!==e&&this.transport.sendResult(e,{})}catch(t){void 0!==e&&this.sendRuntimeCommandError(e,t)}finally{o||await this.runtimeLeases.release(r)}}hasActiveRun(e){return!e.promptDone&&(void 0!==e.pendingPromptId||void 0!==e.currentRunId||void 0!==e.gatewayRequestId)}observeLifecycle(e,t,s){observeLifecyclePromise(e,this.logger,{component:"bridge-core",action:t,...s?{sessionId:s.sessionId,gatewaySessionKey:s.gatewaySessionKey}:{}})}async handleCancellationResult(e,t){return t.ok?(this.observeLifecycle(t.terminalObservation.then(t=>{if(!t.ok)return this.publishCancellationRecovery(e)}),"cancel_terminal_observation",e),"accepted"):"recovery"in t?(await this.publishCancellationRecovery(e),"recovery_required"):"terminal_already_observed"}async publishCancellationRecovery(e){await this.terminateRunOutput(e,{kind:"failed",mode:"interrupted",message:SESSION_RECOVERY_REQUIRED_MESSAGE,errorCode:SESSION_RECOVERY_REQUIRED_CODE})}releaseSessionWriteGate(e){if(!e.writeGateToken)return;const t=e.writeGateToken;e.writeGateToken=void 0,this.sessionMutationCoordinator.releaseBeforeRuntime(t)}logRuntimeFailure(e,t){this.logger.warn?.(`[bridge-core] runtime_failure_classified session_id=${e.sessionId} gateway_session_key=${e.gatewaySessionKey} run_id=${e.currentRunId??""} gateway_request_id=${e.gatewayRequestId??""} error_code=${t.errorCode} stop_reason=${t.stopReason} source=${t.source} runtime=${t.runtimeKind}`)}async commitRunOutputTerminal(e,t){const s=this.state.sessions.get(e.sessionId);if(!s||s.gatewaySessionKey!==e.gatewaySessionKey)throw new Error(`run output lifecycle session unavailable: ${e.sessionId}`);await this.commitRunOutputTerminalForSession(s,t)}async commitRunOutputTerminalForSession(e,t){if("complete"!==t.kind&&"cancelled"!==t.kind)return"interrupted"===t.mode?(t.classification&&this.logRuntimeFailure(e,t.classification),void await this.endRun(e,{kind:"interrupted",stopReason:t.classification?.stopReason??"interrupted",message:t.message,errorCode:t.errorCode})):void await this.endRun(e,{kind:"error",code:t.code,message:t.message});await this.endRun(e,{kind:"complete",stopReason:t.stopReason})}async terminateRunOutput(e,t){const s=resolveTurnId(e);s&&this.runOutputRouter.hasRoute(s)?await this.runOutputRouter.terminal(s,t):await this.commitRunOutputTerminalForSession(e,t)}sendSessionBusy(e,t,s,i){this.logger.warn?.(`[bridge-core] session_write_gate_busy session_id=${e.sessionId} gateway_session_key=${e.gatewaySessionKey} reason=${s} queue_depth=${i}`),this.transport.sendError(t,Errors.AgentError.rpcCode,"Agent is busy",{code:"session_busy",reason:s,queueDepth:i}),this.transport.sendSessionUpdate(e.sessionId,{sessionUpdate:"stream_complete",stopReason:"interrupted",error:INTERRUPTED_PUBLIC_MESSAGE,errorCode:"session_busy"},{turnId:`req:${String(t)}`})}async endRun(e,t){if(e.promptDone||e.mutationTerminalCommitting)return this.logger.debug?.(`[bridge-core] endRun skipped (promptDone=true) sessionId=${e.sessionId} outcome=${t.kind}`),!1;if(void 0===e.pendingPromptId&&void 0===e.currentRunId&&void 0===e.gatewayRequestId)return this.logger.debug?.(`[bridge-core] endRun skipped (no pendingPromptId and no currentRunId) sessionId=${e.sessionId} outcome=${t.kind}`),!1;e.mutationTerminalCommitting=!0,this.state.clearPromptTimeout(e),this.state.clearStreamIdleTimer(e);const s=e.writeGateToken,i=e.activeRunBinding;let n,r,o,a,l="timeout"===e.mutationTerminalOverride?{kind:"interrupted",stopReason:"timeout",message:"The agent did not finish before the session timeout.",errorCode:"gateway_timeout"}:t;if(s){const e="complete"===l.kind?"cancelled"===l.stopReason?"cancelled":"completed":"interrupted"===l.kind&&"timeout"===l.stopReason?"timeout":"error",t=await this.sessionMutationCoordinator.converge(s,e);t.ok?n=t.permit:l={kind:"interrupted",stopReason:"interrupted",message:SESSION_RECOVERY_REQUIRED_MESSAGE,errorCode:SESSION_RECOVERY_REQUIRED_CODE}}try{const t=resolveTurnId(e),s=t?{turnId:t}:void 0;e.promptDone=!0;const i=e.currentRunId;if("complete"===l.kind){if(i&&this.onRunComplete){const t=e.agentAssistantTextSoFar??e.chatAssistantTextSoFar??"";this.onRunComplete(i,t,l.stopReason)}void 0!==e.pendingPromptId&&this.transport.sendResult(e.pendingPromptId,{stopReason:l.stopReason}),this.transport.sendSessionUpdate(e.sessionId,{sessionUpdate:"stream_complete",stopReason:l.stopReason},s),this.tryGenerateTitleFromHistory(e,e.sessionId)}else"interrupted"===l.kind?(i&&this.onRunFailed&&this.onRunFailed(i,l.message),void 0!==e.pendingPromptId&&this.transport.sendError(e.pendingPromptId,Errors.AgentError.rpcCode,l.message,{code:l.errorCode??"interrupted",stopReason:l.stopReason}),this.transport.sendSessionUpdate(e.sessionId,{sessionUpdate:"stream_complete",stopReason:l.stopReason,error:l.message,...l.errorCode?{errorCode:l.errorCode}:{}},s)):(i&&this.onRunFailed&&this.onRunFailed(i,l.message),void 0!==e.pendingPromptId&&this.transport.sendError(e.pendingPromptId,l.code,l.message),this.transport.sendSessionUpdate(e.sessionId,{sessionUpdate:"stream_complete",stopReason:"error",error:l.message},s))}catch(e){r=e}try{this.cleanupPrompt(e)}catch(e){o=e}finally{e.writeGateToken=void 0,e.activeRunBinding=void 0,n&&this.sessionMutationCoordinator.release(n)}if(i){const e="complete"===l.kind?"cancelled"===l.stopReason?"cancelled":"completed":"interrupted"===l.kind&&"cancelled"===l.stopReason?"cancelled":"failed";this.activeRuns.markTerminal(i,e)}try{this.onRunEnded?.(e,l)}catch(e){a=e}if(r)throw r;if(o)throw o;if(a)throw a;return!0}schedulePromptTimeout(e){let t;if(this.state.clearPromptTimeout(e),e.cronJobId){if(!this.cronOverrides.isInitialized())return void this.cronReady.then(()=>{!e.cronJobId||void 0===e.pendingPromptId&&void 0===e.currentRunId||this.schedulePromptTimeout(e)}).catch(e=>this.logger.error?.(`[bridge-core] cron overrides readiness failed: ${e instanceof Error?e.message:e}`));const s=this.cronOverrides.get(e.cronJobId)??this.config.cronMaxDurationDefaultMs;t=Math.max(CRON_DURATION_FLOOR_MS,Math.min(this.config.cronMaxDurationCapMs,s))}else t=this.config.promptTimeoutMs??DEFAULT_PROMPT_TIMEOUT_MS;t<=0||(e.promptTimeoutTimer=setTimeout(()=>{if(e.mutationTerminalCommitting)return;if(void 0===e.pendingPromptId&&void 0===e.currentRunId)return;this.logger.warn?.(`[bridge-core] Prompt timed out sessionId=${e.sessionId} after ${t}ms`),e.mutationTerminalOverride="timeout";const s=this.getGateway(),i=e.writeGateToken;s?.isConnected&&i?this.observeLifecycle(this.sessionMutationCoordinator.requestCancel(i,s).then(t=>this.handleCancellationResult(e,t)),"prompt_timeout_cancel",e):s?.isConnected?this.observeLifecycle(s.chatAbort(e.gatewaySessionKey,e.currentRunId).then(()=>this.interruptPrompt(e,classifyAgentRuntimeFailure({raw:`Prompt timed out after ${t}ms`,source:"live_gateway",runtimeKind:"openclaw"}))),"prompt_timeout_unowned_cancel",e):i&&(this.sessionMutationCoordinator.requireRecovery(i,"gateway_disconnected","disconnect"),this.observeLifecycle(this.terminateRunOutput(e,{kind:"failed",mode:"interrupted",message:SESSION_RECOVERY_REQUIRED_MESSAGE,errorCode:SESSION_RECOVERY_REQUIRED_CODE}),"prompt_timeout_disconnected",e))},t),"object"==typeof e.promptTimeoutTimer&&"unref"in e.promptTimeoutTimer&&e.promptTimeoutTimer.unref())}adoptBackgroundMutation(e,t){const s=this.sessionMutationCoordinator.attachOrStartExternal({sessionKey:e.gatewaySessionKey,label:"background_run"},"acp",t);return s.ok?("owner"===s.ownership&&(e.writeGateToken=s.token),s):(this.logger.error?.(`[bridge-core] background mutation rejected sessionKey=${e.gatewaySessionKey} runId=${t} reason=${s.reason}`),s)}cleanupPrompt(e){this.state.clearPromptTimeout(e),this.state.clearStreamIdleTimer(e);const t=e.currentRunId;if(t){this.state.promptsByRunId.delete(t),this.state.runIdToCronJobId.delete(t),this.state.markRunCompleted(t);const e=`tc_${t}_`;for(const[t,s]of this.state.toolCallIdMap)s.startsWith(e)&&this.state.toolCallIdMap.delete(t)}e.gatewayRequestId&&this.state.promptsByGatewayRequestId.delete(e.gatewayRequestId),e.pendingPromptId=void 0,e.promptDone=void 0,e.currentRunId=void 0,e.gatewayRequestId=void 0,e.assistantStreamStartedAt=void 0,e.gatewayMethod=void 0,e.mutationTerminalOverride=void 0,e.mutationTerminalCommitting=void 0,e.assistantMq?.close(),e.assistantMq=void 0,e.pendingUserTurnUpdates=void 0}async completePrompt(e,t){"cancelled"!==t?await this.terminateRunOutput(e,{kind:"complete",stopReason:t}):await this.terminateRunOutput(e,{kind:"cancelled",stopReason:"cancelled"})}async interruptPrompt(e,t){await this.terminateRunOutput(e,{kind:"failed",mode:"interrupted",message:t.publicMessage,errorCode:t.errorCode,classification:t})}async failPrompt(e,t,s){await this.terminateRunOutput(e,{kind:"failed",mode:"error",code:t,message:s})}cleanupAllPendingPrompts(){for(const e of this.state.sessions.values())e.writeGateToken?(this.sessionMutationCoordinator.requireRecovery(e.writeGateToken,"gateway_disconnected","disconnect"),this.observeLifecycle(this.terminateRunOutput(e,{kind:"failed",mode:"interrupted",message:SESSION_RECOVERY_REQUIRED_MESSAGE,errorCode:SESSION_RECOVERY_REQUIRED_CODE}),"gateway_disconnect_recovery",e)):this.observeLifecycle(this.interruptPrompt(e,classifyAgentRuntimeFailure({raw:Errors.GatewayDisconnected.message,source:"live_gateway",runtimeKind:"openclaw"})),"gateway_disconnect_interrupt",e)}handleGatewayResponse(e){const t=e.id;if(!t)return;const s=this.state.promptsByGatewayRequestId.get(t);if(!s||s.promptDone)return;if(!e.ok){const i=e.error,n=i?.message??"gateway returned error";this.logger.warn?.(`[bridge-core] handleGatewayResponse: gateway rejected prompt gatewayRequestId=${t} error=${n}`);const r=classifyAgentRuntimeFailure({raw:n,source:"live_gateway",runtimeKind:"openclaw"});return void("interrupted"===r.kind?this.observeLifecycle(this.interruptPrompt(s,r),"gateway_response_interrupt",s):(this.logRuntimeFailure(s,r),this.observeLifecycle(this.failPrompt(s,Errors.SendFailed.rpcCode,r.publicMessage),"gateway_response_failure",s)))}const i=e.payload,n=i?.runId;if(n)s.currentRunId=n,this.sessionMutationCoordinator.bindRun(s.writeGateToken,n),this.observeActiveRun(s),s.assistantStreamStartedAt=Date.now(),this.state.promptsByRunId.set(n,s),openRunOutputRoute(this.runOutputRouter,s,n,["acp"]),this.logger.debug?.(`[bridge-core] handleGatewayResponse: bound runId=${n} gatewayRequestId=${t} sessionId=${s.sessionId}`);else{const e=resolveTurnId(s);e&&openRunOutputRoute(this.runOutputRouter,s,e,["acp"]),this.logger.debug?.(`[bridge-core] handleGatewayResponse: no runId in payload gatewayRequestId=${t} sessionId=${s.sessionId}`)}}async handleCronRequest(e){const t=this.getGateway();if(!t?.isConnected)return void this.transport.sendAcpError(e.id,Errors.GatewayNotConnected);const s=e.method.replace("/","."),i={...e.params??{}};["cron.add","cron.update","cron.remove","cron.run","cron.runs"].includes(s)&&(i.__beeosCronIngress="acp"),"cron.update"===s&&(i.patch=i.patch&&"object"==typeof i.patch?{...i.patch}:{});try{const t=await this.dispatchLegacyCronCapability(s,i),n=await this.mapCronResultForAcp(s,t);this.transport.sendResult(e.id,n??{}),"cron.add"!==s&&"cron.update"!==s&&"cron.remove"!==s||this.refreshCronIsolatedSessionKeys()}catch(t){const i=t instanceof Error?t.message:"cron request failed";this.logger.warn?.(`[bridge-core] ${s} failed: ${i}`),this.transport.sendAcpError(e.id,Errors.SendFailed,{message:i})}}async mapCronResultForAcp(e,t){if(!t||"object"!=typeof t)return t;const s=t;await this.cronOverrides.snapshot();const i="cron.list"===e||"cron.status"===e,n=e=>{if(!e||"object"!=typeof e)return e;const t=e,s=t.id;if("string"!=typeof s)return e;const n=this.cronOverrides.localId(s)??s,r=this.cronChannelCache.get(n),o=r?encodeBeeosChannelTag("string"==typeof t.description?t.description:void 0,r):t.description,a=this.cronOverrides.get(n);return{...t,id:n,...void 0===o?{}:{description:o},...i&&"number"==typeof a?{_beeos_meta:{maxDurationMs:a}}:{}}};if(Array.isArray(s.jobs))return{...s,jobs:s.jobs.map(n)};if(s.jobs&&"object"==typeof s.jobs){const e=s.jobs,t={};for(const[s,i]of Object.entries(e))t[s]=n(i);return{...s,jobs:t}}return Array.isArray(s.runs)?{...s,runs:s.runs.map(e=>{if(!e||"object"!=typeof e)return e;const t=e,s="string"==typeof t.jobId?this.cronOverrides.localId(t.jobId)??t.jobId:t.jobId;return{...t,jobId:s}})}:"cron.add"===e||"cron.update"===e?n(t):t}async allocateCronChannel(e){if(!this.agentFetch||!this.agentGatewayUrl)return void this.logger.debug?.("[bridge-core] agentFetch/agentGatewayUrl unavailable, skip channel alloc");const t=this.getAuthoritativeInstanceId?.()?.trim();if(!t?.startsWith("inst_"))return void this.logger.warn?.("[bridge-core] no authoritative inst_* instance id resolved yet, skip channel alloc");const s=this.config.gateway.agentId||this.getDefaultAgentId(),i=this.resolvePlatformAgentId(s)?.trim();if(!i)return void this.logger.warn?.(`[bridge-core] no platform agent id for localAgentId=${s}, skip channel alloc`);const n={source:"cron",job_name:e,protocol:"beeos-conversation",target_agent_id:i,title:e};try{const e=`${this.agentGatewayUrl}/api/v1/channels`,s=await this.agentFetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({participants:[t,"service:cron"],metadata:n})});if(!s.ok)return void this.logger.warn?.(`[bridge-core] channel alloc failed: ${s.status}`);return(await s.json()).id}catch(e){return void this.logger.warn?.(`[bridge-core] channel alloc error: ${e instanceof Error?e.message:e}`)}}setCronChannelCache(e,t){if(this.cronChannelCache.size>=AcpGatewayBridgeCore.CRON_CHANNEL_CACHE_MAX){const e=this.cronChannelCache.keys().next().value;void 0!==e&&this.cronChannelCache.delete(e)}this.cronChannelCache.set(e,t)}async refreshCronIsolatedSessionKeys(){const e=this.getGateway();if(e?.isConnected)try{const t=await e.request("cron.list",{}),s=t?.jobs;if(!Array.isArray(s))return;const i=new Set,n=[];for(const e of s){if(!e||"object"!=typeof e)continue;const t=e;if("string"==typeof t.id&&n.push(t.id),"isolated"!==t.sessionTarget)continue;const s=t.sessionKey;"string"==typeof s&&s.length>0&&i.add(s)}this.cronIsolatedSessionKeys=i,await this.cronOverrides.reconcile(n)}catch(e){this.logger.debug?.(`[bridge-core] refreshCronIsolatedSessionKeys failed: ${e instanceof Error?e.message:e}`)}}}async function fetchWithRetry(e,t={}){const s=t.maxRetries??3;let i;for(let n=0;n<=s;n++){const r=await fetch(e,{signal:AbortSignal.timeout(12e4),redirect:"follow"});if(r.ok)return Buffer.from(await r.arrayBuffer());if(429===r.status||r.status>=500){const o=Number(r.headers.get("retry-after"))||0,a=1e3*Math.max(o,2**n);if(i=new Error(`HTTP ${r.status} from ${e}`),n<s){t.logger?.info?.(`[bridge-core] Download ${r.status}, retry ${n+1}/${s} in ${(a/1e3).toFixed(0)}s`),await new Promise(e=>setTimeout(e,a));continue}}throw new Error(`Failed to download ${e}: HTTP ${r.status}`)}throw i??new Error(`Download failed after retries: ${e}`)}const SKILL_ROOT_MARKERS=["SKILL.md","skill.md","SKILL.MD"];function normalizeSkillArtifactPath(e){const t=e.replaceAll("\\","/").replace(/^\.\//,"");if(!t||t.startsWith("/")||t.split("/").includes(".."))throw new RuntimeCommandError("INVALID_PARAMS",`Invalid skill artifact path: ${e}`);return t}function parseSkillZip(e){if(e.byteLength>MAX_ZIP_BYTES)throw new RuntimeCommandError("INVALID_PARAMS","Skill archive too large");const t=new AdmZip(e).getEntries().filter(e=>!e.isDirectory).map(e=>({name:normalizeSkillArtifactPath(e.entryName),bytes:e.getData()})).filter(e=>!e.name.split("/").includes("__MACOSX"));if(0===t.length)throw new RuntimeCommandError("INVALID_PARAMS","Skill archive is empty");const s=1===new Set(t.map(e=>e.name.split("/")[0])).size&&t.every(e=>e.name.includes("/")),i=new Map;for(const e of t){const t=s?e.name.slice(e.name.indexOf("/")+1):e.name;if(!t||i.has(t))throw new RuntimeCommandError("INVALID_PARAMS",`Duplicate skill path: ${t}`);i.set(t,e.bytes)}return i}async function resolveSkillRoot(e){for(const t of SKILL_ROOT_MARKERS)try{return await stat(join(e,t)),e}catch{}const t=(await readdir(e,{withFileTypes:!0})).filter(e=>e.isDirectory()&&!e.name.startsWith(".")&&"__MACOSX"!==e.name);if(1===t.length){const s=join(e,t[0].name);for(const e of SKILL_ROOT_MARKERS)try{return await stat(join(s,e)),s}catch{}return s}return e}async function extractZipToDir(e,t){const s=new AdmZip(e),i=join(t,".beeos-extract-tmp-"+Date.now());try{await mkdir(i,{recursive:!0}),s.extractAllTo(i,!0);const e=await resolveSkillRoot(i),n=await readdir(e,{withFileTypes:!0});let r=0;for(const s of n){if(s.name.startsWith(".beeos-extract-tmp")||"__MACOSX"===s.name)continue;const i=join(e,s.name),n=join(t,s.name);await rename(i,n).catch(async()=>{await cp(i,n,{recursive:!0})}),r++}if(0===r)throw new Error("Zip archive contained no extractable files")}finally{await rm(i,{recursive:!0,force:!0}).catch(()=>{})}}const VALID_SLUG_PATTERN=/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,NON_ASCII_PATTERN=/[^\x00-\x7F]/;function validateSkillSlug(e){const t=e.trim();if(!t||t.includes("/")||t.includes("\\")||t.includes(".."))throw new Error(`Invalid skill slug: ${e}`);if(NON_ASCII_PATTERN.test(t)||!VALID_SLUG_PATTERN.test(t))throw new Error(`Invalid skill slug: ${e}`);return t}function safeDirName(e){const t=e.trim();return t?t.replaceAll("/","__").replaceAll("\\","__"):t}function extractMcpCredentialMaterial(e){const t=mcpStringMap(e.headers,"MCP headers"),s=mcpStringMap(e.env,"MCP environment");if(!(t&&0!==Object.keys(t).length||s&&0!==Object.keys(s).length))return;const i=e.transport??("string"==typeof e.command?"stdio":"string"==typeof e.url?"streamable-http":void 0);if("stdio"===i&&t&&Object.keys(t).length)throw new Error("MCP stdio config cannot include headers");if("stdio"!==i&&s&&Object.keys(s).length)throw new Error("MCP HTTP config cannot include env");return{...t&&Object.keys(t).length?{headers:t}:{},...s&&Object.keys(s).length?{env:s}:{}}}function mcpStringMap(e,t){if(void 0!==e){if(!e||"object"!=typeof e||Array.isArray(e)||Object.entries(e).some(([e,t])=>!e||/[\u0000-\u001f\u007f]/.test(e)||"string"!=typeof t))throw new Error(`${t} invalid`);return{...e}}}function requireCredentialReferencePort(e){if(!e)throw new Error("MCP credential reference vault is unavailable");return e}function resolveSkillInstallDir(e,t){const s=safeDirName(t),i=join(e,s),n=resolve(e),r=resolve(i),o=relative(n,r);if(!o||".."===o||o.startsWith(`..${sep}`)||path.isAbsolute(o))throw new Error("Invalid skill target path");return i}
1
+ import{randomUUID,createHash}from"node:crypto";import{mkdir,readFile,rm,stat,readdir,rename,cp}from"node:fs/promises";import path from"node:path";import{join,resolve,relative,sep}from"node:path";import{homedir}from"node:os";import AdmZip from"adm-zip";import{sanitizePromptPayload}from"../message-sanitization/prompt-payload-sanitizer.js";import{previewSanitizedText,stripTransportMetadata}from"../message-sanitization/transport-metadata-sanitizer.js";import{isHeartbeatPromptText,isCronPromptText,extractCronJobId,classifyHeartbeatFilter}from"../message-sanitization/message-classifier.js";import{createCronOverridesStore,createInMemoryCronOverridesStore}from"../cron-overrides.js";import{CRON_DURATION_FLOOR_MS}from"../config.js";import{CANVAS_TERM_MAPPING,CANVAS_UPDATE_HINT,buildActiveSurfacesContext}from"../canvas/tools.js";import{parseMentionsParam,buildMentionInstruction}from"../delegation/mention-resolver.js";import{EXTERNAL_PROTOCOL_TAGS}from"./external-protocol-tags.js";import{extractPromptBlocks,toGatewayPrompt,resolveSlashCommandName as resolveSlashCmd}from"./prompt-converter.js";import{extractBeeosUserTextFromHistoryContent}from"./beeos-user-envelope.js";import{loadHistory,loadHistoryPage,groupIntoTurns,splitUserReplayBlocks}from"./history-replay.js";import{classifyHistoricalAssistantTurnCompletion}from"./history-turn-completion-policy.js";import{LocalSessionHistory}from"./local-session-history.js";import{reconcileMissingArtifacts}from"./missing-artifacts-reconciler.js";import{AcpRunOutputChannel}from"./acp-run-output-channel.js";import{buildResolutionPlan,resolveResources,applyResolutions}from"./prompt-resource-resolver.js";import{readEnrichmentCache,readUploadToolResult}from"../upload/result-cache.js";import{buildToolResultSessionContentBlocks}from"./tool-result-blocks.js";import{prefersChatFileArtifacts}from"./file-artifact-presenter.js";import{buildToolCallPayload,buildToolCallUpdatePayload}from"./tool-call-payload-builder.js";import{Errors}from"../errors/index.js";import{classifyAgentRuntimeFailure,INTERRUPTED_PUBLIC_MESSAGE}from"../runtime/agent-runtime-failure-classifier.js";import{isPlaceholderTitle}from"../runtime/conversation-title.js";import{SESSION_RECOVERY_REQUIRED_CODE,SESSION_RECOVERY_REQUIRED_MESSAGE}from"../runtime/session-mutation-coordinator.js";import{observeLifecyclePromise}from"../runtime/lifecycle-promise-observer.js";import{RunOutputRouter}from"../runtime/run-output-router.js";import{AgentMappingRegistry}from"../runtime/agent-mapping-registry.js";import{RuntimeSessionNewRejectedError}from"../runtime/runtime-rpc-consumer.js";import{normalizeRuntimeAgentLocalKey,OpenClawRuntimeCommandDispatcher,RuntimeCommandError}from"../runtime/runtime-command-dispatcher.js";import{FileRuntimeAgentIdentityRegistry}from"../runtime/runtime-agent-identity-registry.js";import{FileRuntimeLeasePort}from"../runtime/runtime-operation-lease.js";import{FileRuntimeCommandJournal,InMemoryRuntimeCommandJournal}from"../runtime/runtime-command-journal.js";import{OpenClawRuntimeCapabilities}from"../runtime/openclaw-runtime-capabilities.js";import{RuntimeCanvasService}from"../canvas/runtime-canvas-service.js";import{RuntimeSkillsService}from"../runtime/runtime-skills-service.js";import{RuntimeCronService,encodeBeeosChannelTag}from"../runtime/runtime-cron-service.js";export{BEEOS_CHANNEL_TAG_RE,decodeBeeosChannelId,encodeBeeosChannelTag,stripBeeosChannelTag}from"../runtime/runtime-cron-service.js";import{openClawKeyMatchesConversation,openClawSessionKeysFromList,resolveLiveOpenClawSessionKey}from"../runtime/openclaw-session-key-resolution.js";import{LocalConversationHistoryBoundary,OpenClawGatewaySessionMetadataPort,OpenClawSessionMetadataService,RuntimeSessionControlService}from"../runtime/runtime-session-control-service.js";import{pageRuntimeSessions,parseRuntimeSessionListPage}from"../runtime/runtime-session-list-policy.js";import{assertTrustedCoreExecutionAttempt,createAcpCoreExecutionAttempt}from"../runtime/core-execution-attempt-context.js";import{CompositeActiveRunPort}from"../runtime/composite-active-run-port.js";import{resolveChannelFromSessionKey}from"../channel-detection.js";import{resolveTurnId,recordTurnIdForRole,recordToolCallTurnForUpdate}from"./gateway-events.js";import{emitToolResultProjectionForTurn}from"./tool-result-emitter.js";import{extractPersistedToolResultEnvelope,findPersistedToolResultEnvelopeByMessageId}from"./tool-result-transcript.js";import{projectPersistedToolResultForReplay}from"./tool-result-projection.js";import{openRunOutputRoute}from"./run-output-route.js";import{McpService}from"./mcp-service.js";import{McpDesiredStateStore,normalizeMcpPublicServerConfig}from"./mcp-desired-state-store.js";import{requireSecretFreeMcpServerName}from"./mcp-public-config-security.js";import{McpDesiredStateController}from"./mcp-desired-state-controller.js";import{CredentialReferenceRuntimeResolver}from"./mcp-credential-reference-port.js";import{DEFAULT_PROMPT_TIMEOUT_MS}from"./types.js";import{ACP_PROTOCOL_VERSION}from"@beeos-ai/beeos-types/acp";import{canonicalizeJcs}from"@beeos-ai/beeos-types/runtime";import{MAX_ZIP_BYTES,parseAndValidateTemplatePackage,TemplatePackageError}from"./agent-template-package.js";const DEFAULT_AGENT_ID="main";export function normalizeAgentId(e){return normalizeRuntimeAgentLocalKey(e)}function narrowContentBlock(e){switch(e.type){case"text":return"string"==typeof e.text?{type:"text",text:e.text}:void 0;case"resource_link":return"string"==typeof e.uri?{type:"resource_link",uri:e.uri,...void 0!==e.name?{name:e.name}:{},...void 0!==e.mimeType?{mimeType:e.mimeType}:{}}:void 0;case"file":return{type:"file",...void 0!==e.text?{text:e.text}:{},...void 0!==e.uri?{uri:e.uri}:{},...void 0!==e.mimeType?{mimeType:e.mimeType}:{}};case"image":return{type:"image",...void 0!==e.uri?{uri:e.uri}:{},...void 0!==e.mimeType?{mimeType:e.mimeType}:{}};default:return}}const DEFAULT_MODE={id:"default",name:"Default",description:"Default agent mode"},USER_MESSAGE_PREFIX="User Message From BeeOS:",USER_MESSAGE_PREFIX_WITH_NEWLINE=`${USER_MESSAGE_PREFIX}\n`,SLASH_COMMAND_NAME_RE=/^[a-z0-9_-]+$/i,HISTORY_PAGE_DEFAULT_LIMIT=100,HISTORY_PAGE_MAX_LIMIT=1e3;function withUserMessagePrefix(e){return e.startsWith(USER_MESSAGE_PREFIX)?e:`${USER_MESSAGE_PREFIX_WITH_NEWLINE}${e}`}const BACKGROUND_SESSION_PATTERNS=[":cron:",":hook:","node-",...EXTERNAL_PROTOCOL_TAGS];function isBackgroundSessionKey(e){return BACKGROUND_SESSION_PATTERNS.some(t=>e.includes(t))}function isSystemDefaultSessionKey(e){const t=e.split(":");if(t.length<3||"agent"!==t[0])return!1;const s=t.slice(2).join(":");return"main"===s||s===t[1]}function cleanGatewayDerivedTitle(e){if(!e)return;let t=extractBeeosUserTextFromHistoryContent(e).text;return/^\[System\b/.test(t)?void 0:(t=t.trim(),t||void 0)}function cleanHistoryUserContent(e){return extractBeeosUserTextFromHistoryContent(e).text.trim()}function mergePersistedToolResultEnvelopes(e,t){if(e||t)return{toolName:e?.toolName||t?.toolName||"tool",...e?.toolCallId||t?.toolCallId?{toolCallId:e?.toolCallId??t?.toolCallId}:{},...e?.rawText??t?.rawText?{rawText:e?.rawText??t?.rawText}:{},...void 0!==e?.rawResult||void 0!==t?.rawResult?{rawResult:e?.rawResult??t?.rawResult}:{},...e?.timestamp||t?.timestamp?{timestamp:e?.timestamp??t?.timestamp}:{}}}function resolveStandaloneSlashCommandName(e,t){if(0===e.length)return;if(e.some(e=>"text"!==e.type))return;const s=t.trim();if(!s||s.includes("\n")||!s.startsWith("/"))return;const i=s.match(/^\/([^\s:]+)(?::|\s|$)/)?.[1]?.trim();return i&&!i.includes("/")&&SLASH_COMMAND_NAME_RE.test(i)?i.toLowerCase():void 0}class AcpRuntimeCommandResourceStore{fetchPackage;parsePackage;isValidationError;content=new Map;artifacts=new Map;mcpDefinitions=new Map;constructor(e,t,s){this.fetchPackage=e,this.parsePackage=t,this.isValidationError=s}registerContent(e){const t=`acp-content:sha256:${createHash("sha256").update(e).digest("hex")}`;return this.content.set(t,e),t}registerMcpDefinition(e,t){this.mcpDefinitions.set(JSON.stringify(e),structuredClone(t))}async resolveMcpDefinition(e){const t=this.mcpDefinitions.get(JSON.stringify(e));if(!t)throw new Error("ACP MCP definition was not verified for this request");return structuredClone(t)}async registerArtifact(e,t){try{const s=await this.fetchPackage(e);if(!s.ok)throw new TemplatePackageError(`failed to download package: HTTP ${s.status}`);const i=Buffer.from(await s.arrayBuffer());if(i.length>MAX_ZIP_BYTES)throw new TemplatePackageError(`package too large: ${i.length} bytes (max ${MAX_ZIP_BYTES})`);const n=createHash("sha256").update(i).digest("hex");if(t?.trim()&&t.trim().toLowerCase()!==n)throw new TemplatePackageError("package sha256 mismatch");const r=this.parsePackage(i),o=`acp-artifact:sha256:${n}`;return this.artifacts.set(o,r),{artifactRef:o,version:r.manifest?.version??"0.0.0",sha256:n,sizeBytes:String(i.length),source:"acp-verified"}}catch(e){if(e instanceof TemplatePackageError||this.isValidationError(e))throw new RuntimeCommandError("INVALID_PARAMS",`Invalid template package: ${e instanceof Error?e.message:String(e)}`);throw new RuntimeCommandError("EXECUTION_FAILED",`Apply template failed: ${e instanceof Error?e.message:String(e)}`)}}registerParsedArtifact(e){const t=createHash("sha256");for(const[s,i]of[...e.workspaceFiles].sort(([e],[t])=>e.localeCompare(t)))t.update(s).update("\0").update(i).update("\0");const s=t.digest("hex"),i=`acp-artifact:sha256:${s}`;this.artifacts.set(i,e);const n=[...e.workspaceFiles.values()].reduce((e,t)=>e+t.byteLength,0);return{artifactRef:i,version:e.manifest?.version??"0.0.0",sha256:s,sizeBytes:String(n),source:"acp-verified"}}async readContent(e){const t=this.content.get(e);if(void 0===t)throw new Error(`unknown contentRef: ${e}`);return t}async readVerifiedArtifact(e){const t=this.artifacts.get(e.artifactRef);if(!t)throw new Error(`unknown verified artifact: ${e.artifactRef}`);return t}}export const lateBoundRuntimeAgentProjectionPort=e=>({prepareCommittedMutation:async t=>{const s=e();if(!s)throw new Error("agent projection production is unavailable");return await s.prepareCommittedMutation(t)},applyCommittedMutation:async t=>{const s=e();if(!s)throw new Error("agent projection production is unavailable");return await s.applyCommittedMutation(t)},compactCommittedMutation:async t=>{const s=e();if(!s)throw new Error("agent projection production is unavailable");await s.compactCommittedMutation(t)}});function deriveSessionId(e,t){const s=createHash("sha256").update(`${t}:${e}`).digest("hex").substring(0,32);return`session:${s.slice(0,8)}-${s.slice(8,12)}-${s.slice(12,16)}-${s.slice(16,20)}-${s.slice(20,32)}`}function parseAgentIdFromKey(e){const t=e.split(":");return(t.length>=3&&"agent"===t[0]?t[1]:"")||"main"}function createSessionObject(e){const t=parseAgentIdFromKey(e.gatewayKey);return{sessionId:deriveSessionId(e.gatewayKey,e.instanceId),gatewaySessionKey:e.gatewayKey,agentId:t,platformAgentId:e.resolvePlatformAgentId?.(t),createdAt:Date.now(),chatReplayDedupKeys:new Set,cwd:e.cwd,title:e.title,channelType:resolveChannelFromSessionKey(e.gatewayKey,e.thirdPartyChannels)}}class HistoryUpdateRecorder{transport;updates=[];constructor(e){this.transport=e}sendSessionUpdate(e,t,s){const i=this.transport.materializeSessionUpdate(t,s);return null!==i&&this.updates.push(i),!0}}export class AcpGatewayBridgeCore{state;transport;config;logger;writeObs;getGateway;thirdPartyChannels;beeosFileResolver;agentFetch;agentGatewayUrl;triggerAgentSync;sessionMutationCoordinator;runtimeCommands;runtimeAgentIdentities;runtimeLeases;runtimeCommandResources;localHistoryBoundary;activeRuns;markAgentProjectionDirty;projectionObservationTimeoutMs;cronChannelCache=new Map;static CRON_CHANNEL_CACHE_MAX=256;localSessionHistory;mcp;runtimeTerminalService;runOutputRouter;agentMappings;initialized=!1;get agentMapping(){return this.agentMappings.snapshot()}cronIsolatedSessionKeys=new Set;cronOverrides;cronReady;sessionPreloadInFlight=null;sessionPreloadInFlightGeneration=null;sessionPreloadReady=Promise.resolve();resolveSessionPreloadReady=null;rejectSessionPreloadReady=null;sessionPreloadGeneration=0;sessionPreloadBarrierActive=!1;sessionPreloadFailed=!1;async setAgentMapping(e){await this.agentMappings.replace(e)}async applyAgentMapping(e){for(const t of this.state.sessions.values())!t.platformAgentId&&t.agentId&&(t.platformAgentId=e.get(t.agentId)?.id);this.sessionPreloadFailed&&this.beginGatewaySessionPreload(),this.sessionPreloadBarrierActive&&await this.preloadSessionsFromGateway()}beginGatewaySessionPreload(){this.sessionPreloadBarrierActive&&this.sessionPreloadReady.catch(()=>{}),this.sessionPreloadGeneration+=1,this.sessionPreloadBarrierActive=!0,this.sessionPreloadFailed=!1,this.sessionPreloadReady=new Promise((e,t)=>{this.resolveSessionPreloadReady=e,this.rejectSessionPreloadReady=t}),this.sessionPreloadReady.catch(()=>{})}failGatewaySessionPreload(){this.sessionPreloadBarrierActive&&(this.sessionPreloadBarrierActive=!1,this.sessionPreloadFailed=!0,this.rejectSessionPreloadReady?.(new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway disconnected before session hydration completed")),this.resolveSessionPreloadReady=null,this.rejectSessionPreloadReady=null)}async waitForGatewaySessionPreload(){await this.sessionPreloadReady}onceAgentMappingReady(e){this.agentMappings.onceReady(e)}onAgentMappingUpdated(e){const t=this.agentMappings.snapshot();t&&t.size>0&&e(new Map(t)),this.agentMappings.subscribe(t=>e(new Map(t)))}markAgentSyncDone(){this.agentMappings.markSynchronized()}waitForAgentMapping(e=15e3){return this.agentMappings.waitForSynchronization(e)}handleTerminalRequest=null;handleTerminalNotification=null;onRunComplete=null;onRunFailed=null;onRunEnded=null;handleSlashCommand=null;onPromptStart=null;instanceId;getAuthoritativeInstanceId;mcpCredentialReferences;constructor(e){this.state=e.state,this.transport=e.transport,this.config=e.config,this.logger=e.logger,this.writeObs=e.writeObs,this.getGateway=e.getGateway,this.instanceId=e.instanceId||"unknown",this.getAuthoritativeInstanceId=e.getAuthoritativeInstanceId,this.mcpCredentialReferences=e.mcpCredentialReferences,this.thirdPartyChannels=e.thirdPartyChannels??new Set,this.beeosFileResolver=e.beeosFileResolver,this.agentFetch=e.agentFetch,this.agentGatewayUrl=e.agentGatewayUrl,this.triggerAgentSync=e.triggerAgentSync,this.markAgentProjectionDirty=e.markAgentProjectionDirty,this.projectionObservationTimeoutMs=e.projectionObservationTimeoutMs??5e3,this.sessionMutationCoordinator=e.sessionMutationCoordinator,this.agentMappings=e.agentMappings??new AgentMappingRegistry,this.agentMappings.subscribe(e=>this.applyAgentMapping(e)),this.runOutputRouter=e.runOutputRouter??new RunOutputRouter([new AcpRunOutputChannel({state:this.state,transport:this.transport})],{lifecycle:{terminal:(e,t)=>this.commitRunOutputTerminal(e,t)}}),this.runtimeAgentIdentities=e.runtimeAgentIdentities??new FileRuntimeAgentIdentityRegistry(join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos","runtime-agent-identities.json")),this.runtimeLeases=e.runtimeLeases??new FileRuntimeLeasePort(join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos","runtime-operation-leases.json")),this.runtimeCommandResources=e.runtimeCommandResources??new AcpRuntimeCommandResourceStore(e.agentFetch??((e,t)=>fetch(e,t)),e.runtimeTemplatePackageParser?.parse??parseAndValidateTemplatePackage,e.runtimeTemplatePackageParser?.isValidationError??(e=>e instanceof TemplatePackageError));const t=e.runtimeCommandJournal??(e.runtimeLeases||process.env.VITEST?new InMemoryRuntimeCommandJournal:new FileRuntimeCommandJournal(join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos","runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:e=>this.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=threshold error=${e instanceof Error?e.message:String(e)}`)}));this.localSessionHistory=new LocalSessionHistory({logger:this.logger}),this.cronOverrides=e.cronOverrides??(process.env.VITEST?createInMemoryCronOverridesStore():createCronOverridesStore(this.logger)),this.cronReady=this.cronOverrides.initialize(),this.cronReady.catch(()=>{});const s=e.mcpCredentialReferences?new CredentialReferenceRuntimeResolver(e.mcpCredentialReferences):void 0,i=e.mcpProjection?new McpDesiredStateController(new McpDesiredStateStore(e.mcpDesiredStatePath??join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos","mcp-desired-state.json")),e.mcpProjection,s,e.mcpCredentialReferences,t,e.runtimeReceiptHistory,this.logger):void 0;this.mcp=new McpService({transport:this.transport,logger:this.logger,state:this.state,...i?{desiredState:i}:{}}),this.localHistoryBoundary=new LocalConversationHistoryBoundary(e=>{const t=this.state.sessions.get(e);t&&(t.chatReplayDedupKeys.clear(),this.onSessionCleared?.(t.sessionId,t.gatewaySessionKey))}),this.activeRuns=e.activeRuns??new CompositeActiveRunPort;const n=new RuntimeCronService({getGateway:e.getGateway,overrides:this.cronOverrides,durationFloorMs:CRON_DURATION_FLOOR_MS,durationCapMs:this.config.cronMaxDurationCapMs,channels:{allocate:async e=>await this.allocateCronChannel(e),lookup:e=>this.cronChannelCache.get(e),remember:(e,t)=>this.setCronChannelCache(e,t),forget:e=>{this.cronChannelCache.delete(e)}}}),r=new RuntimeSessionControlService({convergeRun:async({runtimeSessionKey:e})=>{const t=this.state.sessionsByGatewayKey.get(e),s=(new Date).toISOString();if(!t||t.promptDone&&void 0===t.pendingPromptId&&void 0===t.currentRunId)return{state:"no_active_run",quiescence:{state:"verified",observedAt:s}};const i=this.getGateway();if(!i?.isConnected)return{state:"recovery_required",reason:"gateway_disconnected"};if(!t.writeGateToken||!t.currentRunId)return{state:"recovery_required",reason:"run_conflict",runtimeRunId:t.currentRunId??"unknown"};const n=t.currentRunId,r=await this.sessionMutationCoordinator.requestCancel(t.writeGateToken,i);if(!r.ok)return{state:"recovery_required",reason:"terminal_not_observed",runtimeRunId:n};const o=await r.terminalObservation;return o.ok?{state:"terminal_observed",runtimeRunId:n,abortObservation:r.acknowledged?{state:"target_acknowledged",aborted:!0,runIds:[n],targetRunAcknowledged:!0}:{state:"already_stopped",aborted:!1,runIds:[]},terminalState:"cancelled",quiescence:{state:"verified",observedAt:(new Date).toISOString()}}:{state:"recovery_required",reason:"quiescence_failed"===o.recovery.reason?"quiescence_failed":"quiescence_timeout"===o.recovery.reason?"quiescence_timeout":"terminal_not_observed",runtimeRunId:n}},reset:async e=>{const t=this.getGateway();if(!t?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");await t.sessionsReset(e)}},new OpenClawSessionMetadataService(new OpenClawGatewaySessionMetadataPort(e.getGateway,(e,t)=>{const s=this.state.sessionsByGatewayKey.get(e);if(!s)throw new RuntimeCommandError("INVALID_PARAMS",`setModel projection requires a live local session for key=${e}`);void 0===t.modelOverrideId?delete s.modelOverrideId:s.modelOverrideId=t.modelOverrideId,s.effectiveModelId=t.effectiveModelId,void 0===t.modelProvider?delete s.modelProvider:s.modelProvider=t.modelProvider},{resolveLiveKey:e=>resolveLiveOpenClawSessionKey(e,this.state.sessions.values()),waitForHydration:async()=>await this.waitForGatewaySessionPreload(),reconcileMissingLiveKey:async(e,t)=>await this.reconcileMissingGatewaySession(e,t),materializeMissingModelKey:async(e,t)=>await this.materializeMissingModelSessionKey(e,t),registerMaterializedModelKey:e=>this.registerMaterializedModelSession(e)})),this.activeRuns),o=e.getAgentProjection?lateBoundRuntimeAgentProjectionPort(e.getAgentProjection):void 0,a=e.runtimeCommandDispatcher??new OpenClawRuntimeCommandDispatcher({getGateway:e.getGateway,resources:this.runtimeCommandResources,identities:this.runtimeAgentIdentities,leases:this.runtimeLeases,journal:t,logger:e.logger,...o?{projection:o}:{},...e.templateOriginReporter?{templateOriginReporter:e.templateOriginReporter}:{},...e.waitForChatIdle?{waitForChatIdle:e.waitForChatIdle}:{}});this.runtimeCommands=a,a instanceof OpenClawRuntimeCommandDispatcher&&a.setCapabilities(new OpenClawRuntimeCapabilities({getGateway:e.getGateway,identities:this.runtimeAgentIdentities,sessions:r,skills:new RuntimeSkillsService({getGateway:e.getGateway,resources:this.runtimeCommandResources,getOpenclawHome:()=>this.getOpenclawHome()}),mcp:this.mcp,terminal:{execute:async(e,t)=>{if(!this.runtimeTerminalService)throw new RuntimeCommandError("INVALID_PARAMS","terminal canonical delegate is not configured");return await this.runtimeTerminalService.execute(e,t)}},canvas:new RuntimeCanvasService({apply:async(e,t)=>{await this.applyCanonicalCanvasControl(e,t)}}),cron:n})),this.transport.setSessionUpdateObserver((e,t,s)=>{const i=s?.turnId;if(!i)return;const n=this.state.sessions.get(e);n&&(recordTurnIdForRole(n,t,i),recordToolCallTurnForUpdate(n,t,i))})}setRuntimeTerminalService(e){this.runtimeTerminalService=e}setRuntimeReceiptHistoryPort(e){this.mcp.setRuntimeReceiptHistoryPort(e)}getRuntimeCommandDispatcher(){return this.runtimeCommands}async startMcp(){await this.mcp.start()}async stopMcp(){await this.mcp.stop()}nudgeMcpNativeProjection(){this.mcp.nudgeNativeProjection()}observeActiveRun(e){const t=e.currentRunId,s=e.writeGateToken,i=this.sessionMutationCoordinator.snapshot(e.gatewaySessionKey),n=void 0===e.pendingPromptId?e.gatewayRequestId??t:String(e.pendingPromptId);if(!t||!i||!n)return;const r={runtimeConversationUid:e.sessionId,runtimeSessionKey:e.gatewaySessionKey,historyGeneration:this.localHistoryBoundary.currentGeneration(e.sessionId),targetMessageId:n,runtimeRunId:t,activeRunRegistrationId:`acp:${i.operationId}`};return this.activeRuns.register(r,async()=>{const n=this.getGateway();if(!n?.isConnected||e.currentRunId!==t)return"recovery_required";const r=s?.operationId===i.operationId?await this.sessionMutationCoordinator.requestCancel(s,n):await this.sessionMutationCoordinator.requestCancelForSession(e.gatewaySessionKey,n);return r.ok?"accepted":!("reason"in r)||"terminal_already_observed"!==r.reason&&"no_active_mutation"!==r.reason?"recovery_required":"already_terminal"}),e.activeRunBinding=r,r}getActiveRunAuthority(){return{activeRuns:this.activeRuns,historyGeneration:e=>this.localHistoryBoundary.currentGeneration(e)}}resolvePlatformAgentId(e){return this.agentMapping?.get(e)?.id}resolveLocalAgentId(e){if(this.agentMapping&&e)for(const[t,s]of this.agentMapping)if(s.id===e)return t}getDefaultAgentId(){return this.agentMapping&&this.agentMapping.size>0?this.agentMapping.keys().next().value??"main":"main"}registerSession(e,t={}){const{ensureVerbose:s=!0}=t;this.state.sessions.has(e.sessionId);this.state.sessions.set(e.sessionId,e),this.state.sessionsByGatewayKey.set(e.gatewaySessionKey,e),s&&this.ensureVerboseFull(e),this.subscribeSessionMessages(e)}async ensureVerboseFull(e){if(e.verbosePatched)return;const t=this.getGateway();if(t?.isConnected){e.verbosePatched=!0;try{await t.sessionsPatch(e.gatewaySessionKey,{verboseLevel:"full"})}catch(t){e.verbosePatched=!1,this.logger.debug?.(`[bridge-core] ensureVerboseFull failed: ${t instanceof Error?t.message:t}`)}}}ensureSessionForKey(e){const t=this.state.findSessionByKey(e);if(t)return t;const s=createSessionObject({gatewayKey:e,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels});return this.registerSession(s),this.logger.debug?.(`[bridge-core] ensureSessionForKey: lazily created session sid=${s.sessionId} key=${e}`),this.onSessionLoaded?.(s.sessionId,s.gatewaySessionKey,s.platformAgentId),s}async preloadSessionsFromGateway(){const e=this.sessionPreloadGeneration;if(this.sessionPreloadInFlight&&this.sessionPreloadInFlightGeneration===e)return await this.sessionPreloadInFlight;const t=this.preloadGatewaySessionIndex(e);this.sessionPreloadInFlight=t,this.sessionPreloadInFlightGeneration=e;try{await t,e===this.sessionPreloadGeneration&&this.sessionPreloadBarrierActive&&(this.sessionPreloadBarrierActive=!1,this.resolveSessionPreloadReady?.(),this.resolveSessionPreloadReady=null,this.rejectSessionPreloadReady=null)}catch(t){throw e===this.sessionPreloadGeneration&&this.sessionPreloadBarrierActive&&(this.sessionPreloadBarrierActive=!1,this.sessionPreloadFailed=!0,this.rejectSessionPreloadReady?.(t instanceof Error?t:new Error("session preload failed")),this.resolveSessionPreloadReady=null,this.rejectSessionPreloadReady=null),t}finally{this.sessionPreloadInFlight===t&&(this.sessionPreloadInFlight=null,this.sessionPreloadInFlightGeneration=null)}}async preloadGatewaySessionIndex(e){const t=this.getGateway();if(this.logSessionAuthority("session_preload_start",{generation:e,reason:t?.isConnected?"gateway_connected":"gateway_not_connected"}),!t?.isConnected)throw this.logSessionAuthority("session_preload_failure",{generation:e,reason:"gateway_not_connected"}),new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected for session hydration");try{const s=await t.sessionsList(),i=this.mergeGatewaySessions(s);this.logSessionAuthority("session_preload_success",{generation:e,loaded:i,reason:"authoritative_sessions_list"})}catch(t){throw this.logSessionAuthority("session_preload_failure",{generation:e,reason:t instanceof RuntimeCommandError?t.code:"sessions_list_failed"}),t}}mergeGatewaySessions(e,t){if(!e||"object"!=typeof e)return 0;const s=e.sessions;if(!Array.isArray(s))return 0;let i=0;for(const e of s){if(!e||"object"!=typeof e||Array.isArray(e))continue;const s=e,n=s.sessionKey??s.key??s.id??s.name;if("string"!=typeof n||!n.trim()||n.trim()!==n||t&&!t(n)||this.state.findSessionByKey(n))continue;const r=s.label||s.derivedTitle||s.title||void 0,o=r&&!isPlaceholderTitle(r)?r:void 0,a=createSessionObject({gatewayKey:n,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels,title:o});this.registerSession(a,{ensureVerbose:!1}),this.onSessionLoaded?.(a.sessionId,a.gatewaySessionKey,a.platformAgentId),i+=1}return i}async reconcileMissingGatewaySession(e,t){await this.assertSessionReconcileFence(t);const s=this.getGateway();if(!s?.isConnected)throw this.logSessionAuthority("session_reconcile_decision",{reason:"gateway_not_connected",runtimeSessionKeyHash:this.sessionKeyHash(e)}),new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected for session reconciliation");const i=await s.sessionsList();await this.assertSessionReconcileFence(t);const n=!e.startsWith("agent:"),r=[...openClawSessionKeysFromList(i)].filter(t=>t===e?!n:openClawKeyMatchesConversation(t,e));if(this.logSessionAuthority("session_reconcile_decision",{reason:r.length?"exact_candidates":"no_exact_candidate",runtimeSessionKeyHash:this.sessionKeyHash(e),candidateHashes:r.map(e=>this.sessionKeyHash(e))}),!r.length)return;const o=new Set(r),a=this.mergeGatewaySessions(i,e=>o.has(e));this.logSessionAuthority("session_reconcile_decision",{reason:"reconciled_exact_candidates",runtimeSessionKeyHash:this.sessionKeyHash(e),loaded:a,candidateHashes:r.map(e=>this.sessionKeyHash(e))})}async materializeMissingModelSessionKey(e,t){await this.assertSessionReconcileFence(t);const s=t.attempt,i=s.verifiedMessageGrant?.claims,n=i?.command;if("service"!==s.origin||!i||"conversation"!==i.scope||"session/set_model"!==i.method||"session.setModel"!==n?.name||"conversation"!==n.context.scope||i.runtimeSessionKey!==e||i.runtimeConversationUid!==e||n.context.conversation.runtimeSessionKey!==e||n.context.conversation.runtimeConversationUid!==i.runtimeConversationUid)throw this.logSessionAuthority("model_intent_materialization_rejected",{reason:"verified_service_target_required",runtimeSessionKeyHash:this.sessionKeyHash(e)}),new RuntimeCommandError("INVALID_PARAMS","model-intent session materialization requires an exact verified Service target");if(!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e))throw new RuntimeCommandError("INVALID_PARAMS","model-intent session materialization requires a canonical conversation UUID");let r;try{if(!this.runtimeAgentIdentities.resolveActiveLocalKey)throw new Error("identity resolver unavailable");r=await this.runtimeAgentIdentities.resolveActiveLocalKey(i.runtimeAgentUid)}catch{throw new RuntimeCommandError("INVALID_PARAMS","verified model-intent runtime agent is not active")}if(this.resolveLocalAgentId(i.platformAgentId)!==r)throw new RuntimeCommandError("INVALID_PARAMS","verified model-intent agent does not match the active platform projection");const o=`agent:${r}:beeos-conversation:ctx-${e}`,a=this.getGateway();if(!a?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected for model-intent session materialization");const l=await a.sessionsList();await this.assertSessionReconcileFence(t);const d=[...openClawSessionKeysFromList(l)].filter(t=>openClawKeyMatchesConversation(t,e));if(d.length){if(1===d.length&&d[0]===o)return this.mergeGatewaySessions(l,e=>e===o),o;throw new RuntimeCommandError("INVALID_PARAMS","verified model-intent target conflicts with an existing OpenClaw session")}return this.logSessionAuthority("model_intent_materialization_allowed",{runtimeSessionKeyHash:this.sessionKeyHash(e),openClawSessionKeyHash:this.sessionKeyHash(o)}),o}registerMaterializedModelSession(e){this.state.findSessionByKey(e)||(this.registerSession(createSessionObject({gatewayKey:e,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels}),{ensureVerbose:!1}),this.logSessionAuthority("model_intent_materialization_committed",{openClawSessionKeyHash:this.sessionKeyHash(e)}))}async assertSessionReconcileFence(e){if(e.lease.operationId!==e.attempt.operationId||e.lease.runtimeEpoch!==e.attempt.runtimeEpoch||e.attempt.abortSignal?.aborted)throw this.logSessionAuthority("session_reconcile_decision",{reason:"operation_fence_mismatch"}),new RuntimeCommandError("INVALID_PARAMS","session reconciliation is fenced");try{await this.runtimeLeases.renew(e.lease)}catch{throw this.logSessionAuthority("session_reconcile_decision",{reason:"lease_epoch_mismatch"}),new RuntimeCommandError("INVALID_PARAMS","session reconciliation is fenced")}if(e.attempt.abortSignal?.aborted)throw this.logSessionAuthority("session_reconcile_decision",{reason:"operation_aborted"}),new RuntimeCommandError("INVALID_PARAMS","session reconciliation is fenced")}sessionKeyHash(e){return createHash("sha256").update(e).digest("hex").slice(0,16)}logSessionAuthority(e,t){this.logger.info?.(`[bridge-core] ${JSON.stringify({event:e,...t})}`)}async handleRequest(e){this.writeObs({component:"bridge-core",domain:"forward",name:`rpc.request.${e.method}`,severity:"debug",payload:{id:e.id,method:e.method}});const t=e;try{switch(t.method){case"initialize":await this.handleInitialize(t);break;case"ping":this.transport.sendResult(t.id,{pong:!0});break;case"session/new":await this.handleSessionNew(t);break;case"session/load":await this.handleSessionLoad(t);break;case"session/list":await this.handleSessionList(t);break;case"session/rename":await this.handleSessionRename(t);break;case"session/delete":await this.handleSessionDelete(t);break;case"session/clear":await this.handleSessionClear(t);break;case"session/prompt":await this.handleSessionPrompt(t);break;case"session/set_mode":await this.handleSessionSetMode(t);break;case"session/set_model":await this.handleSessionSetModel(t);break;case"agent/create":await this.handleAgentCreate(t);break;case"agent/update":await this.handleAgentUpdate(t);break;case"agent/delete":await this.handleAgentDelete(t);break;case"agent/applyTemplate":await this.handleApplyTemplate(t);break;case"skills/list":await this.handleSkillsList(t);break;case"skills/install":await this.handleSkillsInstall(t);break;case"skills/uninstall":await this.handleSkillsUninstall(t);break;case"skills/update":await this.handleSkillsUpdate(t);break;case"models/list":await this.handleModelsList(t);break;case"session/cancel":await this.handleSessionCancel(t.id,t.params);break;case"cron/list":case"cron/status":case"cron/add":case"cron/update":case"cron/remove":case"cron/run":case"cron/runs":await this.handleCronRequest(t);break;default:"agent/list"===e.method?await this.handleAgentList(e):"_beeos/session/history"===e.method?await this.handleSessionHistoryPage(e):"mcp/list"===e.method?await this.handleMcpList(e):"mcp/set"===e.method?await this.handleMcpSet(e):"mcp/unset"===e.method?await this.handleMcpUnset(e):e.method.startsWith("terminal/")&&this.handleTerminalRequest?await this.handleTerminalRequest(e):this.transport.sendAcpError(e.id,Errors.MethodNotFound,{message:`Method not found: ${e.method}`})}}catch(t){this.transport.sendAcpError(e.id,Errors.InternalError,{message:t instanceof Error?t.message:"Internal error"})}}handleCanvasNotification=null;getCanvasState=null;onSessionLoaded=null;onSkillInstalled=null;onSessionDeleted=null;onSessionCleared=null;handleNotification(e){switch(e.method){case"session/cancel":this.observeLifecycle(this.handleSessionCancel(void 0,e.params),"session_cancel_notification");break;case"canvas/toggle":case"canvas/clear":case"canvas/reference":case"canvas/dimensions":this.observeLifecycle(this.dispatchLegacyCanvasNotification(e.method,e.params),`canvas_notification_${e.method.replace("/","_")}`);break;default:e.method.startsWith("terminal/")&&this.handleTerminalNotification?this.handleTerminalNotification(e.method,e.params):this.logger.debug?.(`[bridge-core] Unknown notification: ${e.method}`)}}async handleInitialize(e){await this.cronReady,this.initialized=!0;const t=this.transport.forwardControl;!1===e.params?.forwardThinking&&(t.forwardThinking=!1),!1===e.params?.forwardToolCalls&&(t.forwardToolCalls=!1),this.transport.sendResult(e.id,{protocolVersion:ACP_PROTOCOL_VERSION,agentCapabilities:{loadSession:!0,skills:!0,multipleSessions:!0,sessionCapabilities:{"web-ssh":!!this.config.bridge.shell.enabled,canvas:!!this.config.canvasEnabled},promptCapabilities:{image:!0,audio:!1,embeddedContext:!0},mcp:{}},agentInfo:{name:"beeos-claw",title:"BeeOS Agent Bridge",version:"0.1.0"},_meta:{},authMethods:[]})}async createRuntimeSession(e,t){const s=e.operationId.trim(),i=e.platformAgentId.trim();if(!s)throw new Error("session/new operationId is required");if(assertTrustedCoreExecutionAttempt(t,{operationId:s,runtimeEpoch:t.runtimeEpoch}),"service"!==t.origin)throw new Error("session/new runtime allocation requires Service authority");const n=this.resolveLocalAgentId(i);if(!i||!n)throw new RuntimeSessionNewRejectedError("session/new target agent is unknown");const r=`agent:${n}:service:${s}`;return{sessionId:this.allocateSession({agentId:n,gatewaySessionKey:r,cwd:e.cwd??".",...void 0===e.title?{}:{title:e.title}}).sessionId,gatewaySessionKey:r,agentId:n,platformAgentId:i}}async handleSessionNew(e){const t="string"==typeof e.params?.agentId&&e.params.agentId||this.config.gateway.agentId||this.getDefaultAgentId(),s=`agent:${t}:${randomUUID()}`,i="string"==typeof e.params?.cwd?e.params.cwd:".",n=this.allocateSession({agentId:t,gatewaySessionKey:s,cwd:i});this.transport.sendResult(e.id,{sessionId:n.sessionId,modes:{availableModes:[{...DEFAULT_MODE}],currentModeId:DEFAULT_MODE.id},_meta:{sessionKey:s,agentId:n.agentId,platformAgentId:n.platformAgentId??null}}),this.fetchHistoryInBackground(s)}allocateSession(e){const t=this.state.findSessionByKey(e.gatewaySessionKey);if(t)return t;const s=createSessionObject({gatewayKey:e.gatewaySessionKey,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels,...void 0===e.cwd?{}:{cwd:e.cwd},...void 0===e.title?{}:{title:e.title}});if(s.agentId!==e.agentId)throw new Error("session/new gateway key agent mismatch");return this.registerSession(s,{ensureVerbose:!1}),s}fetchHistoryInBackground(e){const t=this.getGateway();t?.isConnected&&loadHistory(t,e,50).catch(e=>{this.logger.debug?.(`[bridge-core] Background history fetch failed: ${e instanceof Error?e.message:e}`)})}async subscribeSessionMessages(e){if(e.messageSubscriptionActive)return;const t=this.getGateway();if(t?.isConnected)try{await t.sessionsMessagesSubscribe(e.gatewaySessionKey),e.messageSubscriptionActive=!0,this.logger.debug?.(`[bridge-core] session.message subscribed sessionKey=${e.gatewaySessionKey}`)}catch(t){this.logger.debug?.(`[bridge-core] session.message subscribe failed key=${e.gatewaySessionKey}: ${t instanceof Error?t.message:t}`)}}async resubscribeAllSessionMessages(){for(const e of this.state.sessions.values())e.messageSubscriptionActive=!1,await this.subscribeSessionMessages(e)}handleSessionMessage(e){const t=e?.sessionKey,s=e?.messageId;if(!t||!s)return;const i=this.state.sessionsByGatewayKey.get(t);if(!i)return;const n=e.message?.role;if("toolResult"===n||"tool"===n)return void this.handlePersistedToolResultMessage(i,e).catch(e=>{this.logger.warn?.(`[bridge-core] persisted toolResult handling failed sessionKey=${t} messageId=${s}: ${e instanceof Error?e.message:String(e)}`)});const r="user"===n?"user":"system"===n?"system":"assistant",o=i.lastTurnIdByRole?.[r];if(!o)return void this.logger.debug?.(`[bridge-core] session.message skipped: no lastTurnId role=${r} sessionKey=${t} messageId=${s}`);const a=e.message?.timestamp,l="number"==typeof a&&a>0?new Date(a).toISOString():void 0,d={turnId:o};"number"==typeof e.messageSeq&&(d.seq=e.messageSeq),this.transport.sendSessionUpdate(i.sessionId,{sessionUpdate:"message_id_bind",role:r,messageId:s,...l?{timestamp:l}:{}},d),i.lastTurnIdByRole&&(i.lastTurnIdByRole[r]=void 0),"assistant"===r&&reconcileMissingArtifacts({localHistory:this.localSessionHistory,runOutputRouter:this.runOutputRouter,config:this.config,logger:this.logger,writeObs:this.writeObs,getCanvasState:()=>this.getCanvasState?.()??null,isSessionAlive:e=>this.state.sessions.has(e)},{session:i,turnId:o,messageId:s,runId:o})}async handlePersistedToolResultMessage(e,t){const s=t.messageId;if(!s)return;const i=extractPersistedToolResultEnvelope(t.message),n=this.extractPersistedToolResultFromHistory(e,s),r=mergePersistedToolResultEnvelopes(n,i);if(!r?.toolCallId)return void this.writeObs({component:"bridge-core",domain:"tool_result",name:"tool_result.skipped_no_tool_call_id",severity:"warn",payload:{sessionId:e.sessionId,messageId:s}});n&&this.writeObs({component:"bridge-core",domain:"tool_result",name:"tool_result.hydrated_from_history",severity:"debug",payload:{sessionId:e.sessionId,messageId:s,toolCallId:r.toolCallId,toolName:r.toolName}});const o=e.toolCallTurns?.get(r.toolCallId),a=o?.turnId??resolveTurnId(e);if(!a)return void this.writeObs({component:"bridge-core",domain:"tool_result",name:"tool_result.skipped_no_turn",severity:"warn",payload:{sessionId:e.sessionId,messageId:t.messageId,toolCallId:r.toolCallId,toolName:r.toolName}});const l=projectPersistedToolResultForReplay({toolCallId:r.toolCallId,toolName:r.toolName||o?.toolName||"tool",...r.rawText?{rawText:r.rawText}:{},...r.rawResult?{rawResult:r.rawResult}:{},...r.timestamp?{timestamp:r.timestamp}:{}});await emitToolResultProjectionForTurn({session:e,projection:l,toolCallId:r.toolCallId,turnId:a,config:this.config,logger:this.logger,writeObs:this.writeObs,getCanvasState:()=>this.getCanvasState?.()??null,sendUpdate:s=>(a&&void 0===s._turnId&&(s._turnId=a),this.transport.sendSessionUpdate(e.sessionId,s,{turnId:a,..."number"==typeof t.messageSeq?{seq:t.messageSeq}:{}}))})}extractPersistedToolResultFromHistory(e,t){if(!e.agentId)return;const s=this.localSessionHistory.readEntries({agentId:e.agentId,sessionKey:e.gatewaySessionKey,sessionId:e.sessionId});return findPersistedToolResultEnvelopeByMessageId(s,t)}async recoverSessionFromGateway(e){const t=this.getGateway();if(t?.isConnected)try{const s=await t.sessionsList();if(!s||"object"!=typeof s)return;const i=s.sessions;if(!Array.isArray(i))return;for(const t of i){const s=t,i=s.sessionKey??s.key??s.id??s.name;if(i&&"string"==typeof i&&deriveSessionId(i,this.instanceId)===e){const e=s.label||s.derivedTitle||s.title||void 0,t=e&&!isPlaceholderTitle(e)?e:void 0,n=createSessionObject({gatewayKey:i,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels,title:t});return this.registerSession(n),n}}}catch(e){this.logger.debug?.("[bridge-core] recoverSessionFromGateway failed:",e)}}async resolveOrCreateSession(e){let t=this.state.sessions.get(e);if(!t&&e.startsWith("agent:")&&(t=this.state.findSessionByKey(e)),t||e.startsWith("agent:")||(t=await this.recoverSessionFromGateway(e)),t)return{session:t,created:!1};const s=e.startsWith("agent:"),i=this.config.gateway.agentId||this.getDefaultAgentId();return t=createSessionObject({gatewayKey:s?e:`agent:${i}:${e}`,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels}),this.registerSession(t),{session:t,created:!0}}async handleSessionLoad(e){const t=e.params?.sessionId;if(!t)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId"});const{session:s}=await this.resolveOrCreateSession(t),i=s.sessionId;this.state.lastActiveSessionId=i,s.chatReplayDedupKeys.clear(),s.sessionLoadInProgress=!0;try{const e=await this.emitHistoryTurns(s);this.emitCurrentStreamSnapshot(s,e)}finally{s.sessionLoadInProgress=!1}this.transport.sendResult(e.id,{sessionId:i}),this.onSessionLoaded?.(i,s.gatewaySessionKey,s.platformAgentId),this.pushSkillsList().catch(e=>{this.logger.warn?.(`[bridge-core] pushSkillsList failed: ${e instanceof Error?e.message:e}`)})}async emitHistoryTurns(e){const t=new Set,s=e.sessionId;try{const i=this.getGateway(),n=e.assistantMq&&!e.assistantMq.closed,r=n?null:i,o=await loadHistory(r,e.gatewaySessionKey,100,{localHistory:this.localSessionHistory,agentId:e.agentId,sessionId:e.sessionId,logger:this.logger,emitObs:e=>{this.writeObs({component:"history-replay",domain:"lifecycle",name:e.name,severity:e.severity,payload:e.payload})}}),a=groupIntoTurns(o),l=a.filter(e=>"user"===e.role).length,d=a.filter(e=>"assistant"===e.role).length,c=a.filter(e=>e.toolCalls?.length).length,m=a.reduce((e,t)=>e+(t.toolCalls?.length??0),0);if(this.logger.debug?.(`[bridge-core] handleSessionLoad sid=${s} messages=${o.length} turns=${a.length} userTurns=${l} asstTurns=${d} turnsWithToolCalls=${c} totalToolCalls=${m}`),l>0&&e.pendingUserTurnUpdates&&e.pendingUserTurnUpdates.length>0&&(e.pendingUserTurnUpdates=[]),a.length>0&&isPlaceholderTitle(e.title)&&"manual"!==e.titleSource){const t=a.find(e=>{if("user"!==e.role)return!1;if("internal_system"===e.provenance?.kind)return!1;if(isCronPromptText(e.content))return!1;const t=cleanHistoryUserContent(e.content);return t.length>0&&!/^Sender\s*\(untrusted/i.test(t)});if(t?.content){const i=cleanHistoryUserContent(t.content).slice(0,64).replace(/\n/g," ").trim();if(i){e.title=i,e.titleSource="truncated";const t=this.getGateway();t?.isConnected&&t.sessionsPatch(e.gatewaySessionKey,{label:i}).catch(()=>{}),this.transport.sendSessionUpdate(s,{sessionUpdate:"title_update",title:i}),this.tryGenerateTitleFromHistory(e,s,a)}}}const u=t=>`history:${e.gatewaySessionKey}:${t}`;let p=-1;if(n)for(let e=a.length-1;e>=0;e--)if("assistant"===a[e].role){p=e;break}const{replayedToolCallIds:h,streamCompleteCount:g}=await this.replayHistoryTurnsToSink(e,a,this.transport,{mqActive:Boolean(n),lastAsstIdx:p,turnIdFor:(e,t)=>u(t)});for(const e of h)t.add(e);this.logger.debug?.(`[bridge-core] emitHistoryTurns DONE sid=${s} historyStreamCompleteEmitted=${g} replayedToolCallIds=${t.size}`)}catch(e){this.logger.warn?.("[bridge-core] Failed to load session history:",e)}return t}async replayHistoryTurnsToSink(e,t,s,i){const n=e.sessionId,r=new Set;let o=0;for(let a=0;a<t.length;a++){const l=t[a],d=i.turnIdFor(l,a);if(i.mqActive&&a===i.lastAsstIdx){const e=classifyHistoricalAssistantTurnCompletion(l,{mqActive:i.mqActive,isSkippedForLiveMq:!0});this.logger.debug?.(`[bridge-core] SKIP history_turn[${a}] assistant — ${"live"===e.kind?e.reason:"mq active"}, deferring to emitCurrentStreamSnapshot`);continue}if("user"===l.role&&"internal_system"===l.provenance?.kind){this.logger.debug?.(`[bridge-core] SKIP history_turn[${a}] internal_system user message`);continue}if("user"===l.role&&/^\s*system\s*\(untrusted[^)]*\)/i.test(l.content)){this.logger.debug?.(`[bridge-core] SKIP history_turn[${a}] system-injected user message`);continue}if("user"===l.role&&isHeartbeatPromptText(l.content)){this.logger.info?.(`[bridge-core] SKIP history_turn[${a}] heartbeat cron prompt sessionId=${e.sessionId} contentLen=${l.content.length} preview="${previewSanitizedText(l.content)}"`);continue}if("user"===l.role&&isCronPromptText(l.content)){this.logger.info?.(`[bridge-core] SKIP history_turn[${a}] cron-triggered user message sessionId=${e.sessionId} contentLen=${l.content.length} preview="${previewSanitizedText(l.content)}"`);continue}if("assistant"===l.role){const t=classifyHeartbeatFilter(l.content);if(t.filtered){this.logger.info?.(`[bridge-core] SKIP history_turn[${a}] heartbeat assistant reply reason=${t.reason} sessionId=${e.sessionId} contentLen=${l.content.length} preview="${previewSanitizedText(l.content)}"`);continue}}const c="assistant"===l.role?classifyHistoricalAssistantTurnCompletion(l,{mqActive:i.mqActive,isSkippedForLiveMq:!1}):void 0;if("error"===c?.kind||"interrupted"===c?.kind&&c.failure)s.sendSessionUpdate(n,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:""},...l.timestamp?{timestamp:l.timestamp}:{}},{turnId:d,...void 0!==l._seq?{seq:l._seq}:{}}),s.sendSessionUpdate(n,{sessionUpdate:"stream_complete",stopReason:c.stopReason,error:c.message,errorCode:c.errorCode},{turnId:d}),this.logger.info?.(`[bridge-core] history_runtime_failure_terminalized session_id=${n} turn_id=${d} error_code=${c.errorCode} stop_reason=${c.stopReason} source=history_replay`),o+=1;else if("system"!==l.role)if(this.logger.debug?.(`[bridge-core] SENDING history_turn[${a}] role=${l.role} contentLen=${l.content.length} content=${JSON.stringify(l.content.slice(0,80))} tcCount=${l.toolCalls?.length??0}`),"user"===l.role)this.replayUserTurnAsChunks(s,n,l.content,l.metadata,l.timestamp,d,l._seq);else{if(await this.replayAssistantTurnAsStream(s,n,l,d),l.toolCalls)for(const e of l.toolCalls)e.id&&r.add(e.id);const e=c??classifyHistoricalAssistantTurnCompletion(l,{mqActive:i.mqActive,isSkippedForLiveMq:!1});if("interrupted"===e.kind){const t=new Set(e.incompleteToolCallIds);for(const i of l.toolCalls??[])t.has(i.id)&&s.sendSessionUpdate(n,buildToolCallUpdatePayload({toolCallId:i.id,toolName:i.name,outputText:e.message,kind:i.kind,status:"failed",timestamp:l.timestamp}),{turnId:d});this.logger.info?.(`[bridge-core] history_orphan_turn_terminalized session_id=${n} turn_id=${d} tool_call_ids=${e.incompleteToolCallIds.join(",")} stop_reason=${e.stopReason} source=history_replay`),s.sendSessionUpdate(n,{sessionUpdate:"stream_complete",stopReason:e.stopReason,error:e.message,errorCode:e.errorCode},{turnId:d}),o+=1;continue}if("completed"!==e.kind)continue;s.sendSessionUpdate(n,{sessionUpdate:"stream_complete",stopReason:e.stopReason},{turnId:d}),o+=1}else this.logger.debug?.(`[bridge-core] SENDING system_message[${a}] contentLen=${l.content.length}`),s.sendSessionUpdate(n,{sessionUpdate:"system_message_chunk",content:{type:"text",text:l.content},metadata:l.metadata,...l.timestamp?{timestamp:l.timestamp}:{}},{turnId:d,...void 0!==l._seq?{seq:l._seq}:{}})}return{replayedToolCallIds:r,streamCompleteCount:o}}async handleSessionHistoryPage(e){const t=e.params??{},s="string"==typeof t.sessionId?t.sessionId:"";if(!s)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId"});const i="string"==typeof t.cursor&&t.cursor.length>0?t.cursor:void 0,n="number"==typeof t.limit&&Number.isFinite(t.limit)&&t.limit>0?Math.min(1e3,Math.floor(t.limit)):100,{session:r}=await this.resolveOrCreateSession(s),o=this.getGateway(),a=await loadHistoryPage(o,r.gatewaySessionKey,{...i?{cursor:i}:{},limit:n,logger:this.logger}),l=groupIntoTurns(a.messages),d=new HistoryUpdateRecorder(this.transport);await this.replayHistoryTurnsToSink(r,l,d,{mqActive:!1,lastAsstIdx:-1,turnIdFor:(e,t)=>void 0!==e._seq?`history:${r.gatewaySessionKey}:seq:${e._seq}`:`history:${r.gatewaySessionKey}:seq:none:${i??"head"}:${t}`}),this.logger.debug?.(`[bridge-core] _beeos/session/history sid=${r.sessionId} cursor=${i??"<head>"} limit=${n} turns=${l.length} updates=${d.updates.length} hasMore=${a.hasMore} nextCursor=${a.nextCursor??"<none>"}`),this.transport.sendResult(e.id,{updates:d.updates,...a.nextCursor?{nextCursor:a.nextCursor}:{},hasMore:a.hasMore})}emitCurrentStreamSnapshot(e,t){const s=e.pendingUserTurnUpdates;if(s&&s.length>0)for(const t of s)this.transport.sendSessionUpdate(e.sessionId,t);const i=e.assistantMq;if(i&&!i.closed){const s=i.peek(0);let n=0;if(t&&t.size>0)for(let e=0;e<s.length;e++){const i=s[e],r=i.sessionUpdate,o=i.toolCallId;if("tool_call"!==r&&"tool_call_update"!==r||!o||!t.has(o)){if("agent_message_chunk"===r){if(""===(i.content?.text??"").trim()&&e<s.length-1){const i=s[e+1];if(("tool_call"===i.sessionUpdate||"tool_call_update"===i.sessionUpdate)&&i.toolCallId&&t.has(i.toolCallId)){n=e+1;continue}}break}break}n=e+1}const r=resolveTurnId(e),o=r?{turnId:r}:void 0;for(let t=n;t<s.length;t++)this.transport.sendSessionUpdate(e.sessionId,s[t],o)}}async handleSessionList(e){let t;try{t=parseRuntimeSessionListPage(e.params)}catch(t){return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:t instanceof Error?t.message:"Invalid session/list params"})}const s=[];for(const[,e]of this.state.sessions)isBackgroundSessionKey(e.gatewaySessionKey)||this.cronIsolatedSessionKeys.has(e.gatewaySessionKey)||!t.includeDeleted&&this.state.deletedGatewaySessionKeys.has(e.gatewaySessionKey)||s.push({sessionId:e.sessionId,title:e.title||"New chat",cwd:e.cwd,updatedAt:e.lastStreamActivityTs??e.createdAt,agentId:e.agentId,platformAgentId:e.platformAgentId,deletable:!isSystemDefaultSessionKey(e.gatewaySessionKey)});const i=this.getGateway();if(i?.isConnected)try{const e=await i.sessionsList();if(e&&"object"==typeof e&&Array.isArray(e.sessions)){const i=e.sessions;for(const e of i){const i=e,n=i.sessionKey??i.key??i.id??i.name;if(!n||"string"!=typeof n)continue;if(isBackgroundSessionKey(n))continue;if(this.cronIsolatedSessionKeys.has(n))continue;const r=deriveSessionId(n,this.instanceId),o=s.find(e=>e.sessionId===n||e.sessionId===r||this.state.sessions.get(e.sessionId)?.gatewaySessionKey===n);if(!0===i.deleted){if(this.state.deletedGatewaySessionKeys.add(n),!t.includeDeleted)continue}else this.state.deletedGatewaySessionKeys.delete(n);const a=i.label||cleanGatewayDerivedTitle(i.derivedTitle)||i.title||void 0,l=a&&!isPlaceholderTitle(a)?a:void 0,d=i.model,c=i.modelProvider;if(o){if(l&&isPlaceholderTitle(o.title)){o.title=l;const e=this.state.sessions.get(o.sessionId);e&&isPlaceholderTitle(e.title)&&(e.title=l)}const e=i.updatedAt;e&&(o.updatedAt=e),d&&(o.model=d),c&&(o.modelProvider=c)}else{const e=createSessionObject({gatewayKey:n,instanceId:this.instanceId,resolvePlatformAgentId:e=>this.resolvePlatformAgentId(e),thirdPartyChannels:this.thirdPartyChannels,cwd:i.cwd,title:l});this.registerSession(e,{ensureVerbose:!1}),s.push({sessionId:e.sessionId,title:l||"",cwd:i.cwd,updatedAt:i.updatedAt,agentId:e.agentId,platformAgentId:e.platformAgentId,deletable:!isSystemDefaultSessionKey(n),model:d,modelProvider:c})}}}}catch(e){this.logger.debug?.("[bridge-core] sessions.list enrichment failed:",e)}const n=t.includeDeleted?s:s.filter(e=>{const t=this.state.sessions.get(e.sessionId)?.gatewaySessionKey;return!t||!this.state.deletedGatewaySessionKeys.has(t)});n.sort((e,t)=>(t.updatedAt??0)-(e.updatedAt??0)||e.sessionId.localeCompare(t.sessionId)),this.transport.sendResult(e.id,{sessions:pageRuntimeSessions(n,t)})}async handleSessionRename(e){const t=e.params?.sessionId,s=e.params?.title;if(!t||!s)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId, title"});const i=s.slice(0,64),n=this.state.sessions.get(t);if(n)try{const s=await this.dispatchLegacyConversationCapability("session.rename",n,{title:i});n.title=s.title,n.titleSource="manual",this.transport.sendResult(e.id,{sessionId:t,title:s.title})}catch(s){this.logger.warn?.(`[bridge-core] ACP rename projection deferred: ${s instanceof Error?s.message:String(s)}`),n.title=i,n.titleSource="manual",this.transport.sendResult(e.id,{sessionId:t,title:i})}else this.transport.sendAcpError(e.id,Errors.SessionNotFound)}static TITLE_TEMPLATES=[{match:["今日日报","日报"],zh:"今日工作日报",en:"Daily Work Log"},{match:["周报","周报总结","本周周报"],zh:"本周工作周报",en:"Weekly Work Report"},{match:["月报","月度汇报","月度总结"],zh:"本月工作月报",en:"Monthly Work Report"},{match:["工作日志","日志"],zh:"工作日志整理",en:"Work Log Review"},{match:["晨会","今日晨会"],zh:"今日晨会纪要",en:"Daily Standup Notes"},{match:["复盘","今日复盘"],zh:"今日工作复盘",en:"Daily Retrospective"}];static TITLE_SYSTEM_PROMPT='You are a conversation title generator. Your task is to generate a concise, human-readable title summarizing the topic of the user\'s question together with the assistant\'s answer.\n\n### Rules:\n- Length: 3-6 words in English, or 6-20 characters in Chinese/Japanese/Korean (up to 24 if needed to keep all key domain terms intact).\n- Language: Match the user\'s input language exactly. Mixed zh/en input → use the dominant language. Do NOT switch languages. (IM path injects Client UI locale via conversation-title.ts — keep ACP in sync with that module when changing examples.)\n- Extract the core topic (noun-centric) of the Q&A exchange, not the action verb.\n- Use the assistant\'s answer to disambiguate or enrich the topic when the question alone is vague (e.g. "帮我看下" + an answer about x402 → "x402 支付集成").\n- No punctuation, no quotation marks, no emojis, no markdown.\n- No prefix like "Title:" or "Here is". Output the title only.\n- Avoid generic words: "help", "question", "chat", "problem", "task".\n- Greetings and self-introductions ARE concrete topics (e.g. "自我介绍", "自己紹介", "Self Introduction") — never treat them as vague.\n- NEVER return placeholder / empty-chat titles such as "新会话", "新對話", "New chat", "New Conversation", "新しいチャット", "새 채팅", "แชตใหม่". Always invent a concrete noun-centric topic from whatever words are present.\n\n### Input Format:\nThe user message will contain both the question and the assistant\'s answer, delimited like this:\n[User Question]\n...question text...\n\n[Assistant Answer]\n...answer text (may be truncated with "..." in the middle)...\n\n### Output Format:\nRespond ONLY with valid JSON, no other text:\n{"title": "<your title here>"}\n\n### Examples:\nInput:\n[User Question]\n帮我写一个关于BeeOS设备接入的PRD\n\n[Assistant Answer]\n好的,这是 BeeOS 设备接入 PRD 的草稿:1. 目标 ...\nOutput: {"title": "BeeOS 设备接入 PRD"}\n\nInput:\n[User Question]\n帮我看下\n\n[Assistant Answer]\n你说的 x402 支付协议是一个基于 HTTP 402 的链上支付标准 ...\nOutput: {"title": "x402 支付协议解析"}\n\nInput:\n[User Question]\nWhat\'s the best way to integrate x402 payment protocol?\n\n[Assistant Answer]\nYou have three options: 1. Use the official SDK ...\nOutput: {"title": "x402 Payment Integration"}\n\nInput:\n[User Question]\n用一句话介绍一下你自己。\n\n[Assistant Answer]\n我是 BeeOS 智能助手,可以帮助你完成设备控制与任务协作。\nOutput: {"title": "自我介绍"}\n\nInput:\n[User Question]\nhi\n\n[Assistant Answer]\nHello! How can I help you today?\nOutput: {"title": "Greeting"}';matchTitleTemplate(e){const t=e.trim().toLowerCase();for(const s of AcpGatewayBridgeCore.TITLE_TEMPLATES)if(s.match.some(e=>e.toLowerCase()===t))return/[\u4e00-\u9fff]/.test(e)?s.zh:s.en}isGuardMessage(e){const t=e.trim();return[...t].length<3||[...t].every(e=>/[\p{P}\p{S}\s]/u.test(e))}truncateQuestionForTitle(e){return e.length>2e3?e.slice(0,1500):e}truncateAnswerForTitle(e){return e.length<=1e3?e:`${e.slice(0,500)}\n...\n${e.slice(-500)}`}generateTitleFromQA(e,t,s,i){if(this.isGuardMessage(s))return void this.logger.info?.(`[bridge-core] title-gen SKIP guard message sid=${t}`);const n=this.matchTitleTemplate(s);if(n){e.title=n,e.titleSource="template";const s=this.getGateway();return s?.isConnected&&s.sessionsPatch(e.gatewaySessionKey,{label:n}).catch(()=>{}),void this.transport.sendSessionUpdate(t,{sessionUpdate:"title_update",title:n})}const r=(process.env.AROUTER_BASE_URL||"").replace(/\/$/,""),o=process.env.AROUTER_API_KEY||"";if(!r||!o)return void this.logger.info?.(`[bridge-core] title-gen SKIP sid=${t}: no AROUTER env vars`);const a=`[User Question]\n${this.truncateQuestionForTitle(s)}\n\n[Assistant Answer]\n${this.truncateAnswerForTitle(i)}`;this.logger.info?.(`[bridge-core] title-gen CALLING LLM sid=${t} q="${s.slice(0,60)}" aLen=${i.length}`),fetch(`${r}/v1/chat/completions`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({model:"openai/gpt-4o-mini",max_tokens:32,temperature:.3,messages:[{role:"system",content:AcpGatewayBridgeCore.TITLE_SYSTEM_PROMPT},{role:"user",content:a}]})}).then(async s=>{if(!s.ok){const e=await s.text().catch(()=>"");return void this.logger.info?.(`[bridge-core] title-gen FAILED sid=${t} status=${s.status} body=${e.slice(0,300)}`)}const i=await s.json(),n=i.choices?.[0]?.message?.content?.trim();if(!n)return;const r=n.replace(/^```json\s*/,"").replace(/```$/,"").trim();let o;try{o=JSON.parse(r).title?.trim()??""}catch{return void this.logger.debug?.(`[bridge-core] title-gen JSON parse failed: ${r}`)}if(!o)return;if(isPlaceholderTitle(o))return void this.logger.debug?.("[bridge-core] title-gen discarded: placeholder LLM title");if("manual"===e.titleSource)return void this.logger.debug?.("[bridge-core] title-gen discarded: user already renamed");this.logger.info?.(`[bridge-core] title-gen OK sid=${t} title="${o}"`),e.title=o,e.titleSource="auto";const a=this.getGateway();a?.isConnected&&a.sessionsPatch(e.gatewaySessionKey,{label:o}).catch(()=>{}),this.transport.sendSessionUpdate(t,{sessionUpdate:"title_update",title:o})}).catch(e=>{this.logger.info?.(`[bridge-core] title-gen ERROR sid=${t}: ${e instanceof Error?e.message:e}`)})}async tryGenerateTitleFromHistory(e,t,s){if("manual"===e.titleSource||"auto"===e.titleSource)return;const i=this.getGateway();if(!i?.isConnected)return;let n=s;if(!n)try{const t=await loadHistory(i,e.gatewaySessionKey);n=groupIntoTurns(t)}catch(e){return void this.logger.debug?.(`[bridge-core] title-gen loadHistory failed sid=${t}: ${e instanceof Error?e.message:e}`)}const r=n.find(e=>{if("user"!==e.role)return!1;if(!e.content?.trim())return!1;if("internal_system"===e.provenance?.kind)return!1;if(isCronPromptText(e.content))return!1;const t=cleanHistoryUserContent(e.content);return!!t&&!/^Sender\s*\(untrusted/i.test(t)});if(!r)return;const o=n.indexOf(r),a=n.slice(o+1).find(e=>"assistant"===e.role&&!!e.content?.trim());if(!a)return;const l=cleanHistoryUserContent(r.content),d=a.content.trim();l&&d&&this.generateTitleFromQA(e,t,l,d)}async handleSessionDelete(e){const t=e.params?.sessionId;if(!t)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId"});const s=this.state.sessions.get(t);if(!s)return this.logger.info?.(`[bridge-core] session/delete sessionId=${t} NOT FOUND in local state`),void this.transport.sendAcpError(e.id,Errors.SessionNotFound);const i=s.gatewaySessionKey,n=this.sessionMutationCoordinator.tryStartExclusive({sessionKey:i,label:"session_delete"},"acp");if(!n.ok)return void this.sendSessionBusy(s,e.id,"active_session_mutation",n.queueDepth);const r=n.token;try{if(this.hasActiveRun(s)){const t=this.sessionMutationCoordinator.gateSnapshot(i);return void this.sendSessionBusy(s,e.id,"active_session_mutation",t.queueDepth)}this.logger.info?.(`[bridge-core] session/delete sessionId=${t} gatewayKey=${i} gwConnected=${!!this.getGateway()?.isConnected}`);let n=!1;const r=this.getGateway();if(r?.isConnected&&i)try{await r.sessionsDelete(i),n=!0,this.logger.info?.(`[bridge-core] Gateway sessions.delete SUCCESS for key=${i}`)}catch(t){const s=t instanceof Error?t.message:String(t);if(this.logger.info?.(`[bridge-core] Gateway sessions.delete FAILED for key=${i}: ${s}`),!s.includes("Cannot delete the main session"))return void this.transport.sendResult(e.id,{deleted:!1});try{await r.sessionsReset(i),n=!0,this.logger.info?.(`[bridge-core] Gateway sessions.reset SUCCESS for key=${i}`)}catch(t){return this.logger.warn?.(`[bridge-core] Gateway sessions.reset also failed for key=${i}: ${t instanceof Error?t.message:t}`),void this.transport.sendResult(e.id,{deleted:!1})}}else i&&this.logger.warn?.(`[bridge-core] Gateway not connected, proceeding with local-only delete for key=${i}`);this.state.clearPromptTimeout(s),this.state.clearStreamIdleTimer(s),s.assistantMq?.close(),s.assistantMq=void 0,s.currentRunId&&this.state.promptsByRunId.delete(s.currentRunId);const o=new Set;for(const[e,s]of this.state.toolCallToSessionMap)s===t&&(this.state.toolCallToSessionMap.delete(e),o.add(e));for(const[e,t]of this.state.toolCallIdMap)o.has(t)&&this.state.toolCallIdMap.delete(e);this.state.sessions.delete(t),this.state.sessionsByGatewayKey.delete(i),this.state.deletedGatewaySessionKeys.add(i),this.transport.resetSessionUpdateCount(t),n&&this.onSessionDeleted?.(i),this.logger.info?.(`[bridge-core] session/delete DONE sessionId=${t} gatewayKey=${i} deleted=true`),this.transport.sendResult(e.id,{deleted:!0})}finally{this.sessionMutationCoordinator.releaseBeforeRuntime(r)}}async handleSessionClear(e){this.logger.info?.(`[bridge-core] handleSessionClear called, params=${JSON.stringify(e.params)}`);const t=e.params?.sessionId;if(!t)return this.logger.warn?.("[bridge-core] session/clear missing sessionId"),void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId"});const s=this.state.sessions.get(t);if(!s)return this.logger.warn?.(`[bridge-core] session/clear sessionId=${t} not found in state`),void this.transport.sendAcpError(e.id,Errors.SessionNotFound);this.logger.info?.(`[bridge-core] session/clear sessionId=${t} gatewayKey=${s.gatewaySessionKey}`);const i=this.getGateway(),n=i?.isConnected;if(!s.promptDone&&(void 0!==s.pendingPromptId||void 0!==s.currentRunId)){this.logger.info?.(`[bridge-core] session/clear: cancelling active run sid=${t} runId=${s.currentRunId}`);const r=s.writeGateToken;let o=!1;if(n&&r){const e=await this.sessionMutationCoordinator.requestCancel(r,i);o="recovery_required"===await this.handleCancellationResult(s,e)}else n&&await i.chatAbort(s.gatewaySessionKey,s.currentRunId);return void this.transport.sendError(e.id,Errors.AgentError.rpcCode,o?SESSION_RECOVERY_REQUIRED_MESSAGE:"Cancellation is still converging",o?{code:SESSION_RECOVERY_REQUIRED_CODE,stopReason:"interrupted"}:{code:"session_cancellation_pending",stopReason:"cancelled"})}try{await this.dispatchLegacyConversationCapability("session.clear",s,{}),this.transport.sendResult(e.id,{cleared:!0}),this.logger.info?.("[bridge-core] session/clear DONE, sent cleared=true")}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleSessionSetMode(e){const t=e.params?.sessionId,s=e.params?.mode,i=t?this.state.sessions.get(t):void 0;if(t&&s)if(i)try{const t=await this.dispatchLegacyConversationCapability("session.setMode",i,{mode:s});this.transport.sendResult(e.id,t)}catch(t){this.sendRuntimeCommandError(e.id,t)}else this.transport.sendAcpError(e.id,Errors.SessionNotFound);else this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId, mode"})}async handleSessionSetModel(e){const t=e.params,s=null===t||"object"!=typeof t||Array.isArray(t)?void 0:t,i="string"==typeof s?.sessionId?s.sessionId:void 0;if(!i||!s||!Object.prototype.hasOwnProperty.call(s,"model"))return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId, model"});const n=s.model;if(null!==n&&("string"!=typeof n||0===n.length||n.trim()!==n))return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"model must be a non-empty string or null"});const r=i?this.state.sessions.get(i):void 0;if(r)try{const t=await this.dispatchLegacyConversationCapability("session.setModel",r,{modelOverrideId:n});this.transport.sendResult(e.id,{model:t.effectiveModelId,modelProvider:t.modelProvider??null})}catch(t){this.sendRuntimeCommandError(e.id,t)}else this.transport.sendAcpError(e.id,Errors.SessionNotFound)}sendRuntimeCommandError(e,t){if(t instanceof RuntimeCommandError)switch(t.code){case"MISSING_PARAM":return void this.transport.sendAcpError(e,Errors.MissingParam,{...t.param?{param:t.param}:{}});case"INVALID_PARAMS":return void this.transport.sendAcpError(e,Errors.InvalidParams,{message:t.message});case"GATEWAY_NOT_CONNECTED":return void this.transport.sendAcpError(e,Errors.GatewayNotConnected);case"EXECUTION_FAILED":case"AGENT_DEFAULT_MODEL_NOT_CONFIGURED":case"OUTCOME_UNKNOWN":case"INSTANCE_EPOCH_MISMATCH":case"COMMAND_EXPIRED":case"IDEMPOTENCY_CONFLICT":case"UNAUTHORIZED_TARGET":return void this.transport.sendAcpError(e,Errors.InternalError,{message:t.message})}else this.transport.sendAcpError(e,Errors.InternalError,{message:t instanceof Error?t.message:"Internal error"})}createOperationId(){const e=randomUUID();return`${e.slice(0,14)}7${e.slice(15)}`}canonicalContextBase(e,t=this.createOperationId(),s="0"){const i=Date.now(),n=createHash("sha256").update(JSON.stringify(e)).digest("hex");return{operationId:t,runtimeInstanceUid:this.instanceId,dispatchEpoch:s,paramsHash:n,idempotencyKeyHash:createHash("sha256").update(t).digest("hex"),claimDeadline:new Date(i+3e4).toISOString(),executionDeadline:new Date(i+3e5).toISOString()}}acpExecutionAttempt(e){return createAcpCoreExecutionAttempt({operationId:e.context.operationId,runtimeEpoch:e.context.dispatchEpoch,resources:this.runtimeCommandResources,historyBoundary:this.localHistoryBoundary})}instanceCommand(e,t,s){return{name:e,params:t,context:{...this.canonicalContextBase(t,s.operationId,s.runtimeEpoch),scope:"instance"}}}async agentCommand(e,t,s,i){return{name:e,params:s,context:{...this.canonicalContextBase(s,i.operationId,i.runtimeEpoch),scope:"agent",agent:{runtimeLocalKey:t,runtimeAgentUid:await this.runtimeAgentIdentities.resolveOrCreate(t)}}}}async dispatchLegacyAgentCapability(e,t,s){const i=normalizeRuntimeAgentLocalKey(t),n=await this.runtimeLeases.claim(this.createOperationId());let r=!1;try{const t={name:e,params:s,context:{...this.canonicalContextBase(s,n.operationId,n.runtimeEpoch),scope:"agent",agent:{runtimeLocalKey:i,runtimeAgentUid:await this.runtimeAgentIdentities.resolveOrCreate(i)}}},o=this.runtimeCommands.dispatch(t,this.acpExecutionAttempt(t));return r=!0,await o.completion}finally{r||await this.runtimeLeases.release(n)}}async dispatchLegacyInstanceCapability(e,t){const s=await this.runtimeLeases.claim(this.createOperationId());let i=!1;try{const n={name:e,params:t,context:{...this.canonicalContextBase(t,s.operationId,s.runtimeEpoch),scope:"instance"}},r=this.runtimeCommands.dispatch(n,this.acpExecutionAttempt(n));return i=!0,await r.completion}finally{i||await this.runtimeLeases.release(s)}}async handleMcpList(e){const t=await this.dispatchLegacyInstanceCapability("mcp.list",{});this.transport.sendResult(e.id,{servers:Object.fromEntries(t.servers.map(e=>[e.name,{transport:e.transport,...e.url?{url:e.url}:{},...e.command?{command:e.command}:{},...e.args?{args:[...e.args]}:{},...e.cwd?{cwd:e.cwd}:{},...e.connectionTimeoutMs?{connectionTimeoutMs:e.connectionTimeoutMs}:{}}]))})}async handleMcpSet(e){const t=e.params&&"object"==typeof e.params?e.params:{},s="string"==typeof t.name?t.name:"";let i;try{if(requireSecretFreeMcpServerName(s),!t.server||"object"!=typeof t.server||Array.isArray(t.server))throw new Error("mcp/set requires name and server");const e=t.server,n=extractMcpCredentialMaterial(e),r={...e};delete r.headers,delete r.env;const o=e.transport??("string"==typeof e.command?"stdio":"string"==typeof e.url?"streamable-http":void 0),a=normalizeMcpPublicServerConfig({...r,transport:o}),l=createHash("sha256").update(canonicalizeJcs(a)).digest("hex");i={definition:{definitionRef:`acp-mcp:sha256:${l}`,version:"local-v1",sha256:l,...n?{credentialRef:await requireCredentialReferencePort(this.mcpCredentialReferences).put({serverName:s,material:n})}:{}},config:structuredClone(a)}}catch(t){return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:t instanceof Error?t.message:"Invalid MCP server config"})}this.runtimeCommandResources.registerMcpDefinition?.(i.definition,i.config);try{await this.dispatchLegacyInstanceCapability("mcp.set",{name:s,definition:i.definition})}catch(e){if(i.definition.credentialRef){!await this.mcp.removeCredentialIfUnreferenced(i.definition.credentialRef)&&this.mcpCredentialReferences?.owns(i.definition.credentialRef)&&await this.mcpCredentialReferences.remove(i.definition.credentialRef)}throw e}this.transport.sendResult(e.id,{ok:!0,restarting:!1})}async handleMcpUnset(e){const t=e.params&&"object"==typeof e.params?e.params:{};let s;try{s=requireSecretFreeMcpServerName(t.name)}catch{return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"mcp/unset requires name"})}await this.dispatchLegacyInstanceCapability("mcp.unset",{name:s}),this.transport.sendResult(e.id,{ok:!0,restarting:!1})}async dispatchLegacyConversationCapability(e,t,s){const i=await this.runtimeLeases.claim(this.createOperationId());let n=!1;try{const r={name:e,params:s,context:{...this.canonicalContextBase(s,i.operationId,i.runtimeEpoch),scope:"conversation",conversation:{runtimeConversationUid:t.sessionId,runtimeSessionKey:t.gatewaySessionKey}}},o=this.runtimeCommands.dispatch(r,this.acpExecutionAttempt(r));return n=!0,await o.completion}finally{n||await this.runtimeLeases.release(i)}}async dispatchLegacyTerminalCapability(e,t,s,i){const n=await this.runtimeLeases.claim(this.createOperationId());let r=!1;try{const o={name:e,params:i,context:{...this.canonicalContextBase(i,n.operationId,n.runtimeEpoch),scope:"terminal",terminal:{terminalId:t,clientId:s}}},a=this.runtimeCommands.dispatch(o,this.acpExecutionAttempt(o));return r=!0,await a.completion}finally{r||await this.runtimeLeases.release(n)}}async dispatchLegacyCanvasNotification(e,t){const s=t&&"object"==typeof t?t:{},i=e.replace("/","."),n="canvas.toggle"===i?{visible:!0===s.visible||!0===s.enabled}:"canvas.reference"===i?{surfaceId:"string"==typeof s.surfaceId?s.surfaceId:""}:"canvas.dimensions"===i?{width:Number(s.width),height:Number(s.height),..."number"==typeof s.devicePixelRatio?{devicePixelRatio:s.devicePixelRatio}:{}}:{},r="string"==typeof s.sessionId?s.sessionId:"acp",o="string"==typeof s.canvasId?s.canvasId:r,a=await this.runtimeLeases.claim(this.createOperationId());let l=!1;try{const e={name:i,params:n,context:{...this.canonicalContextBase(n,a.operationId,a.runtimeEpoch),scope:"canvas",canvas:{canvasId:o,sessionId:r}}},t=this.runtimeCommands.dispatch(e,this.acpExecutionAttempt(e));l=!0,await t.completion}finally{l||await this.runtimeLeases.release(a)}}async applyCanonicalCanvasControl(e,t){if("canvas/reference"===e){const e=this.state.resolveSessionFromParams(t);if(!e)throw new RuntimeCommandError("INVALID_PARAMS","canvas session not found");const s="string"==typeof t.surfaceId?t.surfaceId:void 0;return void(e.referencedSurfaceId=s||void 0)}if(!this.handleCanvasNotification)throw new RuntimeCommandError("INVALID_PARAMS","canvas control delegate is not configured");await this.handleCanvasNotification(e,t)}async dispatchLegacyCronCapability(e,t){const s=normalizeRuntimeAgentLocalKey(this.config.gateway.agentId),i=await this.runtimeLeases.claim(this.createOperationId());let n=!1;try{const r="cron.add"===e?{...t,runtimeCronJobUid:`runtime-cron:${i.operationId}`}:"cron.run"===e?{...t,runToken:`runtime-run:${i.operationId}`}:t,o={name:e,params:r,context:{...this.canonicalContextBase(r,i.operationId,i.runtimeEpoch),scope:"agent",agent:{runtimeLocalKey:s,runtimeAgentUid:await this.runtimeAgentIdentities.resolveOrCreate(s)}}},a=this.runtimeCommands.dispatch(o,this.acpExecutionAttempt(o));return n=!0,await a.completion}finally{n||await this.runtimeLeases.release(i)}}validateConnectedGateway(){if(!this.getGateway()?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected")}async finishClaimedLegacyAgentMutation(e,t){let s=!1;try{const e=await t(),i=this.runtimeCommands.dispatch(e,this.acpExecutionAttempt(e));s=!0;const n=await i.completion;return queueMicrotask(()=>{this.observeAgentProjection()}),n}finally{s||await this.runtimeLeases.release(e)}}async observeAgentProjection(){await(async(e,t)=>{if(!t)return;let s;try{await Promise.race([Promise.resolve().then(t),new Promise((t,i)=>{s=setTimeout(()=>i(new Error(`${e} timed out`)),this.projectionObservationTimeoutMs)})])}catch(t){this.logger.warn?.(`[acp] ${e} failed: ${t instanceof Error?t.message:String(t)}`)}finally{s&&clearTimeout(s)}})("agent projection sync",this.triggerAgentSync)}async handleAgentCreate(e){try{const t=e.params?.name?.trim()??"";if(!t)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");this.validateConnectedGateway();const s=this.createOperationId(),i=await this.runtimeLeases.claim(s),n=await this.finishClaimedLegacyAgentMutation(i,async()=>{const s=await this.runtimeAgentIdentities.allocateCreate(i,"agent.create"),n=Object.entries(e.params?.files??{}).filter(e=>"string"==typeof e[1]).map(([e,t])=>({name:e,contentRef:this.runtimeCommandResources.registerContent(t)}));return this.instanceCommand("agent.create",{runtimeAgentUid:s.runtimeAgentUid,name:t,...e.params?.identity?{identity:e.params.identity}:{},...e.params?.model?{defaultModelId:e.params.model}:{},...n.length?{bootstrapFiles:n}:{}},i)});this.transport.sendResult(e.id,{agentId:n.runtimeLocalKey,platformAgentId:this.resolvePlatformAgentId(n.runtimeLocalKey)??"",workspace:n.workspacePath})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleApplyTemplate(e){try{const t=e.params?.name?.trim()??"";if(!t)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");const s=e.params?.packageUrl?.trim()??"";if(!s)throw new RuntimeCommandError("MISSING_PARAM","Missing packageUrl","packageUrl");this.validateConnectedGateway();const i=await this.runtimeCommandResources.registerArtifact(s,e.params?.sha256),n=this.createOperationId(),r=await this.runtimeLeases.claim(n),o=await this.finishClaimedLegacyAgentMutation(r,async()=>{const s=await this.runtimeAgentIdentities.allocateCreate(r,"agent.applyTemplate");return this.instanceCommand("agent.applyTemplate",{runtimeAgentUid:s.runtimeAgentUid,name:t,artifact:i,...e.params?.identity?{identity:e.params.identity}:{},...e.params?.model?{defaultModelId:e.params.model}:{}},r)});this.transport.sendResult(e.id,{agentId:o.runtimeLocalKey,platformAgentId:this.resolvePlatformAgentId(o.runtimeLocalKey)??"",workspace:o.workspacePath})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleAgentUpdate(e){try{const t=e.params?.agentId?.trim()??"";if(!t)throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");const s={...void 0!==e.params?.name?{name:e.params.name}:{},...void 0!==e.params?.model?{defaultModelId:e.params.model}:{},...void 0!==e.params?.identity?{identity:e.params.identity}:{}};if(0===Object.keys(s).length)throw new RuntimeCommandError("INVALID_PARAMS","No updatable fields provided (name/model/identity)");this.validateConnectedGateway();const i=await this.runtimeLeases.claim(this.createOperationId()),n=await this.finishClaimedLegacyAgentMutation(i,()=>this.agentCommand("agent.update",t,s,i));this.transport.sendResult(e.id,{agentId:n.runtimeLocalKey})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleAgentDelete(e){try{const t=e.params?.agentId?.trim()??"";if(!t)throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");if("main"===normalizeRuntimeAgentLocalKey(t))throw new RuntimeCommandError("INVALID_PARAMS",'"main" cannot be deleted; delete the instance instead');this.validateConnectedGateway();const s=await this.runtimeLeases.claim(this.createOperationId()),i=await this.finishClaimedLegacyAgentMutation(s,()=>this.agentCommand("agent.delete",t,void 0===e.params?.deleteFiles?{}:{deleteFiles:e.params.deleteFiles},s));this.transport.sendResult(e.id,{agentId:i.runtimeLocalKey,...void 0===i.removedBindings?{}:{removedBindings:i.removedBindings}})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleAgentList(e){try{const t=await this.dispatchLegacyInstanceCapability("agent.listProjection",{}),s=t.agents.map(e=>({agentId:e.runtimeLocalKey,name:e.name,...e.emoji?{emoji:e.emoji}:{},...e.avatarUrl?{avatarUrl:e.avatarUrl}:{}})),i={...t.defaultRuntimeLocalKey?{defaultId:t.defaultRuntimeLocalKey}:{},agents:s};this.transport.sendResult(e.id,i)}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleModelsList(e){try{this.transport.sendResult(e.id,await this.dispatchLegacyInstanceCapability("models.list",{}))}catch(t){this.sendRuntimeCommandError(e.id,t)}}async handleSkillsList(e){try{const t=await this.dispatchLegacyAgentCapability("skills.list",e.params?.agentId,{});this.transport.sendResult(e.id,{skills:t.skills.map(e=>({name:e.name,description:e.description??"",...e.emoji?{emoji:e.emoji}:{},...e.always?{always:!0}:{},...e.bundled?{bundled:!0}:{},...e.enabled?{}:{disabled:!0},skillKey:e.skillKey,...e.iconUrl?{iconUrl:e.iconUrl}:{}}))})}catch(t){this.sendRuntimeCommandError(e.id,t)}}async fetchSimplifiedSkills(e){const t=this.getGateway();if(!t?.isConnected)return[];try{const s=await t.skillsStatus(e);if(!s?.skills)return[];const i=s.skills.filter(e=>!1!==e.eligible||!0===e.disabled).sort((e,t)=>e.always&&!t.always?-1:!e.always&&t.always?1:e.name.localeCompare(t.name)).map(e=>({name:e.name,description:e.description,emoji:e.emoji,always:e.always||void 0,bundled:e.bundled||void 0,disabled:e.disabled||void 0,skillKey:e.skillKey||void 0,iconUrl:void 0})),n=await this.getSkillsDir(e);for(const e of i){const t=e.skillKey??e.name;try{const s=await readFile(join(n,t,".beeos-meta.json"),"utf8"),i=JSON.parse(s);i?.iconUrl&&(e.iconUrl=i.iconUrl)}catch{}}return i}catch(e){return this.logger.warn?.(`[bridge-core] Failed to fetch skills: ${e instanceof Error?e.message:e}`),[]}}async pushSkillsList(e){const t=await this.fetchSimplifiedSkills(e);this.transport.sendNotification("session/update",{sessionId:"_system",update:{sessionUpdate:"available_commands_update",commands:t}}),this.writeObs({component:"bridge-core",domain:"lifecycle",name:"skills.pushed",severity:"debug",payload:{count:t.length}})}getOpenclawHome(){return process.env.OPENCLAW_HOME||join(homedir(),".openclaw")}async getSkillsDir(e){const t=this.getOpenclawHome();if(!e||"main"===e)return join(t,"skills");const s=await this.resolveAgentWorkspaceDir(e);return join(s??join(t,`workspace-${e}`),"skills")}async resolveAgentWorkspaceDir(e){const t=this.getGateway();if(!t?.isConnected)return null;try{const s=await t.request("agents.list",{}),i=normalizeAgentId(e),n=s?.agents?.find(e=>normalizeAgentId(e.id)===i);return n?.workspace??null}catch(t){return this.logger.warn?.(`[bridge-core] resolveAgentWorkspaceDir("${e}") failed: ${t instanceof Error?t.message:t}`),null}}async handleSkillsInstall(e){const t=e.params,s=t?.zipUrl,i=t?.files,n=t?.agentId;let r;try{r=validateSkillSlug(t?.slug||t?.name||"")}catch{return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"Invalid or missing skill slug"})}if(i?.length||s)try{let o;if(s){o=parseSkillZip(await fetchWithRetry(s,{maxRetries:4,logger:this.logger}))}else{if(!i?.length)throw new RuntimeCommandError("INVALID_PARAMS","Missing skill files or zipUrl");o=new Map;for(const e of i){const t=normalizeSkillArtifactPath(e.path),s=await fetchWithRetry(e.downloadUrl,{maxRetries:4,logger:this.logger});o.set(t,new Uint8Array(s))}}if(t?.iconUrl&&o.set(".beeos-meta.json",(new TextEncoder).encode(JSON.stringify({iconUrl:t.iconUrl}))),!this.runtimeCommandResources.registerParsedArtifact)throw new RuntimeCommandError("EXECUTION_FAILED","ACP skill artifact ingress is not configured");const a=this.runtimeCommandResources.registerParsedArtifact({manifest:{slug:r,version:"0.0.0"},workspaceFiles:o});await this.dispatchLegacyAgentCapability("skills.install",n,{artifact:a}),this.transport.sendResult(e.id,{success:!0}),this.onSkillInstalled?.(r,resolveSkillInstallDir(await this.getSkillsDir(n),r)),await this.pushSkillsList(n)}catch(t){this.logger.error?.(`[bridge-core] Failed to install skill "${r}": ${t instanceof Error?t.message:t}`),this.transport.sendAcpError(e.id,Errors.InternalError,{message:`Install failed: ${t instanceof Error?t.message:String(t)}`})}else this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"Missing skill files or zipUrl"})}async handleSkillsUninstall(e){const t=e.params,s=t?.agentId;let i;try{i=validateSkillSlug(t?.slug||t?.name||"")}catch{return void this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"Invalid or missing skill name"})}try{await this.dispatchLegacyAgentCapability("skills.uninstall",s,{skillKey:i}),this.transport.sendResult(e.id,{success:!0}),await this.pushSkillsList(s)}catch(t){this.logger.error?.(`[bridge-core] Failed to uninstall skill "${i}": ${t instanceof Error?t.message:t}`),this.transport.sendAcpError(e.id,Errors.InternalError,{message:`Uninstall failed: ${t instanceof Error?t.message:String(t)}`})}}async handleSkillsUpdate(e){const t=e.params,s=t?.skillKey,i=t?.enabled,n=t?.agentId;if(s&&"boolean"==typeof i)try{await this.dispatchLegacyAgentCapability("skills.setEnabled",n,{skillKey:s,enabled:i}),this.logger.info?.(`[bridge-core] Updated skill "${s}" enabled=${i}${n?` for agent "${n}"`:""}`),this.transport.sendResult(e.id,{ok:!0,skillKey:s,enabled:i}),await this.pushSkillsList(n)}catch(t){this.logger.error?.(`[bridge-core] Failed to update skill "${s}": ${t instanceof Error?t.message:t}`),this.transport.sendAcpError(e.id,Errors.InternalError,{message:`Update failed: ${t instanceof Error?t.message:String(t)}`})}else this.transport.sendAcpError(e.id,Errors.InvalidParams,{message:"Missing skillKey or enabled"})}async handleSessionPrompt(e){const t=e.params?.sessionId,s=e.params?.prompt;if(!t||!s)return void this.transport.sendAcpError(e.id,Errors.MissingParam,{param:"sessionId, prompt"});const{session:i,created:n}=await this.resolveOrCreateSession(t);n&&this.onSessionLoaded?.(i.sessionId,i.gatewaySessionKey,i.platformAgentId);const r=i.sessionId;this.state.lastActiveSessionId=r,this.onPromptStart?.();const o=this.getGateway();if(!o?.isConnected)return void this.transport.sendAcpError(e.id,Errors.GatewayNotConnected);if(this.hasActiveRun(i)){const t=this.sessionMutationCoordinator.gateSnapshot(i.gatewaySessionKey);return void this.sendSessionBusy(i,e.id,"active_run",t.queueDepth)}try{await this.ensureVerboseFull(i)}catch(t){return this.releaseSessionWriteGate(i),void this.transport.sendAcpError(e.id,Errors.InternalError,{message:`Failed to prepare agent context: ${t instanceof Error?t.message:String(t)}`})}let a=extractPromptBlocks(sanitizePromptPayload(s));const l=a.filter(e=>"text"===e.type&&"text"in e&&"string"==typeof e.text).map(e=>e.text).join("\n");if(i.fileArtifactDisplayMode=prefersChatFileArtifacts(l)?"chat":"auto",!i.title||isPlaceholderTitle(i.title)){const e=a.find(e=>"text"===e.type&&e.text);if(e&&"text"in e&&e.text){const t=stripTransportMetadata(e.text).trim();if(this.logger.info?.(`[bridge-core] title-gen sid=${r} existing="${i.title}" cleanText="${t.slice(0,80)}" isPlaceholder=${isPlaceholderTitle(t)}`),t&&!isPlaceholderTitle(t)){const e=t.slice(0,64).replace(/\n/g," ").trim();i.title=e,i.titleSource="truncated",o?.isConnected&&e&&o.sessionsPatch(i.gatewaySessionKey,{label:e}).catch(e=>{this.logger.debug?.(`[bridge-core] sessionsPatch (title) failed: ${e instanceof Error?e.message:e}`)}),this.transport.sendSessionUpdate(r,{sessionUpdate:"title_update",title:e})}}}const d=buildResolutionPlan(a);if(d.refs.length>0)try{const e=await resolveResources(d,this.writeObs);a=applyResolutions(a,e)}catch(e){this.logger.warn?.(`[bridge-core] Resource pre-resolution failed: ${e instanceof Error?e.message:e}`)}let c;if(this.beeosFileResolver){const e=this.beeosFileResolver.buildResolutionPlan(a);if(e.refs.length>0)try{c=await this.beeosFileResolver.resolveMetadataForPrompt(e)}catch(e){this.logger.warn?.(`[bridge-core] BeeOS file resolution failed: ${e instanceof Error?e.message:e}`)}}const m=toGatewayPrompt(a,{beeosFileResolutionsByUri:c});if("error"in m)return this.releaseSessionWriteGate(i),void this.transport.sendAcpError(e.id,Errors.PromptConvertFailed,{message:m.error});const{message:u,attachments:p}=m,h=resolveStandaloneSlashCommandName(a,u)??resolveSlashCmd(a,u),g="string"==typeof h;if(h&&this.handleSlashCommand){const t=this;if(await(async()=>{try{return await t.handleSlashCommand(h)}catch(e){return t.logger.warn?.(`[bridge-core] /${h} handler failed: ${e instanceof Error?e.message:e}`),!1}})())return this.emitPromptAsUserSessionUpdates(i,a,resolveTurnId(i)),this.transport.sendSessionUpdate(r,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:`Loading ${h}...`},timestamp:(new Date).toISOString()},{turnId:resolveTurnId(i)}),this.transport.sendResult(e.id,{stopReason:"end_turn"}),void this.releaseSessionWriteGate(i)}const y="chat.send";let f=g?u:withUserMessagePrefix(u);const w=this.config.canvasEnabled&&!g&&!0===i.canvasActive&&!i.channelType;if(w){const e=i.promptsSinceCanvasInjection??0;!i.canvasPromptInjected||e>=10?(f=`${CANVAS_TERM_MAPPING}\n\n${f}`,i.canvasPromptInjected=!0,i.promptsSinceCanvasInjection=0,this.logger.debug?.("[bridge-core] Canvas term mapping injected (re-inject cycle)")):i.promptsSinceCanvasInjection=e+1}if(w){const e=this.getCanvasState?.();if(e){const t=e.resolveCanvasForSession(i.gatewaySessionKey);f=`${buildActiveSurfacesContext(e,t)??"[Canvas] Canvas panel is available. Use render_ui for structured/visual content."}\n\n${f}`}const t=i.referencedSurfaceId;t&&(f=`${CANVAS_UPDATE_HINT}\n[IMPORTANT: The user selected canvas surface "${t}" for editing. You MUST call render_ui with id="${t}" to UPDATE this existing surface. Do NOT create a new surface with a different id.]\n${f}`,i.referencedSurfaceId=void 0)}if(!g){const t=parseMentionsParam(e.params?.mentions);if(t.length>0){i.activeMentions=t;f=`${buildMentionInstruction(t)}\n${f}`,this.logger.debug?.(`[bridge-core] delegation mention instruction injected for ${t.length} agent(s)`)}else i.activeMentions=void 0}if(g&&this.logger.info?.(`[bridge-core] slash command passthrough sessionId=${r} command=/${h}`),this.hasActiveRun(i)){const t=this.sessionMutationCoordinator.gateSnapshot(i.gatewaySessionKey);return void this.sendSessionBusy(i,e.id,"active_run_after_prepare",t.queueDepth)}const I=this.sessionMutationCoordinator.tryStartInteractive({sessionKey:i.gatewaySessionKey,label:"session/prompt"},"acp");if(!I.ok)return void this.sendSessionBusy(i,e.id,I.reason,I.queueDepth);i.writeGateToken=I.token,this.state.resetForNewRun(i,r,this.transport),i.pendingPromptId=e.id,i.gatewayMethod=y;const v=resolveTurnId(i);this.emitPromptAsUserSessionUpdates(i,a,v),this.transport.sendSessionUpdate(r,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:""},timestamp:(new Date).toISOString()},v?{turnId:v}:void 0);const S=extractCronJobId(u);S&&(i.cronJobId=S),this.schedulePromptTimeout(i);const C=p.length>0?p.map(e=>({type:e.type,content:e.content,...e.mimeType?{mimeType:e.mimeType}:{},...e.fileName?{fileName:e.fileName}:{}})):void 0,R=`req_${randomUUID().replace(/-/g,"")}`,A={sessionKey:i.gatewaySessionKey,message:f,deliver:!1,idempotencyKey:randomUUID()};C?.length&&(A.attachments=C);if(!o.sendFrame({type:"req",id:R,method:y,params:A}))return this.state.clearPromptTimeout(i),i.pendingPromptId=void 0,i.assistantMq?.close(),i.assistantMq=void 0,i.pendingUserTurnUpdates=void 0,this.releaseSessionWriteGate(i),void this.transport.sendAcpError(e.id,Errors.SendFailed,{message:"failed to send prompt to gateway"});i.gatewayRequestId=R,this.state.promptsByGatewayRequestId.set(R,i)}replayUserTurnAsChunks(e,t,s,i,n,r,o){const a=splitUserReplayBlocks(s),l=r||void 0!==o?{...r?{turnId:r}:{},...void 0!==o?{seq:o}:{}}:void 0;for(const s of a){let r;if("text"===s.type){if(!s.text)continue;r={type:"text",text:s.text}}else r="resource_link"===s.type?{type:"resource_link",uri:s.uri??"",...s.fileName?{name:s.fileName}:{},...s.mimeType?{mimeType:s.mimeType}:{}}:{type:s.type,...s.mimeType?{mimeType:s.mimeType}:{},...s.fileName?{fileName:s.fileName}:{}};e.sendSessionUpdate(t,{sessionUpdate:"user_message_chunk",content:r,...i?{metadata:i}:{},...n?{timestamp:n}:{}},l)}}async replayAssistantTurnAsStream(e,t,s,i){const n=e=>{if(i||void 0!==e)return{...i?{turnId:i}:{},...void 0!==e?{seq:e}:{}}},r=new Set,o=(s,i,o)=>{if(!s?.length)return;const a=n(o);for(const n of s)n.uri&&!r.has(n.uri)&&(r.add(n.uri),e.sendSessionUpdate(t,{sessionUpdate:"agent_message_chunk",content:{type:"resource_link",uri:n.uri,name:n.name??"file",...n.mimeType?{mimeType:n.mimeType}:{}},...i?{timestamp:i}:{}},a))},a=n(s._seq);if(!s.sourceMessages?.length){const i={sessionUpdate:"history_turn",role:s.role,content:s.content,reasoning:s.reasoning,toolCalls:s.toolCalls,...s.timestamp?{timestamp:s.timestamp}:{}};return s.metadata&&(i.metadata=s.metadata),e.sendSessionUpdate(t,i,a),void o(s.files,s.timestamp,s._seq)}for(const i of s.sourceMessages){const r=i.updateType??"agent_message_chunk",a=n(i._seq);switch(r){case"agent_message_chunk":e.sendSessionUpdate(t,{sessionUpdate:"agent_message_chunk",content:{type:"text",text:i.content},...i.timestamp?{timestamp:i.timestamp}:{},...i.metadata?{metadata:i.metadata}:{}},a);break;case"agent_thought_chunk":e.sendSessionUpdate(t,{sessionUpdate:"agent_thought_chunk",content:{type:"text",text:i.content},...i.timestamp?{timestamp:i.timestamp}:{}},a);break;case"tool_call":{const n=i.toolCallId??"",r=s.toolCalls?.find(e=>e.id===n),o=i.toolName??"tool",l=i.content??"";let d={};if(l)try{d=JSON.parse(l)}catch{}e.sendSessionUpdate(t,buildToolCallPayload({toolCallId:n,toolName:o,args:d,kind:r?.kind,status:r?.status??"in_progress",timestamp:i.timestamp}),a);break}case"tool_call_update":{const n=i.toolCallId??"",r=s.toolCalls?.find(e=>e.id===n),l=i.toolName??r?.name??"tool",d=i.content??r?.output??"",c=await this.resolveReplayResourceLinks(n,l,i.files);e.sendSessionUpdate(t,buildToolCallUpdatePayload({toolCallId:n,toolName:l,outputText:d,kind:r?.kind,status:r?.status??"completed",resourceLinks:c,timestamp:i.timestamp}),a),o(c,i.timestamp,i._seq);break}}}o(s.files,s.timestamp,s._seq)}async resolveReplayResourceLinks(e,t,s){let i=s?.map(e=>({uri:e.uri,name:e.name??"file",...e.mimeType?{mimeType:e.mimeType}:{}}));if(i?.length)return i;if(i=await readEnrichmentCache(e).catch(()=>{}),i?.length)return i;if("beeos_upload_file"!==t)return;const n=await readUploadToolResult(e).catch(()=>{});if(!n)return;const r=buildToolResultSessionContentBlocks(t,n).filter(e=>"resource_link"===e.content?.type&&e.content.uri).map(e=>({uri:e.content.uri,name:e.content.name??"file",...e.content.mimeType?{mimeType:e.content.mimeType}:{}}));return r.length>0?r:void 0}emitPromptAsUserSessionUpdates(e,t,s){const i=(new Date).toISOString(),n=s?{turnId:s}:void 0;for(const r of t){const t=this.toUserSessionUpdateContent(r);if(!t)continue;const o={sessionUpdate:"user_message_chunk",content:t,timestamp:i};s&&void 0===o._turnId&&(o._turnId=s),this.transport.sendSessionUpdate(e.sessionId,o,n),e.pendingUserTurnUpdates?.push(o)}}toUserSessionUpdateContent(e){if("text"===e.type){if(!e.text)return;return{type:"text",text:e.text}}if("image"===e.type){const t=e.mimeType??e.mime_type??"application/octet-stream",s=e.fileName??e.file_name??e.name;if(!e.data&&!e.uri)return;return{type:"image",...e.data?{data:e.data}:{},...e.uri?{uri:e.uri}:{},...t?{mimeType:t}:{},...s?{fileName:s}:{}}}if("resource_link"===e.type){if(!e.uri)return;return{type:"resource_link",uri:e.uri,...e.title?{title:e.title}:{},...e.name?{name:e.name}:{},...e.mimeType??e.mime_type?{mimeType:e.mimeType??e.mime_type}:{}}}if("file"===e.type){const t=e.mimeType??e.mime_type??"application/octet-stream",s=e.fileName??e.file_name??e.filename??e.name;if(!e.data&&!e.text&&!e.uri)return;return{type:"file",...e.data?{data:e.data}:{},...e.text?{text:e.text}:{},...e.uri?{uri:e.uri}:{},...t?{mimeType:t}:{},...s?{fileName:s}:{}}}if("resource"===e.type){const t=e.resource??{},s=t.uri??e.uri,i=t.mimeType??t.mime_type??e.mimeType??e.mime_type,n=t.text??e.text,r=t.data??e.data,o=t.fileName??t.file_name??t.filename??t.name??e.fileName??e.file_name??e.filename??e.name;if(!s&&!n&&!r)return;return{type:"resource",resource:{...s?{uri:s}:{},...i?{mimeType:i}:{},...n?{text:n}:{},...r?{data:r}:{},...o?{fileName:o}:{}}}}}async handleSessionCancel(e,t){const s=t&&"object"==typeof t&&"sessionId"in t?t.sessionId:void 0;if(!s)return void(void 0!==e&&this.transport.sendAcpError(e,Errors.MissingParam,{param:"sessionId"}));const i=this.state.sessions.get(s);if(!i)return void(void 0!==e&&this.transport.sendAcpError(e,Errors.SessionNotFound));const n=i.activeRunBinding??this.observeActiveRun(i);if(!n){const t=this.sessionMutationCoordinator.snapshot(i.gatewaySessionKey);if("exclusive"===t?.attachmentPolicy&&void 0!==e){const t=this.sessionMutationCoordinator.gateSnapshot(i.gatewaySessionKey);return void this.sendSessionBusy(i,e,"exclusive_session_mutation",t.queueDepth)}return void(void 0!==e&&this.transport.sendResult(e,{}))}const r=await this.runtimeLeases.claim(this.createOperationId());let o=!1;try{const s=t&&"object"==typeof t&&"reason"in t&&"string"==typeof t.reason?t.reason:void 0,i={targetMessageId:n.targetMessageId,...s?{reason:s}:{}},a={name:"session.cancel",params:i,context:{...this.canonicalContextBase(i,r.operationId,r.runtimeEpoch),scope:"conversation",historyGeneration:n.historyGeneration,conversation:{runtimeConversationUid:n.runtimeConversationUid,runtimeSessionKey:n.runtimeSessionKey}}},l=this.runtimeCommands.dispatch(a,this.acpExecutionAttempt(a));o=!0,await l.completion,void 0!==e&&this.transport.sendResult(e,{})}catch(t){void 0!==e&&this.sendRuntimeCommandError(e,t)}finally{o||await this.runtimeLeases.release(r)}}hasActiveRun(e){return!e.promptDone&&(void 0!==e.pendingPromptId||void 0!==e.currentRunId||void 0!==e.gatewayRequestId)}observeLifecycle(e,t,s){observeLifecyclePromise(e,this.logger,{component:"bridge-core",action:t,...s?{sessionId:s.sessionId,gatewaySessionKey:s.gatewaySessionKey}:{}})}async handleCancellationResult(e,t){return t.ok?(this.observeLifecycle(t.terminalObservation.then(t=>{if(!t.ok)return this.publishCancellationRecovery(e)}),"cancel_terminal_observation",e),"accepted"):"recovery"in t?(await this.publishCancellationRecovery(e),"recovery_required"):"terminal_already_observed"}async publishCancellationRecovery(e){await this.terminateRunOutput(e,{kind:"failed",mode:"interrupted",message:SESSION_RECOVERY_REQUIRED_MESSAGE,errorCode:SESSION_RECOVERY_REQUIRED_CODE})}releaseSessionWriteGate(e){if(!e.writeGateToken)return;const t=e.writeGateToken;e.writeGateToken=void 0,this.sessionMutationCoordinator.releaseBeforeRuntime(t)}logRuntimeFailure(e,t){this.logger.warn?.(`[bridge-core] runtime_failure_classified session_id=${e.sessionId} gateway_session_key=${e.gatewaySessionKey} run_id=${e.currentRunId??""} gateway_request_id=${e.gatewayRequestId??""} error_code=${t.errorCode} stop_reason=${t.stopReason} source=${t.source} runtime=${t.runtimeKind}`)}async commitRunOutputTerminal(e,t){const s=this.state.sessions.get(e.sessionId);if(!s||s.gatewaySessionKey!==e.gatewaySessionKey)throw new Error(`run output lifecycle session unavailable: ${e.sessionId}`);await this.commitRunOutputTerminalForSession(s,t)}async commitRunOutputTerminalForSession(e,t){if("complete"!==t.kind&&"cancelled"!==t.kind)return"interrupted"===t.mode?(t.classification&&this.logRuntimeFailure(e,t.classification),void await this.endRun(e,{kind:"interrupted",stopReason:t.classification?.stopReason??"interrupted",message:t.message,errorCode:t.errorCode})):void await this.endRun(e,{kind:"error",code:t.code,message:t.message});await this.endRun(e,{kind:"complete",stopReason:t.stopReason})}async terminateRunOutput(e,t){const s=resolveTurnId(e);s&&this.runOutputRouter.hasRoute(s)?await this.runOutputRouter.terminal(s,t):await this.commitRunOutputTerminalForSession(e,t)}sendSessionBusy(e,t,s,i){this.logger.warn?.(`[bridge-core] session_write_gate_busy session_id=${e.sessionId} gateway_session_key=${e.gatewaySessionKey} reason=${s} queue_depth=${i}`),this.transport.sendError(t,Errors.AgentError.rpcCode,"Agent is busy",{code:"session_busy",reason:s,queueDepth:i}),this.transport.sendSessionUpdate(e.sessionId,{sessionUpdate:"stream_complete",stopReason:"interrupted",error:INTERRUPTED_PUBLIC_MESSAGE,errorCode:"session_busy"},{turnId:`req:${String(t)}`})}async endRun(e,t){if(e.promptDone||e.mutationTerminalCommitting)return this.logger.debug?.(`[bridge-core] endRun skipped (promptDone=true) sessionId=${e.sessionId} outcome=${t.kind}`),!1;if(void 0===e.pendingPromptId&&void 0===e.currentRunId&&void 0===e.gatewayRequestId)return this.logger.debug?.(`[bridge-core] endRun skipped (no pendingPromptId and no currentRunId) sessionId=${e.sessionId} outcome=${t.kind}`),!1;e.mutationTerminalCommitting=!0,this.state.clearPromptTimeout(e),this.state.clearStreamIdleTimer(e);const s=e.writeGateToken,i=e.activeRunBinding;let n,r,o,a,l="timeout"===e.mutationTerminalOverride?{kind:"interrupted",stopReason:"timeout",message:"The agent did not finish before the session timeout.",errorCode:"gateway_timeout"}:t;if(s){const e="complete"===l.kind?"cancelled"===l.stopReason?"cancelled":"completed":"interrupted"===l.kind&&"timeout"===l.stopReason?"timeout":"error",t=await this.sessionMutationCoordinator.converge(s,e);t.ok?n=t.permit:l={kind:"interrupted",stopReason:"interrupted",message:SESSION_RECOVERY_REQUIRED_MESSAGE,errorCode:SESSION_RECOVERY_REQUIRED_CODE}}try{const t=resolveTurnId(e),s=t?{turnId:t}:void 0;e.promptDone=!0;const i=e.currentRunId;if("complete"===l.kind){if(i&&this.onRunComplete){const t=e.agentAssistantTextSoFar??e.chatAssistantTextSoFar??"";this.onRunComplete(i,t,l.stopReason)}void 0!==e.pendingPromptId&&this.transport.sendResult(e.pendingPromptId,{stopReason:l.stopReason}),this.transport.sendSessionUpdate(e.sessionId,{sessionUpdate:"stream_complete",stopReason:l.stopReason},s),this.tryGenerateTitleFromHistory(e,e.sessionId)}else"interrupted"===l.kind?(i&&this.onRunFailed&&this.onRunFailed(i,l.message),void 0!==e.pendingPromptId&&this.transport.sendError(e.pendingPromptId,Errors.AgentError.rpcCode,l.message,{code:l.errorCode??"interrupted",stopReason:l.stopReason}),this.transport.sendSessionUpdate(e.sessionId,{sessionUpdate:"stream_complete",stopReason:l.stopReason,error:l.message,...l.errorCode?{errorCode:l.errorCode}:{}},s)):(i&&this.onRunFailed&&this.onRunFailed(i,l.message),void 0!==e.pendingPromptId&&this.transport.sendError(e.pendingPromptId,l.code,l.message),this.transport.sendSessionUpdate(e.sessionId,{sessionUpdate:"stream_complete",stopReason:"error",error:l.message},s))}catch(e){r=e}try{this.cleanupPrompt(e)}catch(e){o=e}finally{e.writeGateToken=void 0,e.activeRunBinding=void 0,n&&this.sessionMutationCoordinator.release(n)}if(i){const e="complete"===l.kind?"cancelled"===l.stopReason?"cancelled":"completed":"interrupted"===l.kind&&"cancelled"===l.stopReason?"cancelled":"failed";this.activeRuns.markTerminal(i,e)}try{this.onRunEnded?.(e,l)}catch(e){a=e}if(r)throw r;if(o)throw o;if(a)throw a;return!0}schedulePromptTimeout(e){let t;if(this.state.clearPromptTimeout(e),e.cronJobId){if(!this.cronOverrides.isInitialized())return void this.cronReady.then(()=>{!e.cronJobId||void 0===e.pendingPromptId&&void 0===e.currentRunId||this.schedulePromptTimeout(e)}).catch(e=>this.logger.error?.(`[bridge-core] cron overrides readiness failed: ${e instanceof Error?e.message:e}`));const s=this.cronOverrides.get(e.cronJobId)??this.config.cronMaxDurationDefaultMs;t=Math.max(CRON_DURATION_FLOOR_MS,Math.min(this.config.cronMaxDurationCapMs,s))}else t=this.config.promptTimeoutMs??DEFAULT_PROMPT_TIMEOUT_MS;t<=0||(e.promptTimeoutTimer=setTimeout(()=>{if(e.mutationTerminalCommitting)return;if(void 0===e.pendingPromptId&&void 0===e.currentRunId)return;this.logger.warn?.(`[bridge-core] Prompt timed out sessionId=${e.sessionId} after ${t}ms`),e.mutationTerminalOverride="timeout";const s=this.getGateway(),i=e.writeGateToken;s?.isConnected&&i?this.observeLifecycle(this.sessionMutationCoordinator.requestCancel(i,s).then(t=>this.handleCancellationResult(e,t)),"prompt_timeout_cancel",e):s?.isConnected?this.observeLifecycle(s.chatAbort(e.gatewaySessionKey,e.currentRunId).then(()=>this.interruptPrompt(e,classifyAgentRuntimeFailure({raw:`Prompt timed out after ${t}ms`,source:"live_gateway",runtimeKind:"openclaw"}))),"prompt_timeout_unowned_cancel",e):i&&(this.sessionMutationCoordinator.requireRecovery(i,"gateway_disconnected","disconnect"),this.observeLifecycle(this.terminateRunOutput(e,{kind:"failed",mode:"interrupted",message:SESSION_RECOVERY_REQUIRED_MESSAGE,errorCode:SESSION_RECOVERY_REQUIRED_CODE}),"prompt_timeout_disconnected",e))},t),"object"==typeof e.promptTimeoutTimer&&"unref"in e.promptTimeoutTimer&&e.promptTimeoutTimer.unref())}adoptBackgroundMutation(e,t){const s=this.sessionMutationCoordinator.attachOrStartExternal({sessionKey:e.gatewaySessionKey,label:"background_run"},"acp",t);return s.ok?("owner"===s.ownership&&(e.writeGateToken=s.token),s):(this.logger.error?.(`[bridge-core] background mutation rejected sessionKey=${e.gatewaySessionKey} runId=${t} reason=${s.reason}`),s)}cleanupPrompt(e){this.state.clearPromptTimeout(e),this.state.clearStreamIdleTimer(e);const t=e.currentRunId;if(t){this.state.promptsByRunId.delete(t),this.state.runIdToCronJobId.delete(t),this.state.markRunCompleted(t);const e=`tc_${t}_`;for(const[t,s]of this.state.toolCallIdMap)s.startsWith(e)&&this.state.toolCallIdMap.delete(t)}e.gatewayRequestId&&this.state.promptsByGatewayRequestId.delete(e.gatewayRequestId),e.pendingPromptId=void 0,e.promptDone=void 0,e.currentRunId=void 0,e.gatewayRequestId=void 0,e.assistantStreamStartedAt=void 0,e.gatewayMethod=void 0,e.mutationTerminalOverride=void 0,e.mutationTerminalCommitting=void 0,e.assistantMq?.close(),e.assistantMq=void 0,e.pendingUserTurnUpdates=void 0}async completePrompt(e,t){"cancelled"!==t?await this.terminateRunOutput(e,{kind:"complete",stopReason:t}):await this.terminateRunOutput(e,{kind:"cancelled",stopReason:"cancelled"})}async interruptPrompt(e,t){await this.terminateRunOutput(e,{kind:"failed",mode:"interrupted",message:t.publicMessage,errorCode:t.errorCode,classification:t})}async failPrompt(e,t,s){await this.terminateRunOutput(e,{kind:"failed",mode:"error",code:t,message:s})}cleanupAllPendingPrompts(){for(const e of this.state.sessions.values())e.writeGateToken?(this.sessionMutationCoordinator.requireRecovery(e.writeGateToken,"gateway_disconnected","disconnect"),this.observeLifecycle(this.terminateRunOutput(e,{kind:"failed",mode:"interrupted",message:SESSION_RECOVERY_REQUIRED_MESSAGE,errorCode:SESSION_RECOVERY_REQUIRED_CODE}),"gateway_disconnect_recovery",e)):this.observeLifecycle(this.interruptPrompt(e,classifyAgentRuntimeFailure({raw:Errors.GatewayDisconnected.message,source:"live_gateway",runtimeKind:"openclaw"})),"gateway_disconnect_interrupt",e)}handleGatewayResponse(e){const t=e.id;if(!t)return;const s=this.state.promptsByGatewayRequestId.get(t);if(!s||s.promptDone)return;if(!e.ok){const i=e.error,n=i?.message??"gateway returned error";this.logger.warn?.(`[bridge-core] handleGatewayResponse: gateway rejected prompt gatewayRequestId=${t} error=${n}`);const r=classifyAgentRuntimeFailure({raw:n,source:"live_gateway",runtimeKind:"openclaw"});return void("interrupted"===r.kind?this.observeLifecycle(this.interruptPrompt(s,r),"gateway_response_interrupt",s):(this.logRuntimeFailure(s,r),this.observeLifecycle(this.failPrompt(s,Errors.SendFailed.rpcCode,r.publicMessage),"gateway_response_failure",s)))}const i=e.payload,n=i?.runId;if(n)s.currentRunId=n,this.sessionMutationCoordinator.bindRun(s.writeGateToken,n),this.observeActiveRun(s),s.assistantStreamStartedAt=Date.now(),this.state.promptsByRunId.set(n,s),openRunOutputRoute(this.runOutputRouter,s,n,["acp"]),this.logger.debug?.(`[bridge-core] handleGatewayResponse: bound runId=${n} gatewayRequestId=${t} sessionId=${s.sessionId}`);else{const e=resolveTurnId(s);e&&openRunOutputRoute(this.runOutputRouter,s,e,["acp"]),this.logger.debug?.(`[bridge-core] handleGatewayResponse: no runId in payload gatewayRequestId=${t} sessionId=${s.sessionId}`)}}async handleCronRequest(e){const t=this.getGateway();if(!t?.isConnected)return void this.transport.sendAcpError(e.id,Errors.GatewayNotConnected);const s=e.method.replace("/","."),i={...e.params??{}};["cron.add","cron.update","cron.remove","cron.run","cron.runs"].includes(s)&&(i.__beeosCronIngress="acp"),"cron.update"===s&&(i.patch=i.patch&&"object"==typeof i.patch?{...i.patch}:{});try{const t=await this.dispatchLegacyCronCapability(s,i),n=await this.mapCronResultForAcp(s,t);this.transport.sendResult(e.id,n??{}),"cron.add"!==s&&"cron.update"!==s&&"cron.remove"!==s||this.refreshCronIsolatedSessionKeys()}catch(t){const i=t instanceof Error?t.message:"cron request failed";this.logger.warn?.(`[bridge-core] ${s} failed: ${i}`),this.transport.sendAcpError(e.id,Errors.SendFailed,{message:i})}}async mapCronResultForAcp(e,t){if(!t||"object"!=typeof t)return t;const s=t;await this.cronOverrides.snapshot();const i="cron.list"===e||"cron.status"===e,n=e=>{if(!e||"object"!=typeof e)return e;const t=e,s=t.id;if("string"!=typeof s)return e;const n=this.cronOverrides.localId(s)??s,r=this.cronChannelCache.get(n),o=r?encodeBeeosChannelTag("string"==typeof t.description?t.description:void 0,r):t.description,a=this.cronOverrides.get(n);return{...t,id:n,...void 0===o?{}:{description:o},...i&&"number"==typeof a?{_beeos_meta:{maxDurationMs:a}}:{}}};if(Array.isArray(s.jobs))return{...s,jobs:s.jobs.map(n)};if(s.jobs&&"object"==typeof s.jobs){const e=s.jobs,t={};for(const[s,i]of Object.entries(e))t[s]=n(i);return{...s,jobs:t}}return Array.isArray(s.runs)?{...s,runs:s.runs.map(e=>{if(!e||"object"!=typeof e)return e;const t=e,s="string"==typeof t.jobId?this.cronOverrides.localId(t.jobId)??t.jobId:t.jobId;return{...t,jobId:s}})}:"cron.add"===e||"cron.update"===e?n(t):t}async allocateCronChannel(e){if(!this.agentFetch||!this.agentGatewayUrl)return void this.logger.debug?.("[bridge-core] agentFetch/agentGatewayUrl unavailable, skip channel alloc");const t=this.getAuthoritativeInstanceId?.()?.trim();if(!t?.startsWith("inst_"))return void this.logger.warn?.("[bridge-core] no authoritative inst_* instance id resolved yet, skip channel alloc");const s=this.config.gateway.agentId||this.getDefaultAgentId(),i=this.resolvePlatformAgentId(s)?.trim();if(!i)return void this.logger.warn?.(`[bridge-core] no platform agent id for localAgentId=${s}, skip channel alloc`);const n={source:"cron",job_name:e,protocol:"beeos-conversation",target_agent_id:i,title:e};try{const e=`${this.agentGatewayUrl}/api/v1/channels`,s=await this.agentFetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({participants:[t,"service:cron"],metadata:n})});if(!s.ok)return void this.logger.warn?.(`[bridge-core] channel alloc failed: ${s.status}`);return(await s.json()).id}catch(e){return void this.logger.warn?.(`[bridge-core] channel alloc error: ${e instanceof Error?e.message:e}`)}}setCronChannelCache(e,t){if(this.cronChannelCache.size>=AcpGatewayBridgeCore.CRON_CHANNEL_CACHE_MAX){const e=this.cronChannelCache.keys().next().value;void 0!==e&&this.cronChannelCache.delete(e)}this.cronChannelCache.set(e,t)}async refreshCronIsolatedSessionKeys(){const e=this.getGateway();if(e?.isConnected)try{const t=await e.request("cron.list",{}),s=t?.jobs;if(!Array.isArray(s))return;const i=new Set,n=[];for(const e of s){if(!e||"object"!=typeof e)continue;const t=e;if("string"==typeof t.id&&n.push(t.id),"isolated"!==t.sessionTarget)continue;const s=t.sessionKey;"string"==typeof s&&s.length>0&&i.add(s)}this.cronIsolatedSessionKeys=i,await this.cronOverrides.reconcile(n)}catch(e){this.logger.debug?.(`[bridge-core] refreshCronIsolatedSessionKeys failed: ${e instanceof Error?e.message:e}`)}}}async function fetchWithRetry(e,t={}){const s=t.maxRetries??3;let i;for(let n=0;n<=s;n++){const r=await fetch(e,{signal:AbortSignal.timeout(12e4),redirect:"follow"});if(r.ok)return Buffer.from(await r.arrayBuffer());if(429===r.status||r.status>=500){const o=Number(r.headers.get("retry-after"))||0,a=1e3*Math.max(o,2**n);if(i=new Error(`HTTP ${r.status} from ${e}`),n<s){t.logger?.info?.(`[bridge-core] Download ${r.status}, retry ${n+1}/${s} in ${(a/1e3).toFixed(0)}s`),await new Promise(e=>setTimeout(e,a));continue}}throw new Error(`Failed to download ${e}: HTTP ${r.status}`)}throw i??new Error(`Download failed after retries: ${e}`)}const SKILL_ROOT_MARKERS=["SKILL.md","skill.md","SKILL.MD"];function normalizeSkillArtifactPath(e){const t=e.replaceAll("\\","/").replace(/^\.\//,"");if(!t||t.startsWith("/")||t.split("/").includes(".."))throw new RuntimeCommandError("INVALID_PARAMS",`Invalid skill artifact path: ${e}`);return t}function parseSkillZip(e){if(e.byteLength>MAX_ZIP_BYTES)throw new RuntimeCommandError("INVALID_PARAMS","Skill archive too large");const t=new AdmZip(e).getEntries().filter(e=>!e.isDirectory).map(e=>({name:normalizeSkillArtifactPath(e.entryName),bytes:e.getData()})).filter(e=>!e.name.split("/").includes("__MACOSX"));if(0===t.length)throw new RuntimeCommandError("INVALID_PARAMS","Skill archive is empty");const s=1===new Set(t.map(e=>e.name.split("/")[0])).size&&t.every(e=>e.name.includes("/")),i=new Map;for(const e of t){const t=s?e.name.slice(e.name.indexOf("/")+1):e.name;if(!t||i.has(t))throw new RuntimeCommandError("INVALID_PARAMS",`Duplicate skill path: ${t}`);i.set(t,e.bytes)}return i}async function resolveSkillRoot(e){for(const t of SKILL_ROOT_MARKERS)try{return await stat(join(e,t)),e}catch{}const t=(await readdir(e,{withFileTypes:!0})).filter(e=>e.isDirectory()&&!e.name.startsWith(".")&&"__MACOSX"!==e.name);if(1===t.length){const s=join(e,t[0].name);for(const e of SKILL_ROOT_MARKERS)try{return await stat(join(s,e)),s}catch{}return s}return e}async function extractZipToDir(e,t){const s=new AdmZip(e),i=join(t,".beeos-extract-tmp-"+Date.now());try{await mkdir(i,{recursive:!0}),s.extractAllTo(i,!0);const e=await resolveSkillRoot(i),n=await readdir(e,{withFileTypes:!0});let r=0;for(const s of n){if(s.name.startsWith(".beeos-extract-tmp")||"__MACOSX"===s.name)continue;const i=join(e,s.name),n=join(t,s.name);await rename(i,n).catch(async()=>{await cp(i,n,{recursive:!0})}),r++}if(0===r)throw new Error("Zip archive contained no extractable files")}finally{await rm(i,{recursive:!0,force:!0}).catch(()=>{})}}const VALID_SLUG_PATTERN=/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,NON_ASCII_PATTERN=/[^\x00-\x7F]/;function validateSkillSlug(e){const t=e.trim();if(!t||t.includes("/")||t.includes("\\")||t.includes(".."))throw new Error(`Invalid skill slug: ${e}`);if(NON_ASCII_PATTERN.test(t)||!VALID_SLUG_PATTERN.test(t))throw new Error(`Invalid skill slug: ${e}`);return t}function safeDirName(e){const t=e.trim();return t?t.replaceAll("/","__").replaceAll("\\","__"):t}function extractMcpCredentialMaterial(e){const t=mcpStringMap(e.headers,"MCP headers"),s=mcpStringMap(e.env,"MCP environment");if(!(t&&0!==Object.keys(t).length||s&&0!==Object.keys(s).length))return;const i=e.transport??("string"==typeof e.command?"stdio":"string"==typeof e.url?"streamable-http":void 0);if("stdio"===i&&t&&Object.keys(t).length)throw new Error("MCP stdio config cannot include headers");if("stdio"!==i&&s&&Object.keys(s).length)throw new Error("MCP HTTP config cannot include env");return{...t&&Object.keys(t).length?{headers:t}:{},...s&&Object.keys(s).length?{env:s}:{}}}function mcpStringMap(e,t){if(void 0!==e){if(!e||"object"!=typeof e||Array.isArray(e)||Object.entries(e).some(([e,t])=>!e||/[\u0000-\u001f\u007f]/.test(e)||"string"!=typeof t))throw new Error(`${t} invalid`);return{...e}}}function requireCredentialReferencePort(e){if(!e)throw new Error("MCP credential reference vault is unavailable");return e}function resolveSkillInstallDir(e,t){const s=safeDirName(t),i=join(e,s),n=resolve(e),r=resolve(i),o=relative(n,r);if(!o||".."===o||o.startsWith(`..${sep}`)||path.isAbsolute(o))throw new Error("Invalid skill target path");return i}
@@ -1 +1 @@
1
- import{WebSocketServer,WebSocket}from"ws";import{randomUUID}from"node:crypto";import{AsyncLocalStorage}from"node:async_hooks";let BridgeClientCtor;import{TerminalManager}from"./terminal/session-manager.js";import{getOrCreateRuntimeTerminalService}from"./terminal/runtime-terminal-service.js";import{RuntimeTerminalOutboundRegistration}from"./terminal/runtime-terminal-outbound-registration.js";import{FileTerminalAuthorityJournal}from"./terminal/terminal-authority-journal.js";import{LocalTerminalAuthorityRegistry}from"./terminal/local-terminal-authority.js";import{TerminalLeaseBoundBridgeClient}from"./terminal/terminal-lease-bound-bridge-client.js";import{join}from"node:path";import{AcpTransport}from"./acp-gateway/transport.js";import{AcpGatewaySessionState}from"./acp-gateway/session-state.js";import{AcpGatewayBridgeCore}from"./acp-gateway/bridge-core.js";import{BeeosFileResolver}from"./acp-gateway/beeos-file-resolver.js";import{AcpGatewayEvents}from"./acp-gateway/gateway-events.js";import{AcpRunOutputChannel}from"./acp-gateway/acp-run-output-channel.js";import{openRunOutputRoute}from"./acp-gateway/run-output-route.js";import{createAcpRunOutputRouter}from"./runtime/run-output-composition.js";import{AgentMappingRegistry}from"./runtime/agent-mapping-registry.js";import{sweepStaleSessionLocks}from"./acp-gateway/stale-session-lock-sweeper.js";import{TerminalBridgeHandler}from"./terminal/bridge-handler.js";import{isAgentSyncWaitForGateway,syncAgentsToPlatform}from"./agent-sync.js";import{CanvasOrchestrator}from"./canvas-orchestrator.js";import{discoverBridgeFromGW}from"./bridge-discovery.js";import{writeDiscoveryCache}from"./discovery-cache.js";function formatBridgeEventArgs(e){return 0===e.length?"(no args)":e.map((e,t)=>`arg${t}=${formatBridgeEventValue(e)}`).join(" ")}function formatBridgeEventValue(e){if(void 0===e)return"undefined";if(null===e)return"null";if(e instanceof Error){const t=e.stack?e.stack.split("\n").slice(0,4).join(" | "):"";return`Error(name=${e.name},message=${e.message}${t?`,stack=${t}`:""})`}if("object"==typeof e)try{return JSON.stringify(e,jsonReplacerForLogs)}catch{return String(e)}return String(e)}function jsonReplacerForLogs(e,t){return t instanceof Error?{name:t.name,message:t.message,stack:t.stack}:t}export class ACPServer{agentFetchFn;instanceId;runtimeAuthority;mcpProjection;mcpCredentialReferences;boundAgentFetchFn;wss=null;gateway;client=null;config;logger;writeObs;transport;state;core;events;terminalMgr;terminalHandler;localTerminalAuthorityRegistry;acpConnectionTerminalHandlers=new WeakMap;runtimeTerminalService;terminalOutbound=null;terminalServiceReady=!1;runtimeAgentCapabilities;agentProjection;markAgentProjectionDirty;templateOriginReporter;agentMappings;runtimeRpcConsumer;runtimeRpcConsumerFactory;canvasOrch;sessionMutationCoordinator;runOutputRouter;cronConversationDelivery;pendingToolCallSessionKeys=[];thirdPartyChannels;agentSyncRetryTimer=null;agentSyncInFlight=!1;runtimeServiceFactoryFenced=!1;legacyFencedSyncFailureCount=0;lifecyclePhase="stopped";lifecycleGeneration=0;activeGeneration;bridgeInstance=null;bridgeClosePromises=new WeakMap;consumerClosePromises=new WeakMap;teardownContext=new AsyncLocalStorage;bridgeConsecutiveFailures=0;static RE_DISCOVER_THRESHOLD=2;static LIFECYCLE_CONNECT_TIMEOUT_MS=1e4;static LIFECYCLE_TEARDOWN_TIMEOUT_MS=5e3;drainNextDeferredSend(e){const t=e.pendingAgentSends?.shift();t&&(e.pendingAgentSends?.length||(e.pendingAgentSends=void 0),this.agentSend(t.message,t.attachments,e.gatewaySessionKey).catch(e=>this.logger.warn?.(`[acp-server] Deferred agentSend failed: ${e instanceof Error?e.message:String(e)}`)))}async gatewayRequest(e,t){if(!this.gateway?.isConnected)throw new Error("Gateway not connected");return this.gateway.request(e,t)}async chatAbort(e,t){const i=this.gateway;if(!i?.isConnected)throw new Error("Gateway not connected");return i.chatAbort(e,t)}async agentSend(e,t,i){if(!this.gateway?.isConnected)throw new Error("Gateway not connected");const n=this.config.gateway.agentId||this.core.getDefaultAgentId();let r,s;i?(r=this.state.findSessionByKey(i),s=i,r?this.logger.info?.(`[acp-server] agentSend: resolved session for key=${i} sid=${r.sessionId}`):(r=this.core.ensureSessionForKey(s),this.logger.info?.(`[acp-server] agentSend: created session for key=${i} sid=${r.sessionId}`))):(s=`agent:${n}:ephemeral:${randomUUID()}`,r=this.core.ensureSessionForKey(s),this.logger.info?.(`[acp-server] agentSend: no targetSessionKey, created ephemeral session key=${s}`));const a=await this.sessionMutationCoordinator.acquireInternal({sessionKey:s,label:"agentSend"});let o=!1;try{const i=this.gateway;if(!i?.isConnected)throw new Error("Gateway not connected");if(r&&!r.promptDone&&(void 0!==r.pendingPromptId||void 0!==r.currentRunId||void 0!==r.gatewayRequestId))return(r.pendingAgentSends??=[]).push({message:e,attachments:t}),this.logger.info?.("[acp-server] Session has active run, deferred message (queue=%d)",r.pendingAgentSends.length),void this.sessionMutationCoordinator.releaseBeforeRuntime(a);o=!0;const c=await i.agentSend(s,e,n,t);return c?.runId&&r?(this.state.resetForNewRun(r,r.sessionId,this.transport),r.currentRunId=c.runId,this.sessionMutationCoordinator.bindRun(a,c.runId),this.core.observeActiveRun(r),r.assistantStreamStartedAt=Date.now(),r.writeGateToken=a,this.state.promptsByRunId.set(c.runId,r),openRunOutputRoute(this.runOutputRouter,r,c.runId,["acp"])):this.sessionMutationCoordinator.releaseBeforeRuntime(a),c}catch(e){throw o?this.sessionMutationCoordinator.requireRecovery(a,"terminal_not_observed"):this.sessionMutationCoordinator.releaseBeforeRuntime(a),e}}setCronConversationDelivery(e){this.cronConversationDelivery=e}getRuntimeCommandDispatcher(){return this.core.getRuntimeCommandDispatcher()}getActiveRunAuthority(){return this.core.getActiveRunAuthority()}nudgeMcpNativeProjection(){this.core.nudgeMcpNativeProjection()}ensureImInboundSession(e,t,i,n){const r=this.core.ensureSessionForKey(e);r.channelId=t,r.inboundMessageId=i,r.activeMentions=n.length>0?n:void 0}sendBridgeMessage=e=>{const t=this.client;if(t&&t.readyState===WebSocket.OPEN)try{t.send(JSON.stringify(e))}catch(e){this.logger.debug?.("[acp-server] bridge send failed (socket may be closing):",e)}else this.logger.debug?.("[acp-server] bridge not ready, dropping message")};constructor(e,t,i,n,r,s,a,o,c,l,h,g,d,u={}){this.agentFetchFn=r,this.instanceId=s,this.runtimeAuthority=c,this.mcpProjection=h,this.mcpCredentialReferences=g,this.boundAgentFetchFn=d,this.config=e,this.logger=t,this.writeObs=n??(()=>{}),this.thirdPartyChannels=a??new Set,this.sessionMutationCoordinator=i,this.gateway=u.gateway??null,this.agentProjection=u.agentProjection,this.markAgentProjectionDirty=u.markAgentProjectionDirty,this.templateOriginReporter=u.templateOriginReporter,this.agentMappings=u.agentMappings??new AgentMappingRegistry,this.runtimeRpcConsumerFactory=l,this.runtimeAgentCapabilities=u.agentCapabilities??{canvas:this.config.canvasEnabled,terminal:!1,webSsh:!1,fileSystem:!0,image:!0,audio:!1,mcp:!0,cron:!0,streaming:!0,pushNotifications:this.config.messageBus.enabled},this.runtimeAuthority?.setRuntimeRealtimeCapabilityReady("canvas",this.config.canvasEnabled),this.runtimeAuthority?.setRuntimeRealtimeCapabilityReady("terminal",!0===this.config.terminalServiceEnabled),this.transport=new AcpTransport(this.sendBridgeMessage,this.writeObs,void 0,()=>{const e=this.client;return!!e&&e.readyState===WebSocket.OPEN}),this.state=new AcpGatewaySessionState(this.logger);const m=(u.createRunOutputRouter??createAcpRunOutputRouter)({acp:new AcpRunOutputChannel({state:this.state,transport:this.transport}),lifecycle:{terminal:(e,t)=>this.core.commitRunOutputTerminal(e,t)}});this.runOutputRouter=m;const p=this.agentFetchFn&&this.config.agentGatewayUrl?new BeeosFileResolver({agentGatewayUrl:this.config.agentGatewayUrl,agentFetch:this.agentFetchFn,writeObs:this.writeObs}):void 0;this.core=new AcpGatewayBridgeCore({state:this.state,transport:this.transport,config:this.config,logger:this.logger,writeObs:this.writeObs,getGateway:()=>this.gateway,instanceId:this.instanceId,getAuthoritativeInstanceId:()=>process.env.BEEOS_INSTANCE_ID?.trim()||this.getDefaultPlatformAgent()?.instanceId,thirdPartyChannels:this.thirdPartyChannels,beeosFileResolver:p,agentFetch:this.agentFetchFn,agentGatewayUrl:this.config.agentGatewayUrl,sessionMutationCoordinator:i,triggerAgentSync:()=>this.tryAgentSync(),runtimeAgentIdentities:this.runtimeAuthority?.identities,runtimeCommandJournal:this.runtimeAuthority?.journal,markAgentProjectionDirty:this.markAgentProjectionDirty,getAgentProjection:this.agentProjection?()=>this.agentProjection:void 0,templateOriginReporter:this.templateOriginReporter,mcpProjection:this.mcpProjection,mcpCredentialReferences:this.mcpCredentialReferences,runOutputRouter:m,agentMappings:this.agentMappings}),this.canvasOrch=new CanvasOrchestrator({state:this.state,transport:this.transport,config:this.config,logger:this.logger,writeObs:this.writeObs,getSessionIdForToolCall:e=>this.getSessionIdForToolCall(e),gateway:null,sendAgentMessage:(e,t)=>this.agentSend(e,void 0,t),resolvePlatformAgentId:e=>this.core.resolvePlatformAgentId(e)??""}),this.events=new AcpGatewayEvents({state:this.state,transport:this.transport,config:this.config,logger:this.logger,writeObs:this.writeObs,runOutputRouter:m,schedulePromptTimeout:e=>this.core.schedulePromptTimeout(e),adoptBackgroundMutation:(e,t)=>this.core.adoptBackgroundMutation(e,t),abortGatewayRun:async(e,t)=>{await this.chatAbort(e,t)},agentFetch:this.agentFetchFn,getCronConversationDelivery:()=>this.cronConversationDelivery??null,getCanvasState:()=>this.canvasOrch.canvasState,gatewayRequest:(e,t)=>{const i=this.gateway;return i?.isConnected?i.request(e,t):Promise.reject(new Error("gateway not connected"))},cronChannelCache:this.core.cronChannelCache}),this.core.onRunEnded=(e,t)=>{"complete"===t.kind?this.canvasOrch.onPromptComplete(e):this.canvasOrch.onPromptFailed(),this.drainNextDeferredSend(e)},this.terminalMgr=o??new TerminalManager,this.localTerminalAuthorityRegistry=new LocalTerminalAuthorityRegistry(this.terminalMgr),this.terminalHandler=this.createTerminalHandler(this.transport),this.runtimeTerminalService=getOrCreateRuntimeTerminalService(this.terminalMgr),this.core.setRuntimeTerminalService(this.runtimeTerminalService),this.core.handleTerminalRequest=e=>this.terminalHandler.handleRequest(e),this.core.handleTerminalNotification=(e,t)=>this.terminalHandler.handleNotification(e,t),this.core.handleCanvasNotification=(e,t)=>{this.canvasOrch.handleCanvasNotification(e,t)},this.core.getCanvasState=()=>this.canvasOrch.canvasState,this.core.onSessionLoaded=(e,t,i)=>{this.canvasOrch.onSessionLoaded(e,t,i)},this.core.onSkillInstalled=(e,t)=>{this.registerSkillFormSchemas(e,t)},this.core.onSessionDeleted=e=>{this.canvasOrch.onSessionDeleted(e)},this.core.onSessionCleared=(e,t)=>{this.canvasOrch.onSessionCleared(e,t)},this.core.handleSlashCommand=async e=>this.canvasOrch.handleSlashCommand(e),this.core.onPromptStart=()=>{this.canvasOrch.onPromptStart()},this.state.startSessionGc(e=>{e.writeGateToken&&this.sessionMutationCoordinator.requireRecovery(e.writeGateToken,"terminal_not_observed"),this.core.cleanupPrompt(e)},e=>{this.canvasOrch.canvasState?.removeSession(e.gatewaySessionKey),this.transport.resetSessionUpdateCount(e.sessionId)}),this.gateway&&this.bindGateway(this.gateway)}setRuntimeRpcConsumerFactory(e){if(e!==this.runtimeRpcConsumerFactory){if("stopped"!==this.lifecyclePhase)throw new Error("runtime Service factory is immutable after ACP start");this.runtimeRpcConsumerFactory=e}}async rebindRuntimeDelivery(){if("started"!==this.lifecyclePhase)return;const e=this.activeGeneration,t=e?.factory??this.runtimeRpcConsumerFactory,i=e?.authority??this.runtimeAuthority;if(!e||!t||!i)return;if(!i.authorityLease)return;const n=i.currentConsumerBinding();if(!n)return void this.logger.warn?.("[acp-server] runtime Service delivery rebind skipped — no consumer binding");const r=e.runtimeConsumer;let s;r&&(e.runtimeConsumer=void 0,this.runtimeRpcConsumer===r&&(this.runtimeRpcConsumer=void 0),this.core.setRuntimeReceiptHistoryPort(void 0),await this.closeConsumerOnce(r).catch(()=>{}));try{s=await t.create({dispatcher:this.core.getRuntimeCommandDispatcher(),binding:n,getBinding:()=>i.currentConsumerBinding()??n,getLease:()=>i.authorityLease,createRuntimeSession:async(e,t)=>await this.core.createRuntimeSession(e,t),signal:e.abortController.signal})}catch(e){this.logger.error?.(`[acp-server] runtime Service delivery rebind failed: ${e instanceof Error?e.stack??e.message:String(e)}`);try{i.setRuntimeConsumerLive(!1)}catch{}return}if(this.activeGeneration!==e||"started"!==this.lifecyclePhase||e.abortController.signal.aborted)await this.closeConsumerOnce(s).catch(()=>{});else{this.core.setRuntimeReceiptHistoryPort(s.receiptHistory),e.runtimeConsumer=s,this.runtimeRpcConsumer=s,s.start();try{i.setRuntimeConsumerLive(!0)}catch{}this.runtimeServiceFactoryFenced=!1,this.logger.info?.("[acp-server] runtime Service delivery rebound after MessageClient connect")}}start(e){const t=this.activeGeneration;if("starting"===this.lifecyclePhase||"started"===this.lifecyclePhase)return this.logger.info?.("[acp-server] start() skipped — already started (singleton reuse)"),t?.startPromise??Promise.resolve();if("stopping"===this.lifecyclePhase){return(t?.stopPromise??Promise.resolve()).then(()=>this.start(e),()=>this.start(e))}const i={id:++this.lifecycleGeneration,factory:this.runtimeRpcConsumerFactory,authority:this.runtimeAuthority,abortController:new AbortController,startPromise:Promise.resolve(),authorityOwned:!1};return this.lifecyclePhase="starting",this.activeGeneration=i,i.startPromise=this.startGeneration(i,e).then(()=>{this.activeGeneration===i&&"starting"===this.lifecyclePhase&&(this.lifecyclePhase="started")}).catch(async e=>{try{await this.teardownGeneration(i)}catch(e){this.logger.debug?.(`[acp-server] failed-start teardown also failed: ${e instanceof Error?e.message:String(e)}`)}finally{this.activeGeneration===i&&"starting"===this.lifecyclePhase&&(this.activeGeneration=void 0,this.lifecyclePhase="stopped")}throw e}),i.startPromise}async startGeneration(e,t){const i=e.authority,n=e.factory;let r=!1;if(i&&n){e.authorityOwned=!0;const t=await this.waitForGeneration(i.prepareConsumerBinding(),e);if("completed"!==t.status||!this.shouldContinueStart(e))return;const s=n.create({dispatcher:this.core.getRuntimeCommandDispatcher(),binding:t.value,getBinding:()=>i.currentConsumerBinding()??t.value,getLease:()=>i.authorityLease,createRuntimeSession:async(e,t)=>await this.core.createRuntimeSession(e,t),signal:e.abortController.signal}),a=await this.waitForGeneration(s,e);if("completed"!==a.status)return void("cancelled"===a.status&&s.then(e=>this.closeConsumerOnce(e),()=>{}));const o=a.value;if(!this.shouldContinueStart(e))return void this.closeConsumerOnce(o,e.abortController.signal).catch(()=>{});if(this.core.setRuntimeReceiptHistoryPort(o.receiptHistory),e.runtimeConsumer=o,this.runtimeRpcConsumer=o,o.start(),!this.shouldContinueStart(e))return;i.setRuntimeConsumerLive(!0),r=!0,this.runtimeServiceFactoryFenced=!1}else i&&(e.authorityOwned=!0),i?.setRuntimeConsumerLive(!1),i&&(this.runtimeServiceFactoryFenced=!0,this.logger.warn?.("[acp-server] runtime Service factory unavailable — registration fenced"));if(this.core.startMcp().catch(e=>{this.logger.warn?.(`[acp-server] MCP desired-state reconcile failed: ${e instanceof Error?e.message:String(e)}`)}),!this.shouldContinueStart(e))return;if(i&&r){if("completed"!==(await this.waitForGeneration(i.start(e.abortController.signal),e)).status||!this.shouldContinueStart(e))return}const s=Date.now();this.logger.info?.("[acp-server] start() begin"),t&&(this.config.bridge.url=t);const a=this.config.bridge.url,o=this.config.keyFile;if(a&&!BridgeClientCtor)try{const t=Date.now(),i=await this.waitForGeneration(import("@beeos-ai/bridge-client"),e);if("completed"!==i.status)return;BridgeClientCtor=i.value.BridgeClient,this.logger.info?.(`[acp-server] bridge-client import took ${Date.now()-t}ms`)}catch(e){this.logger.warn?.("[acp-server] Bridge SDK not available")}if(this.shouldContinueStart(e)){if(!0===this.config.terminalServiceEnabled&&a&&i){const t=new TerminalLeaseBoundBridgeClient({bridgeUrl:a,authority:i,keys:i.agentKeys}),n=new RuntimeTerminalOutboundRegistration({client:t,manager:this.terminalMgr,service:this.runtimeTerminalService,authorityJournal:new FileTerminalAuthorityJournal(join(i.stateDirectory,"terminal-authority-journal.json")),onAvailabilityChange:t=>this.setTerminalServiceReady(e,t),logger:this.logger});if(!this.shouldContinueStart(e))return void n.drain({timeoutMs:5e3,terminateAfterTimeout:!0,signal:e.abortController.signal}).catch(()=>{});e.terminalOutbound=n,this.terminalOutbound=n;try{const t=await this.waitForGeneration(n.start(),e,ACPServer.LIFECYCLE_CONNECT_TIMEOUT_MS);if("cancelled"===t.status)return;if("timed_out"===t.status)throw new Error("Terminal connection timed out")}catch(e){this.logger.warn?.(`[terminal-service] initial outbound registration failed; no legacy fallback: ${e instanceof Error?e.message:String(e)}`)}if(!this.shouldContinueStart(e))return}else!0!==this.config.terminalServiceEnabled||this.runtimeAuthority||this.logger.warn?.("[terminal-service] disabled: active Runtime registration authority unavailable");try{const t=await this.waitForGeneration(sweepStaleSessionLocks({logger:this.logger}),e);if("completed"!==t.status)return;const i=t.value;this.logger.info?.(`[acp-server] stale lock sweep scanned=${i.scanned} quarantined=${i.quarantined} skipped=${i.skipped}`)}catch(e){this.logger.warn?.(`[acp-server] stale lock sweep failed: ${e instanceof Error?e.message:String(e)}`)}if(this.shouldContinueStart(e)){if(a&&o&&BridgeClientCtor){Date.now();await this.startBridgeMode(a,o,e)}else this.logger.warn?.("[acp-server] Bridge not configured; running in standby (no ACP listener). Use handleUpgrade() for noServer mode."),this.core.markAgentSyncDone();this.logger.info?.(`[acp-server] start() total ${Date.now()-s}ms`)}}}shouldContinueStart(e){return!e.abortController.signal.aborted&&this.activeGeneration===e&&"starting"===this.lifecyclePhase}isGenerationActive(e){return!e.abortController.signal.aborted&&this.activeGeneration===e&&("starting"===this.lifecyclePhase||"started"===this.lifecyclePhase)}setTerminalServiceReady(e,t){if(this.activeGeneration!==e)return;const i=!0===this.config.terminalServiceEnabled&&t;this.terminalServiceReady!==i&&(this.terminalServiceReady=i,this.runtimeAgentCapabilities.terminal=i,this.markAgentProjectionDirty?this.markAgentProjectionDirty():this.tryAgentSync())}async waitForGeneration(e,t,i){const n=t.abortController.signal;return n.aborted?(e.catch(()=>{}),{status:"cancelled"}):await new Promise((t,r)=>{let s,a=!1;const o=e=>{a||(a=!0,n.removeEventListener("abort",c),s&&clearTimeout(s),t(e))},c=()=>o({status:"cancelled"});n.addEventListener("abort",c,{once:!0}),void 0!==i&&(s=setTimeout(()=>o({status:"timed_out"}),i),s.unref?.()),e.then(e=>o({status:"completed",value:e}),e=>{a||(a=!0,n.removeEventListener("abort",c),s&&clearTimeout(s),r(e))})})}closeConsumerOnce(e,t){const i=this.consumerClosePromises.get(e);if(i)return i;const n=Promise.resolve().then(()=>e.stop(t));return this.consumerClosePromises.set(e,n),n}async startBridgeMode(e,t,i){if(!BridgeClientCtor)throw new Error("@beeos-ai/bridge-client not available");if(!this.isGenerationActive(i))return;const n=new BridgeClientCtor({bridgeUrl:e,service:this.config.bridge.service||"acp",keyFile:t});if(!this.isGenerationActive(i))return void this.closeBridgeOnce(n,!1).catch(()=>{});i.bridge=n,this.bridgeInstance=n;const r=()=>this.bridgeInstance===n;n.on("connected",()=>{r()&&(this.terminalHandler.destroy(),this.terminalHandler=this.createTerminalHandler(this.transport),this.bridgeConsecutiveFailures=0,this.logger.info?.("[acp-server] Connected to Bridge (publicKey auth)"),this.writeObs({component:"acp-server",domain:"lifecycle",name:"bridge.connected",severity:"info"}),this.tryAgentSync())}),n.on("disconnected",(...e)=>{if(!r())return;this.terminalHandler.destroy();const s=String(e[0]??"unknown"),a=formatBridgeEventArgs(e);if(this.logger.warn?.(`[acp-server] Bridge disconnected: ${s} (detail=${a})`),this.core.initialized=!1,this.bridgeConsecutiveFailures++,this.bridgeConsecutiveFailures>=ACPServer.RE_DISCOVER_THRESHOLD&&this.agentFetchFn&&this.config.agentGatewayUrl){this.bridgeConsecutiveFailures=0;const e=this.config.bridge.url;discoverBridgeFromGW(this.config.agentGatewayUrl,this.agentFetchFn).then(async s=>{if(this.isGenerationActive(i)&&r()&&s&&s.url!==e){if(this.logger.info?.(`[acp-server] Bridge re-discovery: URL changed ${e} → ${s.url}, switching`),this.config.bridge.url=s.url,writeDiscoveryCache({bridge:{url:s.url,discoveredAt:Date.now()}}),await this.closeBridgeOnce(n,!1),!this.isGenerationActive(i)||this.bridgeInstance!==n)return;i.bridge===n&&(i.bridge=void 0),this.activeGeneration!==i||"starting"!==this.lifecyclePhase&&"started"!==this.lifecyclePhase||await this.startBridgeMode(s.url,t,i)}}).catch(()=>{})}}),n.on("message",(...e)=>{if(!r())return;const t=e[0],i="string"==typeof t?t:Buffer.from(t).toString("utf-8");try{const e=JSON.parse(i);"id"in e&&void 0!==e.id?this.core.handleRequest(e).catch(e=>{this.logger.error?.(`[acp-server] Bridge request failed: ${e instanceof Error?e.message:String(e)}`)}):this.core.handleNotification(e)}catch{this.logger.error?.("[acp-server] Invalid JSON-RPC message from Bridge")}}),n.on("error",(...e)=>{if(!r())return;const t=formatBridgeEventArgs(e);this.logger.error?.(`[acp-server] Bridge error: ${t}`)});const s={readyState:WebSocket.OPEN,send:(e,t)=>{n.send(e).then(()=>t?.(),e=>{const i=e instanceof Error?e:new Error(String(e));this.logger.warn?.(`[acp-server] bridge.send failed: ${i.message}`),t?.(i)})},close:()=>{this.closeBridgeOnce(n).catch(e=>{this.logger.debug?.(`[acp-server] bridge.close failed: ${e instanceof Error?e.message:String(e)}`)})},terminate:()=>{this.closeBridgeOnce(n,!1).catch(e=>{this.logger.debug?.(`[acp-server] bridge.terminate failed: ${e instanceof Error?e.message:String(e)}`)})},on:()=>s,once:()=>s,removeListener:()=>s,removeAllListeners:()=>s,ping:()=>{}};Object.defineProperty(s,"readyState",{get:()=>"connected"===n.state?WebSocket.OPEN:WebSocket.CLOSED}),i.bridgeSocket=s,this.terminalHandler.destroy(),this.terminalHandler=this.createTerminalHandler(this.transport),this.client=s;const a=Date.now();try{const e=await this.waitForGeneration(n.connect(),i,ACPServer.LIFECYCLE_CONNECT_TIMEOUT_MS);if("cancelled"===e.status)return;if("timed_out"===e.status)throw new Error("Bridge connection timed out");this.logger.info?.(`[acp-server] bridge.connect() took ${Date.now()-a}ms`)}catch(e){this.logger.warn?.(`[acp-server] Initial bridge connect failed after ${Date.now()-a}ms (will retry in background): ${e?.message}`)}this.core.initialized=!1,this.logger.info?.("[acp-server] Running in Bridge mode (WS client)")}closeBridgeOnce(e,t){const i=this.bridgeClosePromises.get(e);if(i)return i;const n=Promise.resolve().then(()=>e.close(t));return this.bridgeClosePromises.set(e,n),n}handleUpgrade(e,t,i){this.wss||(this.wss=new WebSocketServer({noServer:!0}),this.wss.on("connection",(e,t)=>this.handleConnection(e,t))),this.wss.handleUpgrade(e,t,i,t=>{this.wss.emit("connection",t,e)})}async tryAgentSync(e=0){if(this.agentSyncRetryTimer&&(clearTimeout(this.agentSyncRetryTimer),this.agentSyncRetryTimer=null),!this.client||this.client.readyState!==WebSocket.OPEN)return;if(!this.gateway||!this.agentFetchFn||!this.config.agentGatewayUrl)return void this.core.markAgentSyncDone();if(this.agentProjection){if(this.runtimeAuthority?.authorityLease)return;if(!this.runtimeServiceFactoryFenced)return;0===e&&this.logger.error?.("[acp-server] runtime Service factory fenced with no authority lease — attempting one-shot legacy agent-sync bootstrap to recover Agent Identity")}const t=!!this.agentProjection;if(t||(this.legacyFencedSyncFailureCount=0),this.agentSyncInFlight)return;const i=[1e3,2e3,5e3,1e4],n=3e5,r=()=>{const r=e+1;if(t&&(this.legacyFencedSyncFailureCount+=1,this.legacyFencedSyncFailureCount>=4)){const e=Math.round(5);return this.logger.error?.(`[acp-server] legacy agent-sync bootstrap fallback has failed ${this.legacyFencedSyncFailureCount} consecutive times while the runtime Service factory is fenced — this fallback path is confirmed incompatible with a backend that requires the runtime-projection agent-sync schema and cannot self-heal; restart this instance once Message Service / the runtime Service factory recover. Backing off to a ${e}m retry interval to stop busy-looping a request that cannot succeed.`),void(this.agentSyncRetryTimer=setTimeout(()=>{this.tryAgentSync(r)},n))}const s=i[Math.min(e,i.length-1)],a=Math.round(s/1e3),o=`[acp-server] agent sync retry #${r} in ${a}s`;r>0&&r%30==0?this.logger.warn?.(`[acp-server] agent sync still failing after ${r} attempts — retrying every ${a}s`):r<=i.length?this.logger.info?.(o):this.logger.debug?.(o),this.agentSyncRetryTimer=setTimeout(()=>{this.tryAgentSync(r)},s)},s=Date.now();this.agentSyncInFlight=!0;try{const e=await syncAgentsToPlatform({gateway:this.gateway,agentGatewayUrl:this.config.agentGatewayUrl,capabilities:{canvas:this.config.canvasEnabled,terminal:this.terminalServiceReady,fileSystem:!0,cron:!0,streaming:!0},deliveryMode:this.config.messageBus.enabled?"push":void 0,boundAgentFetch:this.boundAgentFetchFn,logger:this.logger});isAgentSyncWaitForGateway(e)?this.logger.info?.("[acp-server] agent sync waiting for OpenClaw Gateway connection"):e?(this.legacyFencedSyncFailureCount=0,await this.core.setAgentMapping(e),this.logger.info?.(`[acp-server] agent mapping loaded: ${[...e.keys()].join(", ")} (sync + session hydration took ${Date.now()-s}ms)`),this.agentProjection&&this.runtimeServiceFactoryFenced&&this.logger.error?.("[acp-server] agent bootstrap sync recovered Agent Identity while the runtime Service factory was fenced — restart this instance to complete registration")):r()}catch(e){this.logger.warn?.(`[acp-server] agent sync failed: ${e}`),r()}finally{this.agentSyncInFlight=!1}}dispatchGatewayEvent(e,t,i){try{i()}catch(i){const n=i instanceof Error?i.message:String(i),r=n.startsWith("run output route unavailable:");this.logger.error?.(`[acp-server] Gateway ${e} handler failed${t?` runId=${t}`:""}: ${n}`),this.writeObs({component:"acp-server",domain:"lifecycle",name:r?"run_output.route_missing":"gateway_event.handler_failed",severity:"error",summary:n,payload:{event:e,...t?{runId:t}:{}}})}}bindGateway(e){e.on("chat",e=>{this.dispatchGatewayEvent("chat",e.runId,()=>this.events.handleChatEvent(e))}),e.on("agent",e=>{this.dispatchGatewayEvent("agent",e.runId,()=>this.events.handleAgentEvent(e))}),e.on("cron",e=>{this.dispatchGatewayEvent("cron",void 0,()=>this.events.handleCronEvent(e))}),e.on("sessionMessage",e=>{this.dispatchGatewayEvent("session.message",void 0,()=>this.core.handleSessionMessage(e))}),e.on("response",e=>{this.dispatchGatewayEvent("response",void 0,()=>this.core.handleGatewayResponse(e))});const t=e;this.canvasOrch.updateDeps({gateway:t?{get isConnected(){return t.isConnected},request:(e,t)=>this.gateway.request(e,t)}:null}),this.config.openclawCanvasCompat&&(e.on("canvas",e=>this.canvasOrch.handleOpenClawCanvasEvent(e)),this.logger.info?.("[acp-server] OpenClaw native canvas compat enabled — listening for canvas events")),e.on("connected",()=>{this.core.beginGatewaySessionPreload(),this.logger.info?.("[acp-server] connection transport=openclaw_gateway_ws state=connected"),this.writeObs({component:"acp-server",domain:"lifecycle",name:"gateway.connected",severity:"info"}),this.core.resubscribeAllSessionMessages(),this.core.refreshCronIsolatedSessionKeys(),this.tryAgentSync()}),e.on("disconnected",()=>{this.core.failGatewaySessionPreload(),this.logger.warn?.("[acp-server] connection transport=openclaw_gateway_ws state=disconnected"),this.writeObs({component:"acp-server",domain:"lifecycle",name:"gateway.disconnected",severity:"warn"}),this.core.cleanupAllPendingPrompts()}),e.on("reconnecting",e=>{this.logger.info?.(`[acp-server] connection transport=openclaw_gateway_ws state=reconnecting attempt=${e.attempt} delay_ms=${Math.round(e.delayMs)}`)}),e.on("reconnect-exhausted",e=>{this.logger.error?.(`[acp-server] Gateway reconnect exhausted after ${e} attempts`)}),e.on("error",e=>{this.logger.error?.("[acp-server] Gateway error:",e.message)}),e.on("transport",e=>{const t="connect_fail"===e.event&&!0===e.retryable,i=`[acp-server] connection transport=openclaw_gateway_ws event=${e.event} duration_ms=${e.durationMs??"?"}`;t?this.logger.debug?.(i):this.logger.info?.(i),this.writeObs({component:"gateway-client",domain:"lifecycle",name:`transport.${e.event}`,severity:t?"debug":"connect_fail"===e.event||"error"===e.event?"warn":"debug",payload:e})}),e.on("reconnect-notify-scheduled",e=>{this.logger.info?.(`[acp-server] Server-initiated reconnect scheduled in ${e.delayMs}ms`)}),e.on("reconnect-notify-fired",()=>{this.logger.info?.("[acp-server] Server-initiated reconnect firing")})}handleConnection(e,t){if(this.client){this.logger.warn?.("[acp-server] Replacing existing ACP client with new connection (takeover)");const e=this.client;this.client=null;const t=this.acpConnectionTerminalHandlers.get(e);t?t.destroy():this.terminalHandler.destroy(),this.acpConnectionTerminalHandlers.delete(e);try{e.close(1008,"replaced by new client")}catch{e.terminate()}}this.client=e;const i=this.createTerminalHandler(new AcpTransport(t=>{if(this.client===e&&e.readyState===WebSocket.OPEN)try{e.send(JSON.stringify(t))}catch(e){this.logger.debug?.("[acp-server] connection terminal send failed",e)}},this.writeObs));this.acpConnectionTerminalHandlers.set(e,i),this.core.initialized=!1,this.logger.info?.("[acp-server] ACP client connected"),this.writeObs({component:"acp-server",domain:"lifecycle",name:"client.connected",severity:"info"}),e.on("message",t=>{if(this.client!==e)return;const n="string"==typeof t?t:t.toString("utf-8");try{const e=JSON.parse(n);if("id"in e&&void 0!==e.id){(e.method.startsWith("terminal/")?i.handleRequest(e):this.core.handleRequest(e)).catch(e=>{this.logger.error?.(`[acp-server] request failed: ${e instanceof Error?e.message:String(e)}`)})}else e.method.startsWith("terminal/")?i.handleNotification(e.method,e.params):this.core.handleNotification(e)}catch{this.logger.error?.("[acp-server] Invalid JSON-RPC message")}}),e.on("close",()=>{i.destroy(),this.acpConnectionTerminalHandlers.delete(e),this.client===e&&(this.client=null,this.core.initialized=!1),this.logger.info?.("[acp-server] ACP client disconnected"),this.writeObs({component:"acp-server",domain:"lifecycle",name:"client.disconnected",severity:"info"})})}createTerminalHandler(e){const t=new TerminalBridgeHandler({terminalMgr:this.terminalMgr,transport:e,config:this.config,logger:this.logger,writeObs:this.writeObs,localAuthority:this.localTerminalAuthorityRegistry.forPrincipal(this.localTerminalAuthorityRegistry.issuePrincipal()),executeCanonical:(e,t,i,n)=>this.core.dispatchLegacyTerminalCapability(e,t,i,n)});return t.setupListeners(),t}setCanvasState(e,t){this.canvasOrch.setCanvasState(e,t)}wireRelayActionHandler(){this.canvasOrch.wireRelayActionHandler()}isCanvasActive(e){return this.canvasOrch.isCanvasActive(e)}isCanvasActiveForGatewayKey(e){return this.canvasOrch.isCanvasActiveForGatewayKey(e)}broadcastNotification(e,t){this.transport.sendNotification(e,t)}getActiveSessionId(){return this.state.lastActiveSessionId}getSessionIdForToolCall(e){const t=this.pendingToolCallSessionKeys.length>0?this.pendingToolCallSessionKeys[this.pendingToolCallSessionKeys.length-1]:void 0;if(t){const i=this.state.findSessionByKey(t)??this.core.ensureSessionForKey(t);if(i?.sessionId)return e&&this.state.toolCallToSessionMap.set(e,i.sessionId),i.sessionId}if(e){const t=this.state.toolCallToSessionMap.get(e);if(t)return t;const i=this.state.toolCallIdMap.get(e);if(i&&i!==e){const e=this.state.toolCallToSessionMap.get(i);if(e)return e}const n=e.split("_");if(n.length>=3&&"tc"===n[0]){const e=n[1],t=this.state.promptsByRunId.get(e);if(t?.sessionId)return t.sessionId}}this.logger.warn?.(`[acp-server] getSessionIdForToolCall MISS: toolCallId=${e??"(none)"}, sessions=${this.state.sessions.size}, pendingKey=${t??"none"}`)}getSessionIdForActivePrompt(){return this.getSessionIdForToolCall()}setPendingToolCallSessionKey(e){e&&(this.pendingToolCallSessionKeys.push(e),this.pendingToolCallSessionKeys.length>16&&this.pendingToolCallSessionKeys.shift())}preMapToolCallSession(e){const t=this.pendingToolCallSessionKeys.length>0?this.pendingToolCallSessionKeys[this.pendingToolCallSessionKeys.length-1]:void 0;if(!t||!e)return;if(this.state.toolCallToSessionMap.has(e))return;const i=this.state.findSessionByKey(t)??this.core.ensureSessionForKey(t);i?.sessionId&&this.state.toolCallToSessionMap.set(e,i.sessionId)}waitForAgentMapping(){return this.agentMappings.waitForSynchronization()}onceAgentMappingReady(e){this.agentMappings.onceReady(e)}onAgentMappingUpdated(e){const t=this.agentMappings.snapshot();t&&t.size>0&&e(new Map(t)),this.agentMappings.subscribe(t=>e(new Map(t)))}flushPendingCanvasLinks(){this.canvasOrch.flushPendingCanvasLinks()}onRelayReconnected(){this.canvasOrch.onRelayReconnected()}getLocalAgentId(){return this.config.gateway.agentId||this.core.getDefaultAgentId()}resolveLocalAgentIdForPlatformAgent(e){return this.core.resolveLocalAgentId(e)}getDefaultPlatformAgentId(){return this.getDefaultPlatformAgent()?.id}getDefaultPlatformAgent(){const e=this.agentMappings.snapshot();if(e&&0!==e.size)return e.values().next().value??void 0}getPlatformAgentMappings(){const e=this.agentMappings.snapshot();return e&&0!==e.size?[...e.entries()].map(([e,t])=>({localAgentId:e,platformAgent:t})):[]}getPlatformAgentIdForToolCall(e){const t=this.getSessionIdForToolCall(e);if(!t)return;const i=this.state.sessions.get(t);return this.core.resolvePlatformAgentId(i?.agentId??"")??i?.platformAgentId}getGatewaySessionKeyForToolCall(e){const t=this.getSessionIdForToolCall(e);if(!t)return;const i=this.state.sessions.get(t);return i?.gatewaySessionKey}getMentionsForToolCall(e){const t=this.getSessionIdForToolCall(e);if(!t)return;const i=this.state.sessions.get(t);return i?.activeMentions}getResourceRenderer(){return this.canvasOrch.resourceRenderer}getDescriptorRegistry(){return this.canvasOrch.descriptorRegistry}getFormBuilder(){return this.canvasOrch.formBuilder}getSchemaRegistry(){return this.canvasOrch.schemaRegistry}registerSkillFormSchemas(e,t){const i=this.canvasOrch.schemaRegistry;i&&import("node:fs/promises").then(async({readFile:n})=>{try{const r=`${t}/skill.json`,s=await n(r,"utf-8"),a=JSON.parse(s);if(a.formSchemas?.length){for(const e of a.formSchemas)i.register({...e,submitLabel:e.submitLabel||"Submit",fields:e.fields});this.logger.info?.(`[acp-server] Registered ${a.formSchemas.length} form schema(s) from skill "${e}"`)}}catch{}}).catch(e=>{this.logger.debug?.(`[acp-server] skill form schema import failed: ${e instanceof Error?e.message:String(e)}`)})}emitCanvasActionAsUserMessage(e,t){this.canvasOrch.emitCanvasActionAsUserMessage(e,t)}stop(){const e=this.teardownContext.getStore();if(e?.server===this){const t=e.generation;return this.activeGeneration!==t?t.stopPromise??t.teardownPromise??Promise.resolve():(e.detachOwner(),t.stopPromise??Promise.resolve())}const t=this.activeGeneration;if(!t||"stopped"===this.lifecyclePhase)return Promise.resolve();if("stopping"===this.lifecyclePhase)return t.stopPromise??Promise.resolve();let i,n;const r=new Promise((e,t)=>{i=e,n=t});t.stopPromise=r,this.lifecyclePhase="stopping",t.abortController.abort();const s=this.teardownGeneration(t);return(async()=>{try{t.startPromise.catch(()=>{}),await s,i()}catch(e){n(e)}finally{this.activeGeneration===t&&(this.activeGeneration=void 0,this.lifecyclePhase="stopped")}})(),r}teardownGeneration(e){return e.teardownPromise||(e.teardownPromise=this.teardownGenerationOnce(e)),e.teardownPromise}async teardownGenerationOnce(e){const t=[],i=new AbortController;let n,r=!1;const s=new Promise(e=>{n=setTimeout(()=>{r=!0,i.abort(),e()},ACPServer.LIFECYCLE_TEARDOWN_TIMEOUT_MS),n.unref?.()}),a=async(n,r)=>{let a;const o=new Promise(e=>{a=e}),c=Promise.resolve().then(()=>this.teardownContext.run({server:this,generation:e,detachOwner:a},()=>r(i.signal))),l=await Promise.race([c.then(()=>({status:"completed"}),e=>({status:"failed",error:e})),s.then(()=>({status:"timed_out"})),o.then(()=>({status:"detached"}))]);if("failed"===l.status)t.push(l.error);else if("timed_out"===l.status){const i=new Error(`lifecycle teardown timed out: ${n}`);t.push(i),this.logger.error?.(`[acp-server] ${i.message} generation=${e.id}`),this.writeObs({component:"acp-server",domain:"lifecycle",name:"lifecycle.teardown_timeout",severity:"error",payload:{owner:n,generation:e.id,timeoutMs:ACPServer.LIFECYCLE_TEARDOWN_TIMEOUT_MS}})}},o=[];this.setTerminalServiceReady(e,!1),o.push(a("session-mutation-coordinator",()=>this.sessionMutationCoordinator.fenceActive("gateway_disconnected"))),this.agentSyncRetryTimer&&(clearTimeout(this.agentSyncRetryTimer),this.agentSyncRetryTimer=null),o.push(a("session-state",()=>this.state.destroy())),o.push(a("canvas-orchestrator",()=>this.canvasOrch.destroy())),o.push(a("mcp-receipt-history",()=>this.core.setRuntimeReceiptHistoryPort(void 0)));const c=e.bridge,l=e.bridgeSocket,h=e.terminalOutbound,g=e.runtimeConsumer,d=this.client&&this.client!==l?this.client:null,u=this.wss;if(e.bridge=void 0,e.bridgeSocket=void 0,e.terminalOutbound=void 0,e.runtimeConsumer=void 0,this.bridgeInstance===c&&(this.bridgeInstance=null),this.client!==d&&this.client!==l||(this.client=null),this.terminalOutbound===h&&(this.terminalOutbound=null),this.runtimeRpcConsumer===g&&(this.runtimeRpcConsumer=void 0),this.wss=null,this.bridgeConsecutiveFailures=0,d&&o.push(a("incoming-client",()=>{d.removeAllListeners(),d.close()})),c&&o.push(a("bridge",async()=>{try{await this.closeBridgeOnce(c)}catch(e){this.logger.debug?.(`[acp-server] bridge.close failed during stop: ${e instanceof Error?e.message:String(e)}`)}})),h&&o.push(a("terminal-outbound",e=>h.drain({timeoutMs:ACPServer.LIFECYCLE_TEARDOWN_TIMEOUT_MS,terminateAfterTimeout:!0,signal:e}))),e.authorityOwned&&(e.authorityOwned=!1,o.push(a("runtime-authority",()=>e.authority?.stop()))),g&&o.push(a("runtime-consumer",e=>this.closeConsumerOnce(g,e))),u&&o.push(a("websocket-server",()=>{u.removeAllListeners(),u.close()})),await Promise.all(o),await a("runtime-authority-reset",()=>e.authority?.setRuntimeConsumerLive(!1)),await a("terminal-handler",()=>this.terminalHandler.destroy()),await a("terminal-manager",()=>this.terminalMgr.destroy()),n&&clearTimeout(n),r||i.abort(),this.logger.info?.("[acp-server] Stopped"),this.writeObs({component:"acp-server",domain:"lifecycle",name:"lifecycle.shutdown",severity:"info"}),t.length>0)throw t[0]}}
1
+ import{WebSocketServer,WebSocket}from"ws";import{randomUUID}from"node:crypto";import{AsyncLocalStorage}from"node:async_hooks";let BridgeClientCtor;import{TerminalManager}from"./terminal/session-manager.js";import{getOrCreateRuntimeTerminalService}from"./terminal/runtime-terminal-service.js";import{RuntimeTerminalOutboundRegistration}from"./terminal/runtime-terminal-outbound-registration.js";import{FileTerminalAuthorityJournal}from"./terminal/terminal-authority-journal.js";import{LocalTerminalAuthorityRegistry}from"./terminal/local-terminal-authority.js";import{TerminalLeaseBoundBridgeClient}from"./terminal/terminal-lease-bound-bridge-client.js";import{join}from"node:path";import{AcpTransport}from"./acp-gateway/transport.js";import{AcpGatewaySessionState}from"./acp-gateway/session-state.js";import{AcpGatewayBridgeCore}from"./acp-gateway/bridge-core.js";import{BeeosFileResolver}from"./acp-gateway/beeos-file-resolver.js";import{AcpGatewayEvents}from"./acp-gateway/gateway-events.js";import{AcpRunOutputChannel}from"./acp-gateway/acp-run-output-channel.js";import{openRunOutputRoute}from"./acp-gateway/run-output-route.js";import{createAcpRunOutputRouter}from"./runtime/run-output-composition.js";import{AgentMappingRegistry}from"./runtime/agent-mapping-registry.js";import{sweepStaleSessionLocks}from"./acp-gateway/stale-session-lock-sweeper.js";import{TerminalBridgeHandler}from"./terminal/bridge-handler.js";import{isAgentSyncWaitForGateway,syncAgentsToPlatform}from"./agent-sync.js";import{CanvasOrchestrator}from"./canvas-orchestrator.js";import{discoverBridgeFromGW}from"./bridge-discovery.js";import{writeDiscoveryCache}from"./discovery-cache.js";function formatBridgeEventArgs(e){return 0===e.length?"(no args)":e.map((e,t)=>`arg${t}=${formatBridgeEventValue(e)}`).join(" ")}function formatBridgeEventValue(e){if(void 0===e)return"undefined";if(null===e)return"null";if(e instanceof Error){const t=e.stack?e.stack.split("\n").slice(0,4).join(" | "):"";return`Error(name=${e.name},message=${e.message}${t?`,stack=${t}`:""})`}if("object"==typeof e)try{return JSON.stringify(e,jsonReplacerForLogs)}catch{return String(e)}return String(e)}function jsonReplacerForLogs(e,t){return t instanceof Error?{name:t.name,message:t.message,stack:t.stack}:t}export class ACPServer{agentFetchFn;instanceId;runtimeAuthority;mcpProjection;mcpCredentialReferences;boundAgentFetchFn;wss=null;gateway;client=null;config;logger;writeObs;transport;state;core;events;terminalMgr;terminalHandler;localTerminalAuthorityRegistry;acpConnectionTerminalHandlers=new WeakMap;runtimeTerminalService;terminalOutbound=null;terminalServiceReady=!1;runtimeAgentCapabilities;agentProjection;markAgentProjectionDirty;templateOriginReporter;agentMappings;runtimeRpcConsumer;runtimeRpcConsumerFactory;canvasOrch;sessionMutationCoordinator;runOutputRouter;cronConversationDelivery;pendingToolCallSessionKeys=[];thirdPartyChannels;agentSyncRetryTimer=null;agentSyncInFlight=!1;runtimeServiceFactoryFenced=!1;legacyFencedSyncFailureCount=0;lifecyclePhase="stopped";lifecycleGeneration=0;activeGeneration;bridgeInstance=null;bridgeClosePromises=new WeakMap;consumerClosePromises=new WeakMap;teardownContext=new AsyncLocalStorage;bridgeConsecutiveFailures=0;static RE_DISCOVER_THRESHOLD=2;static LIFECYCLE_CONNECT_TIMEOUT_MS=1e4;static LIFECYCLE_TEARDOWN_TIMEOUT_MS=5e3;drainNextDeferredSend(e){const t=e.pendingAgentSends?.shift();t&&(e.pendingAgentSends?.length||(e.pendingAgentSends=void 0),this.agentSend(t.message,t.attachments,e.gatewaySessionKey).catch(e=>this.logger.warn?.(`[acp-server] Deferred agentSend failed: ${e instanceof Error?e.message:String(e)}`)))}async gatewayRequest(e,t){if(!this.gateway?.isConnected)throw new Error("Gateway not connected");return this.gateway.request(e,t)}async chatAbort(e,t){const i=this.gateway;if(!i?.isConnected)throw new Error("Gateway not connected");return i.chatAbort(e,t)}async agentSend(e,t,i){if(!this.gateway?.isConnected)throw new Error("Gateway not connected");const n=this.config.gateway.agentId||this.core.getDefaultAgentId();let r,s;i?(r=this.state.findSessionByKey(i),s=i,r?this.logger.info?.(`[acp-server] agentSend: resolved session for key=${i} sid=${r.sessionId}`):(r=this.core.ensureSessionForKey(s),this.logger.info?.(`[acp-server] agentSend: created session for key=${i} sid=${r.sessionId}`))):(s=`agent:${n}:ephemeral:${randomUUID()}`,r=this.core.ensureSessionForKey(s),this.logger.info?.(`[acp-server] agentSend: no targetSessionKey, created ephemeral session key=${s}`));const a=await this.sessionMutationCoordinator.acquireInternal({sessionKey:s,label:"agentSend"});let o=!1;try{const i=this.gateway;if(!i?.isConnected)throw new Error("Gateway not connected");if(r&&!r.promptDone&&(void 0!==r.pendingPromptId||void 0!==r.currentRunId||void 0!==r.gatewayRequestId))return(r.pendingAgentSends??=[]).push({message:e,attachments:t}),this.logger.info?.("[acp-server] Session has active run, deferred message (queue=%d)",r.pendingAgentSends.length),void this.sessionMutationCoordinator.releaseBeforeRuntime(a);o=!0;const c=await i.agentSend(s,e,n,t);return c?.runId&&r?(this.state.resetForNewRun(r,r.sessionId,this.transport),r.currentRunId=c.runId,this.sessionMutationCoordinator.bindRun(a,c.runId),this.core.observeActiveRun(r),r.assistantStreamStartedAt=Date.now(),r.writeGateToken=a,this.state.promptsByRunId.set(c.runId,r),openRunOutputRoute(this.runOutputRouter,r,c.runId,["acp"])):this.sessionMutationCoordinator.releaseBeforeRuntime(a),c}catch(e){throw o?this.sessionMutationCoordinator.requireRecovery(a,"terminal_not_observed"):this.sessionMutationCoordinator.releaseBeforeRuntime(a),e}}setCronConversationDelivery(e){this.cronConversationDelivery=e}getRuntimeCommandDispatcher(){return this.core.getRuntimeCommandDispatcher()}getActiveRunAuthority(){return this.core.getActiveRunAuthority()}nudgeMcpNativeProjection(){this.core.nudgeMcpNativeProjection()}ensureImInboundSession(e,t,i,n){const r=this.core.ensureSessionForKey(e);r.channelId=t,r.inboundMessageId=i,r.activeMentions=n.length>0?n:void 0}sendBridgeMessage=e=>{const t=this.client;if(t&&t.readyState===WebSocket.OPEN)try{t.send(JSON.stringify(e))}catch(e){this.logger.debug?.("[acp-server] bridge send failed (socket may be closing):",e)}else this.logger.debug?.("[acp-server] bridge not ready, dropping message")};constructor(e,t,i,n,r,s,a,o,c,l,h,g,d,u={}){this.agentFetchFn=r,this.instanceId=s,this.runtimeAuthority=c,this.mcpProjection=h,this.mcpCredentialReferences=g,this.boundAgentFetchFn=d,this.config=e,this.logger=t,this.writeObs=n??(()=>{}),this.thirdPartyChannels=a??new Set,this.sessionMutationCoordinator=i,this.gateway=u.gateway??null,this.agentProjection=u.agentProjection,this.markAgentProjectionDirty=u.markAgentProjectionDirty,this.templateOriginReporter=u.templateOriginReporter,this.agentMappings=u.agentMappings??new AgentMappingRegistry,this.runtimeRpcConsumerFactory=l,this.runtimeAgentCapabilities=u.agentCapabilities??{canvas:this.config.canvasEnabled,terminal:!1,webSsh:!1,fileSystem:!0,image:!0,audio:!1,mcp:!0,cron:!0,streaming:!0,pushNotifications:this.config.messageBus.enabled},this.runtimeAuthority?.setRuntimeRealtimeCapabilityReady("canvas",this.config.canvasEnabled),this.runtimeAuthority?.setRuntimeRealtimeCapabilityReady("terminal",!0===this.config.terminalServiceEnabled),this.transport=new AcpTransport(this.sendBridgeMessage,this.writeObs,void 0,()=>{const e=this.client;return!!e&&e.readyState===WebSocket.OPEN}),this.state=new AcpGatewaySessionState(this.logger);const m=(u.createRunOutputRouter??createAcpRunOutputRouter)({acp:new AcpRunOutputChannel({state:this.state,transport:this.transport}),lifecycle:{terminal:(e,t)=>this.core.commitRunOutputTerminal(e,t)}});this.runOutputRouter=m;const p=this.agentFetchFn&&this.config.agentGatewayUrl?new BeeosFileResolver({agentGatewayUrl:this.config.agentGatewayUrl,agentFetch:this.agentFetchFn,writeObs:this.writeObs}):void 0;this.core=new AcpGatewayBridgeCore({state:this.state,transport:this.transport,config:this.config,logger:this.logger,writeObs:this.writeObs,getGateway:()=>this.gateway,instanceId:this.instanceId,getAuthoritativeInstanceId:()=>process.env.BEEOS_INSTANCE_ID?.trim()||this.getDefaultPlatformAgent()?.instanceId,thirdPartyChannels:this.thirdPartyChannels,beeosFileResolver:p,agentFetch:this.agentFetchFn,agentGatewayUrl:this.config.agentGatewayUrl,sessionMutationCoordinator:i,triggerAgentSync:()=>this.tryAgentSync(),runtimeAgentIdentities:this.runtimeAuthority?.identities,runtimeCommandJournal:this.runtimeAuthority?.journal,markAgentProjectionDirty:this.markAgentProjectionDirty,getAgentProjection:this.agentProjection?()=>this.agentProjection:void 0,templateOriginReporter:this.templateOriginReporter,...u.waitForChatIdle?{waitForChatIdle:u.waitForChatIdle}:{},mcpProjection:this.mcpProjection,mcpCredentialReferences:this.mcpCredentialReferences,runOutputRouter:m,agentMappings:this.agentMappings}),this.canvasOrch=new CanvasOrchestrator({state:this.state,transport:this.transport,config:this.config,logger:this.logger,writeObs:this.writeObs,getSessionIdForToolCall:e=>this.getSessionIdForToolCall(e),gateway:null,sendAgentMessage:(e,t)=>this.agentSend(e,void 0,t),resolvePlatformAgentId:e=>this.core.resolvePlatformAgentId(e)??""}),this.events=new AcpGatewayEvents({state:this.state,transport:this.transport,config:this.config,logger:this.logger,writeObs:this.writeObs,runOutputRouter:m,schedulePromptTimeout:e=>this.core.schedulePromptTimeout(e),adoptBackgroundMutation:(e,t)=>this.core.adoptBackgroundMutation(e,t),abortGatewayRun:async(e,t)=>{await this.chatAbort(e,t)},agentFetch:this.agentFetchFn,getCronConversationDelivery:()=>this.cronConversationDelivery??null,getCanvasState:()=>this.canvasOrch.canvasState,gatewayRequest:(e,t)=>{const i=this.gateway;return i?.isConnected?i.request(e,t):Promise.reject(new Error("gateway not connected"))},cronChannelCache:this.core.cronChannelCache}),this.core.onRunEnded=(e,t)=>{"complete"===t.kind?this.canvasOrch.onPromptComplete(e):this.canvasOrch.onPromptFailed(),this.drainNextDeferredSend(e)},this.terminalMgr=o??new TerminalManager,this.localTerminalAuthorityRegistry=new LocalTerminalAuthorityRegistry(this.terminalMgr),this.terminalHandler=this.createTerminalHandler(this.transport),this.runtimeTerminalService=getOrCreateRuntimeTerminalService(this.terminalMgr),this.core.setRuntimeTerminalService(this.runtimeTerminalService),this.core.handleTerminalRequest=e=>this.terminalHandler.handleRequest(e),this.core.handleTerminalNotification=(e,t)=>this.terminalHandler.handleNotification(e,t),this.core.handleCanvasNotification=(e,t)=>{this.canvasOrch.handleCanvasNotification(e,t)},this.core.getCanvasState=()=>this.canvasOrch.canvasState,this.core.onSessionLoaded=(e,t,i)=>{this.canvasOrch.onSessionLoaded(e,t,i)},this.core.onSkillInstalled=(e,t)=>{this.registerSkillFormSchemas(e,t)},this.core.onSessionDeleted=e=>{this.canvasOrch.onSessionDeleted(e)},this.core.onSessionCleared=(e,t)=>{this.canvasOrch.onSessionCleared(e,t)},this.core.handleSlashCommand=async e=>this.canvasOrch.handleSlashCommand(e),this.core.onPromptStart=()=>{this.canvasOrch.onPromptStart()},this.state.startSessionGc(e=>{e.writeGateToken&&this.sessionMutationCoordinator.requireRecovery(e.writeGateToken,"terminal_not_observed"),this.core.cleanupPrompt(e)},e=>{this.canvasOrch.canvasState?.removeSession(e.gatewaySessionKey),this.transport.resetSessionUpdateCount(e.sessionId)}),this.gateway&&this.bindGateway(this.gateway)}setRuntimeRpcConsumerFactory(e){if(e!==this.runtimeRpcConsumerFactory){if("stopped"!==this.lifecyclePhase)throw new Error("runtime Service factory is immutable after ACP start");this.runtimeRpcConsumerFactory=e}}async rebindRuntimeDelivery(){if("started"!==this.lifecyclePhase)return;const e=this.activeGeneration,t=e?.factory??this.runtimeRpcConsumerFactory,i=e?.authority??this.runtimeAuthority;if(!e||!t||!i)return;if(!i.authorityLease)return;const n=i.currentConsumerBinding();if(!n)return void this.logger.warn?.("[acp-server] runtime Service delivery rebind skipped — no consumer binding");const r=e.runtimeConsumer;let s;r&&(e.runtimeConsumer=void 0,this.runtimeRpcConsumer===r&&(this.runtimeRpcConsumer=void 0),this.core.setRuntimeReceiptHistoryPort(void 0),await this.closeConsumerOnce(r).catch(()=>{}));try{s=await t.create({dispatcher:this.core.getRuntimeCommandDispatcher(),binding:n,getBinding:()=>i.currentConsumerBinding()??n,getLease:()=>i.authorityLease,createRuntimeSession:async(e,t)=>await this.core.createRuntimeSession(e,t),signal:e.abortController.signal})}catch(e){this.logger.error?.(`[acp-server] runtime Service delivery rebind failed: ${e instanceof Error?e.stack??e.message:String(e)}`);try{i.setRuntimeConsumerLive(!1)}catch{}return}if(this.activeGeneration!==e||"started"!==this.lifecyclePhase||e.abortController.signal.aborted)await this.closeConsumerOnce(s).catch(()=>{});else{this.core.setRuntimeReceiptHistoryPort(s.receiptHistory),e.runtimeConsumer=s,this.runtimeRpcConsumer=s,s.start();try{i.setRuntimeConsumerLive(!0)}catch{}this.runtimeServiceFactoryFenced=!1,this.logger.info?.("[acp-server] runtime Service delivery rebound after MessageClient connect")}}start(e){const t=this.activeGeneration;if("starting"===this.lifecyclePhase||"started"===this.lifecyclePhase)return this.logger.info?.("[acp-server] start() skipped — already started (singleton reuse)"),t?.startPromise??Promise.resolve();if("stopping"===this.lifecyclePhase){return(t?.stopPromise??Promise.resolve()).then(()=>this.start(e),()=>this.start(e))}const i={id:++this.lifecycleGeneration,factory:this.runtimeRpcConsumerFactory,authority:this.runtimeAuthority,abortController:new AbortController,startPromise:Promise.resolve(),authorityOwned:!1};return this.lifecyclePhase="starting",this.activeGeneration=i,i.startPromise=this.startGeneration(i,e).then(()=>{this.activeGeneration===i&&"starting"===this.lifecyclePhase&&(this.lifecyclePhase="started")}).catch(async e=>{try{await this.teardownGeneration(i)}catch(e){this.logger.debug?.(`[acp-server] failed-start teardown also failed: ${e instanceof Error?e.message:String(e)}`)}finally{this.activeGeneration===i&&"starting"===this.lifecyclePhase&&(this.activeGeneration=void 0,this.lifecyclePhase="stopped")}throw e}),i.startPromise}async startGeneration(e,t){const i=e.authority,n=e.factory;let r=!1;if(i&&n){e.authorityOwned=!0;const t=await this.waitForGeneration(i.prepareConsumerBinding(),e);if("completed"!==t.status||!this.shouldContinueStart(e))return;const s=n.create({dispatcher:this.core.getRuntimeCommandDispatcher(),binding:t.value,getBinding:()=>i.currentConsumerBinding()??t.value,getLease:()=>i.authorityLease,createRuntimeSession:async(e,t)=>await this.core.createRuntimeSession(e,t),signal:e.abortController.signal}),a=await this.waitForGeneration(s,e);if("completed"!==a.status)return void("cancelled"===a.status&&s.then(e=>this.closeConsumerOnce(e),()=>{}));const o=a.value;if(!this.shouldContinueStart(e))return void this.closeConsumerOnce(o,e.abortController.signal).catch(()=>{});if(this.core.setRuntimeReceiptHistoryPort(o.receiptHistory),e.runtimeConsumer=o,this.runtimeRpcConsumer=o,o.start(),!this.shouldContinueStart(e))return;i.setRuntimeConsumerLive(!0),r=!0,this.runtimeServiceFactoryFenced=!1}else i&&(e.authorityOwned=!0),i?.setRuntimeConsumerLive(!1),i&&(this.runtimeServiceFactoryFenced=!0,this.logger.warn?.("[acp-server] runtime Service factory unavailable — registration fenced"));if(this.core.startMcp().catch(e=>{this.logger.warn?.(`[acp-server] MCP desired-state reconcile failed: ${e instanceof Error?e.message:String(e)}`)}),!this.shouldContinueStart(e))return;if(i&&r){if("completed"!==(await this.waitForGeneration(i.start(e.abortController.signal),e)).status||!this.shouldContinueStart(e))return}const s=Date.now();this.logger.info?.("[acp-server] start() begin"),t&&(this.config.bridge.url=t);const a=this.config.bridge.url,o=this.config.keyFile;if(a&&!BridgeClientCtor)try{const t=Date.now(),i=await this.waitForGeneration(import("@beeos-ai/bridge-client"),e);if("completed"!==i.status)return;BridgeClientCtor=i.value.BridgeClient,this.logger.info?.(`[acp-server] bridge-client import took ${Date.now()-t}ms`)}catch(e){this.logger.warn?.("[acp-server] Bridge SDK not available")}if(this.shouldContinueStart(e)){if(!0===this.config.terminalServiceEnabled&&a&&i){const t=new TerminalLeaseBoundBridgeClient({bridgeUrl:a,authority:i,keys:i.agentKeys}),n=new RuntimeTerminalOutboundRegistration({client:t,manager:this.terminalMgr,service:this.runtimeTerminalService,authorityJournal:new FileTerminalAuthorityJournal(join(i.stateDirectory,"terminal-authority-journal.json")),onAvailabilityChange:t=>this.setTerminalServiceReady(e,t),logger:this.logger});if(!this.shouldContinueStart(e))return void n.drain({timeoutMs:5e3,terminateAfterTimeout:!0,signal:e.abortController.signal}).catch(()=>{});e.terminalOutbound=n,this.terminalOutbound=n;try{const t=await this.waitForGeneration(n.start(),e,ACPServer.LIFECYCLE_CONNECT_TIMEOUT_MS);if("cancelled"===t.status)return;if("timed_out"===t.status)throw new Error("Terminal connection timed out")}catch(e){this.logger.warn?.(`[terminal-service] initial outbound registration failed; no legacy fallback: ${e instanceof Error?e.message:String(e)}`)}if(!this.shouldContinueStart(e))return}else!0!==this.config.terminalServiceEnabled||this.runtimeAuthority||this.logger.warn?.("[terminal-service] disabled: active Runtime registration authority unavailable");try{const t=await this.waitForGeneration(sweepStaleSessionLocks({logger:this.logger}),e);if("completed"!==t.status)return;const i=t.value;this.logger.info?.(`[acp-server] stale lock sweep scanned=${i.scanned} quarantined=${i.quarantined} skipped=${i.skipped}`)}catch(e){this.logger.warn?.(`[acp-server] stale lock sweep failed: ${e instanceof Error?e.message:String(e)}`)}if(this.shouldContinueStart(e)){if(a&&o&&BridgeClientCtor){Date.now();await this.startBridgeMode(a,o,e)}else this.logger.warn?.("[acp-server] Bridge not configured; running in standby (no ACP listener). Use handleUpgrade() for noServer mode."),this.core.markAgentSyncDone();this.logger.info?.(`[acp-server] start() total ${Date.now()-s}ms`)}}}shouldContinueStart(e){return!e.abortController.signal.aborted&&this.activeGeneration===e&&"starting"===this.lifecyclePhase}isGenerationActive(e){return!e.abortController.signal.aborted&&this.activeGeneration===e&&("starting"===this.lifecyclePhase||"started"===this.lifecyclePhase)}setTerminalServiceReady(e,t){if(this.activeGeneration!==e)return;const i=!0===this.config.terminalServiceEnabled&&t;this.terminalServiceReady!==i&&(this.terminalServiceReady=i,this.runtimeAgentCapabilities.terminal=i,this.markAgentProjectionDirty?this.markAgentProjectionDirty():this.tryAgentSync())}async waitForGeneration(e,t,i){const n=t.abortController.signal;return n.aborted?(e.catch(()=>{}),{status:"cancelled"}):await new Promise((t,r)=>{let s,a=!1;const o=e=>{a||(a=!0,n.removeEventListener("abort",c),s&&clearTimeout(s),t(e))},c=()=>o({status:"cancelled"});n.addEventListener("abort",c,{once:!0}),void 0!==i&&(s=setTimeout(()=>o({status:"timed_out"}),i),s.unref?.()),e.then(e=>o({status:"completed",value:e}),e=>{a||(a=!0,n.removeEventListener("abort",c),s&&clearTimeout(s),r(e))})})}closeConsumerOnce(e,t){const i=this.consumerClosePromises.get(e);if(i)return i;const n=Promise.resolve().then(()=>e.stop(t));return this.consumerClosePromises.set(e,n),n}async startBridgeMode(e,t,i){if(!BridgeClientCtor)throw new Error("@beeos-ai/bridge-client not available");if(!this.isGenerationActive(i))return;const n=new BridgeClientCtor({bridgeUrl:e,service:this.config.bridge.service||"acp",keyFile:t});if(!this.isGenerationActive(i))return void this.closeBridgeOnce(n,!1).catch(()=>{});i.bridge=n,this.bridgeInstance=n;const r=()=>this.bridgeInstance===n;n.on("connected",()=>{r()&&(this.terminalHandler.destroy(),this.terminalHandler=this.createTerminalHandler(this.transport),this.bridgeConsecutiveFailures=0,this.logger.info?.("[acp-server] Connected to Bridge (publicKey auth)"),this.writeObs({component:"acp-server",domain:"lifecycle",name:"bridge.connected",severity:"info"}),this.tryAgentSync())}),n.on("disconnected",(...e)=>{if(!r())return;this.terminalHandler.destroy();const s=String(e[0]??"unknown"),a=formatBridgeEventArgs(e);if(this.logger.warn?.(`[acp-server] Bridge disconnected: ${s} (detail=${a})`),this.core.initialized=!1,this.bridgeConsecutiveFailures++,this.bridgeConsecutiveFailures>=ACPServer.RE_DISCOVER_THRESHOLD&&this.agentFetchFn&&this.config.agentGatewayUrl){this.bridgeConsecutiveFailures=0;const e=this.config.bridge.url;discoverBridgeFromGW(this.config.agentGatewayUrl,this.agentFetchFn).then(async s=>{if(this.isGenerationActive(i)&&r()&&s&&s.url!==e){if(this.logger.info?.(`[acp-server] Bridge re-discovery: URL changed ${e} → ${s.url}, switching`),this.config.bridge.url=s.url,writeDiscoveryCache({bridge:{url:s.url,discoveredAt:Date.now()}}),await this.closeBridgeOnce(n,!1),!this.isGenerationActive(i)||this.bridgeInstance!==n)return;i.bridge===n&&(i.bridge=void 0),this.activeGeneration!==i||"starting"!==this.lifecyclePhase&&"started"!==this.lifecyclePhase||await this.startBridgeMode(s.url,t,i)}}).catch(()=>{})}}),n.on("message",(...e)=>{if(!r())return;const t=e[0],i="string"==typeof t?t:Buffer.from(t).toString("utf-8");try{const e=JSON.parse(i);"id"in e&&void 0!==e.id?this.core.handleRequest(e).catch(e=>{this.logger.error?.(`[acp-server] Bridge request failed: ${e instanceof Error?e.message:String(e)}`)}):this.core.handleNotification(e)}catch{this.logger.error?.("[acp-server] Invalid JSON-RPC message from Bridge")}}),n.on("error",(...e)=>{if(!r())return;const t=formatBridgeEventArgs(e);this.logger.error?.(`[acp-server] Bridge error: ${t}`)});const s={readyState:WebSocket.OPEN,send:(e,t)=>{n.send(e).then(()=>t?.(),e=>{const i=e instanceof Error?e:new Error(String(e));this.logger.warn?.(`[acp-server] bridge.send failed: ${i.message}`),t?.(i)})},close:()=>{this.closeBridgeOnce(n).catch(e=>{this.logger.debug?.(`[acp-server] bridge.close failed: ${e instanceof Error?e.message:String(e)}`)})},terminate:()=>{this.closeBridgeOnce(n,!1).catch(e=>{this.logger.debug?.(`[acp-server] bridge.terminate failed: ${e instanceof Error?e.message:String(e)}`)})},on:()=>s,once:()=>s,removeListener:()=>s,removeAllListeners:()=>s,ping:()=>{}};Object.defineProperty(s,"readyState",{get:()=>"connected"===n.state?WebSocket.OPEN:WebSocket.CLOSED}),i.bridgeSocket=s,this.terminalHandler.destroy(),this.terminalHandler=this.createTerminalHandler(this.transport),this.client=s;const a=Date.now();try{const e=await this.waitForGeneration(n.connect(),i,ACPServer.LIFECYCLE_CONNECT_TIMEOUT_MS);if("cancelled"===e.status)return;if("timed_out"===e.status)throw new Error("Bridge connection timed out");this.logger.info?.(`[acp-server] bridge.connect() took ${Date.now()-a}ms`)}catch(e){this.logger.warn?.(`[acp-server] Initial bridge connect failed after ${Date.now()-a}ms (will retry in background): ${e?.message}`)}this.core.initialized=!1,this.logger.info?.("[acp-server] Running in Bridge mode (WS client)")}closeBridgeOnce(e,t){const i=this.bridgeClosePromises.get(e);if(i)return i;const n=Promise.resolve().then(()=>e.close(t));return this.bridgeClosePromises.set(e,n),n}handleUpgrade(e,t,i){this.wss||(this.wss=new WebSocketServer({noServer:!0}),this.wss.on("connection",(e,t)=>this.handleConnection(e,t))),this.wss.handleUpgrade(e,t,i,t=>{this.wss.emit("connection",t,e)})}async tryAgentSync(e=0){if(this.agentSyncRetryTimer&&(clearTimeout(this.agentSyncRetryTimer),this.agentSyncRetryTimer=null),!this.client||this.client.readyState!==WebSocket.OPEN)return;if(!this.gateway||!this.agentFetchFn||!this.config.agentGatewayUrl)return void this.core.markAgentSyncDone();if(this.agentProjection){if(this.runtimeAuthority?.authorityLease)return;if(!this.runtimeServiceFactoryFenced)return;0===e&&this.logger.error?.("[acp-server] runtime Service factory fenced with no authority lease — attempting one-shot legacy agent-sync bootstrap to recover Agent Identity")}const t=!!this.agentProjection;if(t||(this.legacyFencedSyncFailureCount=0),this.agentSyncInFlight)return;const i=[1e3,2e3,5e3,1e4],n=3e5,r=()=>{const r=e+1;if(t&&(this.legacyFencedSyncFailureCount+=1,this.legacyFencedSyncFailureCount>=4)){const e=Math.round(5);return this.logger.error?.(`[acp-server] legacy agent-sync bootstrap fallback has failed ${this.legacyFencedSyncFailureCount} consecutive times while the runtime Service factory is fenced — this fallback path is confirmed incompatible with a backend that requires the runtime-projection agent-sync schema and cannot self-heal; restart this instance once Message Service / the runtime Service factory recover. Backing off to a ${e}m retry interval to stop busy-looping a request that cannot succeed.`),void(this.agentSyncRetryTimer=setTimeout(()=>{this.tryAgentSync(r)},n))}const s=i[Math.min(e,i.length-1)],a=Math.round(s/1e3),o=`[acp-server] agent sync retry #${r} in ${a}s`;r>0&&r%30==0?this.logger.warn?.(`[acp-server] agent sync still failing after ${r} attempts — retrying every ${a}s`):r<=i.length?this.logger.info?.(o):this.logger.debug?.(o),this.agentSyncRetryTimer=setTimeout(()=>{this.tryAgentSync(r)},s)},s=Date.now();this.agentSyncInFlight=!0;try{const e=await syncAgentsToPlatform({gateway:this.gateway,agentGatewayUrl:this.config.agentGatewayUrl,capabilities:{canvas:this.config.canvasEnabled,terminal:this.terminalServiceReady,fileSystem:!0,cron:!0,streaming:!0},deliveryMode:this.config.messageBus.enabled?"push":void 0,boundAgentFetch:this.boundAgentFetchFn,logger:this.logger});isAgentSyncWaitForGateway(e)?this.logger.info?.("[acp-server] agent sync waiting for OpenClaw Gateway connection"):e?(this.legacyFencedSyncFailureCount=0,await this.core.setAgentMapping(e),this.logger.info?.(`[acp-server] agent mapping loaded: ${[...e.keys()].join(", ")} (sync + session hydration took ${Date.now()-s}ms)`),this.agentProjection&&this.runtimeServiceFactoryFenced&&this.logger.error?.("[acp-server] agent bootstrap sync recovered Agent Identity while the runtime Service factory was fenced — restart this instance to complete registration")):r()}catch(e){this.logger.warn?.(`[acp-server] agent sync failed: ${e}`),r()}finally{this.agentSyncInFlight=!1}}dispatchGatewayEvent(e,t,i){try{i()}catch(i){const n=i instanceof Error?i.message:String(i),r=n.startsWith("run output route unavailable:");this.logger.error?.(`[acp-server] Gateway ${e} handler failed${t?` runId=${t}`:""}: ${n}`),this.writeObs({component:"acp-server",domain:"lifecycle",name:r?"run_output.route_missing":"gateway_event.handler_failed",severity:"error",summary:n,payload:{event:e,...t?{runId:t}:{}}})}}bindGateway(e){e.on("chat",e=>{this.dispatchGatewayEvent("chat",e.runId,()=>this.events.handleChatEvent(e))}),e.on("agent",e=>{this.dispatchGatewayEvent("agent",e.runId,()=>this.events.handleAgentEvent(e))}),e.on("cron",e=>{this.dispatchGatewayEvent("cron",void 0,()=>this.events.handleCronEvent(e))}),e.on("sessionMessage",e=>{this.dispatchGatewayEvent("session.message",void 0,()=>this.core.handleSessionMessage(e))}),e.on("response",e=>{this.dispatchGatewayEvent("response",void 0,()=>this.core.handleGatewayResponse(e))});const t=e;this.canvasOrch.updateDeps({gateway:t?{get isConnected(){return t.isConnected},request:(e,t)=>this.gateway.request(e,t)}:null}),this.config.openclawCanvasCompat&&(e.on("canvas",e=>this.canvasOrch.handleOpenClawCanvasEvent(e)),this.logger.info?.("[acp-server] OpenClaw native canvas compat enabled — listening for canvas events")),e.on("connected",()=>{this.core.beginGatewaySessionPreload(),this.logger.info?.("[acp-server] connection transport=openclaw_gateway_ws state=connected"),this.writeObs({component:"acp-server",domain:"lifecycle",name:"gateway.connected",severity:"info"}),this.core.resubscribeAllSessionMessages(),this.core.refreshCronIsolatedSessionKeys(),this.tryAgentSync()}),e.on("disconnected",()=>{this.core.failGatewaySessionPreload(),this.logger.warn?.("[acp-server] connection transport=openclaw_gateway_ws state=disconnected"),this.writeObs({component:"acp-server",domain:"lifecycle",name:"gateway.disconnected",severity:"warn"}),this.core.cleanupAllPendingPrompts()}),e.on("reconnecting",e=>{this.logger.info?.(`[acp-server] connection transport=openclaw_gateway_ws state=reconnecting attempt=${e.attempt} delay_ms=${Math.round(e.delayMs)}`)}),e.on("reconnect-exhausted",e=>{this.logger.error?.(`[acp-server] Gateway reconnect exhausted after ${e} attempts`)}),e.on("error",e=>{this.logger.error?.("[acp-server] Gateway error:",e.message)}),e.on("transport",e=>{const t="connect_fail"===e.event&&!0===e.retryable,i=`[acp-server] connection transport=openclaw_gateway_ws event=${e.event} duration_ms=${e.durationMs??"?"}`;t?this.logger.debug?.(i):this.logger.info?.(i),this.writeObs({component:"gateway-client",domain:"lifecycle",name:`transport.${e.event}`,severity:t?"debug":"connect_fail"===e.event||"error"===e.event?"warn":"debug",payload:e})}),e.on("reconnect-notify-scheduled",e=>{this.logger.info?.(`[acp-server] Server-initiated reconnect scheduled in ${e.delayMs}ms`)}),e.on("reconnect-notify-fired",()=>{this.logger.info?.("[acp-server] Server-initiated reconnect firing")})}handleConnection(e,t){if(this.client){this.logger.warn?.("[acp-server] Replacing existing ACP client with new connection (takeover)");const e=this.client;this.client=null;const t=this.acpConnectionTerminalHandlers.get(e);t?t.destroy():this.terminalHandler.destroy(),this.acpConnectionTerminalHandlers.delete(e);try{e.close(1008,"replaced by new client")}catch{e.terminate()}}this.client=e;const i=this.createTerminalHandler(new AcpTransport(t=>{if(this.client===e&&e.readyState===WebSocket.OPEN)try{e.send(JSON.stringify(t))}catch(e){this.logger.debug?.("[acp-server] connection terminal send failed",e)}},this.writeObs));this.acpConnectionTerminalHandlers.set(e,i),this.core.initialized=!1,this.logger.info?.("[acp-server] ACP client connected"),this.writeObs({component:"acp-server",domain:"lifecycle",name:"client.connected",severity:"info"}),e.on("message",t=>{if(this.client!==e)return;const n="string"==typeof t?t:t.toString("utf-8");try{const e=JSON.parse(n);if("id"in e&&void 0!==e.id){(e.method.startsWith("terminal/")?i.handleRequest(e):this.core.handleRequest(e)).catch(e=>{this.logger.error?.(`[acp-server] request failed: ${e instanceof Error?e.message:String(e)}`)})}else e.method.startsWith("terminal/")?i.handleNotification(e.method,e.params):this.core.handleNotification(e)}catch{this.logger.error?.("[acp-server] Invalid JSON-RPC message")}}),e.on("close",()=>{i.destroy(),this.acpConnectionTerminalHandlers.delete(e),this.client===e&&(this.client=null,this.core.initialized=!1),this.logger.info?.("[acp-server] ACP client disconnected"),this.writeObs({component:"acp-server",domain:"lifecycle",name:"client.disconnected",severity:"info"})})}createTerminalHandler(e){const t=new TerminalBridgeHandler({terminalMgr:this.terminalMgr,transport:e,config:this.config,logger:this.logger,writeObs:this.writeObs,localAuthority:this.localTerminalAuthorityRegistry.forPrincipal(this.localTerminalAuthorityRegistry.issuePrincipal()),executeCanonical:(e,t,i,n)=>this.core.dispatchLegacyTerminalCapability(e,t,i,n)});return t.setupListeners(),t}setCanvasState(e,t){this.canvasOrch.setCanvasState(e,t)}wireRelayActionHandler(){this.canvasOrch.wireRelayActionHandler()}isCanvasActive(e){return this.canvasOrch.isCanvasActive(e)}isCanvasActiveForGatewayKey(e){return this.canvasOrch.isCanvasActiveForGatewayKey(e)}broadcastNotification(e,t){this.transport.sendNotification(e,t)}getActiveSessionId(){return this.state.lastActiveSessionId}getSessionIdForToolCall(e){const t=this.pendingToolCallSessionKeys.length>0?this.pendingToolCallSessionKeys[this.pendingToolCallSessionKeys.length-1]:void 0;if(t){const i=this.state.findSessionByKey(t)??this.core.ensureSessionForKey(t);if(i?.sessionId)return e&&this.state.toolCallToSessionMap.set(e,i.sessionId),i.sessionId}if(e){const t=this.state.toolCallToSessionMap.get(e);if(t)return t;const i=this.state.toolCallIdMap.get(e);if(i&&i!==e){const e=this.state.toolCallToSessionMap.get(i);if(e)return e}const n=e.split("_");if(n.length>=3&&"tc"===n[0]){const e=n[1],t=this.state.promptsByRunId.get(e);if(t?.sessionId)return t.sessionId}}this.logger.warn?.(`[acp-server] getSessionIdForToolCall MISS: toolCallId=${e??"(none)"}, sessions=${this.state.sessions.size}, pendingKey=${t??"none"}`)}getSessionIdForActivePrompt(){return this.getSessionIdForToolCall()}setPendingToolCallSessionKey(e){e&&(this.pendingToolCallSessionKeys.push(e),this.pendingToolCallSessionKeys.length>16&&this.pendingToolCallSessionKeys.shift())}preMapToolCallSession(e){const t=this.pendingToolCallSessionKeys.length>0?this.pendingToolCallSessionKeys[this.pendingToolCallSessionKeys.length-1]:void 0;if(!t||!e)return;if(this.state.toolCallToSessionMap.has(e))return;const i=this.state.findSessionByKey(t)??this.core.ensureSessionForKey(t);i?.sessionId&&this.state.toolCallToSessionMap.set(e,i.sessionId)}waitForAgentMapping(){return this.agentMappings.waitForSynchronization()}onceAgentMappingReady(e){this.agentMappings.onceReady(e)}onAgentMappingUpdated(e){const t=this.agentMappings.snapshot();t&&t.size>0&&e(new Map(t)),this.agentMappings.subscribe(t=>e(new Map(t)))}flushPendingCanvasLinks(){this.canvasOrch.flushPendingCanvasLinks()}onRelayReconnected(){this.canvasOrch.onRelayReconnected()}getLocalAgentId(){return this.config.gateway.agentId||this.core.getDefaultAgentId()}resolveLocalAgentIdForPlatformAgent(e){return this.core.resolveLocalAgentId(e)}getDefaultPlatformAgentId(){return this.getDefaultPlatformAgent()?.id}getDefaultPlatformAgent(){const e=this.agentMappings.snapshot();if(e&&0!==e.size)return e.values().next().value??void 0}getPlatformAgentMappings(){const e=this.agentMappings.snapshot();return e&&0!==e.size?[...e.entries()].map(([e,t])=>({localAgentId:e,platformAgent:t})):[]}getPlatformAgentIdForToolCall(e){const t=this.getSessionIdForToolCall(e);if(!t)return;const i=this.state.sessions.get(t);return this.core.resolvePlatformAgentId(i?.agentId??"")??i?.platformAgentId}getGatewaySessionKeyForToolCall(e){const t=this.getSessionIdForToolCall(e);if(!t)return;const i=this.state.sessions.get(t);return i?.gatewaySessionKey}getMentionsForToolCall(e){const t=this.getSessionIdForToolCall(e);if(!t)return;const i=this.state.sessions.get(t);return i?.activeMentions}getResourceRenderer(){return this.canvasOrch.resourceRenderer}getDescriptorRegistry(){return this.canvasOrch.descriptorRegistry}getFormBuilder(){return this.canvasOrch.formBuilder}getSchemaRegistry(){return this.canvasOrch.schemaRegistry}registerSkillFormSchemas(e,t){const i=this.canvasOrch.schemaRegistry;i&&import("node:fs/promises").then(async({readFile:n})=>{try{const r=`${t}/skill.json`,s=await n(r,"utf-8"),a=JSON.parse(s);if(a.formSchemas?.length){for(const e of a.formSchemas)i.register({...e,submitLabel:e.submitLabel||"Submit",fields:e.fields});this.logger.info?.(`[acp-server] Registered ${a.formSchemas.length} form schema(s) from skill "${e}"`)}}catch{}}).catch(e=>{this.logger.debug?.(`[acp-server] skill form schema import failed: ${e instanceof Error?e.message:String(e)}`)})}emitCanvasActionAsUserMessage(e,t){this.canvasOrch.emitCanvasActionAsUserMessage(e,t)}stop(){const e=this.teardownContext.getStore();if(e?.server===this){const t=e.generation;return this.activeGeneration!==t?t.stopPromise??t.teardownPromise??Promise.resolve():(e.detachOwner(),t.stopPromise??Promise.resolve())}const t=this.activeGeneration;if(!t||"stopped"===this.lifecyclePhase)return Promise.resolve();if("stopping"===this.lifecyclePhase)return t.stopPromise??Promise.resolve();let i,n;const r=new Promise((e,t)=>{i=e,n=t});t.stopPromise=r,this.lifecyclePhase="stopping",t.abortController.abort();const s=this.teardownGeneration(t);return(async()=>{try{t.startPromise.catch(()=>{}),await s,i()}catch(e){n(e)}finally{this.activeGeneration===t&&(this.activeGeneration=void 0,this.lifecyclePhase="stopped")}})(),r}teardownGeneration(e){return e.teardownPromise||(e.teardownPromise=this.teardownGenerationOnce(e)),e.teardownPromise}async teardownGenerationOnce(e){const t=[],i=new AbortController;let n,r=!1;const s=new Promise(e=>{n=setTimeout(()=>{r=!0,i.abort(),e()},ACPServer.LIFECYCLE_TEARDOWN_TIMEOUT_MS),n.unref?.()}),a=async(n,r)=>{let a;const o=new Promise(e=>{a=e}),c=Promise.resolve().then(()=>this.teardownContext.run({server:this,generation:e,detachOwner:a},()=>r(i.signal))),l=await Promise.race([c.then(()=>({status:"completed"}),e=>({status:"failed",error:e})),s.then(()=>({status:"timed_out"})),o.then(()=>({status:"detached"}))]);if("failed"===l.status)t.push(l.error);else if("timed_out"===l.status){const i=new Error(`lifecycle teardown timed out: ${n}`);t.push(i),this.logger.error?.(`[acp-server] ${i.message} generation=${e.id}`),this.writeObs({component:"acp-server",domain:"lifecycle",name:"lifecycle.teardown_timeout",severity:"error",payload:{owner:n,generation:e.id,timeoutMs:ACPServer.LIFECYCLE_TEARDOWN_TIMEOUT_MS}})}},o=[];this.setTerminalServiceReady(e,!1),o.push(a("session-mutation-coordinator",()=>this.sessionMutationCoordinator.fenceActive("gateway_disconnected"))),this.agentSyncRetryTimer&&(clearTimeout(this.agentSyncRetryTimer),this.agentSyncRetryTimer=null),o.push(a("session-state",()=>this.state.destroy())),o.push(a("canvas-orchestrator",()=>this.canvasOrch.destroy())),o.push(a("mcp-receipt-history",()=>this.core.setRuntimeReceiptHistoryPort(void 0)));const c=e.bridge,l=e.bridgeSocket,h=e.terminalOutbound,g=e.runtimeConsumer,d=this.client&&this.client!==l?this.client:null,u=this.wss;if(e.bridge=void 0,e.bridgeSocket=void 0,e.terminalOutbound=void 0,e.runtimeConsumer=void 0,this.bridgeInstance===c&&(this.bridgeInstance=null),this.client!==d&&this.client!==l||(this.client=null),this.terminalOutbound===h&&(this.terminalOutbound=null),this.runtimeRpcConsumer===g&&(this.runtimeRpcConsumer=void 0),this.wss=null,this.bridgeConsecutiveFailures=0,d&&o.push(a("incoming-client",()=>{d.removeAllListeners(),d.close()})),c&&o.push(a("bridge",async()=>{try{await this.closeBridgeOnce(c)}catch(e){this.logger.debug?.(`[acp-server] bridge.close failed during stop: ${e instanceof Error?e.message:String(e)}`)}})),h&&o.push(a("terminal-outbound",e=>h.drain({timeoutMs:ACPServer.LIFECYCLE_TEARDOWN_TIMEOUT_MS,terminateAfterTimeout:!0,signal:e}))),e.authorityOwned&&(e.authorityOwned=!1,o.push(a("runtime-authority",()=>e.authority?.stop()))),g&&o.push(a("runtime-consumer",e=>this.closeConsumerOnce(g,e))),u&&o.push(a("websocket-server",()=>{u.removeAllListeners(),u.close()})),await Promise.all(o),await a("runtime-authority-reset",()=>e.authority?.setRuntimeConsumerLive(!1)),await a("terminal-handler",()=>this.terminalHandler.destroy()),await a("terminal-manager",()=>this.terminalMgr.destroy()),n&&clearTimeout(n),r||i.abort(),this.logger.info?.("[acp-server] Stopped"),this.writeObs({component:"acp-server",domain:"lifecycle",name:"lifecycle.shutdown",severity:"info"}),t.length>0)throw t[0]}}
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{resolveConfigWithMeta,mirrorOpenClawConfig}from"./config.js";import{hasRuntimeReplyApi}from"./runtime/reply-router.js";import{PromptRunStore}from"./runtime/prompt-run-store.js";import{createMcpNativeProjectionNudge,fingerprintMcpServers}from"./acp-gateway/mcp-native-projection-nudge.js";import{SessionWriteGate}from"./acp-gateway/session-write-gate.js";import{OpenClawSessionWriteQuiescence}from"./runtime/openclaw-session-write-quiescence.js";import{SessionMutationCoordinator}from"./runtime/session-mutation-coordinator.js";import{scheduleProcessRuntimeRestart}from"./runtime/runtime-restart-scheduler.js";import{createImInboundHandler}from"./im-inbound-handler.js";import{createTokenProvider}from"@beeos-ai/message-sdk/auth";import{BeeosClawMessageClientProcessOwner}from"./message-client-composition.js";import{MessageProjectionInboundConsumer}from"./message-projection-inbound-consumer.js";import{MessageClientTerminalStateRecovery}from"./runtime/message-client-terminal-state-recovery.js";import{RuntimeAuthorityMessageBusLifecycle}from"./runtime/runtime-authority-message-bus-lifecycle.js";import{FileInboundExecutionJournal}from"./inbound-execution-journal.js";import{completeA2ATask,registerA2ATools,TaskHandleRegistry}from"./a2a/tools.js";import{BEEOS_UPLOAD_TOOL_NAME,BEEOS_UPLOAD_TOOL_SCHEMA,BEEOS_UPLOAD_TOOL_DESCRIPTION,executeUpload}from"./upload/tool.js";import{BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,executeDesktopScreenshot}from"./upload/desktop-screenshot-tool.js";import{BEEOS_LIST_FILES_TOOL_NAME,BEEOS_LIST_FILES_TOOL_SCHEMA,BEEOS_LIST_FILES_TOOL_DESCRIPTION,executeListFiles}from"./files/list-tool.js";import{BEEOS_FETCH_FILE_TOOL_NAME,BEEOS_FETCH_FILE_TOOL_SCHEMA,BEEOS_FETCH_FILE_TOOL_DESCRIPTION,executeFetchFile}from"./files/fetch-tool.js";import{writeUploadToolResult,cleanupUploadToolResultCache}from"./upload/result-cache.js";import{handleFileStage}from"./upload/file-stage.js";import{handleStatusRequest}from"./status.js";import{ACPServer}from"./acp-server.js";import{GatewayClient}from"./gateway-client.js";import{createObsEventWriter}from"./observability.js";import{resolvePluginVersion}from"./plugin-version.js";import{createTerminalWebSocketService}from"./terminal/websocket.js";import{TerminalManager}from"./terminal/session-manager.js";import{loadAgentKeyPair,agentBoundFetch,agentFetch}from"./agent-auth.js";import{readDiscoveryCache,writeDiscoveryCache}from"./discovery-cache.js";import{discoverBridgeFromGW}from"./bridge-discovery.js";import{setUploadDeps}from"./upload/upload-file.js";import{registerLocalFileExtractor}from"./upload/local-file-extractors.js";import{browserScreenshotExtractor}from"./browser/screenshot-extractor.js";import{BEEOS_FILE_RESOURCE_ROUTING_RULE}from"./files/beeos-file-uri.js";import{CONNECTOR_MCP_PROXY_PATH,handleConnectorMcpRequest}from"./mcp-connector-proxy.js";import{CanvasStateManager}from"./canvas/state.js";import{CanvasRelayClient}from"./canvas/relay-client.js";import{RENDER_UI_TOOL_NAME,RENDER_UI_TOOL_DESCRIPTION,RENDER_UI_TOOL_SCHEMA,CLOSE_UI_TOOL_NAME,CLOSE_UI_TOOL_DESCRIPTION,CLOSE_UI_TOOL_SCHEMA,createRenderUiHandler,createCloseUiHandler,CANVAS_SYSTEM_PROMPT}from"./canvas/tools.js";import{FormSchemaRegistry}from"./surface/form-schema-registry.js";import{buildCronSchemas}from"./surface/cron-schemas.js";import{RENDER_FORM_TOOL_NAME,RENDER_FORM_TOOL_SCHEMA,buildRenderFormDescription,createRenderFormHandler}from"./surface/render-form-tool.js";import{ResourceDescriptorRegistry}from"./surface/resource-descriptor-registry.js";import{CRON_RESOURCE}from"./surface/cron-resource.js";import{generateActionTools}from"./surface/action-tool-generator.js";import{buildThirdPartyChannelSet,isThirdPartyChannel}from"./channel-detection.js";import{registerArouterMediaProviders}from"./media/index.js";import{homedir}from"node:os";import{join}from"node:path";import{RuntimeProductionAuthority}from"./runtime/runtime-production-authority.js";import{FileRuntimeCommandJournal}from"./runtime/runtime-command-journal.js";import{FileRuntimePoisonDeliveryJournal}from"./runtime/runtime-poison-delivery-journal.js";import{OpenClawNativeMcpProjection}from"./acp-gateway/openclaw-native-mcp-projection.js";import{LocalEncryptedCredentialReferenceVault}from"./acp-gateway/mcp-credential-reference-port.js";import{AgentGatewayGrantJwksReadinessAdapter,RuntimeServiceTransportReadinessAdapter,createRuntimeServiceProductionCandidate}from"./runtime/runtime-service-production-candidate.js";import{MessageServiceCronDeliveryHttpAuthority}from"./runtime/message-service-cron-delivery-http-authority.js";import{AgentMappingRegistry}from"./runtime/agent-mapping-registry.js";import{OpenClawRuntimeProcessOwner}from"./runtime/openclaw-runtime-process-owner.js";import{AgentGatewayHttpProjectionTransport,AgentGatewayTemplateOriginReporter,AgentProjectionProduction,OpenClawAgentProjectionSource}from"./runtime/agent-projection-production.js";import{MessageServiceRunOutputChannel}from"./runtime/message-service-run-output-channel.js";import{AssistantMessageCommitRouter}from"./runtime/assistant-message-commit-router.js";import{ToolResultObservationRouter}from"./runtime/tool-result-observation-router.js";import{ModelCallObservationRouter,registerModelCallObservationHooks}from"./runtime/model-call-observability.js";import{registerArouterLatencyRouting}from"./runtime/arouter-latency-routing.js";import{createPeerRunOutputRouterFactory}from"./runtime/run-output-composition.js";const PLUGIN_VERSION=resolvePluginVersion();let agentKeys=null,sharedCanvasState=null,sharedAcpServer=null;const sharedSessionWriteGate=new SessionWriteGate,sharedPromptRunStore=new PromptRunStore;let lastOpenclawConfig;const sharedMcpNativeProjectionNudge=createMcpNativeProjectionNudge({getActiveImRunCount:()=>sharedPromptRunStore.size(),nudge:()=>{sharedAcpServer?.nudgeMcpNativeProjection()}});sharedPromptRunStore.setOnBecameIdle(()=>{sharedMcpNativeProjectionNudge.flushIfIdle({fingerprint:fingerprintMcpServers(lastOpenclawConfig)})});const sharedAssistantMessageCommitRouter=new AssistantMessageCommitRouter,sharedToolResultObservationRouter=new ToolResultObservationRouter,sharedModelCallObservationRouter=new ModelCallObservationRouter;let sharedSessionMutationCoordinator,sharedRuntimeAuthority;const sharedAgentMappings=new AgentMappingRegistry;let sharedGatewayClient,sharedRuntimeOwner,sharedAgentProjection,sharedRuntimeAgentCapabilities,sharedMcpProjection,sharedMcpCredentialVault;const sharedMessageClientOwner=new BeeosClawMessageClientProcessOwner;let sharedActiveMessageConsumer,sharedInboundExecutionJournal;const sharedTerminalManager=new TerminalManager;let _initializing=!1,_pluginRegisteredAt=0;const ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex");function agentKeyPairToIdentity(e){return{publicKeyBase64:()=>Buffer.from(e.publicKey).toString("base64"),sign:async t=>{const{createPrivateKey:r,sign:o}=await import("node:crypto"),n=r({key:32===e.privateKey.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(e.privateKey)]):Buffer.from(e.privateKey),format:"der",type:"pkcs8"});return new Uint8Array(o(null,Buffer.from(t),n))}}}let unhandledRejectionObserverInstalled=!1;function installUnhandledRejectionObserver(e,t,r){unhandledRejectionObserverInstalled||(unhandledRejectionObserverInstalled=!0,process.on("unhandledRejection",r=>{const o=r instanceof Error?r.message:String(r),n=r instanceof Error?r.stack:void 0;e.logger.error?.(`[beeos-claw] unhandledRejection: ${o}${n?`\n${n}`:""}`),t({component:"plugin",domain:"fatal",name:"unhandled_rejection",severity:"error",summary:o,error:{code:"unhandled_rejection",message:o,...n?{stack:n}:{}}})}),process.on("uncaughtException",r=>{const o=r instanceof Error?r.message:String(r);e.logger.error?.(`[beeos-claw] uncaughtException: ${r instanceof Error?r.stack??r.message:String(r)}`),t({component:"plugin",domain:"fatal",name:"uncaught_exception",severity:"error",summary:o,error:{code:"uncaught_exception",message:o}})}))}function initializePlugin(e){let t,r;try{t=e.runtime?.config?.loadConfig?.()}catch{}try{r=resolveConfigWithMeta({pluginConfig:e.pluginConfig??{},openclawConfig:t})}catch(t){return e.logger.error?.(`[beeos-claw] Failed to resolve config: ${t instanceof Error?t.message:String(t)}`),null}const o=r.config,n=buildThirdPartyChannelSet(t);n.size>0&&e.logger.info?.(`[beeos-claw] Third-party channels detected: ${[...n].join(", ")}`);const a=o.log.enabled?createObsEventWriter({dir:o.log.dir,redaction:{gatewayToken:o.gateway.token},logger:e.logger}):()=>{};a({component:"plugin",domain:"lifecycle",name:"lifecycle.startup",severity:"info",payload:{pluginVersion:PLUGIN_VERSION,pid:process.pid,nodeVersion:process.version,cwd:process.cwd()}}),a({component:"plugin",domain:"config",name:"config.resolved",severity:"info",payload:{config:o,sources:r.sources,openclawConfigPath:r.openclawConfigPath}});for(const t of r.validation)a({component:"plugin",domain:"config",name:"config.validation",severity:t.severity,summary:t.message,error:{code:t.code,message:t.message,nextSteps:t.nextSteps}}),"error"===t.severity?e.logger.error?.(`[beeos-claw] ${t.message}`):e.logger.warn?.(`[beeos-claw] ${t.message}`);if(r.validation.some(e=>"error"===e.severity)&&e.logger.error?.("[beeos-claw] Plugin has configuration errors — some features may not work."),o.keyFile)try{agentKeys=loadAgentKeyPair(o.keyFile);const t=Buffer.from(agentKeys.publicKey).toString("base64");e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (publicKey: ${t})`)}catch(t){e.logger.warn?.(`[beeos-claw] Failed to load agent key pair: ${t}`),e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no key pair — signed requests disabled)`)}else e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no keyFile configured)`);return agentKeys&&o.agentGatewayUrl&&(setUploadDeps({gatewayUrl:o.agentGatewayUrl,keys:agentKeys,logger:e.logger}),registerLocalFileExtractor(browserScreenshotExtractor),e.logger.info?.("[beeos-claw] Upload deps + browser screenshot extractor registered")),lastOpenclawConfig=t,{cfg:o,writeObs:a,thirdPartyChannels:n,openclawConfig:t}}function registerUploadTools(e,t,r){const{cfg:o,writeObs:n}=t;cleanupUploadToolResultCache().catch(t=>{e.logger.warn?.(`[beeos_upload_file] cache cleanup failed on startup: ${String(t)}`)}),e.registerTool({name:BEEOS_UPLOAD_TOOL_NAME,description:BEEOS_UPLOAD_TOOL_DESCRIPTION,parameters:BEEOS_UPLOAD_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_upload_file] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeUpload(r,o,agentKeys,a);return e.logger.debug?.(`[beeos_upload_file] toolCallId=${t} ok=${i.ok}`),n({component:"upload-tool",domain:"tool",name:i.ok?"upload.success":"upload.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,fileCount:i.files?.length??0,error:i.error}}),(async r=>{try{await writeUploadToolResult(t,BEEOS_UPLOAD_TOOL_NAME,r),await cleanupUploadToolResultCache()}catch(r){e.logger.warn?.(`[beeos_upload_file] persist cache failed toolCallId=${t}: ${String(r)}`)}return r})(i)}}),e.registerTool({name:BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,description:BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,parameters:BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeDesktopScreenshot(r,o,agentKeys,a);return e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} ok=${i.ok}`),n({component:"desktop-screenshot-tool",domain:"tool",name:i.ok?"desktop_screenshot.success":"desktop_screenshot.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,path:i.path,fileCount:i.files?.length??0,error:i.error}}),(async r=>{try{await writeUploadToolResult(t,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,r),await cleanupUploadToolResultCache()}catch(r){e.logger.warn?.(`[beeos_desktop_screenshot] persist cache failed toolCallId=${t}: ${String(r)}`)}return r})(i)}}),e.registerHttpRoute({path:"/beeos/files/stage",auth:"gateway",async handler(e,r){await handleFileStage(e,r,t.cfg)}}),e.registerHttpRoute({path:"/beeos/status",auth:"plugin",handler(e,t){handleStatusRequest(e,t,{publicKey:agentKeys?.publicKey??null,pluginVersion:PLUGIN_VERSION})}}),e.registerTool({name:BEEOS_LIST_FILES_TOOL_NAME,description:BEEOS_LIST_FILES_TOOL_DESCRIPTION,parameters:BEEOS_LIST_FILES_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_list_files] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeListFiles(r,o,agentKeys,a);return n({component:"file-list-tool",domain:"tool",name:i.ok?"list_files.success":"list_files.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,fileCount:i.files?.length??0,error:i.error}}),i}}),e.registerTool({name:BEEOS_FETCH_FILE_TOOL_NAME,description:BEEOS_FETCH_FILE_TOOL_DESCRIPTION,parameters:BEEOS_FETCH_FILE_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_fetch_file] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeFetchFile(r,o,agentKeys,{signal:a,resolvePath:e.resolvePath});return n({component:"file-fetch-tool",domain:"tool",name:i.ok?"fetch_file.success":"fetch_file.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,path:i.path,size:i.size,error:i.error}}),i}})}function registerConnectorMcpProxy(e,t){e.registerHttpRoute({path:CONNECTOR_MCP_PROXY_PATH,auth:"plugin",async handler(r,o){await handleConnectorMcpRequest(r,o,{agentGatewayUrl:t.cfg.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger})}})}function registerServices(e,t,r){const{cfg:o,writeObs:n,thirdPartyChannels:a}=t;if("function"!=typeof e.on){if(o.messageBus.enabled)throw new Error("openclaw_before_message_write_hook_unavailable")}else registerModelCallObservationHooks((t,r)=>e.on(t,r),sharedModelCallObservationRouter),e.on("before_message_write",(t,r)=>{const o=t.sessionKey??r.sessionKey;if(!o)return void e.logger.debug?.("[before_message_write] ignored assistant write without sessionKey");sharedAssistantMessageCommitRouter.route(o,t.message)||e.logger.debug?.(`[before_message_write] no active IM commit target sessionKey=${o} active_targets=${sharedAssistantMessageCommitRouter.size()}`)}),e.on("after_tool_call",(t,r)=>{const o=r.sessionKey,n=t.toolCallId??r.toolCallId,a=t.toolName||r.toolName;if(!o||!n||!a)return void e.logger.debug?.(`[after_tool_call] ignored structured result without correlation sessionKey=${o??"missing"} toolCallId=${n??"missing"}`);sharedToolResultObservationRouter.route(o,{toolCallId:n,toolName:a,rawResult:t.result,isError:Boolean(t.error)})||e.logger.debug?.(`[after_tool_call] no active IM observation target sessionKey=${o} active_targets=${sharedToolResultObservationRouter.size()}`)});const i=agentKeys?(e,t)=>agentFetch(e,agentKeys,t):void 0,s=agentKeys?(e,t)=>agentBoundFetch(e,agentKeys,t):void 0,l=agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):void 0,c=join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos"),g=sharedInboundExecutionJournal??=new FileInboundExecutionJournal(join(c,"inbound-execution-journal.v1.jsonl"));if(sharedMcpProjection??=new OpenClawNativeMcpProjection,!sharedMcpCredentialVault&&agentKeys&&l&&(sharedMcpCredentialVault=new LocalEncryptedCredentialReferenceVault(join(c,"mcp-credential-vault.json"),l,agentKeys.privateKey)),!sharedRuntimeAuthority&&agentKeys&&o.agentGatewayUrl&&i&&s&&(sharedRuntimeAuthority=new RuntimeProductionAuthority({stateDirectory:c,agentGatewayUrl:o.agentGatewayUrl,agentFetch:i,boundAgentFetch:s,keys:agentKeys,logger:e.logger})),sharedGatewayClient??=new GatewayClient({url:o.gateway.url,token:o.gateway.token||"",clientId:o.gateway.clientId,openclawCanvasCompat:!!o.openclawCanvasCompat,retry:{baseMs:o.retry.baseMs,maxMs:o.retry.maxMs,maxAttempts:o.retry.maxAttempts}}),sharedRuntimeAgentCapabilities??={canvas:o.canvasEnabled,terminal:!1,webSsh:!1,fileSystem:!0,image:!0,audio:!1,mcp:!0,cron:!0,streaming:!0,pushNotifications:o.messageBus.enabled},!sharedAgentProjection&&sharedRuntimeAuthority&&i&&o.agentGatewayUrl){const t=sharedRuntimeAuthority;sharedAgentProjection=new AgentProjectionProduction({authority:t,stateFile:join(t.stateDirectory,"runtime-agent-projection.json"),journal:t.journal,identities:t.identities,source:new OpenClawAgentProjectionSource(()=>sharedGatewayClient),transport:new AgentGatewayHttpProjectionTransport({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:t.boundAgentFetch,getRuntimeLeaseCredential:e=>{const r=t.authorityLease;if(!r||r.instanceId!==e.instanceId||r.leaseId!==e.leaseId||r.runtimeEpoch!==e.runtimeEpoch||r.handlerIdentity!==e.handlerIdentity)throw new Error("agent projection fenced by current runtime lease");return r.runtimeLeaseCredential},onAccepted:t=>{0!==t.size&&sharedAgentMappings.replace(t).catch(t=>{e.logger.warn?.("[agent-projection] mapping publication failed: "+(t instanceof Error?t.message:String(t)))})}}),capabilities:sharedRuntimeAgentCapabilities,logger:e.logger})}sharedRuntimeOwner??=new OpenClawRuntimeProcessOwner(sharedGatewayClient,sharedAgentProjection,e.logger);const d=sharedRuntimeOwner;sharedSessionMutationCoordinator??=new SessionMutationCoordinator(sharedSessionWriteGate,new OpenClawSessionWriteQuiescence(e.logger,{agentId:o.gateway.agentId||"main"}),e.logger,{onRecoveryRequired:t=>{scheduleProcessRuntimeRestart(t,e.logger)}});const u=sharedSessionMutationCoordinator;if(!sharedAcpServer){const t=sharedRuntimeAuthority&&o.agentGatewayUrl?new AgentGatewayTemplateOriginReporter({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:sharedRuntimeAuthority.boundAgentFetch}):void 0;sharedAcpServer=new ACPServer(o,e.logger,u,n,i,l,a,sharedTerminalManager,sharedRuntimeAuthority,void 0,sharedMcpProjection,sharedMcpCredentialVault,s,{gateway:d.client,agentProjection:sharedAgentProjection,markAgentProjectionDirty:()=>sharedAgentProjection?.markDirty(),templateOriginReporter:t,agentMappings:sharedAgentMappings,agentCapabilities:sharedRuntimeAgentCapabilities,createRunOutputRouter:createPeerRunOutputRouterFactory(new MessageServiceRunOutputChannel({getClient:()=>sharedMessageClientOwner.current?.client??null,logger:e.logger,writeObs:n}))})}const p=sharedAcpServer,m=()=>sharedAgentMappings.snapshot()?.values().next().value;r.current=p,sharedMcpNativeProjectionNudge.request({registrationMode:e.registrationMode,fingerprint:fingerprintMcpServers(t.openclawConfig),logger:e.logger});const y=sharedPromptRunStore,{enabled:h,principalId:f,a2aEnabled:w}=o.messageBus;let v;o.agentGatewayUrl&&agentKeys&&(v=createTokenProvider({agentGatewayUrl:o.agentGatewayUrl,identity:agentKeyPairToIdentity(agentKeys)}));const b=process.env.MESSAGE_RUNTIME_DELIVERY_KEY?.trim(),C=f||process.env.BEEOS_PRINCIPAL_ID||"",_=v?sharedMessageClientOwner.configure({tokenProvider:v,identityId:C,...o.messageBus.serviceUrl?{serviceUrl:o.messageBus.serviceUrl}:{},logger:e.logger,...sharedRuntimeAuthority&&b?{runtimeDelivery:{scopedDeliveryKey:b,authority:{currentLease:()=>{const e=sharedRuntimeAuthority?.authorityLease,t=sharedRuntimeAuthority?.currentConsumerBinding();return e&&t?{...e,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration}:null}}}}:{}}):void 0,R=sharedRuntimeAuthority,O=o.messageBus.serviceUrl;if(_&&R&&O&&b){const e=_.nodeTransport;p.setCronConversationDelivery({deliver:async t=>{const r=R.authorityLease;if(!r)throw new Error("current runtime authority lease unavailable for cron delivery");const o=new MessageServiceCronDeliveryHttpAuthority({messageServiceUrl:O,fetchAuthorized:async(t,o)=>await e.fetchRuntimeChannel(t,{runtimeLeaseCredential:r.runtimeLeaseCredential},o)});return await o.deliver({...t,runtimeLeaseCredential:r.runtimeLeaseCredential})}})}else p.setCronConversationDelivery(void 0);const E=sharedRuntimeAuthority?.journal??new FileRuntimeCommandJournal(join(c,"runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:t=>e.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=threshold error=${t instanceof Error?t.message:String(t)}`)}),S=d.createRegistrationToken();e.registerService({id:"beeos-acp",async start(){const t=Date.now(),r=_pluginRegisteredAt?t-_pluginRegisteredAt:-1;e.logger.info?.(`[beeos-claw] beeos-acp service start() begin, sincePluginRegister=${r}ms, bridge.url=${o.bridge.url||"(empty)"}`);const n=await createRuntimeServiceProductionCandidate({...o.agentGatewayUrl&&i?{authorizationAdapter:new AgentGatewayGrantJwksReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl})}:{},..._&&o.agentGatewayUrl&&s&&b?{transportAdapter:new RuntimeServiceTransportReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl,messageService:_.nodeTransport,messageClient:_.client,configuredServiceUrl:o.messageBus.serviceUrl,boundAgentFetch:s,scopedDeliveryKey:b})}:{},commandJournal:E,poisonJournal:new FileRuntimePoisonDeliveryJournal(join(c,"runtime-poison-deliveries.json")),onConsumerDeliveryError:t=>{const r=t instanceof Error?`${t.name}: ${t.message}${"status"in t?` status=${t.status}`:""}${"code"in t?` code=${t.code}`:""}\n${t.stack??""}`:String(t);e.logger.error?.(`[runtime-service] delivery worker failed before acknowledgement; delivery retained for retry: ${r}`)},onConsumerLeaseStarvation:({starvedForMs:t,recovered:r})=>{const o=Math.round(t/1e3);r?e.logger.info?.(`[runtime-service] runtime authority lease restored after ${o}s without delivery reads`):e.logger.warn?.(`[runtime-service] no runtime authority lease for ${o}s; delivery reads suspended, queued runtime operations (model switch, session control) will not be consumed`)},onConsumerRequestFailure:t=>e.logger.error?.(`[runtime-service] request failed operationId=${t.operationId} method=${t.method}: ${t.error instanceof Error?t.error.stack??t.error.message:String(t.error)}`)});if(sharedRuntimeAuthority?.setRuntimeServiceMethods("ready"===n.status?n.serviceMethods:[]),sharedRuntimeAuthority?.setRuntimeResourceBindingContractRevision("ready"===n.status?n.resourceBindingContractRevision:void 0),p.setRuntimeRpcConsumerFactory("ready"===n.status?n.factory:void 0),"unavailable"===n.status?e.logger.warn?.("[runtime-service] capability fenced: "+n.blockers.map(e=>e.id).join(",")):n.capabilityBlockers.length&&e.logger.warn?.("[runtime-service] method capability fenced: "+n.capabilityBlockers.map(e=>e.method).join(",")),!o.bridge.url&&o.agentGatewayUrl&&i){const t=Date.now(),r=await discoverBridgeFromGW(o.agentGatewayUrl,i);if(e.logger.info?.(`[beeos-claw] bridge discovery took ${Date.now()-t}ms result=${r?.url??"null"}`),r)o.bridge.url=r.url,writeDiscoveryCache({bridge:{url:r.url,discoveredAt:Date.now()}}),e.logger.info?.(`[beeos-claw] Bridge URL discovered from Agent Gateway: ${r.url}`);else{const t=readDiscoveryCache();t?.bridge?.url?(o.bridge.url=t.bridge.url,e.logger.info?.(`[beeos-claw] Bridge URL from cache (GW unreachable): ${t.bridge.url}`)):e.logger.debug?.("[beeos-claw] Bridge URL not available (no GW, no cache) — bridge disabled")}}await d.acquire(S);try{await p.start(o.bridge.url||void 0)}catch(e){throw await d.release(S),e}e.logger.info?.(`[beeos-claw] ACP Server started (total service start: ${Date.now()-t}ms)`)},async stop(){try{await d.release(S)}finally{await p.stop()}await E.checkpoint(),e.logger.info?.("[runtime-service] command journal checkpoint completed"),e.logger.info?.("[beeos-claw] ACP Server stopped")}});if(h&&void 0!==v&&v){const t=sharedMessageClientOwner.createRegistrationToken(),r=async(e,t)=>{const r=await v(e);if(!r.identityId||r.identityId!==e)throw new Error("Message SDK token identity did not match the current runtime authority");return{principalId:e,runtimeEpoch:t}},a=(e,t,r)=>({getLocalAgentId:()=>t,resolveLocalAgentId:e=>e===r?t:void 0,getDefaultPlatformAgent:()=>({instanceId:e.principalId}),ensureImInboundSession:(e,t,r,o)=>{p.ensureImInboundSession(e,t,r,o)},chatAbort:(e,t)=>p.chatAbort(e,t)}),i=sharedRuntimeAuthority?new RuntimeAuthorityMessageBusLifecycle({authority:sharedRuntimeAuthority,logger:e.logger,bus:{connect:async i=>{await sharedMessageClientOwner.acquire(t,async t=>{const s=await r(i.instanceId,i.runtimeEpoch),l=t.client,c=new Map,d=new MessageProjectionInboundConsumer({client:l,bindings:()=>{const t=sharedRuntimeAuthority?.authorityLease;if(!t||t.instanceId!==s.principalId||t.runtimeEpoch!==s.runtimeEpoch)return[];const r=sharedAgentMappings.snapshot();return r?[...r.entries()].flatMap(([r,i])=>{if(!i.id||i.instanceId!==s.principalId)return[];let g=c.get(i.id);return g&&g.localAgentId===r||(g={localAgentId:r,inbound:createImInboundHandler({runtime:e.runtime,pluginConfig:e.pluginConfig,client:l,localServer:a(s,r,i.id),promptRunStore:y,assistantMessageCommitRouter:sharedAssistantMessageCommitRouter,toolResultObservationRouter:sharedToolResultObservationRouter,modelCallObservationRouter:sharedModelCallObservationRouter,sessionMutationCoordinator:u,logger:e.logger,writeObs:n,principalIdOverride:s.principalId,promptTimeoutMs:o.promptTimeoutMs,activeRunAuthority:p.getActiveRunAuthority(),...o.agentGatewayUrl&&agentKeys?{completeA2ATask:(t,r,n)=>completeA2ATask({agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},t,r,n)}:{}})},c.set(i.id,g)),[{principalId:s.principalId,targetAgentId:i.id,runtimeEpoch:t.runtimeEpoch,inbound:g.inbound}]}):[]},executionJournal:g,logger:e.logger}),m=sharedAgentMappings.subscribe(()=>{sharedActiveMessageConsumer===d&&d.refresh()}),h=sharedRuntimeAuthority?.subscribeAuthority(()=>{sharedActiveMessageConsumer===d&&d.refresh()})??(()=>{}),f=new MessageClientTerminalStateRecovery({logger:e.logger,isCurrent:()=>{if(sharedActiveMessageConsumer!==d)return!1;const e=sharedRuntimeAuthority?.authorityLease;return null!=e&&e.instanceId===s.principalId&&e.runtimeEpoch===s.runtimeEpoch&&e.leaseId===i.leaseId&&e.handlerIdentity===i.handlerIdentity},reconnect:async()=>{if("failed"===l.getSnapshot().connection){await l.connect();try{await p.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient recovery: ${t instanceof Error?t.message:String(t)}`)}}}});let w=l.getSnapshot().connection;const v=l.subscribe(()=>{const t=l.getSnapshot();t.connection!==w&&(w=t.connection,"connected"===t.connection?(f.notifyConnected(),e.logger.info?.(`[beeos-claw] connection transport=message_service_realtime state=connected localAgentId=${p.getLocalAgentId()} principalId=${s.principalId}`)):"failed"===t.connection?(e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=failed principalId=${s.principalId} recoveryError=${t.recoveryError??"unknown"}`),f.notifyFailed()):"disconnected"!==t.connection&&"reconnecting"!==t.connection||e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=${t.connection} principalId=${s.principalId}; SDK owns reconnect/recovery`))});sharedActiveMessageConsumer=d,d.start();try{await l.connect()}catch(e){throw sharedActiveMessageConsumer===d&&(sharedActiveMessageConsumer=void 0),f.dispose(),await d.stop(),m(),h(),v(),await l.disconnect().catch(()=>{}),e}try{await p.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient connect: ${t instanceof Error?t.message:String(t)}`)}return async()=>{sharedActiveMessageConsumer===d&&(sharedActiveMessageConsumer=void 0),f.dispose(),await d.stop(),h(),v(),await l.disconnect(),e.logger.info?.("[beeos-claw] MessageClient disconnected")}})},disconnect:async()=>{await sharedMessageClientOwner.release(t)}}}):void 0;e.registerService({id:"beeos-message-bus",async start(){i?i.start():e.logger.warn?.("[beeos-claw] Message bus requires runtime authority; waiting is unavailable without Agent Gateway runtime identity")},async stop(){await(i?.stop())}})}else h?e.logger.warn?.("[beeos-claw] Message bus enabled but Agent Gateway token provider is unavailable — disabled"):e.logger.info?.(`[beeos-claw] Message bus disabled (enabled=${h})`);if(!1!==w&&o.agentGatewayUrl){const t=e=>p.getGatewaySessionKeyForToolCall(e),r=e=>p.getMentionsForToolCall(e),n=(e,t)=>{p.broadcastNotification(e,t)},a=new TaskHandleRegistry;registerA2ATools(e,{agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},a,t,r,n,()=>m()?.instanceId),e.logger.info?.("[beeos-claw] A2A tools registered (beeos_discover_agents, beeos_call_agent) via REST")}if(o.canvasEnabled){sharedCanvasState||(sharedCanvasState=new CanvasStateManager);const t=sharedCanvasState;if(o.agentGatewayUrl&&agentKeys){let r;t.setLogger(e.logger);{const e=o.agentGatewayUrl,t=agentKeys;r=async()=>{const r=await agentFetch(`${e}/api/v1/canvas/token`,t,{method:"POST"});if(!r.ok){const e=await r.text().catch(()=>"");throw new Error(`Canvas token returned ${r.status}: ${e}`)}return r.json()}}const n=n=>{const a=new CanvasRelayClient({canvasRelayUrl:o.canvasRelayUrl,agentKeys:agentKeys,agentId:n.id,displayName:n.displayName,avatarUrl:n.avatarUrl,tokenProvider:r,onConnected:()=>p.flushPendingCanvasLinks(),onReconnect:()=>p.onRelayReconnected(),logger:e.logger});t.setRelayClient(a),t.setAgentId(n.id),p.wireRelayActionHandler(),a.connect(),e.logger.info?.(`[beeos-claw] Canvas Relay multiplexed client connected (agentId=${n.id}, name=${n.name??"none"})`)};e.registerService({id:"beeos-canvas-relay",async start(){try{await sharedAgentMappings.waitForSynchronization();const t=m();if(t?.id)return void n(t);e.logger.info?.("[beeos-claw] Canvas Relay deferred: waiting for late agent sync..."),sharedAgentMappings.onceReady(()=>{const t=m();if(t?.id)try{n(t)}catch(t){e.logger.warn?.(`[beeos-claw] Deferred Canvas Relay start failed: ${t}`)}else e.logger.warn?.("[beeos-claw] Agent mapping ready but no platform agent; Canvas Relay stays disabled")})}catch(t){e.logger.warn?.(`[beeos-claw] Failed to start Canvas Relay client: ${t}`)}},stop(){t.getRelayClient()?.disconnect()}}),e.logger.info?.("[beeos-claw] Canvas Relay configured")}else e.logger.warn?.("[beeos-claw] Canvas Relay unavailable: agentGatewayUrl or agentKeys missing");const r={stateManager:t,logger:e.logger,writeObs:e=>n(e),isCanvasActive:e=>!!e&&p.isCanvasActiveForGatewayKey(e),getActiveSessionId:e=>p.getSessionIdForToolCall(e),getPlatformAgentId:e=>p.getPlatformAgentIdForToolCall(e),getGatewaySessionKey:e=>p.getGatewaySessionKeyForToolCall(e),waitForAgentSync:()=>sharedAgentMappings.waitForSynchronization()},i=new FormSchemaRegistry,s=buildCronSchemas(a,{capMs:o.cronMaxDurationCapMs});i.register(s.create),i.register(s.update);const l=new ResourceDescriptorRegistry;l.register(CRON_RESOURCE);const c=e=>async(t,r,o)=>(p.preMapToolCallSession(t),e(t,r));e.registerTool({name:RENDER_UI_TOOL_NAME,description:RENDER_UI_TOOL_DESCRIPTION,parameters:RENDER_UI_TOOL_SCHEMA,execute:c(createRenderUiHandler(r))}),e.registerTool({name:CLOSE_UI_TOOL_NAME,description:CLOSE_UI_TOOL_DESCRIPTION,parameters:CLOSE_UI_TOOL_SCHEMA,execute:c(createCloseUiHandler(r))}),p.setCanvasState(t,{schemaRegistry:i,descriptorRegistry:l});const g=p.getFormBuilder();g&&e.registerTool({name:RENDER_FORM_TOOL_NAME,description:buildRenderFormDescription(i),parameters:RENDER_FORM_TOOL_SCHEMA,execute:c(createRenderFormHandler({registry:i,builder:g,logger:e.logger}))});const d=p.getResourceRenderer();if(d){generateActionTools(l,e,async(e,t)=>p.gatewayRequest(e,t),d,e.logger)}const u=`\n\nTool selection guidance:\n- browser tool: Use when the user wants to OPEN, VISIT, NAVIGATE to a webpage, take SCREENSHOTS of web pages, interact with elements (click, type), or visually inspect pages.\n- ${BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME}: Use to capture the agent desktop (XFCE/VNC DISPLAY). Prefer this over shell/python X11 dumps; never invent raw Xlib/PIL capture.\n- beeos_search: Use ONLY for text-based information retrieval (fact-finding, research queries). Never for page interaction or screenshots.\n- beeos_fetch: Use ONLY to extract article text from a known URL. Never for visual content or screenshots.\n- ${BEEOS_FILE_RESOURCE_ROUTING_RULE}\nWhen the user says "open", "visit", "go to", or mentions seeing/viewing a webpage, ALWAYS prefer the browser tool. When they ask for a desktop / screen / VNC capture of the agent workspace, use beeos_desktop_screenshot.`,y=new Set([RENDER_UI_TOOL_NAME,CLOSE_UI_TOOL_NAME,RENDER_FORM_TOOL_NAME]),h=256,f=new Map;if("function"==typeof e.on){try{e.on("before_prompt_build",(e,t)=>{if(t?.sessionKey){if(f.size>=h&&!f.has(t.sessionKey)){const e=f.keys().next().value;void 0!==e&&f.delete(e)}f.set(t.sessionKey,t.channelId)}if(isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:t?.channelId}))return{appendSystemContext:'This conversation is in a third-party channel (Lark/Telegram/Discord/Slack); Canvas surfaces are not visible here. Do NOT use render_ui, close_ui, or render_form. Use built-in tools directly (e.g. the `cron` tool with `action:"add"` for scheduled tasks) and respond with plain text.'};if(!t?.sessionKey)return{appendSystemContext:u};return p.isCanvasActiveForGatewayKey(t.sessionKey)?{appendSystemContext:CANVAS_SYSTEM_PROMPT+u}:{appendSystemContext:"Canvas is closed by user. Do NOT use render_ui, close_ui, or render_form. Respond with plain text only."+u}}),e.logger.debug?.("[beeos-claw] Canvas system prompt hook registered: before_prompt_build")}catch{e.logger.debug?.("[beeos-claw] before_prompt_build hook not supported")}try{e.on("before_tool_call",(e,t)=>{if(t?.sessionKey&&p.setPendingToolCallSessionKey(t.sessionKey),!y.has(e.toolName))return;const r=t?.sessionKey?f.get(t.sessionKey):void 0;return isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:r})?{block:!0,blockReason:"Canvas tools are not available in this channel. Respond with plain text only."}:t?.sessionKey?p.isCanvasActiveForGatewayKey(t.sessionKey)?void 0:{block:!0,blockReason:"Canvas is closed by user. Respond with plain text instead."}:{block:!0,blockReason:"Canvas session unknown. Respond with plain text instead."}}),e.logger.debug?.("[beeos-claw] Canvas tool guard registered: before_tool_call")}catch{e.logger.debug?.("[beeos-claw] before_tool_call hook not supported")}}e.logger.info?.("[beeos-claw] Canvas tools registered (render_ui, close_ui, render_form + auto-action tools)")}else e.logger.info?.("[beeos-claw] Canvas disabled by config (canvasEnabled=false)");if(o.bridge.shell.enabled&&!0===o.legacyTerminalWsEnabled){const t=createTerminalWebSocketService({config:{port:o.terminalWsPort,token:agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):"",allowedOrigins:o.terminalWsAllowedOrigins},terminalMgr:sharedTerminalManager,logger:e.logger,writeObs:n});e.registerService({id:"beeos-terminal-ws",start(){t.start(),e.logger.info?.(`[beeos-claw] Terminal WS started on port ${o.terminalWsPort}`)},stop(){t.stop(),e.logger.info?.("[beeos-claw] Terminal WS stopped")}})}else o.bridge.shell.enabled?e.logger.info?.("[beeos-claw] Legacy static-token Terminal WS disabled (default)"):e.logger.info?.("[beeos-claw] Terminal/shell disabled by config (bridge.shell.enabled=false)");if(t.openclawConfig){const r=mirrorOpenClawConfig(t.openclawConfig);r.copied?e.logger.info?.(`[beeos-claw] mirrored openclaw config to ${r.destinationPath}`):e.logger.debug?.(`[beeos-claw] openclaw config mirror skipped: ${r.reason}`)}}const CAPABILITY_REGISTRATION_MODES=new Set(["full","discovery","tool-discovery"]),plugin={id:"beeos-claw",name:"beeos-claw",description:"BeeOS platform bridge plugin for OpenClaw. Provides file upload/download, ACP bridge, and observability.",version:PLUGIN_VERSION,register(e){if(!_initializing)if(!e.registrationMode||CAPABILITY_REGISTRATION_MODES.has(e.registrationMode)){_initializing=!0;try{if(!hasRuntimeReplyApi(e.runtime))throw e.logger.error?.("[beeos-claw] FATAL: OpenClaw runtime.channel.reply.dispatchReplyFromConfig not exposed. Upgrade OpenClaw or revert beeos-claw."),new Error("openclaw_runtime_reply_api_unavailable");const t=initializePlugin(e);if(!t)return;const r={current:null};installUnhandledRejectionObserver(e,t.writeObs,r),registerUploadTools(e,t,r),registerConnectorMcpProxy(e,t),registerArouterLatencyRouting(e),registerArouterMediaProviders(e),registerServices(e,t,r),_pluginRegisteredAt=Date.now(),e.logger.info?.("[beeos-claw] Plugin registered successfully")}finally{_initializing=!1}}else e.logger.debug?.(`[beeos-claw] Skipping registration (mode=${e.registrationMode})`)}};export default plugin;
1
+ import{resolveConfigWithMeta,mirrorOpenClawConfig}from"./config.js";import{hasRuntimeReplyApi}from"./runtime/reply-router.js";import{PromptRunStore}from"./runtime/prompt-run-store.js";import{createMcpNativeProjectionNudge,fingerprintMcpServers}from"./acp-gateway/mcp-native-projection-nudge.js";import{createImHotPathDeferral}from"./runtime/im-hot-path-deferral.js";import{SessionWriteGate}from"./acp-gateway/session-write-gate.js";import{OpenClawSessionWriteQuiescence}from"./runtime/openclaw-session-write-quiescence.js";import{SessionMutationCoordinator}from"./runtime/session-mutation-coordinator.js";import{scheduleProcessRuntimeRestart}from"./runtime/runtime-restart-scheduler.js";import{createImInboundHandler}from"./im-inbound-handler.js";import{createTokenProvider}from"@beeos-ai/message-sdk/auth";import{BeeosClawMessageClientProcessOwner}from"./message-client-composition.js";import{MessageProjectionInboundConsumer}from"./message-projection-inbound-consumer.js";import{MessageClientTerminalStateRecovery}from"./runtime/message-client-terminal-state-recovery.js";import{RuntimeAuthorityMessageBusLifecycle}from"./runtime/runtime-authority-message-bus-lifecycle.js";import{FileInboundExecutionJournal}from"./inbound-execution-journal.js";import{completeA2ATask,registerA2ATools,TaskHandleRegistry}from"./a2a/tools.js";import{BEEOS_UPLOAD_TOOL_NAME,BEEOS_UPLOAD_TOOL_SCHEMA,BEEOS_UPLOAD_TOOL_DESCRIPTION,executeUpload}from"./upload/tool.js";import{BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,executeDesktopScreenshot}from"./upload/desktop-screenshot-tool.js";import{BEEOS_LIST_FILES_TOOL_NAME,BEEOS_LIST_FILES_TOOL_SCHEMA,BEEOS_LIST_FILES_TOOL_DESCRIPTION,executeListFiles}from"./files/list-tool.js";import{BEEOS_FETCH_FILE_TOOL_NAME,BEEOS_FETCH_FILE_TOOL_SCHEMA,BEEOS_FETCH_FILE_TOOL_DESCRIPTION,executeFetchFile}from"./files/fetch-tool.js";import{writeUploadToolResult,cleanupUploadToolResultCache}from"./upload/result-cache.js";import{handleFileStage}from"./upload/file-stage.js";import{handleStatusRequest}from"./status.js";import{ACPServer}from"./acp-server.js";import{GatewayClient}from"./gateway-client.js";import{createObsEventWriter}from"./observability.js";import{resolvePluginVersion}from"./plugin-version.js";import{createTerminalWebSocketService}from"./terminal/websocket.js";import{TerminalManager}from"./terminal/session-manager.js";import{loadAgentKeyPair,agentBoundFetch,agentFetch}from"./agent-auth.js";import{readDiscoveryCache,writeDiscoveryCache}from"./discovery-cache.js";import{discoverBridgeFromGW}from"./bridge-discovery.js";import{setUploadDeps}from"./upload/upload-file.js";import{registerLocalFileExtractor}from"./upload/local-file-extractors.js";import{browserScreenshotExtractor}from"./browser/screenshot-extractor.js";import{BEEOS_FILE_RESOURCE_ROUTING_RULE}from"./files/beeos-file-uri.js";import{CONNECTOR_MCP_PROXY_PATH,handleConnectorMcpRequest}from"./mcp-connector-proxy.js";import{CanvasStateManager}from"./canvas/state.js";import{CanvasRelayClient}from"./canvas/relay-client.js";import{RENDER_UI_TOOL_NAME,RENDER_UI_TOOL_DESCRIPTION,RENDER_UI_TOOL_SCHEMA,CLOSE_UI_TOOL_NAME,CLOSE_UI_TOOL_DESCRIPTION,CLOSE_UI_TOOL_SCHEMA,createRenderUiHandler,createCloseUiHandler,CANVAS_SYSTEM_PROMPT}from"./canvas/tools.js";import{FormSchemaRegistry}from"./surface/form-schema-registry.js";import{buildCronSchemas}from"./surface/cron-schemas.js";import{RENDER_FORM_TOOL_NAME,RENDER_FORM_TOOL_SCHEMA,buildRenderFormDescription,createRenderFormHandler}from"./surface/render-form-tool.js";import{ResourceDescriptorRegistry}from"./surface/resource-descriptor-registry.js";import{CRON_RESOURCE}from"./surface/cron-resource.js";import{generateActionTools}from"./surface/action-tool-generator.js";import{buildThirdPartyChannelSet,isThirdPartyChannel}from"./channel-detection.js";import{registerArouterMediaProviders}from"./media/index.js";import{homedir}from"node:os";import{join}from"node:path";import{RuntimeProductionAuthority}from"./runtime/runtime-production-authority.js";import{FileRuntimeCommandJournal}from"./runtime/runtime-command-journal.js";import{FileRuntimePoisonDeliveryJournal}from"./runtime/runtime-poison-delivery-journal.js";import{OpenClawNativeMcpProjection}from"./acp-gateway/openclaw-native-mcp-projection.js";import{LocalEncryptedCredentialReferenceVault}from"./acp-gateway/mcp-credential-reference-port.js";import{AgentGatewayGrantJwksReadinessAdapter,RuntimeServiceTransportReadinessAdapter,createRuntimeServiceProductionCandidate}from"./runtime/runtime-service-production-candidate.js";import{MessageServiceCronDeliveryHttpAuthority}from"./runtime/message-service-cron-delivery-http-authority.js";import{AgentMappingRegistry}from"./runtime/agent-mapping-registry.js";import{OpenClawRuntimeProcessOwner}from"./runtime/openclaw-runtime-process-owner.js";import{AgentGatewayHttpProjectionTransport,AgentGatewayTemplateOriginReporter,AgentProjectionProduction,OpenClawAgentProjectionSource}from"./runtime/agent-projection-production.js";import{MessageServiceRunOutputChannel}from"./runtime/message-service-run-output-channel.js";import{AssistantMessageCommitRouter}from"./runtime/assistant-message-commit-router.js";import{ToolResultObservationRouter}from"./runtime/tool-result-observation-router.js";import{ModelCallObservationRouter,registerModelCallObservationHooks}from"./runtime/model-call-observability.js";import{registerArouterLatencyRouting}from"./runtime/arouter-latency-routing.js";import{createPeerRunOutputRouterFactory}from"./runtime/run-output-composition.js";const PLUGIN_VERSION=resolvePluginVersion();let agentKeys=null,sharedCanvasState=null,sharedAcpServer=null;const sharedSessionWriteGate=new SessionWriteGate,sharedPromptRunStore=new PromptRunStore;let lastOpenclawConfig;const sharedMcpNativeProjectionNudge=createMcpNativeProjectionNudge({getActiveImRunCount:()=>sharedPromptRunStore.size(),nudge:()=>{sharedAcpServer?.nudgeMcpNativeProjection()}}),sharedImHotPathDeferral=createImHotPathDeferral({getActiveImRunCount:()=>sharedPromptRunStore.size()});sharedPromptRunStore.setOnBecameIdle(()=>{sharedMcpNativeProjectionNudge.flushIfIdle({fingerprint:fingerprintMcpServers(lastOpenclawConfig)}),sharedImHotPathDeferral.notifyIdle()});const sharedAssistantMessageCommitRouter=new AssistantMessageCommitRouter,sharedToolResultObservationRouter=new ToolResultObservationRouter,sharedModelCallObservationRouter=new ModelCallObservationRouter;let sharedSessionMutationCoordinator,sharedRuntimeAuthority;const sharedAgentMappings=new AgentMappingRegistry;let sharedGatewayClient,sharedRuntimeOwner,sharedAgentProjection,sharedRuntimeAgentCapabilities,sharedMcpProjection,sharedMcpCredentialVault;const sharedMessageClientOwner=new BeeosClawMessageClientProcessOwner;let sharedActiveMessageConsumer,sharedInboundExecutionJournal;const sharedTerminalManager=new TerminalManager;let _initializing=!1,_pluginRegisteredAt=0;const ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex");function agentKeyPairToIdentity(e){return{publicKeyBase64:()=>Buffer.from(e.publicKey).toString("base64"),sign:async t=>{const{createPrivateKey:r,sign:o}=await import("node:crypto"),n=r({key:32===e.privateKey.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(e.privateKey)]):Buffer.from(e.privateKey),format:"der",type:"pkcs8"});return new Uint8Array(o(null,Buffer.from(t),n))}}}let unhandledRejectionObserverInstalled=!1;function installUnhandledRejectionObserver(e,t,r){unhandledRejectionObserverInstalled||(unhandledRejectionObserverInstalled=!0,process.on("unhandledRejection",r=>{const o=r instanceof Error?r.message:String(r),n=r instanceof Error?r.stack:void 0;e.logger.error?.(`[beeos-claw] unhandledRejection: ${o}${n?`\n${n}`:""}`),t({component:"plugin",domain:"fatal",name:"unhandled_rejection",severity:"error",summary:o,error:{code:"unhandled_rejection",message:o,...n?{stack:n}:{}}})}),process.on("uncaughtException",r=>{const o=r instanceof Error?r.message:String(r);e.logger.error?.(`[beeos-claw] uncaughtException: ${r instanceof Error?r.stack??r.message:String(r)}`),t({component:"plugin",domain:"fatal",name:"uncaught_exception",severity:"error",summary:o,error:{code:"uncaught_exception",message:o}})}))}function initializePlugin(e){let t,r;try{t=e.runtime?.config?.loadConfig?.()}catch{}try{r=resolveConfigWithMeta({pluginConfig:e.pluginConfig??{},openclawConfig:t})}catch(t){return e.logger.error?.(`[beeos-claw] Failed to resolve config: ${t instanceof Error?t.message:String(t)}`),null}const o=r.config,n=buildThirdPartyChannelSet(t);n.size>0&&e.logger.info?.(`[beeos-claw] Third-party channels detected: ${[...n].join(", ")}`);const a=o.log.enabled?createObsEventWriter({dir:o.log.dir,redaction:{gatewayToken:o.gateway.token},logger:e.logger}):()=>{};a({component:"plugin",domain:"lifecycle",name:"lifecycle.startup",severity:"info",payload:{pluginVersion:PLUGIN_VERSION,pid:process.pid,nodeVersion:process.version,cwd:process.cwd()}}),a({component:"plugin",domain:"config",name:"config.resolved",severity:"info",payload:{config:o,sources:r.sources,openclawConfigPath:r.openclawConfigPath}});for(const t of r.validation)a({component:"plugin",domain:"config",name:"config.validation",severity:t.severity,summary:t.message,error:{code:t.code,message:t.message,nextSteps:t.nextSteps}}),"error"===t.severity?e.logger.error?.(`[beeos-claw] ${t.message}`):e.logger.warn?.(`[beeos-claw] ${t.message}`);if(r.validation.some(e=>"error"===e.severity)&&e.logger.error?.("[beeos-claw] Plugin has configuration errors — some features may not work."),o.keyFile)try{agentKeys=loadAgentKeyPair(o.keyFile);const t=Buffer.from(agentKeys.publicKey).toString("base64");e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (publicKey: ${t})`)}catch(t){e.logger.warn?.(`[beeos-claw] Failed to load agent key pair: ${t}`),e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no key pair — signed requests disabled)`)}else e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no keyFile configured)`);return agentKeys&&o.agentGatewayUrl&&(setUploadDeps({gatewayUrl:o.agentGatewayUrl,keys:agentKeys,logger:e.logger}),registerLocalFileExtractor(browserScreenshotExtractor),e.logger.info?.("[beeos-claw] Upload deps + browser screenshot extractor registered")),lastOpenclawConfig=t,{cfg:o,writeObs:a,thirdPartyChannels:n,openclawConfig:t}}function registerUploadTools(e,t,r){const{cfg:o,writeObs:n}=t;cleanupUploadToolResultCache().catch(t=>{e.logger.warn?.(`[beeos_upload_file] cache cleanup failed on startup: ${String(t)}`)}),e.registerTool({name:BEEOS_UPLOAD_TOOL_NAME,description:BEEOS_UPLOAD_TOOL_DESCRIPTION,parameters:BEEOS_UPLOAD_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_upload_file] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeUpload(r,o,agentKeys,a);return e.logger.debug?.(`[beeos_upload_file] toolCallId=${t} ok=${i.ok}`),n({component:"upload-tool",domain:"tool",name:i.ok?"upload.success":"upload.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,fileCount:i.files?.length??0,error:i.error}}),(async r=>{try{await writeUploadToolResult(t,BEEOS_UPLOAD_TOOL_NAME,r),await cleanupUploadToolResultCache()}catch(r){e.logger.warn?.(`[beeos_upload_file] persist cache failed toolCallId=${t}: ${String(r)}`)}return r})(i)}}),e.registerTool({name:BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,description:BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,parameters:BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeDesktopScreenshot(r,o,agentKeys,a);return e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} ok=${i.ok}`),n({component:"desktop-screenshot-tool",domain:"tool",name:i.ok?"desktop_screenshot.success":"desktop_screenshot.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,path:i.path,fileCount:i.files?.length??0,error:i.error}}),(async r=>{try{await writeUploadToolResult(t,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,r),await cleanupUploadToolResultCache()}catch(r){e.logger.warn?.(`[beeos_desktop_screenshot] persist cache failed toolCallId=${t}: ${String(r)}`)}return r})(i)}}),e.registerHttpRoute({path:"/beeos/files/stage",auth:"gateway",async handler(e,r){await handleFileStage(e,r,t.cfg)}}),e.registerHttpRoute({path:"/beeos/status",auth:"plugin",handler(e,t){handleStatusRequest(e,t,{publicKey:agentKeys?.publicKey??null,pluginVersion:PLUGIN_VERSION})}}),e.registerTool({name:BEEOS_LIST_FILES_TOOL_NAME,description:BEEOS_LIST_FILES_TOOL_DESCRIPTION,parameters:BEEOS_LIST_FILES_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_list_files] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeListFiles(r,o,agentKeys,a);return n({component:"file-list-tool",domain:"tool",name:i.ok?"list_files.success":"list_files.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,fileCount:i.files?.length??0,error:i.error}}),i}}),e.registerTool({name:BEEOS_FETCH_FILE_TOOL_NAME,description:BEEOS_FETCH_FILE_TOOL_DESCRIPTION,parameters:BEEOS_FETCH_FILE_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_fetch_file] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeFetchFile(r,o,agentKeys,{signal:a,resolvePath:e.resolvePath});return n({component:"file-fetch-tool",domain:"tool",name:i.ok?"fetch_file.success":"fetch_file.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,path:i.path,size:i.size,error:i.error}}),i}})}function registerConnectorMcpProxy(e,t){e.registerHttpRoute({path:CONNECTOR_MCP_PROXY_PATH,auth:"plugin",async handler(r,o){await handleConnectorMcpRequest(r,o,{agentGatewayUrl:t.cfg.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger})}})}function registerServices(e,t,r){const{cfg:o,writeObs:n,thirdPartyChannels:a}=t;if("function"!=typeof e.on){if(o.messageBus.enabled)throw new Error("openclaw_before_message_write_hook_unavailable")}else registerModelCallObservationHooks((t,r)=>e.on(t,r),sharedModelCallObservationRouter),e.on("before_message_write",(t,r)=>{const o=t.sessionKey??r.sessionKey;if(!o)return void e.logger.debug?.("[before_message_write] ignored assistant write without sessionKey");sharedAssistantMessageCommitRouter.route(o,t.message)||e.logger.debug?.(`[before_message_write] no active IM commit target sessionKey=${o} active_targets=${sharedAssistantMessageCommitRouter.size()}`)}),e.on("after_tool_call",(t,r)=>{const o=r.sessionKey,n=t.toolCallId??r.toolCallId,a=t.toolName||r.toolName;if(!o||!n||!a)return void e.logger.debug?.(`[after_tool_call] ignored structured result without correlation sessionKey=${o??"missing"} toolCallId=${n??"missing"}`);sharedToolResultObservationRouter.route(o,{toolCallId:n,toolName:a,rawResult:t.result,isError:Boolean(t.error)})||e.logger.debug?.(`[after_tool_call] no active IM observation target sessionKey=${o} active_targets=${sharedToolResultObservationRouter.size()}`)});const i=agentKeys?(e,t)=>agentFetch(e,agentKeys,t):void 0,s=agentKeys?(e,t)=>agentBoundFetch(e,agentKeys,t):void 0,l=agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):void 0,c=join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos"),g=sharedInboundExecutionJournal??=new FileInboundExecutionJournal(join(c,"inbound-execution-journal.v1.jsonl"));if(sharedMcpProjection??=new OpenClawNativeMcpProjection,!sharedMcpCredentialVault&&agentKeys&&l&&(sharedMcpCredentialVault=new LocalEncryptedCredentialReferenceVault(join(c,"mcp-credential-vault.json"),l,agentKeys.privateKey)),!sharedRuntimeAuthority&&agentKeys&&o.agentGatewayUrl&&i&&s&&(sharedRuntimeAuthority=new RuntimeProductionAuthority({stateDirectory:c,agentGatewayUrl:o.agentGatewayUrl,agentFetch:i,boundAgentFetch:s,keys:agentKeys,logger:e.logger})),sharedGatewayClient??=new GatewayClient({url:o.gateway.url,token:o.gateway.token||"",clientId:o.gateway.clientId,openclawCanvasCompat:!!o.openclawCanvasCompat,retry:{baseMs:o.retry.baseMs,maxMs:o.retry.maxMs,maxAttempts:o.retry.maxAttempts}}),sharedRuntimeAgentCapabilities??={canvas:o.canvasEnabled,terminal:!1,webSsh:!1,fileSystem:!0,image:!0,audio:!1,mcp:!0,cron:!0,streaming:!0,pushNotifications:o.messageBus.enabled},!sharedAgentProjection&&sharedRuntimeAuthority&&i&&o.agentGatewayUrl){const t=sharedRuntimeAuthority;sharedAgentProjection=new AgentProjectionProduction({authority:t,stateFile:join(t.stateDirectory,"runtime-agent-projection.json"),journal:t.journal,identities:t.identities,source:new OpenClawAgentProjectionSource(()=>sharedGatewayClient),transport:new AgentGatewayHttpProjectionTransport({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:t.boundAgentFetch,getRuntimeLeaseCredential:e=>{const r=t.authorityLease;if(!r||r.instanceId!==e.instanceId||r.leaseId!==e.leaseId||r.runtimeEpoch!==e.runtimeEpoch||r.handlerIdentity!==e.handlerIdentity)throw new Error("agent projection fenced by current runtime lease");return r.runtimeLeaseCredential},onAccepted:t=>{0!==t.size&&sharedAgentMappings.replace(t).catch(t=>{e.logger.warn?.("[agent-projection] mapping publication failed: "+(t instanceof Error?t.message:String(t)))})}}),capabilities:sharedRuntimeAgentCapabilities,logger:e.logger})}sharedRuntimeOwner??=new OpenClawRuntimeProcessOwner(sharedGatewayClient,sharedAgentProjection,e.logger);const d=sharedRuntimeOwner;sharedSessionMutationCoordinator??=new SessionMutationCoordinator(sharedSessionWriteGate,new OpenClawSessionWriteQuiescence(e.logger,{agentId:o.gateway.agentId||"main"}),e.logger,{onRecoveryRequired:t=>{scheduleProcessRuntimeRestart(t,e.logger)}});const u=sharedSessionMutationCoordinator;if(!sharedAcpServer){const t=sharedRuntimeAuthority&&o.agentGatewayUrl?new AgentGatewayTemplateOriginReporter({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:sharedRuntimeAuthority.boundAgentFetch}):void 0;sharedAcpServer=new ACPServer(o,e.logger,u,n,i,l,a,sharedTerminalManager,sharedRuntimeAuthority,void 0,sharedMcpProjection,sharedMcpCredentialVault,s,{gateway:d.client,agentProjection:sharedAgentProjection,markAgentProjectionDirty:()=>sharedAgentProjection?.markDirty(),templateOriginReporter:t,agentMappings:sharedAgentMappings,agentCapabilities:sharedRuntimeAgentCapabilities,createRunOutputRouter:createPeerRunOutputRouterFactory(new MessageServiceRunOutputChannel({getClient:()=>sharedMessageClientOwner.current?.client??null,logger:e.logger,writeObs:n})),waitForChatIdle:(t,r)=>sharedImHotPathDeferral.waitIfBusy(t,e.logger,r).then(()=>{})})}const p=sharedAcpServer,m=()=>sharedAgentMappings.snapshot()?.values().next().value;r.current=p,sharedMcpNativeProjectionNudge.request({registrationMode:e.registrationMode,fingerprint:fingerprintMcpServers(t.openclawConfig),logger:e.logger});const y=sharedPromptRunStore,{enabled:h,principalId:f,a2aEnabled:w}=o.messageBus;let v;o.agentGatewayUrl&&agentKeys&&(v=createTokenProvider({agentGatewayUrl:o.agentGatewayUrl,identity:agentKeyPairToIdentity(agentKeys)}));const b=process.env.MESSAGE_RUNTIME_DELIVERY_KEY?.trim(),C=f||process.env.BEEOS_PRINCIPAL_ID||"",_=v?sharedMessageClientOwner.configure({tokenProvider:v,identityId:C,...o.messageBus.serviceUrl?{serviceUrl:o.messageBus.serviceUrl}:{},logger:e.logger,...sharedRuntimeAuthority&&b?{runtimeDelivery:{scopedDeliveryKey:b,authority:{currentLease:()=>{const e=sharedRuntimeAuthority?.authorityLease,t=sharedRuntimeAuthority?.currentConsumerBinding();return e&&t?{...e,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration}:null}}}}:{}}):void 0,R=sharedRuntimeAuthority,O=o.messageBus.serviceUrl;if(_&&R&&O&&b){const e=_.nodeTransport;p.setCronConversationDelivery({deliver:async t=>{const r=R.authorityLease;if(!r)throw new Error("current runtime authority lease unavailable for cron delivery");const o=new MessageServiceCronDeliveryHttpAuthority({messageServiceUrl:O,fetchAuthorized:async(t,o)=>await e.fetchRuntimeChannel(t,{runtimeLeaseCredential:r.runtimeLeaseCredential},o)});return await o.deliver({...t,runtimeLeaseCredential:r.runtimeLeaseCredential})}})}else p.setCronConversationDelivery(void 0);const E=sharedRuntimeAuthority?.journal??new FileRuntimeCommandJournal(join(c,"runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:t=>e.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=threshold error=${t instanceof Error?t.message:String(t)}`)}),S=d.createRegistrationToken();e.registerService({id:"beeos-acp",async start(){const t=Date.now(),r=_pluginRegisteredAt?t-_pluginRegisteredAt:-1;e.logger.info?.(`[beeos-claw] beeos-acp service start() begin, sincePluginRegister=${r}ms, bridge.url=${o.bridge.url||"(empty)"}`);const n=await createRuntimeServiceProductionCandidate({...o.agentGatewayUrl&&i?{authorizationAdapter:new AgentGatewayGrantJwksReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl})}:{},..._&&o.agentGatewayUrl&&s&&b?{transportAdapter:new RuntimeServiceTransportReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl,messageService:_.nodeTransport,messageClient:_.client,configuredServiceUrl:o.messageBus.serviceUrl,boundAgentFetch:s,scopedDeliveryKey:b})}:{},commandJournal:E,poisonJournal:new FileRuntimePoisonDeliveryJournal(join(c,"runtime-poison-deliveries.json")),onConsumerDeliveryError:t=>{const r=t instanceof Error?`${t.name}: ${t.message}${"status"in t?` status=${t.status}`:""}${"code"in t?` code=${t.code}`:""}\n${t.stack??""}`:String(t);e.logger.error?.(`[runtime-service] delivery worker failed before acknowledgement; delivery retained for retry: ${r}`)},onConsumerLeaseStarvation:({starvedForMs:t,recovered:r})=>{const o=Math.round(t/1e3);r?e.logger.info?.(`[runtime-service] runtime authority lease restored after ${o}s without delivery reads`):e.logger.warn?.(`[runtime-service] no runtime authority lease for ${o}s; delivery reads suspended, queued runtime operations (model switch, session control) will not be consumed`)},onConsumerRequestFailure:t=>e.logger.error?.(`[runtime-service] request failed operationId=${t.operationId} method=${t.method}: ${t.error instanceof Error?t.error.stack??t.error.message:String(t.error)}`)});if(sharedRuntimeAuthority?.setRuntimeServiceMethods("ready"===n.status?n.serviceMethods:[]),sharedRuntimeAuthority?.setRuntimeResourceBindingContractRevision("ready"===n.status?n.resourceBindingContractRevision:void 0),p.setRuntimeRpcConsumerFactory("ready"===n.status?n.factory:void 0),"unavailable"===n.status?e.logger.warn?.("[runtime-service] capability fenced: "+n.blockers.map(e=>e.id).join(",")):n.capabilityBlockers.length&&e.logger.warn?.("[runtime-service] method capability fenced: "+n.capabilityBlockers.map(e=>e.method).join(",")),!o.bridge.url&&o.agentGatewayUrl&&i){const t=Date.now(),r=await discoverBridgeFromGW(o.agentGatewayUrl,i);if(e.logger.info?.(`[beeos-claw] bridge discovery took ${Date.now()-t}ms result=${r?.url??"null"}`),r)o.bridge.url=r.url,writeDiscoveryCache({bridge:{url:r.url,discoveredAt:Date.now()}}),e.logger.info?.(`[beeos-claw] Bridge URL discovered from Agent Gateway: ${r.url}`);else{const t=readDiscoveryCache();t?.bridge?.url?(o.bridge.url=t.bridge.url,e.logger.info?.(`[beeos-claw] Bridge URL from cache (GW unreachable): ${t.bridge.url}`)):e.logger.debug?.("[beeos-claw] Bridge URL not available (no GW, no cache) — bridge disabled")}}await d.acquire(S);try{await p.start(o.bridge.url||void 0)}catch(e){throw await d.release(S),e}e.logger.info?.(`[beeos-claw] ACP Server started (total service start: ${Date.now()-t}ms)`)},async stop(){try{await d.release(S)}finally{await p.stop()}await E.checkpoint(),e.logger.info?.("[runtime-service] command journal checkpoint completed"),e.logger.info?.("[beeos-claw] ACP Server stopped")}});if(h&&void 0!==v&&v){const t=sharedMessageClientOwner.createRegistrationToken(),r=async(e,t)=>{const r=await v(e);if(!r.identityId||r.identityId!==e)throw new Error("Message SDK token identity did not match the current runtime authority");return{principalId:e,runtimeEpoch:t}},a=(e,t,r)=>({getLocalAgentId:()=>t,resolveLocalAgentId:e=>e===r?t:void 0,getDefaultPlatformAgent:()=>({instanceId:e.principalId}),ensureImInboundSession:(e,t,r,o)=>{p.ensureImInboundSession(e,t,r,o)},chatAbort:(e,t)=>p.chatAbort(e,t)}),i=sharedRuntimeAuthority?new RuntimeAuthorityMessageBusLifecycle({authority:sharedRuntimeAuthority,logger:e.logger,bus:{connect:async i=>{await sharedMessageClientOwner.acquire(t,async t=>{const s=await r(i.instanceId,i.runtimeEpoch),l=t.client,c=new Map,d=new MessageProjectionInboundConsumer({client:l,bindings:()=>{const t=sharedRuntimeAuthority?.authorityLease;if(!t||t.instanceId!==s.principalId||t.runtimeEpoch!==s.runtimeEpoch)return[];const r=sharedAgentMappings.snapshot();return r?[...r.entries()].flatMap(([r,i])=>{if(!i.id||i.instanceId!==s.principalId)return[];let g=c.get(i.id);return g&&g.localAgentId===r||(g={localAgentId:r,inbound:createImInboundHandler({runtime:e.runtime,pluginConfig:e.pluginConfig,client:l,localServer:a(s,r,i.id),promptRunStore:y,assistantMessageCommitRouter:sharedAssistantMessageCommitRouter,toolResultObservationRouter:sharedToolResultObservationRouter,modelCallObservationRouter:sharedModelCallObservationRouter,sessionMutationCoordinator:u,logger:e.logger,writeObs:n,principalIdOverride:s.principalId,promptTimeoutMs:o.promptTimeoutMs,activeRunAuthority:p.getActiveRunAuthority(),...o.agentGatewayUrl&&agentKeys?{completeA2ATask:(t,r,n)=>completeA2ATask({agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},t,r,n)}:{}})},c.set(i.id,g)),[{principalId:s.principalId,targetAgentId:i.id,runtimeEpoch:t.runtimeEpoch,inbound:g.inbound}]}):[]},executionJournal:g,logger:e.logger}),m=sharedAgentMappings.subscribe(()=>{sharedActiveMessageConsumer===d&&d.refresh()}),h=sharedRuntimeAuthority?.subscribeAuthority(()=>{sharedActiveMessageConsumer===d&&d.refresh()})??(()=>{}),f=new MessageClientTerminalStateRecovery({logger:e.logger,isCurrent:()=>{if(sharedActiveMessageConsumer!==d)return!1;const e=sharedRuntimeAuthority?.authorityLease;return null!=e&&e.instanceId===s.principalId&&e.runtimeEpoch===s.runtimeEpoch&&e.leaseId===i.leaseId&&e.handlerIdentity===i.handlerIdentity},reconnect:async()=>{if("failed"===l.getSnapshot().connection){await l.connect();try{await p.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient recovery: ${t instanceof Error?t.message:String(t)}`)}}}});let w=l.getSnapshot().connection;const v=l.subscribe(()=>{const t=l.getSnapshot();t.connection!==w&&(w=t.connection,"connected"===t.connection?(f.notifyConnected(),e.logger.info?.(`[beeos-claw] connection transport=message_service_realtime state=connected localAgentId=${p.getLocalAgentId()} principalId=${s.principalId}`)):"failed"===t.connection?(e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=failed principalId=${s.principalId} recoveryError=${t.recoveryError??"unknown"}`),f.notifyFailed()):"disconnected"!==t.connection&&"reconnecting"!==t.connection||e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=${t.connection} principalId=${s.principalId}; SDK owns reconnect/recovery`))});sharedActiveMessageConsumer=d,d.start();try{await l.connect()}catch(e){throw sharedActiveMessageConsumer===d&&(sharedActiveMessageConsumer=void 0),f.dispose(),await d.stop(),m(),h(),v(),await l.disconnect().catch(()=>{}),e}try{await p.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient connect: ${t instanceof Error?t.message:String(t)}`)}return async()=>{sharedActiveMessageConsumer===d&&(sharedActiveMessageConsumer=void 0),f.dispose(),await d.stop(),h(),v(),await l.disconnect(),e.logger.info?.("[beeos-claw] MessageClient disconnected")}})},disconnect:async()=>{await sharedMessageClientOwner.release(t)}}}):void 0;e.registerService({id:"beeos-message-bus",async start(){i?i.start():e.logger.warn?.("[beeos-claw] Message bus requires runtime authority; waiting is unavailable without Agent Gateway runtime identity")},async stop(){await(i?.stop())}})}else h?e.logger.warn?.("[beeos-claw] Message bus enabled but Agent Gateway token provider is unavailable — disabled"):e.logger.info?.(`[beeos-claw] Message bus disabled (enabled=${h})`);if(!1!==w&&o.agentGatewayUrl){const t=e=>p.getGatewaySessionKeyForToolCall(e),r=e=>p.getMentionsForToolCall(e),n=(e,t)=>{p.broadcastNotification(e,t)},a=new TaskHandleRegistry;registerA2ATools(e,{agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},a,t,r,n,()=>m()?.instanceId),e.logger.info?.("[beeos-claw] A2A tools registered (beeos_discover_agents, beeos_call_agent) via REST")}if(o.canvasEnabled){sharedCanvasState||(sharedCanvasState=new CanvasStateManager);const t=sharedCanvasState;if(o.agentGatewayUrl&&agentKeys){let r;t.setLogger(e.logger);{const e=o.agentGatewayUrl,t=agentKeys;r=async()=>{const r=await agentFetch(`${e}/api/v1/canvas/token`,t,{method:"POST"});if(!r.ok){const e=await r.text().catch(()=>"");throw new Error(`Canvas token returned ${r.status}: ${e}`)}return r.json()}}const n=n=>{const a=new CanvasRelayClient({canvasRelayUrl:o.canvasRelayUrl,agentKeys:agentKeys,agentId:n.id,displayName:n.displayName,avatarUrl:n.avatarUrl,tokenProvider:r,onConnected:()=>p.flushPendingCanvasLinks(),onReconnect:()=>p.onRelayReconnected(),logger:e.logger});t.setRelayClient(a),t.setAgentId(n.id),p.wireRelayActionHandler(),a.connect(),e.logger.info?.(`[beeos-claw] Canvas Relay multiplexed client connected (agentId=${n.id}, name=${n.name??"none"})`)};e.registerService({id:"beeos-canvas-relay",async start(){try{await sharedAgentMappings.waitForSynchronization();const t=m();if(t?.id)return void n(t);e.logger.info?.("[beeos-claw] Canvas Relay deferred: waiting for late agent sync..."),sharedAgentMappings.onceReady(()=>{const t=m();if(t?.id)try{n(t)}catch(t){e.logger.warn?.(`[beeos-claw] Deferred Canvas Relay start failed: ${t}`)}else e.logger.warn?.("[beeos-claw] Agent mapping ready but no platform agent; Canvas Relay stays disabled")})}catch(t){e.logger.warn?.(`[beeos-claw] Failed to start Canvas Relay client: ${t}`)}},stop(){t.getRelayClient()?.disconnect()}}),e.logger.info?.("[beeos-claw] Canvas Relay configured")}else e.logger.warn?.("[beeos-claw] Canvas Relay unavailable: agentGatewayUrl or agentKeys missing");const r={stateManager:t,logger:e.logger,writeObs:e=>n(e),isCanvasActive:e=>!!e&&p.isCanvasActiveForGatewayKey(e),getActiveSessionId:e=>p.getSessionIdForToolCall(e),getPlatformAgentId:e=>p.getPlatformAgentIdForToolCall(e),getGatewaySessionKey:e=>p.getGatewaySessionKeyForToolCall(e),waitForAgentSync:()=>sharedAgentMappings.waitForSynchronization()},i=new FormSchemaRegistry,s=buildCronSchemas(a,{capMs:o.cronMaxDurationCapMs});i.register(s.create),i.register(s.update);const l=new ResourceDescriptorRegistry;l.register(CRON_RESOURCE);const c=e=>async(t,r,o)=>(p.preMapToolCallSession(t),e(t,r));e.registerTool({name:RENDER_UI_TOOL_NAME,description:RENDER_UI_TOOL_DESCRIPTION,parameters:RENDER_UI_TOOL_SCHEMA,execute:c(createRenderUiHandler(r))}),e.registerTool({name:CLOSE_UI_TOOL_NAME,description:CLOSE_UI_TOOL_DESCRIPTION,parameters:CLOSE_UI_TOOL_SCHEMA,execute:c(createCloseUiHandler(r))}),p.setCanvasState(t,{schemaRegistry:i,descriptorRegistry:l});const g=p.getFormBuilder();g&&e.registerTool({name:RENDER_FORM_TOOL_NAME,description:buildRenderFormDescription(i),parameters:RENDER_FORM_TOOL_SCHEMA,execute:c(createRenderFormHandler({registry:i,builder:g,logger:e.logger}))});const d=p.getResourceRenderer();if(d){generateActionTools(l,e,async(e,t)=>p.gatewayRequest(e,t),d,e.logger)}const u=`\n\nTool selection guidance:\n- browser tool: Use when the user wants to OPEN, VISIT, NAVIGATE to a webpage, take SCREENSHOTS of web pages, interact with elements (click, type), or visually inspect pages.\n- ${BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME}: Use to capture the agent desktop (XFCE/VNC DISPLAY). Prefer this over shell/python X11 dumps; never invent raw Xlib/PIL capture.\n- beeos_search: Use ONLY for text-based information retrieval (fact-finding, research queries). Never for page interaction or screenshots.\n- beeos_fetch: Use ONLY to extract article text from a known URL. Never for visual content or screenshots.\n- ${BEEOS_FILE_RESOURCE_ROUTING_RULE}\nWhen the user says "open", "visit", "go to", or mentions seeing/viewing a webpage, ALWAYS prefer the browser tool. When they ask for a desktop / screen / VNC capture of the agent workspace, use beeos_desktop_screenshot.`,y=new Set([RENDER_UI_TOOL_NAME,CLOSE_UI_TOOL_NAME,RENDER_FORM_TOOL_NAME]),h=256,f=new Map;if("function"==typeof e.on){try{e.on("before_prompt_build",(e,t)=>{if(t?.sessionKey){if(f.size>=h&&!f.has(t.sessionKey)){const e=f.keys().next().value;void 0!==e&&f.delete(e)}f.set(t.sessionKey,t.channelId)}if(isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:t?.channelId}))return{appendSystemContext:'This conversation is in a third-party channel (Lark/Telegram/Discord/Slack); Canvas surfaces are not visible here. Do NOT use render_ui, close_ui, or render_form. Use built-in tools directly (e.g. the `cron` tool with `action:"add"` for scheduled tasks) and respond with plain text.'};if(!t?.sessionKey)return{appendSystemContext:u};return p.isCanvasActiveForGatewayKey(t.sessionKey)?{appendSystemContext:CANVAS_SYSTEM_PROMPT+u}:{appendSystemContext:"Canvas is closed by user. Do NOT use render_ui, close_ui, or render_form. Respond with plain text only."+u}}),e.logger.debug?.("[beeos-claw] Canvas system prompt hook registered: before_prompt_build")}catch{e.logger.debug?.("[beeos-claw] before_prompt_build hook not supported")}try{e.on("before_tool_call",(e,t)=>{if(t?.sessionKey&&p.setPendingToolCallSessionKey(t.sessionKey),!y.has(e.toolName))return;const r=t?.sessionKey?f.get(t.sessionKey):void 0;return isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:r})?{block:!0,blockReason:"Canvas tools are not available in this channel. Respond with plain text only."}:t?.sessionKey?p.isCanvasActiveForGatewayKey(t.sessionKey)?void 0:{block:!0,blockReason:"Canvas is closed by user. Respond with plain text instead."}:{block:!0,blockReason:"Canvas session unknown. Respond with plain text instead."}}),e.logger.debug?.("[beeos-claw] Canvas tool guard registered: before_tool_call")}catch{e.logger.debug?.("[beeos-claw] before_tool_call hook not supported")}}e.logger.info?.("[beeos-claw] Canvas tools registered (render_ui, close_ui, render_form + auto-action tools)")}else e.logger.info?.("[beeos-claw] Canvas disabled by config (canvasEnabled=false)");if(o.bridge.shell.enabled&&!0===o.legacyTerminalWsEnabled){const t=createTerminalWebSocketService({config:{port:o.terminalWsPort,token:agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):"",allowedOrigins:o.terminalWsAllowedOrigins},terminalMgr:sharedTerminalManager,logger:e.logger,writeObs:n});e.registerService({id:"beeos-terminal-ws",start(){t.start(),e.logger.info?.(`[beeos-claw] Terminal WS started on port ${o.terminalWsPort}`)},stop(){t.stop(),e.logger.info?.("[beeos-claw] Terminal WS stopped")}})}else o.bridge.shell.enabled?e.logger.info?.("[beeos-claw] Legacy static-token Terminal WS disabled (default)"):e.logger.info?.("[beeos-claw] Terminal/shell disabled by config (bridge.shell.enabled=false)");if(t.openclawConfig){const r=mirrorOpenClawConfig(t.openclawConfig);r.copied?e.logger.info?.(`[beeos-claw] mirrored openclaw config to ${r.destinationPath}`):e.logger.debug?.(`[beeos-claw] openclaw config mirror skipped: ${r.reason}`)}}const CAPABILITY_REGISTRATION_MODES=new Set(["full","discovery","tool-discovery"]),plugin={id:"beeos-claw",name:"beeos-claw",description:"BeeOS platform bridge plugin for OpenClaw. Provides file upload/download, ACP bridge, and observability.",version:PLUGIN_VERSION,register(e){if(!_initializing)if(!e.registrationMode||CAPABILITY_REGISTRATION_MODES.has(e.registrationMode)){_initializing=!0;try{if(!hasRuntimeReplyApi(e.runtime))throw e.logger.error?.("[beeos-claw] FATAL: OpenClaw runtime.channel.reply.dispatchReplyFromConfig not exposed. Upgrade OpenClaw or revert beeos-claw."),new Error("openclaw_runtime_reply_api_unavailable");const t=initializePlugin(e);if(!t)return;const r={current:null};installUnhandledRejectionObserver(e,t.writeObs,r),registerUploadTools(e,t,r),registerConnectorMcpProxy(e,t),registerArouterLatencyRouting(e),registerArouterMediaProviders(e),registerServices(e,t,r),_pluginRegisteredAt=Date.now(),e.logger.info?.("[beeos-claw] Plugin registered successfully")}finally{_initializing=!1}}else e.logger.debug?.(`[beeos-claw] Skipping registration (mode=${e.registrationMode})`)}};export default plugin;
@@ -0,0 +1 @@
1
+ export class ImHotPathAbortedError extends Error{reason;constructor(t){super(`hot-path wait aborted reason=${t}`),this.name="ImHotPathAbortedError",this.reason=t}}export const createImHotPathDeferral=t=>{const e=new Set;return{waitIfBusy:async(r,o,n)=>{if(t.getActiveImRunCount()<=0)return"idle";if(n?.aborted)throw new ImHotPathAbortedError(r);return o?.info?.(`[hot-path] deferred_wait reason=${r} active_im_runs=${t.getActiveImRunCount()}`),await new Promise((o,a)=>{const s=()=>{e.delete(i),n?.removeEventListener("abort",d)},i=()=>{s(),o()},d=()=>{s(),a(new ImHotPathAbortedError(r))};e.add(i),n?.addEventListener("abort",d,{once:!0}),t.getActiveImRunCount()<=0&&i()}),o?.info?.(`[hot-path] flushed reason=${r}`),"deferred_wait"},notifyIdle:()=>{if(0===e.size)return;const t=[...e];e.clear();for(const e of t)e()}}};
@@ -1 +1 @@
1
- import{createHash,randomUUID}from"node:crypto";import{mkdir,writeFile}from"node:fs/promises";import{homedir}from"node:os";import path,{join,relative,resolve}from"node:path";import{RUNTIME_HASH_DOMAINS,RUNTIME_METHOD_PARITY_MANIFEST,runtimeHashPreimage}from"@beeos-ai/beeos-types/runtime";import{reduceRuntimeRecoveryPolicy}from"@beeos-ai/beeos-types/runtime/internal";import{assertTrustedCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{InMemoryRuntimeCommandJournal,RuntimeCommandJournalConflictError,runtimeCommandIsReplayableQuery,runtimeCommandResultHash}from"./runtime-command-journal.js";import{RuntimeProjectionTransportError}from"./agent-projection-worker.js";const DEFAULT_AGENT_ID="main",AGENT_VALID_ID_RE=/^[a-z0-9][a-z0-9_-]{0,63}$/i,AGENT_INVALID_CHARS_RE=/[^a-z0-9_-]+/g;export function normalizeRuntimeAgentLocalKey(e){const t=(e??"").trim();return t?AGENT_VALID_ID_RE.test(t)?t.toLowerCase():t.toLowerCase().replace(AGENT_INVALID_CHARS_RE,"-").replace(/^-+/,"").replace(/-+$/,"").slice(0,64)||"main":"main"}export async function awaitRuntimeProjection(e){const t=e;return await(t.awaitProjection?.()??Promise.resolve(e.getSnapshot().projection))}export function hasEffectReceiptHandoff(e){const t=e;return t.effectCommitted instanceof Promise&&"function"==typeof t.acknowledgeEffectReceipt}export class RuntimeCommandError extends Error{code;param;constructor(e,t,r,i){super(t,i),this.code=e,this.param=r,this.name="RuntimeCommandError"}}const emptyProgress=async function*(){};function deferred(){let e;return{promise:new Promise(t=>{e=t}),resolve:e}}async function waitForEffectReceipt(e,t){if(!t)return await e;if(t.aborted)throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt was not durably published before the Service attempt ended");await new Promise((r,i)=>{const n=()=>i(new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt was not durably published before the Service attempt ended"));t.addEventListener("abort",n,{once:!0}),e.then(()=>{t.removeEventListener("abort",n),r()},e=>{t.removeEventListener("abort",n),i(e)})})}export class OpenClawRuntimeCommandDispatcher{deps;logger;getOpenclawHome;createAgentId;journal;capabilities;constructor(e){this.deps=e,this.logger=e.logger??{},this.getOpenclawHome=e.getOpenclawHome??(()=>process.env.OPENCLAW_HOME||join(homedir(),".openclaw")),this.createAgentId=e.createAgentId??(()=>`agent-${randomUUID().replace(/-/g,"").slice(0,12)}`),this.journal=e.journal??new InMemoryRuntimeCommandJournal,this.capabilities=e.capabilities}setCapabilities(e){this.capabilities=e}dispatch(e,t){const r=e;assertTrustedCoreExecutionAttempt(t,{operationId:r.context.operationId,runtimeEpoch:r.context.dispatchEpoch});let i,n,a,o="running";const s=deferred(),c=deferred();let m;const d="service"===t.origin&&("mcp.set"===r.name||"mcp.unset"===r.name),p=this.executeWithLease(r,t,(e,t)=>{o="committed",m={operationId:r.context.operationId,method:r.name,record:t},s.resolve({...m,result:e,committedAt:t.updatedAt})},async()=>{d?await waitForEffectReceipt(c.promise,t.abortSignal):"acp"!==t.origin||"mcp.set"!==r.name&&"mcp.unset"!==r.name||!m?.record.mcpDesiredStateCommit||(await this.journal.releaseMcpLiveEffect({operationId:r.context.operationId,source:"acp_local"}),await this.journal.promoteMcpDesiredStateActive({operationId:r.context.operationId,source:"acp_local"}))},d,e=>{i=e},e=>{n=e}).then(e=>(o="committed",e),e=>{throw o=e instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===e.code||"INSTANCE_EPOCH_MISMATCH"===e.code)?"outcome_unknown":"failed",s.resolve(void 0),e});return{operationId:r.context.operationId,getSnapshot:()=>({effectState:o,...i?{projection:i}:{}}),completion:p,progress:emptyProgress(),effectCommitted:s.promise,acknowledgeEffectReceipt:async e=>{if(!m||e.operationId!==m.operationId||e.method!==m.method)throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt does not match this operation");if(!/^[a-f0-9]{64}$/.test(e.effectRecordHash))throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt hash is invalid");await this.journal.releaseMcpLiveEffect({operationId:e.operationId,source:"service",effectRecordHash:e.effectRecordHash}),c.resolve()},acknowledgeTerminalReceipt:async e=>{if(!(d&&m&&e.operationId===m.operationId&&e.method===m.method&&/^[a-f0-9]{64}$/.test(e.effectRecordHash)&&e.terminalMessageId))throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP terminal receipt does not match this operation");await this.journal.promoteMcpDesiredStateActive({operationId:e.operationId,source:"service",effectRecordHash:e.effectRecordHash,terminalMessageId:e.terminalMessageId});const a=await this.requireReleasedRecord(e.operationId);if(void 0===a.result)throw new RuntimeCommandError("OUTCOME_UNKNOWN","active MCP desired state lost its committed result");await this.afterCommittedEffect(r,a.result,a,t,e=>{i=e},e=>{n=e})},cancel:async()=>"running"===o?{status:"cancel_requested"===await(this.capabilities?.cancel?.(r.context.operationId))?"cancel_requested":"not_cancellable"}:{status:"already_terminal"},awaitProjection:async()=>n?(a??=n(),await a):i}}async executeWithLease(e,t,r,i,n,a,o){const s=await this.deps.leases.claim(e.context.operationId,e.context.dispatchEpoch);if(s.runtimeEpoch!==e.context.dispatchEpoch)throw await this.deps.leases.release(s),new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime operation lease epoch mismatch");const c=this.deps.leases.startHeartbeat(s);try{let c;await this.recoverInterruptedCreates(e.context.operationId);try{c=await this.journal.prepare({operationId:e.context.operationId,command:e.name,paramsHash:e.context.paramsHash,runtimeEpoch:s.runtimeEpoch})}catch(e){if(e instanceof RuntimeCommandJournalConflictError)throw new RuntimeCommandError("OUTCOME_UNKNOWN",e.message,void 0,{cause:e});throw e}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&void 0!==c.result){if(r(c.result,c),await i(),c=await this.requireReleasedRecord(c.operationId),void 0===c.result)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed result disappeared from command journal");return n||await this.afterCommittedEffect(e,c.result,c,t,a,o),c.result}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&"hash_only"===c.resultReceipt?.policy&&runtimeCommandIsReplayableQuery(e.name)){const n=await this.execute(e,s,t);if(runtimeCommandResultHash(n)!==c.resultReceipt.resultHash)throw new RuntimeCommandError("OUTCOME_UNKNOWN","redacted committed query result changed");return r(n,c),await i(),n}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&"hash_only"===c.resultReceipt?.policy){if(r(void 0,c),await i(),c=await this.requireReleasedRecord(c.operationId),!n){if(isRuntimeAgentMutation(e)&&(c.agentProjectionIntent||c.agentProjectionPreparationFailure)){const r=agentProjectionRecoveryResult(c.agentProjectionIntent??c.agentProjectionPreparationFailure);return await this.afterCommittedEffect(e,r,c,t,a,o),r}await this.runAfterEffect(e,void 0,c,t,a)}throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed mutation result is redacted")}if("effect_committed"===c.phase||"projection_committed"===c.phase)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed result receipt is unavailable");if("outcome_unknown"===c.phase)throw new RuntimeCommandError("OUTCOME_UNKNOWN","previous execution outcome is unknown");if("started"===c.phase&&isRuntimeAgentMutation(e)){let n;try{n=await this.recoverStartedAgentMutation(e,s,t)}catch(e){throw await this.markJournalUnknown(c),e instanceof RuntimeCommandError?e:new RuntimeCommandError("OUTCOME_UNKNOWN",`agent desired-state recovery could not prove its effect: ${errorText(e)}`,void 0,{cause:e})}const m=this.deps.projection?await this.prepareAgentProjectionCommit(e,n,t):void 0,d=await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:"effect_committed",result:n,...m?{agentProjection:m}:{}});return r(n,d),await i(),await this.afterCommittedEffect(e,n,d,t,a,o),this.scheduleJournalCompaction(),n}if("started"===c.phase&&"outcome_unknown"===this.recoveryDecision(e.name,c.phase))throw await this.markJournalUnknown(c),new RuntimeCommandError("OUTCOME_UNKNOWN","imperative action crossed its execution boundary");if("failed"===c.phase)throw new RuntimeCommandError("EXECUTION_FAILED","previous execution failed");"prepared"===c.phase&&(c=await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:"started"}));let m=!1;try{const d=await this.execute(e,s,t);m=!0;const p=isRuntimeAgentMutation(e)&&this.deps.projection?await this.prepareAgentProjectionCommit(e,d,t):void 0;let u;try{const e=await this.requireReleasedRecord(c.operationId);u=await this.journal.transition({operationId:e.operationId,expectedRevision:e.revision,phase:"effect_committed",result:d,...p?{agentProjection:p}:{}})}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`effect completed before its journal commit: ${errorText(e)}`,void 0,{cause:e})}return r(d,u),await i(),u=await this.requireReleasedRecord(u.operationId),n||await this.afterCommittedEffect(e,d,u,t,a,o),this.scheduleJournalCompaction(),d}catch(t){if(m)throw t;const r=t instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===t.code||"INSTANCE_EPOCH_MISMATCH"===t.code);if(r&&"clear-saga"===recoveryMetadata(e.name).recoveryClass)throw t;try{await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:r?"outcome_unknown":"failed",errorCode:r?"OUTCOME_UNKNOWN":"EXECUTION_FAILED"})}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`command and journal outcome unknown: ${errorText(e)}`,void 0,{cause:t})}throw t}}finally{try{await c.stop(),await this.deps.leases.release(s)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`runtime operation lease finalization unknown: ${errorText(e)}`,void 0,{cause:e})}}}scheduleJournalCompaction(){this.journal.compact().catch(e=>this.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=effect_committed error=${errorText(e)}`))}async execute(e,t,r){switch(e.name){case"agent.create":return await this.createAgent(e,t,r);case"agent.update":return await this.updateAgent(e);case"agent.delete":return await this.deleteAgent(e,t);case"agent.applyTemplate":return await this.applyTemplate(e,t,r)}if(!this.capabilities)throw new RuntimeCommandError("INVALID_PARAMS",`Unsupported runtime command: ${e.name}`);return await this.capabilities.execute(e,t,r)}async recoverStartedAgentMutation(e,t,r){if("agent.create"===e.name||"agent.applyTemplate"===e.name){let i=await this.bindCreateOperation(e,t,!1);if(i||(i=await this.findCreateRecoveryIdentity(e,!1),"allocated"===i.state&&void 0===i.createOperation?.paramsHash&&await this.bindCreateOperation(e,t,!1),i=await this.findCreateRecoveryIdentity(e)),"active"===i.state)return await this.recoverActiveCreateEffect(i);if(await this.runtimeAgentExists(i.observedRuntimeLocalKey??i.runtimeLocalKey)){const n={runtimeLocalKey:i.observedRuntimeLocalKey??i.runtimeLocalKey,workspacePath:join(this.getOpenclawHome(),`workspace-${i.runtimeLocalKey}`)};return"agent.create"===e.name?await this.completeCreatedAgent(e,t,r,n):await this.completeTemplateAgent(e,t,r,n)}return await this.execute(e,t,r)}const i=e.context.agent,n=await this.runtimeAgentExists(i.runtimeLocalKey);if("agent.delete"===e.name&&!n)return await this.deps.identities.commitDeleteTombstone(i.runtimeAgentUid,i.runtimeLocalKey,t),{runtimeAgentUid:i.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey};if("agent.update"===e.name&&!n)throw new RuntimeCommandError("OUTCOME_UNKNOWN","update recovery target is absent");return await this.execute(e,t,r)}async runtimeAgentExists(e){const t=await this.requireGateway().request("agents.list",{});if(!Array.isArray(t?.agents))throw new RuntimeCommandError("OUTCOME_UNKNOWN","runtime agent directory query is incomplete");return t.agents.some(t=>null!==t&&"object"==typeof t&&(t.id===e||t.name===e))}async findCreateRecoveryIdentity(e,t=!0){const r={operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash,runtimeAgentUid:e.params.runtimeAgentUid},i=void 0!==this.deps.identities.findCreateOperation,n=i?await this.deps.identities.findCreateOperation(r):(await this.deps.identities.pendingAllocations()).filter(e=>e.operationId===r.operationId&&e.command===r.command&&e.runtimeEpoch===r.runtimeEpoch&&e.runtimeAgentUid===r.runtimeAgentUid);if(1!==n.length)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity binding is missing or ambiguous");const a=n[0];if(i&&(!a.createOperation||t&&a.createOperation.paramsHash!==r.paramsHash||a.createOperation.operationId!==r.operationId||a.createOperation.command!==r.command||a.createOperation.runtimeEpoch!==r.runtimeEpoch))throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity hash binding conflicts");return a}async bindCreateOperation(e,t,r=!0){if(!this.deps.identities.findCreateOperation||!this.deps.identities.bindCreateOperation)return;const i={operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash,runtimeAgentUid:e.params.runtimeAgentUid};try{let n=await this.deps.identities.findCreateOperation(i);if(0===n.length){if(!r)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity binding is missing");if((await this.deps.identities.allocateCreate(t,e.name,e.params.runtimeAgentUid)).runtimeAgentUid!==e.params.runtimeAgentUid)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create allocation UID does not match the canonical command");n=await this.deps.identities.findCreateOperation(i)}if(1!==n.length)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create identity binding is missing or ambiguous");const a=n[0],o=await this.deps.identities.bindCreateOperation(a.runtimeAgentUid,a.runtimeLocalKey,{operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash});if(!o.createOperation||o.runtimeAgentUid!==e.params.runtimeAgentUid||o.createOperation.operationId!==i.operationId||o.createOperation.command!==i.command||o.createOperation.runtimeEpoch!==i.runtimeEpoch||o.createOperation.paramsHash!==i.paramsHash)throw new Error("create operation identity binding changed during compare-and-set");if("tombstoned"===o.state)throw new Error("create operation identity is tombstoned");return o}catch(e){if(e instanceof RuntimeCommandError)throw e;throw new RuntimeCommandError("OUTCOME_UNKNOWN",`create operation identity binding is corrupt: ${errorText(e)}`,void 0,{cause:e})}}async recoverActiveCreateEffect(e){const t=e.observedRuntimeLocalKey??e.runtimeLocalKey;if(!await this.runtimeAgentExists(t))throw new RuntimeCommandError("OUTCOME_UNKNOWN","active create identity is absent from the runtime directory");return{runtimeAgentUid:e.runtimeAgentUid,runtimeLocalKey:t,workspacePath:join(this.getOpenclawHome(),`workspace-${e.runtimeLocalKey}`)}}async markJournalUnknown(e){try{await this.journal.transition({operationId:e.operationId,expectedRevision:e.revision,phase:"outcome_unknown",errorCode:"OUTCOME_UNKNOWN"})}catch{}}async requireReleasedRecord(e){const t=await this.journal.get(e);if(!t)throw new RuntimeCommandError("OUTCOME_UNKNOWN","command journal record disappeared");return t}async afterCommittedEffect(e,t,r,i,n,a){if(!isRuntimeAgentMutation(e))return void await this.runAfterEffect(e,t,r,i,n);const o=await this.prepareAgentProjection(e,t,r,i,n);o&&(a(o),"acp"===i.origin&&o().catch(e=>this.logger.warn?.(`[runtime-core] deferred ACP agent projection failed: ${errorText(e)}`)))}async prepareAgentProjectionCommit(e,t,r){const i=agentProjectionRecoverySummary(e,t,r);try{const r=await this.deps.projection.prepareCommittedMutation({command:e,result:t,...i.expectedPlatformAgentId?{expectedPlatformAgentId:i.expectedPlatformAgentId}:{}});return Object.freeze({...r,...i})}catch(e){const t=!(e instanceof RuntimeProjectionTransportError)||e.retryable;return this.logger.warn?.(`[runtime-core] agent projection preparation ${t?"deferred":"blocked"}: ${errorText(e)}`),Object.freeze({...i,state:t?"pending":"blocked",errorKind:t?"snapshot_retryable":"snapshot_nonretryable"})}}async prepareAgentProjection(e,t,r,i,n){if("projection_committed"===r.phase)return n(r.projectionReceipt?.snapshot??Object.freeze({state:"synced",revision:`${Math.max(0,r.revision-1)}`})),r.agentProjectionIntent&&await this.compactAgentProjectionReference(r.agentProjectionIntent),void await this.runPostCommit(e,t);if("effect_committed"!==r.phase||!this.deps.projection)return;let a=r,o=a.agentProjectionIntent;const s=a.agentProjectionPreparationFailure;if(o&&"blocked"===a.agentProjectionTerminal?.state)return n(Object.freeze({state:"blocked",revision:a.agentProjectionTerminal.revision})),await this.compactAgentProjectionReference(o),void await this.runPostCommit(e,t);let c;return o&&n(Object.freeze({state:"pending",revision:o.projectionRevision})),async()=>(c??=(async()=>{if(!o){if(!s||"blocked"===s.state)return Object.freeze({state:"blocked",revision:`${Math.max(0,a.revision)}`});const t=await this.prepareAgentProjectionCommit(e,agentProjectionRecoveryResult(s),i);if(!isProjectionIntent(t))return"blocked"===t.state?Object.freeze({state:"blocked",revision:`${Math.max(0,a.revision)}`}):Object.freeze({state:"pending",revision:`${Math.max(0,a.revision)}`});o=t;try{a=await this.journal.recordAgentProjectionIntent({operationId:a.operationId,expectedRevision:a.revision,intent:o})}catch(e){return void this.logger.warn?.(`[runtime-core] agent projection reservation recovery deferred: ${errorText(e)}`)}n(Object.freeze({state:"pending",revision:o.projectionRevision}))}const r=Object.freeze({state:"pending",revision:o.projectionRevision});let c;try{c=await this.deps.projection.applyCommittedMutation(o)}catch(e){this.logger.warn?.(`[runtime-core] agent projection receipt deferred: ${errorText(e)}`),c=r}if(c.revision!==o.projectionRevision&&(c=Object.freeze({state:"blocked",revision:o.projectionRevision})),"synced"===c.state)try{a=await this.journal.transition({operationId:a.operationId,expectedRevision:a.revision,phase:"projection_committed",projectionSnapshot:c}),await this.compactAgentProjectionReference(o)}catch(e){this.logger.warn?.(`[runtime-core] projection phase commit deferred: ${errorText(e)}`),c=Object.freeze({state:"pending",revision:o.projectionRevision})}if("blocked"===c.state)try{a=await this.journal.recordAgentProjectionBlocked({operationId:a.operationId,expectedRevision:a.revision,revision:o.projectionRevision}),await this.compactAgentProjectionReference(o)}catch(e){this.logger.warn?.(`[runtime-core] blocked projection terminal commit deferred: ${errorText(e)}`),c=r}return"synced"===c.state&&c.platformAgentId&&"projection_committed"===a.phase&&this.reportTemplateOriginBestEffort(e,c.platformAgentId),n(c),await this.runPostCommit(e,t),c})(),await c)}async compactAgentProjectionReference(e){if(this.deps.projection)try{await this.deps.projection.compactCommittedMutation(e)}catch(e){this.logger.warn?.(`[runtime-core] projection reference compaction deferred: ${errorText(e)}`)}}reportTemplateOriginBestEffort(e,t){if("agent.applyTemplate"!==e.name||!this.deps.templateOriginReporter)return;const r=e.params.templateId;r&&this.deps.templateOriginReporter.report({platformAgentId:t,templateId:r}).catch(e=>{this.logger.warn?.(`[runtime-core] active template origin report failed, client fallback remains: ${errorText(e)}`)})}async runAfterEffect(e,t,r,i,n){if("projection_committed"===r.phase)return n(r.projectionReceipt?.snapshot??Object.freeze({state:"synced",revision:`${Math.max(0,r.revision-1)}`})),void await this.runPostCommit(e,t);const a="effect_committed"===r.phase&&await this.runProjection(e,t,i),o=!0===a?Object.freeze({state:"synced",revision:`${r.revision}`}):a&&"object"==typeof a?Object.freeze({...a,revision:`${r.revision}`}):"session.setModel"===e.name?Object.freeze({state:"pending",revision:`${r.revision}`}):void 0;if("synced"===o?.state)try{const e=o;await this.journal.transition({operationId:r.operationId,expectedRevision:r.revision,phase:"projection_committed",projectionSnapshot:e}),n(o)}catch(e){return this.logger.warn?.(`[runtime-core] projection phase commit deferred: ${errorText(e)}`),void n(Object.freeze({revision:o.revision,state:"pending"}))}else o&&n(o);await this.runPostCommit(e,t)}async runPostCommit(e,t){if(this.capabilities?.afterCommit)try{await this.capabilities.afterCommit(e,t)}catch(e){this.logger.warn?.(`[runtime-core] post-commit action failed: ${errorText(e)}`)}}async runProjection(e,t,r){if(!recoveryMetadata(e.name).projectionRequired)return!1;try{return!isRuntimeAgentMutation(e)&&(await(this.capabilities?.projectCommitted?.(e,t,r))??!1)}catch(e){return this.logger.warn?.(`[runtime-core] agent projection deferred: ${errorText(e)}`),!1}}recoveryDecision(e,t){const r=recoveryMetadata(e);return reduceRuntimeRecoveryPolicy({...r,messageServicePhase:"queued",executionBoundaryCrossed:"prepared"!==t,lineageContinuity:"continuous",journalState:"available",journalPhase:t})}requireGateway(){const e=this.deps.getGateway();if(!e?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");return e}async createAgentShell(e,t,r,i,n,a){const o=this.requireGateway(),s=(await this.deps.identities.pendingAllocations()).find(t=>t.runtimeAgentUid===e),c=a??s?.runtimeLocalKey??this.createAgentId(),m=join(this.getOpenclawHome(),`workspace-${c}`);let d;try{d=await o.request("agents.create",{name:c,workspace:m,...n?{model:n}:{},...i?.emoji?{emoji:i.emoji}:{},...i?.avatar?{avatar:i.avatar}:{}})}catch(r){if(isDeterministicAgentsCreateParamsRejection(r)&&(void 0!==a||void 0!==s))try{await this.deps.identities.tombstone(e,c,t)}catch(e){this.logger.warn?.(`[runtime-core] rejected-create allocation cleanup deferred: ${errorText(e)}`)}throw r}const p=d?.agentId??c,u=d?.workspace??m;if(await this.deps.identities.observeEffectKey(e,c,p,t),p!==c)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Create agent outcome unknown: gateway changed reserved local key to ${p}`);try{await o.request("agents.update",{agentId:p,name:r})}catch(e){this.logger.warn?.(`[runtime-core] display-name update failed agent=${p}: ${errorText(e)}`)}return{runtimeLocalKey:p,workspacePath:u}}async createAgent(e,t,r){const i=e.params.name.trim();if(!i)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");try{const n=await this.bindCreateOperation(e,t);if("active"===n?.state)return await this.recoverActiveCreateEffect(n);const a=await this.createAgentShell(e.params.runtimeAgentUid,t,i,e.params.identity,e.params.defaultModelId,n?.runtimeLocalKey);return await this.completeCreatedAgent(e,t,r,a)}catch(e){throw this.executionError("Create agent",e)}}async updateAgent(e){const{runtimeAgentUid:t,runtimeLocalKey:r}=e.context.agent;if(!r.trim())throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");const i=e.params,n=i.name?.trim(),a=i.defaultModelId?.trim(),o=i.identity?.emoji?.trim(),s=i.identity?.avatar?.trim();if(!(n||a||o||s||void 0!==i.description||void 0!==i.version||void 0!==i.capabilities||void 0!==i.skills||void 0!==i.avatarUrl||void 0!==i.homepage||void 0!==i.status||void 0!==i.identity?.vibe))throw new RuntimeCommandError("INVALID_PARAMS","No updatable fields provided (name/model/identity)");try{return await this.deps.identities.requireActive(t,r),await this.requireGateway().request("agents.update",{agentId:r,...n?{name:n}:{},...a?{model:a}:{},...o?{emoji:o}:{},...s?{avatar:s}:{},...i.identity?.vibe?{vibe:i.identity.vibe}:{},...void 0!==i.description?{description:i.description}:{},...void 0!==i.version?{version:i.version}:{},...void 0!==i.capabilities?{capabilities:i.capabilities}:{},...void 0!==i.skills?{skills:i.skills}:{},...void 0!==i.avatarUrl?{avatarUrl:i.avatarUrl}:{},...void 0!==i.homepage?{homepage:i.homepage}:{},...void 0!==i.status?{status:i.status}:{}}),{runtimeAgentUid:t,runtimeLocalKey:r}}catch(e){throw this.executionError("Update agent",e)}}async deleteAgent(e,t){const{runtimeAgentUid:r,runtimeLocalKey:i}=e.context.agent;if(!i.trim())throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");if("main"===normalizeRuntimeAgentLocalKey(i))throw new RuntimeCommandError("INVALID_PARAMS",'"main" cannot be deleted; delete the instance instead');try{if("tombstoned"===await this.deps.identities.requireDeleteTarget(r,i))return{runtimeAgentUid:r,runtimeLocalKey:i};if(!await this.runtimeAgentExists(i))return await this.deps.identities.commitDeleteTombstone(r,i,t),{runtimeAgentUid:r,runtimeLocalKey:i};const n=requireCommittedOpenClawDeleteAck(await this.requireGateway().request("agents.delete",{agentId:i,deleteFiles:e.params.deleteFiles??!0}),i);try{await this.deps.identities.commitDeleteTombstone(r,i,t)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Delete agent outcome unknown: identity tombstone failed: ${errorText(e)}`,void 0,{cause:e})}return{runtimeAgentUid:r,runtimeLocalKey:i,...void 0===n.removedBindings?{}:{removedBindings:n.removedBindings}}}catch(e){throw this.executionError("Delete agent",e)}}async applyTemplate(e,t,r){const i=e.params.name.trim();if(!i)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");this.requireGateway();try{const n=await this.bindCreateOperation(e,t);if("active"===n?.state)return await this.recoverActiveCreateEffect(n);const a=await r.resources.readVerifiedArtifact(e.params.artifact),o=await this.createAgentShell(e.params.runtimeAgentUid,t,i,e.params.identity,e.params.defaultModelId,n?.runtimeLocalKey);return await this.completeTemplateAgent(e,t,r,o,a)}catch(e){throw this.executionError("Apply template",e)}}async completeCreatedAgent(e,t,r,i){for(const t of e.params.bootstrapFiles??[]){if("IDENTITY.md"===t.name){this.logger.warn?.("[runtime-core] agent.create ignoring IDENTITY.md; identity is structured");continue}const e=await r.resources.readContent(t.contentRef);try{await this.requireGateway().request("agents.files.set",{agentId:i.runtimeLocalKey,name:t.name,content:e})}catch(e){this.logger.warn?.(`[runtime-core] bootstrap write failed file=${t.name}: ${errorText(e)}`)}}try{await this.deps.identities.activate(e.params.runtimeAgentUid,i.runtimeLocalKey,t)}catch(r){throw await this.compensateCreatedAgent(e.params.runtimeAgentUid,i.runtimeLocalKey,t,r,"Create agent"),new RuntimeCommandError("EXECUTION_FAILED",`Create agent failed after compensation: ${errorText(r)}`)}return{runtimeAgentUid:e.params.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,workspacePath:i.workspacePath}}async completeTemplateAgent(e,t,r,i,n){const a=n??await r.resources.readVerifiedArtifact(e.params.artifact);try{for(const[e,t]of a.workspaceFiles){const r=resolve(i.workspacePath,e),n=relative(i.workspacePath,r);if(""===n||n.startsWith("..")||path.isAbsolute(n))throw new Error(`refusing to write outside workspace: ${e}`);await(this.deps.mkdirWorkspace??mkdir)(path.dirname(r),{recursive:!0}),await(this.deps.writeWorkspaceFile??writeFile)(r,t)}await this.deps.identities.activate(e.params.runtimeAgentUid,i.runtimeLocalKey,t)}catch(r){throw await this.compensateCreatedAgent(e.params.runtimeAgentUid,i.runtimeLocalKey,t,r,"Apply template"),new RuntimeCommandError("EXECUTION_FAILED",`Apply template failed after compensation: ${errorText(r)}`,void 0,{cause:r})}return{runtimeAgentUid:e.params.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,workspacePath:i.workspacePath}}executionError(e,t){return t instanceof RuntimeCommandError?t:(this.logger.error?.(`[runtime-core] ${e.toLowerCase()} failed: ${errorText(t)}`),new RuntimeCommandError("EXECUTION_FAILED",`${e} failed: ${errorText(t)}`,void 0,{cause:t}))}async compensateCreatedAgent(e,t,r,i,n){try{const i=this.requireGateway();requireCommittedOpenClawDeleteAck(await i.request("agents.delete",{agentId:t,deleteFiles:!0}),t),await this.deps.identities.tombstone(e,t,r)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`${n} outcome unknown after ${errorText(i)}; compensation unproven: ${errorText(e)}`,void 0,{cause:e})}}async recoverInterruptedCreates(e){const t=await this.deps.identities.pendingAllocations();for(const r of t){if(r.operationId===e)continue;const t=await this.deps.leases.claimExpiredForRecovery(r.operationId);if(!t)continue;const i=this.deps.leases.startHeartbeat(t);try{await this.deps.identities.adoptRecoveryLease(r.runtimeAgentUid,r.runtimeLocalKey,t);const e=r.observedRuntimeLocalKey;if(!e){const e=r.createOperation?.operationId??r.operationId,i=await this.journal.get(e);if("failed"===i?.phase&&i.command===r.command&&!await this.runtimeAgentExists(r.runtimeLocalKey)){await this.deps.identities.tombstone(r.runtimeAgentUid,r.runtimeLocalKey,t);continue}throw new Error("no persisted gateway effect key; absence cannot be proven")}requireCommittedOpenClawDeleteAck(await this.requireGateway().request("agents.delete",{agentId:e,deleteFiles:!0}),e),await this.deps.identities.tombstone(r.runtimeAgentUid,r.runtimeLocalKey,t)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Interrupted ${r.command} recovery outcome unknown for operation ${r.operationId}: ${errorText(e)}`,void 0,{cause:e})}finally{let e;try{await i.stop()}catch(t){e=t}try{await this.deps.leases.release(t)}catch(t){e??=t}if(e)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Interrupted recovery lease finalization unknown: ${errorText(e)}`,void 0,{cause:e})}}}}function isRuntimeAgentMutation(e){return"agent.create"===e.name||"agent.update"===e.name||"agent.delete"===e.name||"agent.applyTemplate"===e.name}function servicePlatformAgentId(e){const t=e.verifiedMessageGrant?.claims;return"agent"===t?.scope?t.platformAgentId:void 0}function agentCoreResultHash(e,t){return createHash("sha256").update(runtimeHashPreimage(RUNTIME_HASH_DOMAINS.coreResult,{method:e.name.replace(".","/"),result:t})).digest("hex")}function agentProjectionRecoverySummary(e,t,r){const i="agent.update"===e.name||"agent.delete"===e.name?e.context.agent:createProjectionTarget(e,t),n=t;return Object.freeze({schemaVersion:1,operationId:e.context.operationId,method:e.name,runtimeEpoch:e.context.dispatchEpoch,runtimeAgentUid:i.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,coreResultHash:agentCoreResultHash(e,t),...servicePlatformAgentId(r)?{expectedPlatformAgentId:servicePlatformAgentId(r)}:{},..."agent.delete"===e.name&&Number.isSafeInteger(n?.removedBindings)?{removedBindings:n.removedBindings}:{}})}function createProjectionTarget(e,t){const r=t;if(!r||r.runtimeAgentUid!==e.params.runtimeAgentUid||"string"!=typeof r.runtimeLocalKey||!r.runtimeLocalKey)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed agent effect lacks a recoverable local identity");return{runtimeAgentUid:r.runtimeAgentUid,runtimeLocalKey:r.runtimeLocalKey}}function agentProjectionRecoveryResult(e){return Object.freeze({runtimeAgentUid:e.runtimeAgentUid,runtimeLocalKey:e.runtimeLocalKey,...void 0===e.removedBindings?{}:{removedBindings:e.removedBindings}})}function isProjectionIntent(e){return"projectionRevision"in e}function recoveryMetadata(e){const t=RUNTIME_METHOD_PARITY_MANIFEST.filter(t=>t.serviceAllowed&&t.canonicalCommand===e),r=t[0]?.recoveryClass;return r?{recoveryClass:r,projectionRequired:"mcp.set"===e||"mcp.unset"===e||t.some(e=>!0===e.projectionRequired)}:{recoveryClass:"imperative-ambiguous",projectionRequired:!1}}export function isDeterministicAgentsCreateParamsRejection(e){return e instanceof Error&&/^invalid agents\.create params\b/.test(e.message)}export function requireCommittedOpenClawDeleteAck(e,t){if(!e||"object"!=typeof e)throw new Error("gateway delete lacks the committed target acknowledgement");const r=e,i=Object.keys(r).every(e=>"ok"===e||"agentId"===e||"removedBindings"===e),n=void 0===r.removedBindings||Number.isSafeInteger(r.removedBindings)&&r.removedBindings>=0;if(!i||!0!==r.ok||r.agentId!==t||!n)throw new Error("gateway delete lacks the committed target acknowledgement");return{ok:!0,agentId:t,...void 0===r.removedBindings?{}:{removedBindings:r.removedBindings}}}function errorText(e){return e instanceof Error?e.message:String(e)}
1
+ import{createHash,randomUUID}from"node:crypto";import{mkdir,writeFile}from"node:fs/promises";import{homedir}from"node:os";import path,{join,relative,resolve}from"node:path";import{RUNTIME_HASH_DOMAINS,RUNTIME_METHOD_PARITY_MANIFEST,runtimeHashPreimage}from"@beeos-ai/beeos-types/runtime";import{reduceRuntimeRecoveryPolicy}from"@beeos-ai/beeos-types/runtime/internal";import{assertTrustedCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{InMemoryRuntimeCommandJournal,RuntimeCommandJournalConflictError,runtimeCommandIsReplayableQuery,runtimeCommandResultHash}from"./runtime-command-journal.js";import{RuntimeProjectionTransportError}from"./agent-projection-worker.js";import{ImHotPathAbortedError}from"./im-hot-path-deferral.js";const DEFAULT_AGENT_ID="main",AGENT_VALID_ID_RE=/^[a-z0-9][a-z0-9_-]{0,63}$/i,AGENT_INVALID_CHARS_RE=/[^a-z0-9_-]+/g;export function normalizeRuntimeAgentLocalKey(e){const t=(e??"").trim();return t?AGENT_VALID_ID_RE.test(t)?t.toLowerCase():t.toLowerCase().replace(AGENT_INVALID_CHARS_RE,"-").replace(/^-+/,"").replace(/-+$/,"").slice(0,64)||"main":"main"}export async function awaitRuntimeProjection(e){const t=e;return await(t.awaitProjection?.()??Promise.resolve(e.getSnapshot().projection))}export function hasEffectReceiptHandoff(e){const t=e;return t.effectCommitted instanceof Promise&&"function"==typeof t.acknowledgeEffectReceipt}export class RuntimeCommandError extends Error{code;param;constructor(e,t,r,i){super(t,i),this.code=e,this.param=r,this.name="RuntimeCommandError"}}const emptyProgress=async function*(){};function deferred(){let e;return{promise:new Promise(t=>{e=t}),resolve:e}}async function waitForEffectReceipt(e,t){if(!t)return await e;if(t.aborted)throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt was not durably published before the Service attempt ended");await new Promise((r,i)=>{const n=()=>i(new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt was not durably published before the Service attempt ended"));t.addEventListener("abort",n,{once:!0}),e.then(()=>{t.removeEventListener("abort",n),r()},e=>{t.removeEventListener("abort",n),i(e)})})}export class OpenClawRuntimeCommandDispatcher{deps;logger;getOpenclawHome;createAgentId;journal;capabilities;constructor(e){this.deps=e,this.logger=e.logger??{},this.getOpenclawHome=e.getOpenclawHome??(()=>process.env.OPENCLAW_HOME||join(homedir(),".openclaw")),this.createAgentId=e.createAgentId??(()=>`agent-${randomUUID().replace(/-/g,"").slice(0,12)}`),this.journal=e.journal??new InMemoryRuntimeCommandJournal,this.capabilities=e.capabilities}setCapabilities(e){this.capabilities=e}dispatch(e,t){const r=e;assertTrustedCoreExecutionAttempt(t,{operationId:r.context.operationId,runtimeEpoch:r.context.dispatchEpoch});let i,n,a,o="running";const s=deferred(),c=deferred();let m;const d="service"===t.origin&&("mcp.set"===r.name||"mcp.unset"===r.name),p=this.executeWithLease(r,t,(e,t)=>{o="committed",m={operationId:r.context.operationId,method:r.name,record:t},s.resolve({...m,result:e,committedAt:t.updatedAt})},async()=>{d?await waitForEffectReceipt(c.promise,t.abortSignal):"acp"!==t.origin||"mcp.set"!==r.name&&"mcp.unset"!==r.name||!m?.record.mcpDesiredStateCommit||(await this.journal.releaseMcpLiveEffect({operationId:r.context.operationId,source:"acp_local"}),await this.journal.promoteMcpDesiredStateActive({operationId:r.context.operationId,source:"acp_local"}))},d,e=>{i=e},e=>{n=e}).then(e=>(o="committed",e),e=>{throw o=e instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===e.code||"INSTANCE_EPOCH_MISMATCH"===e.code)?"outcome_unknown":"failed",s.resolve(void 0),e});return{operationId:r.context.operationId,getSnapshot:()=>({effectState:o,...i?{projection:i}:{}}),completion:p,progress:emptyProgress(),effectCommitted:s.promise,acknowledgeEffectReceipt:async e=>{if(!m||e.operationId!==m.operationId||e.method!==m.method)throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt does not match this operation");if(!/^[a-f0-9]{64}$/.test(e.effectRecordHash))throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt hash is invalid");await this.journal.releaseMcpLiveEffect({operationId:e.operationId,source:"service",effectRecordHash:e.effectRecordHash}),c.resolve()},acknowledgeTerminalReceipt:async e=>{if(!(d&&m&&e.operationId===m.operationId&&e.method===m.method&&/^[a-f0-9]{64}$/.test(e.effectRecordHash)&&e.terminalMessageId))throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP terminal receipt does not match this operation");await this.journal.promoteMcpDesiredStateActive({operationId:e.operationId,source:"service",effectRecordHash:e.effectRecordHash,terminalMessageId:e.terminalMessageId});const a=await this.requireReleasedRecord(e.operationId);if(void 0===a.result)throw new RuntimeCommandError("OUTCOME_UNKNOWN","active MCP desired state lost its committed result");await this.afterCommittedEffect(r,a.result,a,t,e=>{i=e},e=>{n=e})},cancel:async()=>"running"===o?{status:"cancel_requested"===await(this.capabilities?.cancel?.(r.context.operationId))?"cancel_requested":"not_cancellable"}:{status:"already_terminal"},awaitProjection:async()=>n?(a??=n(),await a):i}}async executeWithLease(e,t,r,i,n,a,o){const s=await this.deps.leases.claim(e.context.operationId,e.context.dispatchEpoch);if(s.runtimeEpoch!==e.context.dispatchEpoch)throw await this.deps.leases.release(s),new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime operation lease epoch mismatch");const c=this.deps.leases.startHeartbeat(s);try{let c;await this.recoverInterruptedCreates(e.context.operationId);try{c=await this.journal.prepare({operationId:e.context.operationId,command:e.name,paramsHash:e.context.paramsHash,runtimeEpoch:s.runtimeEpoch})}catch(e){if(e instanceof RuntimeCommandJournalConflictError)throw new RuntimeCommandError("OUTCOME_UNKNOWN",e.message,void 0,{cause:e});throw e}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&void 0!==c.result){if(r(c.result,c),await i(),c=await this.requireReleasedRecord(c.operationId),void 0===c.result)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed result disappeared from command journal");return n||await this.afterCommittedEffect(e,c.result,c,t,a,o),c.result}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&"hash_only"===c.resultReceipt?.policy&&runtimeCommandIsReplayableQuery(e.name)){const n=await this.execute(e,s,t);if(runtimeCommandResultHash(n)!==c.resultReceipt.resultHash)throw new RuntimeCommandError("OUTCOME_UNKNOWN","redacted committed query result changed");return r(n,c),await i(),n}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&"hash_only"===c.resultReceipt?.policy){if(r(void 0,c),await i(),c=await this.requireReleasedRecord(c.operationId),!n){if(isRuntimeAgentMutation(e)&&(c.agentProjectionIntent||c.agentProjectionPreparationFailure)){const r=agentProjectionRecoveryResult(c.agentProjectionIntent??c.agentProjectionPreparationFailure);return await this.afterCommittedEffect(e,r,c,t,a,o),r}await this.runAfterEffect(e,void 0,c,t,a)}throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed mutation result is redacted")}if("effect_committed"===c.phase||"projection_committed"===c.phase)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed result receipt is unavailable");if("outcome_unknown"===c.phase)throw new RuntimeCommandError("OUTCOME_UNKNOWN","previous execution outcome is unknown");if("started"===c.phase&&isRuntimeAgentMutation(e)){let n;try{n=await this.recoverStartedAgentMutation(e,s,t)}catch(e){throw await this.markJournalUnknown(c),e instanceof RuntimeCommandError?e:new RuntimeCommandError("OUTCOME_UNKNOWN",`agent desired-state recovery could not prove its effect: ${errorText(e)}`,void 0,{cause:e})}const m=this.deps.projection?await this.prepareAgentProjectionCommit(e,n,t):void 0,d=await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:"effect_committed",result:n,...m?{agentProjection:m}:{}});return r(n,d),await i(),await this.afterCommittedEffect(e,n,d,t,a,o),this.scheduleJournalCompaction(),n}if("started"===c.phase&&"outcome_unknown"===this.recoveryDecision(e.name,c.phase))throw await this.markJournalUnknown(c),new RuntimeCommandError("OUTCOME_UNKNOWN","imperative action crossed its execution boundary");if("failed"===c.phase)throw new RuntimeCommandError("EXECUTION_FAILED","previous execution failed");"prepared"===c.phase&&(c=await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:"started"}));let m=!1;try{const d=await this.execute(e,s,t);m=!0;const p=isRuntimeAgentMutation(e)&&this.deps.projection?await this.prepareAgentProjectionCommit(e,d,t):void 0;let u;try{const e=await this.requireReleasedRecord(c.operationId);u=await this.journal.transition({operationId:e.operationId,expectedRevision:e.revision,phase:"effect_committed",result:d,...p?{agentProjection:p}:{}})}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`effect completed before its journal commit: ${errorText(e)}`,void 0,{cause:e})}return r(d,u),await i(),u=await this.requireReleasedRecord(u.operationId),n||await this.afterCommittedEffect(e,d,u,t,a,o),this.scheduleJournalCompaction(),d}catch(t){if(m)throw t;const r=t instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===t.code||"INSTANCE_EPOCH_MISMATCH"===t.code);if(r&&"clear-saga"===recoveryMetadata(e.name).recoveryClass)throw t;try{await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:r?"outcome_unknown":"failed",errorCode:r?"OUTCOME_UNKNOWN":"EXECUTION_FAILED"})}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`command and journal outcome unknown: ${errorText(e)}`,void 0,{cause:t})}throw t}}finally{try{await c.stop(),await this.deps.leases.release(s)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`runtime operation lease finalization unknown: ${errorText(e)}`,void 0,{cause:e})}}}scheduleJournalCompaction(){this.journal.compact().catch(e=>this.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=effect_committed error=${errorText(e)}`))}async execute(e,t,r){switch(e.name){case"agent.create":return await this.createAgent(e,t,r);case"agent.update":return await this.updateAgent(e);case"agent.delete":return await this.deleteAgent(e,t);case"agent.applyTemplate":return await this.applyTemplate(e,t,r)}if(!this.capabilities)throw new RuntimeCommandError("INVALID_PARAMS",`Unsupported runtime command: ${e.name}`);return await this.capabilities.execute(e,t,r)}async recoverStartedAgentMutation(e,t,r){if("agent.create"===e.name||"agent.applyTemplate"===e.name){let i=await this.bindCreateOperation(e,t,!1);if(i||(i=await this.findCreateRecoveryIdentity(e,!1),"allocated"===i.state&&void 0===i.createOperation?.paramsHash&&await this.bindCreateOperation(e,t,!1),i=await this.findCreateRecoveryIdentity(e)),"active"===i.state)return await this.recoverActiveCreateEffect(i);if(await this.runtimeAgentExists(i.observedRuntimeLocalKey??i.runtimeLocalKey)){const n={runtimeLocalKey:i.observedRuntimeLocalKey??i.runtimeLocalKey,workspacePath:join(this.getOpenclawHome(),`workspace-${i.runtimeLocalKey}`)};return"agent.create"===e.name?await this.completeCreatedAgent(e,t,r,n):await this.completeTemplateAgent(e,t,r,n)}return await this.execute(e,t,r)}const i=e.context.agent,n=await this.runtimeAgentExists(i.runtimeLocalKey);if("agent.delete"===e.name&&!n)return await this.deps.identities.commitDeleteTombstone(i.runtimeAgentUid,i.runtimeLocalKey,t),{runtimeAgentUid:i.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey};if("agent.update"===e.name&&!n)throw new RuntimeCommandError("OUTCOME_UNKNOWN","update recovery target is absent");return await this.execute(e,t,r)}async runtimeAgentExists(e){const t=await this.requireGateway().request("agents.list",{});if(!Array.isArray(t?.agents))throw new RuntimeCommandError("OUTCOME_UNKNOWN","runtime agent directory query is incomplete");return t.agents.some(t=>null!==t&&"object"==typeof t&&(t.id===e||t.name===e))}async findCreateRecoveryIdentity(e,t=!0){const r={operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash,runtimeAgentUid:e.params.runtimeAgentUid},i=void 0!==this.deps.identities.findCreateOperation,n=i?await this.deps.identities.findCreateOperation(r):(await this.deps.identities.pendingAllocations()).filter(e=>e.operationId===r.operationId&&e.command===r.command&&e.runtimeEpoch===r.runtimeEpoch&&e.runtimeAgentUid===r.runtimeAgentUid);if(1!==n.length)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity binding is missing or ambiguous");const a=n[0];if(i&&(!a.createOperation||t&&a.createOperation.paramsHash!==r.paramsHash||a.createOperation.operationId!==r.operationId||a.createOperation.command!==r.command||a.createOperation.runtimeEpoch!==r.runtimeEpoch))throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity hash binding conflicts");return a}async bindCreateOperation(e,t,r=!0){if(!this.deps.identities.findCreateOperation||!this.deps.identities.bindCreateOperation)return;const i={operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash,runtimeAgentUid:e.params.runtimeAgentUid};try{let n=await this.deps.identities.findCreateOperation(i);if(0===n.length){if(!r)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity binding is missing");if((await this.deps.identities.allocateCreate(t,e.name,e.params.runtimeAgentUid)).runtimeAgentUid!==e.params.runtimeAgentUid)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create allocation UID does not match the canonical command");n=await this.deps.identities.findCreateOperation(i)}if(1!==n.length)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create identity binding is missing or ambiguous");const a=n[0],o=await this.deps.identities.bindCreateOperation(a.runtimeAgentUid,a.runtimeLocalKey,{operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash});if(!o.createOperation||o.runtimeAgentUid!==e.params.runtimeAgentUid||o.createOperation.operationId!==i.operationId||o.createOperation.command!==i.command||o.createOperation.runtimeEpoch!==i.runtimeEpoch||o.createOperation.paramsHash!==i.paramsHash)throw new Error("create operation identity binding changed during compare-and-set");if("tombstoned"===o.state)throw new Error("create operation identity is tombstoned");return o}catch(e){if(e instanceof RuntimeCommandError)throw e;throw new RuntimeCommandError("OUTCOME_UNKNOWN",`create operation identity binding is corrupt: ${errorText(e)}`,void 0,{cause:e})}}async recoverActiveCreateEffect(e){const t=e.observedRuntimeLocalKey??e.runtimeLocalKey;if(!await this.runtimeAgentExists(t))throw new RuntimeCommandError("OUTCOME_UNKNOWN","active create identity is absent from the runtime directory");return{runtimeAgentUid:e.runtimeAgentUid,runtimeLocalKey:t,workspacePath:join(this.getOpenclawHome(),`workspace-${e.runtimeLocalKey}`)}}async markJournalUnknown(e){try{await this.journal.transition({operationId:e.operationId,expectedRevision:e.revision,phase:"outcome_unknown",errorCode:"OUTCOME_UNKNOWN"})}catch{}}async requireReleasedRecord(e){const t=await this.journal.get(e);if(!t)throw new RuntimeCommandError("OUTCOME_UNKNOWN","command journal record disappeared");return t}async afterCommittedEffect(e,t,r,i,n,a){if(!isRuntimeAgentMutation(e))return void await this.runAfterEffect(e,t,r,i,n);const o=await this.prepareAgentProjection(e,t,r,i,n);o&&(a(o),"acp"===i.origin&&o().catch(e=>this.logger.warn?.(`[runtime-core] deferred ACP agent projection failed: ${errorText(e)}`)))}async prepareAgentProjectionCommit(e,t,r){const i=agentProjectionRecoverySummary(e,t,r);try{const r=await this.deps.projection.prepareCommittedMutation({command:e,result:t,...i.expectedPlatformAgentId?{expectedPlatformAgentId:i.expectedPlatformAgentId}:{}});return Object.freeze({...r,...i})}catch(e){const t=!(e instanceof RuntimeProjectionTransportError)||e.retryable;return this.logger.warn?.(`[runtime-core] agent projection preparation ${t?"deferred":"blocked"}: ${errorText(e)}`),Object.freeze({...i,state:t?"pending":"blocked",errorKind:t?"snapshot_retryable":"snapshot_nonretryable"})}}async prepareAgentProjection(e,t,r,i,n){if("projection_committed"===r.phase)return n(r.projectionReceipt?.snapshot??Object.freeze({state:"synced",revision:`${Math.max(0,r.revision-1)}`})),r.agentProjectionIntent&&await this.compactAgentProjectionReference(r.agentProjectionIntent),void await this.runPostCommit(e,t);if("effect_committed"!==r.phase||!this.deps.projection)return;let a=r,o=a.agentProjectionIntent;const s=a.agentProjectionPreparationFailure;if(o&&"blocked"===a.agentProjectionTerminal?.state)return n(Object.freeze({state:"blocked",revision:a.agentProjectionTerminal.revision})),await this.compactAgentProjectionReference(o),void await this.runPostCommit(e,t);let c;return o&&n(Object.freeze({state:"pending",revision:o.projectionRevision})),async()=>(c??=(async()=>{if(!o){if(!s||"blocked"===s.state)return Object.freeze({state:"blocked",revision:`${Math.max(0,a.revision)}`});const t=await this.prepareAgentProjectionCommit(e,agentProjectionRecoveryResult(s),i);if(!isProjectionIntent(t))return"blocked"===t.state?Object.freeze({state:"blocked",revision:`${Math.max(0,a.revision)}`}):Object.freeze({state:"pending",revision:`${Math.max(0,a.revision)}`});o=t;try{a=await this.journal.recordAgentProjectionIntent({operationId:a.operationId,expectedRevision:a.revision,intent:o})}catch(e){return void this.logger.warn?.(`[runtime-core] agent projection reservation recovery deferred: ${errorText(e)}`)}n(Object.freeze({state:"pending",revision:o.projectionRevision}))}const r=Object.freeze({state:"pending",revision:o.projectionRevision});let c;try{c=await this.deps.projection.applyCommittedMutation(o)}catch(e){this.logger.warn?.(`[runtime-core] agent projection receipt deferred: ${errorText(e)}`),c=r}if(c.revision!==o.projectionRevision&&(c=Object.freeze({state:"blocked",revision:o.projectionRevision})),"synced"===c.state)try{a=await this.journal.transition({operationId:a.operationId,expectedRevision:a.revision,phase:"projection_committed",projectionSnapshot:c}),await this.compactAgentProjectionReference(o)}catch(e){this.logger.warn?.(`[runtime-core] projection phase commit deferred: ${errorText(e)}`),c=Object.freeze({state:"pending",revision:o.projectionRevision})}if("blocked"===c.state)try{a=await this.journal.recordAgentProjectionBlocked({operationId:a.operationId,expectedRevision:a.revision,revision:o.projectionRevision}),await this.compactAgentProjectionReference(o)}catch(e){this.logger.warn?.(`[runtime-core] blocked projection terminal commit deferred: ${errorText(e)}`),c=r}return"synced"===c.state&&c.platformAgentId&&"projection_committed"===a.phase&&this.reportTemplateOriginBestEffort(e,c.platformAgentId),n(c),await this.runPostCommit(e,t),c})(),await c)}async compactAgentProjectionReference(e){if(this.deps.projection)try{await this.deps.projection.compactCommittedMutation(e)}catch(e){this.logger.warn?.(`[runtime-core] projection reference compaction deferred: ${errorText(e)}`)}}reportTemplateOriginBestEffort(e,t){if("agent.applyTemplate"!==e.name||!this.deps.templateOriginReporter)return;const r=e.params.templateId;r&&this.deps.templateOriginReporter.report({platformAgentId:t,templateId:r}).catch(e=>{this.logger.warn?.(`[runtime-core] active template origin report failed, client fallback remains: ${errorText(e)}`)})}async runAfterEffect(e,t,r,i,n){if("projection_committed"===r.phase)return n(r.projectionReceipt?.snapshot??Object.freeze({state:"synced",revision:`${Math.max(0,r.revision-1)}`})),void await this.runPostCommit(e,t);const a="effect_committed"===r.phase&&await this.runProjection(e,t,i),o=!0===a?Object.freeze({state:"synced",revision:`${r.revision}`}):a&&"object"==typeof a?Object.freeze({...a,revision:`${r.revision}`}):"session.setModel"===e.name?Object.freeze({state:"pending",revision:`${r.revision}`}):void 0;if("synced"===o?.state)try{const e=o;await this.journal.transition({operationId:r.operationId,expectedRevision:r.revision,phase:"projection_committed",projectionSnapshot:e}),n(o)}catch(e){return this.logger.warn?.(`[runtime-core] projection phase commit deferred: ${errorText(e)}`),void n(Object.freeze({revision:o.revision,state:"pending"}))}else o&&n(o);await this.runPostCommit(e,t)}async runPostCommit(e,t){if(this.capabilities?.afterCommit)try{await this.capabilities.afterCommit(e,t)}catch(e){this.logger.warn?.(`[runtime-core] post-commit action failed: ${errorText(e)}`)}}async runProjection(e,t,r){if(!recoveryMetadata(e.name).projectionRequired)return!1;try{return!isRuntimeAgentMutation(e)&&(await(this.capabilities?.projectCommitted?.(e,t,r))??!1)}catch(e){return this.logger.warn?.(`[runtime-core] agent projection deferred: ${errorText(e)}`),!1}}recoveryDecision(e,t){const r=recoveryMetadata(e);return reduceRuntimeRecoveryPolicy({...r,messageServicePhase:"queued",executionBoundaryCrossed:"prepared"!==t,lineageContinuity:"continuous",journalState:"available",journalPhase:t})}requireGateway(){const e=this.deps.getGateway();if(!e?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");return e}async createAgentShell(e,t,r,i,n,a){const o=this.requireGateway(),s=(await this.deps.identities.pendingAllocations()).find(t=>t.runtimeAgentUid===e),c=a??s?.runtimeLocalKey??this.createAgentId(),m=join(this.getOpenclawHome(),`workspace-${c}`);let d;try{d=await o.request("agents.create",{name:c,workspace:m,...n?{model:n}:{},...i?.emoji?{emoji:i.emoji}:{},...i?.avatar?{avatar:i.avatar}:{}})}catch(r){if(isDeterministicAgentsCreateParamsRejection(r)&&(void 0!==a||void 0!==s))try{await this.deps.identities.tombstone(e,c,t)}catch(e){this.logger.warn?.(`[runtime-core] rejected-create allocation cleanup deferred: ${errorText(e)}`)}throw r}const p=d?.agentId??c,u=d?.workspace??m;if(await this.deps.identities.observeEffectKey(e,c,p,t),p!==c)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Create agent outcome unknown: gateway changed reserved local key to ${p}`);try{await o.request("agents.update",{agentId:p,name:r})}catch(e){this.logger.warn?.(`[runtime-core] display-name update failed agent=${p}: ${errorText(e)}`)}return{runtimeLocalKey:p,workspacePath:u}}async createAgent(e,t,r){const i=e.params.name.trim();if(!i)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");try{const n=await this.bindCreateOperation(e,t);if("active"===n?.state)return await this.recoverActiveCreateEffect(n);const a=await this.createAgentShell(e.params.runtimeAgentUid,t,i,e.params.identity,e.params.defaultModelId,n?.runtimeLocalKey);return await this.completeCreatedAgent(e,t,r,a)}catch(e){throw this.executionError("Create agent",e)}}async updateAgent(e){const{runtimeAgentUid:t,runtimeLocalKey:r}=e.context.agent;if(!r.trim())throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");const i=e.params,n=i.name?.trim(),a=i.defaultModelId?.trim(),o=i.identity?.emoji?.trim(),s=i.identity?.avatar?.trim();if(!(n||a||o||s||void 0!==i.description||void 0!==i.version||void 0!==i.capabilities||void 0!==i.skills||void 0!==i.avatarUrl||void 0!==i.homepage||void 0!==i.status||void 0!==i.identity?.vibe))throw new RuntimeCommandError("INVALID_PARAMS","No updatable fields provided (name/model/identity)");try{return await this.deps.identities.requireActive(t,r),await this.requireGateway().request("agents.update",{agentId:r,...n?{name:n}:{},...a?{model:a}:{},...o?{emoji:o}:{},...s?{avatar:s}:{},...i.identity?.vibe?{vibe:i.identity.vibe}:{},...void 0!==i.description?{description:i.description}:{},...void 0!==i.version?{version:i.version}:{},...void 0!==i.capabilities?{capabilities:i.capabilities}:{},...void 0!==i.skills?{skills:i.skills}:{},...void 0!==i.avatarUrl?{avatarUrl:i.avatarUrl}:{},...void 0!==i.homepage?{homepage:i.homepage}:{},...void 0!==i.status?{status:i.status}:{}}),{runtimeAgentUid:t,runtimeLocalKey:r}}catch(e){throw this.executionError("Update agent",e)}}async deleteAgent(e,t){const{runtimeAgentUid:r,runtimeLocalKey:i}=e.context.agent;if(!i.trim())throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");if("main"===normalizeRuntimeAgentLocalKey(i))throw new RuntimeCommandError("INVALID_PARAMS",'"main" cannot be deleted; delete the instance instead');try{if("tombstoned"===await this.deps.identities.requireDeleteTarget(r,i))return{runtimeAgentUid:r,runtimeLocalKey:i};if(!await this.runtimeAgentExists(i))return await this.deps.identities.commitDeleteTombstone(r,i,t),{runtimeAgentUid:r,runtimeLocalKey:i};const n=requireCommittedOpenClawDeleteAck(await this.requireGateway().request("agents.delete",{agentId:i,deleteFiles:e.params.deleteFiles??!0}),i);try{await this.deps.identities.commitDeleteTombstone(r,i,t)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Delete agent outcome unknown: identity tombstone failed: ${errorText(e)}`,void 0,{cause:e})}return{runtimeAgentUid:r,runtimeLocalKey:i,...void 0===n.removedBindings?{}:{removedBindings:n.removedBindings}}}catch(e){throw this.executionError("Delete agent",e)}}async applyTemplate(e,t,r){try{await(this.deps.waitForChatIdle?.("applyTemplate",r.abortSignal))}catch(e){if(e instanceof ImHotPathAbortedError)throw new RuntimeCommandError("INVALID_PARAMS","Apply template aborted while waiting for in-flight chat");throw e}const i=e.params.name.trim();if(!i)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");this.requireGateway();try{const n=await this.bindCreateOperation(e,t);if("active"===n?.state)return await this.recoverActiveCreateEffect(n);const a=await r.resources.readVerifiedArtifact(e.params.artifact),o=await this.createAgentShell(e.params.runtimeAgentUid,t,i,e.params.identity,e.params.defaultModelId,n?.runtimeLocalKey);return await this.completeTemplateAgent(e,t,r,o,a)}catch(e){throw this.executionError("Apply template",e)}}async completeCreatedAgent(e,t,r,i){for(const t of e.params.bootstrapFiles??[]){if("IDENTITY.md"===t.name){this.logger.warn?.("[runtime-core] agent.create ignoring IDENTITY.md; identity is structured");continue}const e=await r.resources.readContent(t.contentRef);try{await this.requireGateway().request("agents.files.set",{agentId:i.runtimeLocalKey,name:t.name,content:e})}catch(e){this.logger.warn?.(`[runtime-core] bootstrap write failed file=${t.name}: ${errorText(e)}`)}}try{await this.deps.identities.activate(e.params.runtimeAgentUid,i.runtimeLocalKey,t)}catch(r){throw await this.compensateCreatedAgent(e.params.runtimeAgentUid,i.runtimeLocalKey,t,r,"Create agent"),new RuntimeCommandError("EXECUTION_FAILED",`Create agent failed after compensation: ${errorText(r)}`)}return{runtimeAgentUid:e.params.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,workspacePath:i.workspacePath}}async completeTemplateAgent(e,t,r,i,n){const a=n??await r.resources.readVerifiedArtifact(e.params.artifact);try{for(const[e,t]of a.workspaceFiles){const r=resolve(i.workspacePath,e),n=relative(i.workspacePath,r);if(""===n||n.startsWith("..")||path.isAbsolute(n))throw new Error(`refusing to write outside workspace: ${e}`);await(this.deps.mkdirWorkspace??mkdir)(path.dirname(r),{recursive:!0}),await(this.deps.writeWorkspaceFile??writeFile)(r,t)}await this.deps.identities.activate(e.params.runtimeAgentUid,i.runtimeLocalKey,t)}catch(r){throw await this.compensateCreatedAgent(e.params.runtimeAgentUid,i.runtimeLocalKey,t,r,"Apply template"),new RuntimeCommandError("EXECUTION_FAILED",`Apply template failed after compensation: ${errorText(r)}`,void 0,{cause:r})}return{runtimeAgentUid:e.params.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,workspacePath:i.workspacePath}}executionError(e,t){return t instanceof RuntimeCommandError?t:(this.logger.error?.(`[runtime-core] ${e.toLowerCase()} failed: ${errorText(t)}`),new RuntimeCommandError("EXECUTION_FAILED",`${e} failed: ${errorText(t)}`,void 0,{cause:t}))}async compensateCreatedAgent(e,t,r,i,n){try{const i=this.requireGateway();requireCommittedOpenClawDeleteAck(await i.request("agents.delete",{agentId:t,deleteFiles:!0}),t),await this.deps.identities.tombstone(e,t,r)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`${n} outcome unknown after ${errorText(i)}; compensation unproven: ${errorText(e)}`,void 0,{cause:e})}}async recoverInterruptedCreates(e){const t=await this.deps.identities.pendingAllocations();for(const r of t){if(r.operationId===e)continue;const t=await this.deps.leases.claimExpiredForRecovery(r.operationId);if(!t)continue;const i=this.deps.leases.startHeartbeat(t);try{await this.deps.identities.adoptRecoveryLease(r.runtimeAgentUid,r.runtimeLocalKey,t);const e=r.observedRuntimeLocalKey;if(!e){const e=r.createOperation?.operationId??r.operationId,i=await this.journal.get(e);if("failed"===i?.phase&&i.command===r.command&&!await this.runtimeAgentExists(r.runtimeLocalKey)){await this.deps.identities.tombstone(r.runtimeAgentUid,r.runtimeLocalKey,t);continue}throw new Error("no persisted gateway effect key; absence cannot be proven")}requireCommittedOpenClawDeleteAck(await this.requireGateway().request("agents.delete",{agentId:e,deleteFiles:!0}),e),await this.deps.identities.tombstone(r.runtimeAgentUid,r.runtimeLocalKey,t)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Interrupted ${r.command} recovery outcome unknown for operation ${r.operationId}: ${errorText(e)}`,void 0,{cause:e})}finally{let e;try{await i.stop()}catch(t){e=t}try{await this.deps.leases.release(t)}catch(t){e??=t}if(e)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Interrupted recovery lease finalization unknown: ${errorText(e)}`,void 0,{cause:e})}}}}function isRuntimeAgentMutation(e){return"agent.create"===e.name||"agent.update"===e.name||"agent.delete"===e.name||"agent.applyTemplate"===e.name}function servicePlatformAgentId(e){const t=e.verifiedMessageGrant?.claims;return"agent"===t?.scope?t.platformAgentId:void 0}function agentCoreResultHash(e,t){return createHash("sha256").update(runtimeHashPreimage(RUNTIME_HASH_DOMAINS.coreResult,{method:e.name.replace(".","/"),result:t})).digest("hex")}function agentProjectionRecoverySummary(e,t,r){const i="agent.update"===e.name||"agent.delete"===e.name?e.context.agent:createProjectionTarget(e,t),n=t;return Object.freeze({schemaVersion:1,operationId:e.context.operationId,method:e.name,runtimeEpoch:e.context.dispatchEpoch,runtimeAgentUid:i.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,coreResultHash:agentCoreResultHash(e,t),...servicePlatformAgentId(r)?{expectedPlatformAgentId:servicePlatformAgentId(r)}:{},..."agent.delete"===e.name&&Number.isSafeInteger(n?.removedBindings)?{removedBindings:n.removedBindings}:{}})}function createProjectionTarget(e,t){const r=t;if(!r||r.runtimeAgentUid!==e.params.runtimeAgentUid||"string"!=typeof r.runtimeLocalKey||!r.runtimeLocalKey)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed agent effect lacks a recoverable local identity");return{runtimeAgentUid:r.runtimeAgentUid,runtimeLocalKey:r.runtimeLocalKey}}function agentProjectionRecoveryResult(e){return Object.freeze({runtimeAgentUid:e.runtimeAgentUid,runtimeLocalKey:e.runtimeLocalKey,...void 0===e.removedBindings?{}:{removedBindings:e.removedBindings}})}function isProjectionIntent(e){return"projectionRevision"in e}function recoveryMetadata(e){const t=RUNTIME_METHOD_PARITY_MANIFEST.filter(t=>t.serviceAllowed&&t.canonicalCommand===e),r=t[0]?.recoveryClass;return r?{recoveryClass:r,projectionRequired:"mcp.set"===e||"mcp.unset"===e||t.some(e=>!0===e.projectionRequired)}:{recoveryClass:"imperative-ambiguous",projectionRequired:!1}}export function isDeterministicAgentsCreateParamsRejection(e){return e instanceof Error&&/^invalid agents\.create params\b/.test(e.message)}export function requireCommittedOpenClawDeleteAck(e,t){if(!e||"object"!=typeof e)throw new Error("gateway delete lacks the committed target acknowledgement");const r=e,i=Object.keys(r).every(e=>"ok"===e||"agentId"===e||"removedBindings"===e),n=void 0===r.removedBindings||Number.isSafeInteger(r.removedBindings)&&r.removedBindings>=0;if(!i||!0!==r.ok||r.agentId!==t||!n)throw new Error("gateway delete lacks the committed target acknowledgement");return{ok:!0,agentId:t,...void 0===r.removedBindings?{}:{removedBindings:r.removedBindings}}}function errorText(e){return e instanceof Error?e.message:String(e)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beeos-claw",
3
- "version": "0.6.55-staging.0",
3
+ "version": "0.6.55-staging.2",
4
4
  "description": "OpenClaw plugin that bridges the BeeOS platform with the local OpenClaw Gateway, providing file transfer, ACP bridge, and observability.",
5
5
  "license": "MIT",
6
6
  "author": "BeeOS <dev@beeos.ai>",
@@ -87,5 +87,5 @@
87
87
  "fast-uri": ">=3.1.4",
88
88
  "brace-expansion": ">=5.0.9"
89
89
  },
90
- "gitHead": "ee8b7631a0c5caa0cb490b489b8d5f26ee4ab690"
90
+ "gitHead": "73c309ca647a327cd591f14721c267b552dfa655"
91
91
  }