beeos-claw 0.6.0 → 0.6.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{WebSocketServer,WebSocket}from"ws";import{randomUUID}from"node:crypto";import{AsyncLocalStorage}from"node:async_hooks";let BridgeClientCtor;import{GatewayClient}from"./gateway-client.js";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{AgentGatewayHttpProjectionTransport,AgentProjectionProduction,OpenClawAgentProjectionSource}from"./runtime/agent-projection-production.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{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";import{MessageClient}from"@beeos-ai/message-sdk";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;mcpToolBridge;mcpCredentialReferences;boundAgentFetchFn;wss=null;gateway=null;client=null;config;logger;writeObs;transport;state;core;events;terminalMgr;terminalHandler;localTerminalAuthorityRegistry;acpConnectionTerminalHandlers=new WeakMap;runtimeTerminalService;terminalOutbound=null;terminalServiceReady=!1;runtimeAgentCapabilities;agentProjection;runtimeRpcConsumer;runtimeRpcConsumerFactory;canvasOrch;sessionMutationCoordinator;messageClient;msgPrincipalId=null;pendingToolCallSessionKeys=[];thirdPartyChannels;agentSyncRetryTimer=null;agentSyncInFlight=!1;runtimeServiceFactoryFenced=!1;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: %s",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)):this.sessionMutationCoordinator.releaseBeforeRuntime(a),c}catch(e){throw o?this.sessionMutationCoordinator.requireRecovery(a,"terminal_not_observed"):this.sessionMutationCoordinator.releaseBeforeRuntime(a),e}}setMessageClient(e,t){this.messageClient.setConfig(e),this.msgPrincipalId=t,this.logger.info?.(`[acp-server] MessageEnvelope v3 streamer ready (principal=${t})`)}getMessageClient(){return this.messageClient}getRuntimeCommandDispatcher(){return this.core.getRuntimeCommandDispatcher()}getActiveRunAuthority(){return this.core.getActiveRunAuthority()}ensureImInboundSession(e,t,i){const n=this.core.ensureSessionForKey(e);n.channelId=t,n.inboundMessageId=i}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){this.agentFetchFn=r,this.instanceId=s,this.runtimeAuthority=c,this.mcpToolBridge=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.runtimeRpcConsumerFactory=l,this.runtimeAgentCapabilities={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),this.messageClient=new MessageClient({logger:this.logger});const m=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,thirdPartyChannels:this.thirdPartyChannels,beeosFileResolver:m,agentFetch:this.agentFetchFn,agentGatewayUrl:this.config.agentGatewayUrl,sessionMutationCoordinator:i,triggerAgentSync:()=>this.tryAgentSync(),runtimeAgentIdentities:this.runtimeAuthority?.identities,runtimeCommandJournal:this.runtimeAuthority?.journal,markAgentProjectionDirty:this.runtimeAuthority?()=>this.agentProjection?.markDirty():void 0,getAgentProjection:this.runtimeAuthority?()=>this.agentProjection:void 0,mcpToolBridge:this.mcpToolBridge,mcpCredentialReferences:this.mcpCredentialReferences}),this.runtimeAuthority&&this.agentFetchFn&&this.config.agentGatewayUrl&&(this.agentProjection=new AgentProjectionProduction({authority:this.runtimeAuthority,stateFile:join(this.runtimeAuthority.stateDirectory,"runtime-agent-projection.json"),journal:this.runtimeAuthority.journal,identities:this.runtimeAuthority.identities,source:new OpenClawAgentProjectionSource(()=>this.gateway),transport:new AgentGatewayHttpProjectionTransport({agentGatewayUrl:this.config.agentGatewayUrl,boundAgentFetch:this.runtimeAuthority.boundAgentFetch,getRuntimeLeaseCredential:e=>{const t=this.runtimeAuthority?.authorityLease;if(!t||t.instanceId!==e.instanceId||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch||t.handlerIdentity!==e.handlerIdentity)throw new Error("agent projection fenced by current runtime lease");return t.runtimeLeaseCredential},onAccepted:e=>{e.size>0&&this.core.setAgentMapping(e)}}),capabilities:this.runtimeAgentCapabilities,logger:this.logger})),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,getDefaultAgentId:()=>this.core.getDefaultAgentId(),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,completePrompt:(e,t)=>this.core.completePrompt(e,t),failPrompt:(e,t,i)=>this.core.failPrompt(e,t,i),interruptPrompt:(e,t)=>this.core.interruptPrompt(e,t),schedulePromptTimeout:e=>this.core.schedulePromptTimeout(e),adoptBackgroundMutation:(e,t)=>this.core.adoptBackgroundMutation(e,t),abortGatewayRun:async(e,t)=>{await this.chatAbort(e,t)},maybeCompletePromptFromChatState:(e,t)=>this.core.maybeCompletePromptFromChatState(e,t),agentFetch:this.agentFetchFn,getMessageClient:()=>this.messageClient,getMsgPrincipalId:()=>this.msgPrincipalId??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)})}setRuntimeRpcConsumerFactory(e){if(e!==this.runtimeRpcConsumerFactory){if("stopped"!==this.lifecyclePhase)throw new Error("runtime Service factory is immutable after ACP start");this.runtimeRpcConsumerFactory=e}}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,projectionStarted:!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,getLease:()=>i.authorityLease,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.shouldContinueStart(e))return;if(i&&r){if("completed"!==(await this.waitForGeneration(i.start(e.abortController.signal),e)).status||!this.shouldContinueStart(e))return}this.agentProjection&&(e.projectionStarted=!0,this.agentProjection.start());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))return;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");const c=Date.now();if(await this.connectGateway(e),this.shouldContinueStart(e)){this.logger.info?.(`[acp-server] connectGateway() took ${Date.now()-c}ms`);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.agentProjection?this.agentProjection.markDirty():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 void this.agentProjection.onGatewayReconnect();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")}if(this.agentSyncInFlight)return;const t=[1e3,2e3,5e3,1e4],i=()=>{const i=e+1,n=t[Math.min(e,t.length-1)],r=Math.round(n/1e3),s=`[acp-server] agent sync retry #${i} in ${r}s`;i>0&&i%30==0?this.logger.warn?.(`[acp-server] agent sync still failing after ${i} attempts — retrying every ${r}s`):i<=t.length?this.logger.info?.(s):this.logger.debug?.(s),this.agentSyncRetryTimer=setTimeout(()=>{this.tryAgentSync(i)},n)},n=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.core.setAgentMapping(e),this.logger.info?.(`[acp-server] agent mapping loaded: ${[...e.keys()].join(", ")} (sync took ${Date.now()-n}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")):i()}catch(e){this.logger.warn?.(`[acp-server] agent sync failed: ${e}`),i()}finally{this.agentSyncInFlight=!1}}async connectGateway(e){const t=this.config.gateway,i=new GatewayClient({url:t.url,token:t.token||"",clientId:t.clientId,openclawCanvasCompat:!!this.config.openclawCanvasCompat,retry:{baseMs:this.config.retry.baseMs,maxMs:this.config.retry.maxMs,maxAttempts:this.config.retry.maxAttempts}});if(e&&!this.shouldContinueStart(e))return i.stop(),void i.removeAllListeners();e&&(e.gateway=i),this.gateway=i,i.on("chat",e=>this.events.handleChatEvent(e)),i.on("agent",e=>this.events.handleAgentEvent(e)),i.on("cron",e=>this.events.handleCronEvent(e)),i.on("sessionMessage",e=>this.core.handleSessionMessage(e)),i.on("response",e=>this.core.handleGatewayResponse(e));const n=i;this.canvasOrch.updateDeps({gateway:n?{get isConnected(){return n.isConnected},request:(e,t)=>this.gateway.request(e,t),agentSend:(e,t,i)=>this.gateway.agentSend(e,t,i)}:null}),this.config.openclawCanvasCompat&&(i.on("canvas",e=>this.canvasOrch.handleOpenClawCanvasEvent(e)),this.logger.info?.("[acp-server] OpenClaw native canvas compat enabled — listening for canvas events")),i.on("connected",()=>{this.agentProjection?.onGatewayReconnect(),this.logger.info?.("[acp-server] Connected to OpenClaw Gateway"),this.writeObs({component:"acp-server",domain:"lifecycle",name:"gateway.connected",severity:"info"}),this.core.resubscribeAllSessionMessages(),this.core.refreshCronIsolatedSessionKeys(),this.tryAgentSync()}),i.on("disconnected",()=>{this.logger.warn?.("[acp-server] Gateway disconnected"),this.writeObs({component:"acp-server",domain:"lifecycle",name:"gateway.disconnected",severity:"warn"}),this.core.cleanupAllPendingPrompts()}),i.on("reconnecting",e=>{this.logger.info?.(`[acp-server] Reconnecting to Gateway attempt=${e.attempt} delay=${Math.round(e.delayMs)}ms`)}),i.on("reconnect-exhausted",e=>{this.logger.error?.(`[acp-server] Gateway reconnect exhausted after ${e} attempts`)}),i.on("error",e=>{this.logger.error?.("[acp-server] Gateway error:",e.message)}),i.on("transport",e=>{this.logger.info?.(`[acp-server] transport.${e.event} durationMs=${e.durationMs??"?"}`),this.writeObs({component:"gateway-client",domain:"lifecycle",name:`transport.${e.event}`,severity:"connect_fail"===e.event||"error"===e.event?"warn":"debug",payload:e})}),i.on("reconnect-notify-scheduled",e=>{this.logger.info?.(`[acp-server] Server-initiated reconnect scheduled in ${e.delayMs}ms`)}),i.on("reconnect-notify-fired",()=>{this.logger.info?.("[acp-server] Server-initiated reconnect firing")});try{if(e){const t=await this.waitForGeneration(i.start(),e,ACPServer.LIFECYCLE_CONNECT_TIMEOUT_MS);if("cancelled"===t.status)return;if("timed_out"===t.status)throw new Error("Gateway connection timed out")}else await i.start()}catch(e){this.logger.error?.("[acp-server] Initial Gateway connect failed:",e)}}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.core.waitForAgentMapping()}onceAgentMappingReady(e){this.core.onceAgentMappingReady(e)}onAgentMappingUpdated(e){this.core.onAgentMappingUpdated(e)}flushPendingCanvasLinks(){this.canvasOrch.flushPendingCanvasLinks()}onRelayReconnected(){this.canvasOrch.onRelayReconnected()}getLocalAgentId(){return this.config.gateway.agentId||this.core.getDefaultAgentId()}getDefaultPlatformAgentId(){return this.getDefaultPlatformAgent()?.id}getDefaultPlatformAgent(){const e=this.core.agentMapping;if(e&&0!==e.size)return e.values().next().value??void 0}getPlatformAgentMappings(){const e=this.core.agentMapping;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),e.projectionStarted&&(e.projectionStarted=!1,o.push(a("agent-projection",()=>this.agentProjection?.stop()))),o.push(a("session-state",()=>this.state.destroy())),o.push(a("canvas-orchestrator",()=>this.canvasOrch.destroy())),o.push(a("mcp-desired-state",()=>this.core.stopMcp()));const c=e.gateway,l=e.bridge,h=e.bridgeSocket,g=e.terminalOutbound,d=e.runtimeConsumer,m=this.client&&this.client!==h?this.client:null,u=this.wss;if(e.gateway=void 0,e.bridge=void 0,e.bridgeSocket=void 0,e.terminalOutbound=void 0,e.runtimeConsumer=void 0,this.gateway===c&&(this.gateway=null),this.bridgeInstance===l&&(this.bridgeInstance=null),this.client!==m&&this.client!==h||(this.client=null),this.terminalOutbound===g&&(this.terminalOutbound=null),this.runtimeRpcConsumer===d&&(this.runtimeRpcConsumer=void 0),this.wss=null,this.bridgeConsecutiveFailures=0,c&&o.push(a("gateway",async()=>{try{await c.stop()}finally{c.removeAllListeners()}})),m&&o.push(a("incoming-client",()=>{m.removeAllListeners(),m.close()})),l&&o.push(a("bridge",async()=>{try{await this.closeBridgeOnce(l)}catch(e){this.logger.debug?.(`[acp-server] bridge.close failed during stop: ${e instanceof Error?e.message:String(e)}`)}})),g&&o.push(a("terminal-outbound",e=>g.drain({timeoutMs:ACPServer.LIFECYCLE_TEARDOWN_TIMEOUT_MS,terminateAfterTimeout:!0,signal:e}))),e.authorityOwned&&(e.authorityOwned=!1,o.push(a("runtime-authority",()=>e.authority?.stop()))),d&&o.push(a("runtime-consumer",e=>this.closeConsumerOnce(d,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{GatewayClient}from"./gateway-client.js";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{AgentGatewayHttpProjectionTransport,AgentProjectionProduction,OpenClawAgentProjectionSource}from"./runtime/agent-projection-production.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{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";import{MessageClient}from"@beeos-ai/message-sdk";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;mcpToolBridge;mcpCredentialReferences;boundAgentFetchFn;wss=null;gateway=null;client=null;config;logger;writeObs;transport;state;core;events;terminalMgr;terminalHandler;localTerminalAuthorityRegistry;acpConnectionTerminalHandlers=new WeakMap;runtimeTerminalService;terminalOutbound=null;terminalServiceReady=!1;runtimeAgentCapabilities;agentProjection;runtimeRpcConsumer;runtimeRpcConsumerFactory;canvasOrch;sessionMutationCoordinator;messageClient;msgPrincipalId=null;pendingToolCallSessionKeys=[];thirdPartyChannels;agentSyncRetryTimer=null;agentSyncInFlight=!1;runtimeServiceFactoryFenced=!1;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: %s",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)):this.sessionMutationCoordinator.releaseBeforeRuntime(a),c}catch(e){throw o?this.sessionMutationCoordinator.requireRecovery(a,"terminal_not_observed"):this.sessionMutationCoordinator.releaseBeforeRuntime(a),e}}setMessageClient(e,t){this.messageClient.setConfig(e),this.msgPrincipalId=t,this.logger.info?.(`[acp-server] MessageEnvelope v3 streamer ready (principal=${t})`)}getMessageClient(){return this.messageClient}getRuntimeCommandDispatcher(){return this.core.getRuntimeCommandDispatcher()}getActiveRunAuthority(){return this.core.getActiveRunAuthority()}ensureImInboundSession(e,t,i){const n=this.core.ensureSessionForKey(e);n.channelId=t,n.inboundMessageId=i}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){this.agentFetchFn=r,this.instanceId=s,this.runtimeAuthority=c,this.mcpToolBridge=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.runtimeRpcConsumerFactory=l,this.runtimeAgentCapabilities={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),this.messageClient=new MessageClient({logger:this.logger});const m=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,thirdPartyChannels:this.thirdPartyChannels,beeosFileResolver:m,agentFetch:this.agentFetchFn,agentGatewayUrl:this.config.agentGatewayUrl,sessionMutationCoordinator:i,triggerAgentSync:()=>this.tryAgentSync(),runtimeAgentIdentities:this.runtimeAuthority?.identities,runtimeCommandJournal:this.runtimeAuthority?.journal,markAgentProjectionDirty:this.runtimeAuthority?()=>this.agentProjection?.markDirty():void 0,getAgentProjection:this.runtimeAuthority?()=>this.agentProjection:void 0,mcpToolBridge:this.mcpToolBridge,mcpCredentialReferences:this.mcpCredentialReferences}),this.runtimeAuthority&&this.agentFetchFn&&this.config.agentGatewayUrl&&(this.agentProjection=new AgentProjectionProduction({authority:this.runtimeAuthority,stateFile:join(this.runtimeAuthority.stateDirectory,"runtime-agent-projection.json"),journal:this.runtimeAuthority.journal,identities:this.runtimeAuthority.identities,source:new OpenClawAgentProjectionSource(()=>this.gateway),transport:new AgentGatewayHttpProjectionTransport({agentGatewayUrl:this.config.agentGatewayUrl,boundAgentFetch:this.runtimeAuthority.boundAgentFetch,getRuntimeLeaseCredential:e=>{const t=this.runtimeAuthority?.authorityLease;if(!t||t.instanceId!==e.instanceId||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch||t.handlerIdentity!==e.handlerIdentity)throw new Error("agent projection fenced by current runtime lease");return t.runtimeLeaseCredential},onAccepted:e=>{e.size>0&&this.core.setAgentMapping(e)}}),capabilities:this.runtimeAgentCapabilities,logger:this.logger})),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,getDefaultAgentId:()=>this.core.getDefaultAgentId(),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,completePrompt:(e,t)=>this.core.completePrompt(e,t),failPrompt:(e,t,i)=>this.core.failPrompt(e,t,i),interruptPrompt:(e,t)=>this.core.interruptPrompt(e,t),schedulePromptTimeout:e=>this.core.schedulePromptTimeout(e),adoptBackgroundMutation:(e,t)=>this.core.adoptBackgroundMutation(e,t),abortGatewayRun:async(e,t)=>{await this.chatAbort(e,t)},maybeCompletePromptFromChatState:(e,t)=>this.core.maybeCompletePromptFromChatState(e,t),agentFetch:this.agentFetchFn,getMessageClient:()=>this.messageClient,getMsgPrincipalId:()=>this.msgPrincipalId??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)})}setRuntimeRpcConsumerFactory(e){if(e!==this.runtimeRpcConsumerFactory){if("stopped"!==this.lifecyclePhase)throw new Error("runtime Service factory is immutable after ACP start");this.runtimeRpcConsumerFactory=e}}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,projectionStarted:!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,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.shouldContinueStart(e))return;if(i&&r){if("completed"!==(await this.waitForGeneration(i.start(e.abortController.signal),e)).status||!this.shouldContinueStart(e))return}this.agentProjection&&(e.projectionStarted=!0,this.agentProjection.start());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))return;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");const c=Date.now();if(await this.connectGateway(e),this.shouldContinueStart(e)){this.logger.info?.(`[acp-server] connectGateway() took ${Date.now()-c}ms`);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.agentProjection?this.agentProjection.markDirty():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 void this.agentProjection.onGatewayReconnect();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")}if(this.agentSyncInFlight)return;const t=[1e3,2e3,5e3,1e4],i=()=>{const i=e+1,n=t[Math.min(e,t.length-1)],r=Math.round(n/1e3),s=`[acp-server] agent sync retry #${i} in ${r}s`;i>0&&i%30==0?this.logger.warn?.(`[acp-server] agent sync still failing after ${i} attempts — retrying every ${r}s`):i<=t.length?this.logger.info?.(s):this.logger.debug?.(s),this.agentSyncRetryTimer=setTimeout(()=>{this.tryAgentSync(i)},n)},n=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.core.setAgentMapping(e),this.logger.info?.(`[acp-server] agent mapping loaded: ${[...e.keys()].join(", ")} (sync took ${Date.now()-n}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")):i()}catch(e){this.logger.warn?.(`[acp-server] agent sync failed: ${e}`),i()}finally{this.agentSyncInFlight=!1}}async connectGateway(e){const t=this.config.gateway,i=new GatewayClient({url:t.url,token:t.token||"",clientId:t.clientId,openclawCanvasCompat:!!this.config.openclawCanvasCompat,retry:{baseMs:this.config.retry.baseMs,maxMs:this.config.retry.maxMs,maxAttempts:this.config.retry.maxAttempts}});if(e&&!this.shouldContinueStart(e))return i.stop(),void i.removeAllListeners();e&&(e.gateway=i),this.gateway=i,i.on("chat",e=>this.events.handleChatEvent(e)),i.on("agent",e=>this.events.handleAgentEvent(e)),i.on("cron",e=>this.events.handleCronEvent(e)),i.on("sessionMessage",e=>this.core.handleSessionMessage(e)),i.on("response",e=>this.core.handleGatewayResponse(e));const n=i;this.canvasOrch.updateDeps({gateway:n?{get isConnected(){return n.isConnected},request:(e,t)=>this.gateway.request(e,t),agentSend:(e,t,i)=>this.gateway.agentSend(e,t,i)}:null}),this.config.openclawCanvasCompat&&(i.on("canvas",e=>this.canvasOrch.handleOpenClawCanvasEvent(e)),this.logger.info?.("[acp-server] OpenClaw native canvas compat enabled — listening for canvas events")),i.on("connected",()=>{this.agentProjection?.onGatewayReconnect(),this.logger.info?.("[acp-server] Connected to OpenClaw Gateway"),this.writeObs({component:"acp-server",domain:"lifecycle",name:"gateway.connected",severity:"info"}),this.core.resubscribeAllSessionMessages(),this.core.refreshCronIsolatedSessionKeys(),this.tryAgentSync()}),i.on("disconnected",()=>{this.logger.warn?.("[acp-server] Gateway disconnected"),this.writeObs({component:"acp-server",domain:"lifecycle",name:"gateway.disconnected",severity:"warn"}),this.core.cleanupAllPendingPrompts()}),i.on("reconnecting",e=>{this.logger.info?.(`[acp-server] Reconnecting to Gateway attempt=${e.attempt} delay=${Math.round(e.delayMs)}ms`)}),i.on("reconnect-exhausted",e=>{this.logger.error?.(`[acp-server] Gateway reconnect exhausted after ${e} attempts`)}),i.on("error",e=>{this.logger.error?.("[acp-server] Gateway error:",e.message)}),i.on("transport",e=>{this.logger.info?.(`[acp-server] transport.${e.event} durationMs=${e.durationMs??"?"}`),this.writeObs({component:"gateway-client",domain:"lifecycle",name:`transport.${e.event}`,severity:"connect_fail"===e.event||"error"===e.event?"warn":"debug",payload:e})}),i.on("reconnect-notify-scheduled",e=>{this.logger.info?.(`[acp-server] Server-initiated reconnect scheduled in ${e.delayMs}ms`)}),i.on("reconnect-notify-fired",()=>{this.logger.info?.("[acp-server] Server-initiated reconnect firing")});try{if(e){const t=await this.waitForGeneration(i.start(),e,ACPServer.LIFECYCLE_CONNECT_TIMEOUT_MS);if("cancelled"===t.status)return;if("timed_out"===t.status)throw new Error("Gateway connection timed out")}else await i.start()}catch(e){this.logger.error?.("[acp-server] Initial Gateway connect failed:",e)}}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.core.waitForAgentMapping()}onceAgentMappingReady(e){this.core.onceAgentMappingReady(e)}onAgentMappingUpdated(e){this.core.onAgentMappingUpdated(e)}flushPendingCanvasLinks(){this.canvasOrch.flushPendingCanvasLinks()}onRelayReconnected(){this.canvasOrch.onRelayReconnected()}getLocalAgentId(){return this.config.gateway.agentId||this.core.getDefaultAgentId()}getDefaultPlatformAgentId(){return this.getDefaultPlatformAgent()?.id}getDefaultPlatformAgent(){const e=this.core.agentMapping;if(e&&0!==e.size)return e.values().next().value??void 0}getPlatformAgentMappings(){const e=this.core.agentMapping;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),e.projectionStarted&&(e.projectionStarted=!1,o.push(a("agent-projection",()=>this.agentProjection?.stop()))),o.push(a("session-state",()=>this.state.destroy())),o.push(a("canvas-orchestrator",()=>this.canvasOrch.destroy())),o.push(a("mcp-desired-state",()=>this.core.stopMcp()));const c=e.gateway,l=e.bridge,h=e.bridgeSocket,g=e.terminalOutbound,d=e.runtimeConsumer,m=this.client&&this.client!==h?this.client:null,u=this.wss;if(e.gateway=void 0,e.bridge=void 0,e.bridgeSocket=void 0,e.terminalOutbound=void 0,e.runtimeConsumer=void 0,this.gateway===c&&(this.gateway=null),this.bridgeInstance===l&&(this.bridgeInstance=null),this.client!==m&&this.client!==h||(this.client=null),this.terminalOutbound===g&&(this.terminalOutbound=null),this.runtimeRpcConsumer===d&&(this.runtimeRpcConsumer=void 0),this.wss=null,this.bridgeConsecutiveFailures=0,c&&o.push(a("gateway",async()=>{try{await c.stop()}finally{c.removeAllListeners()}})),m&&o.push(a("incoming-client",()=>{m.removeAllListeners(),m.close()})),l&&o.push(a("bridge",async()=>{try{await this.closeBridgeOnce(l)}catch(e){this.logger.debug?.(`[acp-server] bridge.close failed during stop: ${e instanceof Error?e.message:String(e)}`)}})),g&&o.push(a("terminal-outbound",e=>g.drain({timeoutMs:ACPServer.LIFECYCLE_TEARDOWN_TIMEOUT_MS,terminateAfterTimeout:!0,signal:e}))),e.authorityOwned&&(e.authorityOwned=!1,o.push(a("runtime-authority",()=>e.authority?.stop()))),d&&o.push(a("runtime-consumer",e=>this.closeConsumerOnce(d,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{MessageClient}from"@beeos-ai/message-sdk";import{hasRuntimeReplyApi}from"./runtime/reply-router.js";import{PromptRunStore}from"./runtime/prompt-run-store.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{MessageServiceRecoveryScanner}from"./im-recovery-scanner.js";import{createTokenProvider}from"@beeos-ai/message-sdk/auth";import{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_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{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{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{InProcessMcpToolBridge}from"./acp-gateway/mcp-tool-bridge.js";import{LocalEncryptedCredentialReferenceVault}from"./acp-gateway/mcp-credential-reference-port.js";import{AgentGatewayGrantJwksReadinessAdapter,RuntimeServiceTransportReadinessAdapter,createRuntimeServiceProductionCandidate}from"./runtime/runtime-service-production-candidate.js";import{MessageServiceRuntimeAuthenticatedFetch}from"./runtime/message-service-runtime-authenticated-fetch.js";const PLUGIN_VERSION=resolvePluginVersion();let agentKeys=null,sharedCanvasState=null,sharedAcpServer=null;const sharedSessionWriteGate=new SessionWriteGate,sharedPromptRunStore=new PromptRunStore;let sharedSessionMutationCoordinator,sharedRuntimeAuthority,sharedMcpToolBridge,sharedMcpCredentialVault;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 r=>{const{createPrivateKey:o,sign:t}=await import("node:crypto"),a=o({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(t(null,Buffer.from(r),a))}}}let unhandledRejectionObserverInstalled=!1;function installUnhandledRejectionObserver(e,r,o){unhandledRejectionObserverInstalled||(unhandledRejectionObserverInstalled=!0,process.on("unhandledRejection",o=>{const t=o instanceof Error?o.message:String(o),a=o instanceof Error?o.stack:void 0;e.logger.error?.(`[beeos-claw] unhandledRejection: ${t}${a?`\n${a}`:""}`),r({component:"plugin",domain:"fatal",name:"unhandled_rejection",severity:"error",summary:t,error:{code:"unhandled_rejection",message:t,...a?{stack:a}:{}}})}),process.on("uncaughtException",o=>{const t=o instanceof Error?o.message:String(o);e.logger.error?.("[beeos-claw] uncaughtException:",o),r({component:"plugin",domain:"fatal",name:"uncaught_exception",severity:"error",summary:t,error:{code:"uncaught_exception",message:t}})}))}function initializePlugin(e){let r,o;try{r=e.runtime?.config?.loadConfig?.()}catch{}try{o=resolveConfigWithMeta({pluginConfig:e.pluginConfig??{},openclawConfig:r})}catch(r){return e.logger.error?.("[beeos-claw] Failed to resolve config:",r),null}const t=o.config,a=buildThirdPartyChannelSet(r);a.size>0&&e.logger.info?.(`[beeos-claw] Third-party channels detected: ${[...a].join(", ")}`);const n=t.log.enabled?createObsEventWriter({dir:t.log.dir,redaction:{gatewayToken:t.gateway.token},logger:e.logger}):()=>{};n({component:"plugin",domain:"lifecycle",name:"lifecycle.startup",severity:"info",payload:{pluginVersion:PLUGIN_VERSION,pid:process.pid,nodeVersion:process.version,cwd:process.cwd()}}),n({component:"plugin",domain:"config",name:"config.resolved",severity:"info",payload:{config:t,sources:o.sources,openclawConfigPath:o.openclawConfigPath}});for(const r of o.validation)n({component:"plugin",domain:"config",name:"config.validation",severity:r.severity,summary:r.message,error:{code:r.code,message:r.message,nextSteps:r.nextSteps}}),"error"===r.severity?e.logger.error?.(`[beeos-claw] ${r.message}`):e.logger.warn?.(`[beeos-claw] ${r.message}`);if(o.validation.some(e=>"error"===e.severity)&&e.logger.error?.("[beeos-claw] Plugin has configuration errors — some features may not work."),t.keyFile)try{agentKeys=loadAgentKeyPair(t.keyFile);const r=Buffer.from(agentKeys.publicKey).toString("base64");e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (publicKey: ${r})`)}catch(r){e.logger.warn?.(`[beeos-claw] Failed to load agent key pair: ${r}`),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&&t.agentGatewayUrl&&(setUploadDeps({gatewayUrl:t.agentGatewayUrl,keys:agentKeys,logger:e.logger}),registerLocalFileExtractor(browserScreenshotExtractor),e.logger.info?.("[beeos-claw] Upload deps + browser screenshot extractor registered")),{cfg:t,writeObs:n,thirdPartyChannels:a,openclawConfig:r}}function registerUploadTools(e,r,o){const{cfg:t,writeObs:a}=r;cleanupUploadToolResultCache().catch(r=>{e.logger.warn?.(`[beeos_upload_file] cache cleanup failed on startup: ${String(r)}`)}),e.registerTool({name:BEEOS_UPLOAD_TOOL_NAME,description:BEEOS_UPLOAD_TOOL_DESCRIPTION,parameters:BEEOS_UPLOAD_TOOL_SCHEMA,async execute(r,o,n){e.logger.debug?.(`[beeos_upload_file] toolCallId=${r} params=${JSON.stringify(o)}`);const s=await executeUpload(o,t,agentKeys,n);return e.logger.debug?.(`[beeos_upload_file] toolCallId=${r} ok=${s.ok}`),a({component:"upload-tool",domain:"tool",name:s.ok?"upload.success":"upload.failure",severity:s.ok?"info":"warn",payload:{toolCallId:r,ok:s.ok,fileCount:s.files?.length??0,error:s.error}}),(async o=>{try{await writeUploadToolResult(r,BEEOS_UPLOAD_TOOL_NAME,o),await cleanupUploadToolResultCache()}catch(o){e.logger.warn?.(`[beeos_upload_file] persist cache failed toolCallId=${r}: ${String(o)}`)}return o})(s)}}),e.registerHttpRoute({path:"/beeos/files/stage",auth:"gateway",async handler(e,o){await handleFileStage(e,o,r.cfg)}}),e.registerHttpRoute({path:"/beeos/status",auth:"plugin",handler(e,r){handleStatusRequest(e,r,{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(r,o,n){e.logger.debug?.(`[beeos_list_files] toolCallId=${r} params=${JSON.stringify(o)}`);const s=await executeListFiles(o,t,agentKeys,n);return a({component:"file-list-tool",domain:"tool",name:s.ok?"list_files.success":"list_files.failure",severity:s.ok?"info":"warn",payload:{toolCallId:r,ok:s.ok,fileCount:s.files?.length??0,error:s.error}}),s}}),e.registerTool({name:BEEOS_FETCH_FILE_TOOL_NAME,description:BEEOS_FETCH_FILE_TOOL_DESCRIPTION,parameters:BEEOS_FETCH_FILE_TOOL_SCHEMA,async execute(r,o,n){e.logger.debug?.(`[beeos_fetch_file] toolCallId=${r} params=${JSON.stringify(o)}`);const s=await executeFetchFile(o,t,agentKeys,{signal:n,resolvePath:e.resolvePath});return a({component:"file-fetch-tool",domain:"tool",name:s.ok?"fetch_file.success":"fetch_file.failure",severity:s.ok?"info":"warn",payload:{toolCallId:r,ok:s.ok,path:s.path,size:s.size,error:s.error}}),s}})}function registerServices(e,r,o){const{cfg:t,writeObs:a,thirdPartyChannels:n}=r,s=agentKeys?(e,r)=>agentFetch(e,agentKeys,r):void 0,i=agentKeys?(e,r)=>agentBoundFetch(e,agentKeys,r):void 0,l=agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):void 0,c=join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos");sharedMcpToolBridge??=new InProcessMcpToolBridge,!sharedMcpCredentialVault&&agentKeys&&l&&(sharedMcpCredentialVault=new LocalEncryptedCredentialReferenceVault(join(c,"mcp-credential-vault.json"),l,agentKeys.privateKey)),sharedMcpToolBridge.registerTools(e),!sharedRuntimeAuthority&&agentKeys&&t.agentGatewayUrl&&s&&i&&(sharedRuntimeAuthority=new RuntimeProductionAuthority({stateDirectory:c,agentGatewayUrl:t.agentGatewayUrl,agentFetch:s,boundAgentFetch:i,keys:agentKeys,logger:e.logger})),sharedSessionMutationCoordinator??=new SessionMutationCoordinator(sharedSessionWriteGate,new OpenClawSessionWriteQuiescence(e.logger,{agentId:t.gateway.agentId||"main"}),e.logger,{onRecoveryRequired:r=>{scheduleProcessRuntimeRestart(r,e.logger)}});const g=sharedSessionMutationCoordinator;sharedAcpServer||(sharedAcpServer=new ACPServer(t,e.logger,g,a,s,l,n,sharedTerminalManager,sharedRuntimeAuthority,void 0,sharedMcpToolBridge,sharedMcpCredentialVault,i));const d=sharedAcpServer;o.current=d;const u=sharedPromptRunStore,{enabled:p,url:m,principalId:f,a2aEnabled:y}=t.messageBus;let w;t.agentGatewayUrl&&agentKeys&&(w=createTokenProvider({agentGatewayUrl:t.agentGatewayUrl,identity:agentKeyPairToIdentity(agentKeys)}));const h=w?new MessageServiceRuntimeAuthenticatedFetch({tokenProvider:w,...t.messageBus.serviceUrl?{configuredServiceUrl:t.messageBus.serviceUrl}:{}}):void 0;e.registerService({id:"beeos-acp",async start(){const r=Date.now(),o=_pluginRegisteredAt?r-_pluginRegisteredAt:-1;e.logger.info?.(`[beeos-claw] beeos-acp service start() begin, sincePluginRegister=${o}ms, bridge.url=${t.bridge.url||"(empty)"}`);const a=await createRuntimeServiceProductionCandidate({...t.agentGatewayUrl&&s?{authorizationAdapter:new AgentGatewayGrantJwksReadinessAdapter({agentGatewayUrl:t.agentGatewayUrl})}:{},...h&&t.agentGatewayUrl&&i&&process.env.MESSAGE_RUNTIME_DELIVERY_KEY?.trim()?{transportAdapter:new RuntimeServiceTransportReadinessAdapter({agentGatewayUrl:t.agentGatewayUrl,messageService:h,boundAgentFetch:i,scopedDeliveryKey:process.env.MESSAGE_RUNTIME_DELIVERY_KEY.trim()})}:{},commandJournal:sharedRuntimeAuthority?.journal??new FileRuntimeCommandJournal(join(c,"runtime-command-journal.json")),poisonJournal:new FileRuntimePoisonDeliveryJournal(join(c,"runtime-poison-deliveries.json")),onConsumerDeliveryError:r=>e.logger.error?.("[runtime-service] delivery worker failed before acknowledgement; delivery retained for retry",r),onConsumerRequestFailure:r=>e.logger.error?.(`[runtime-service] request failed operationId=${r.operationId} method=${r.method}: ${r.error instanceof Error?r.error.stack??r.error.message:String(r.error)}`)});if(sharedRuntimeAuthority?.setRuntimeServiceMethods("ready"===a.status?a.serviceMethods:[]),sharedRuntimeAuthority?.setRuntimeResourceBindingContractRevision("ready"===a.status?a.resourceBindingContractRevision:void 0),d.setRuntimeRpcConsumerFactory("ready"===a.status?a.factory:void 0),"unavailable"===a.status?e.logger.warn?.("[runtime-service] capability fenced: %s",a.blockers.map(e=>e.id).join(",")):a.capabilityBlockers.length&&e.logger.warn?.("[runtime-service] method capability fenced: %s",a.capabilityBlockers.map(e=>e.method).join(",")),!t.bridge.url&&t.agentGatewayUrl&&s){const r=Date.now(),o=await discoverBridgeFromGW(t.agentGatewayUrl,s);if(e.logger.info?.(`[beeos-claw] bridge discovery took ${Date.now()-r}ms result=${o?.url??"null"}`),o)t.bridge.url=o.url,writeDiscoveryCache({bridge:{url:o.url,discoveredAt:Date.now()}}),e.logger.info?.(`[beeos-claw] Bridge URL discovered from Agent Gateway: ${o.url}`);else{const r=readDiscoveryCache();r?.bridge?.url?(t.bridge.url=r.bridge.url,e.logger.info?.(`[beeos-claw] Bridge URL from cache (GW unreachable): ${r.bridge.url}`)):e.logger.debug?.("[beeos-claw] Bridge URL not available (no GW, no cache) — bridge disabled")}}await d.start(t.bridge.url||void 0),e.logger.info?.(`[beeos-claw] ACP Server started (total service start: ${Date.now()-r}ms)`)},async stop(){await d.stop(),e.logger.info?.("[beeos-claw] ACP Server stopped")}});const b=p&&(w||t.messageBus.serviceUrl),_=b?d.getMessageClient():null;if(b&&_){_.on("disconnect",r=>{e.logger.warn?.("[beeos-claw] message bus disconnected (code=%s, reason=%s) — SDK will auto-reconnect",r?.code??"?",r?.reason??"?")}),_.on("error",r=>{e.logger.warn?.(`[beeos-claw] message bus error: ${r instanceof Error?r.message:String(r)}`)});let r=f||process.env.BEEOS_PRINCIPAL_ID||"";const o={serviceUrl:t.messageBus.serviceUrl||"https://msg.invalid",centrifugoUrl:m,tokenProvider:w,logger:e.logger},n=new Map;let s,i=!1;const l=async()=>{if(r||!w)return r;try{const o=await w("");r=o.identityId||"",r&&e.logger.info?.("[beeos-claw] Message bus principal resolved from Agent Gateway token: %s",r)}catch(r){e.logger.warn?.("[beeos-claw] Failed to resolve message bus principal from Agent Gateway token: %s",r instanceof Error?r.message:String(r))}return r},c=()=>{if(r)return[{localAgentId:d.getLocalAgentId(),principalId:r}];const e=d.getPlatformAgentMappings().map(({localAgentId:e,platformAgent:r})=>({localAgentId:e,principalId:r.instanceId||""})).filter(e=>e.principalId);if(e.length>0)return e;const o=d.getDefaultPlatformAgent()?.instanceId||"";return o?[{localAgentId:d.getLocalAgentId(),principalId:o}]:[]},p=e=>({getLocalAgentId:()=>e.localAgentId,getDefaultPlatformAgent:()=>({instanceId:e.principalId}),ensureImInboundSession:(e,r,o)=>{d.ensureImInboundSession(e,r,o)},chatAbort:(e,r)=>d.chatAbort(e,r)}),y=async(r,s)=>{if(n.has(r.principalId))return;const i=s?_:new MessageClient(o);s&&d.setMessageClient(o,r.principalId);const l=createImInboundHandler({runtime:e.runtime,pluginConfig:e.pluginConfig,client:i,localServer:p(r),promptRunStore:u,sessionMutationCoordinator:g,logger:e.logger,writeObs:a,principalIdOverride:r.principalId,promptTimeoutMs:t.promptTimeoutMs,activeRunAuthority:d.getActiveRunAuthority()}),c=new MessageServiceRecoveryScanner({client:i,imInbound:l,logger:e.logger});i.on("connect",()=>{c.trigger("reconnect"),e.logger.info?.("[beeos-claw] MessageClient connected (localAgentId=%s, principalId=%s, via=%s)",r.localAgentId,r.principalId,w?"gateway":"direct")}),i.on("message",r=>{(async()=>{"chat_message"===r.type?await l.onChatMessage(r):"chat_cancel"===r.type&&await l.onChatCancel(r)})().catch(r=>{e.logger.warn?.(`[beeos-claw] message handler failed: ${r instanceof Error?r.message:String(r)}`)})}),n.set(r.principalId,{client:i,scanner:c,primary:s});try{await i.connect({identityId:r.principalId}),c.start(r.principalId)}catch(e){throw n.delete(r.principalId),c.stop(),s||await i.disconnect().catch(()=>{}),e}},h=async r=>{if(i)if(s)await s;else{s=(async()=>{const o=c();if(0===o.length)return void e.logger.warn?.("[beeos-claw] No principalId resolved, message bus disabled");const t=new Set(o.map(e=>e.principalId));let a=!1;for(const[o,s]of n)t.has(o)||(s.primary&&(a=!0),s.scanner.stop(),await s.client.disconnect(),n.delete(o),e.logger.info?.("[beeos-claw] MessageClient disconnected stale identity principalId=%s reason=%s",o,r));if(a&&n.size>0)for(const[o,t]of n)t.scanner.stop(),await t.client.disconnect(),n.delete(o),e.logger.info?.("[beeos-claw] MessageClient disconnected for primary rebalance principalId=%s reason=%s",o,r);let s=[...n.values()].some(e=>e.primary);for(const e of o){const r=!s;await y(e,r),r&&(s=!0)}})();try{await s}finally{s=void 0}}};e.registerService({id:"beeos-message-bus",async start(){if(i=!0,d.onAgentMappingUpdated(()=>{h("agent-mapping-updated").catch(r=>{e.logger.error?.("[beeos-claw] Message bus mapping sync failed:",r)})}),await l(),!r)try{await d.waitForAgentMapping()}catch(r){e.logger.warn?.("[beeos-claw] Failed to resolve principalId from agent sync:",r)}await h("startup")},async stop(){i=!1;const r=[...n.values()];n.clear();for(const e of r)e.scanner.stop(),await e.client.disconnect();e.logger.info?.("[beeos-claw] MessageClient disconnected")}})}else p?e.logger.warn?.("[beeos-claw] Message bus enabled but MESSAGE_SERVICE_URL not set — disabled"):e.logger.info?.("[beeos-claw] Message bus disabled (enabled=%s)",p);if(!1!==y&&t.agentGatewayUrl){const r=e=>d.getGatewaySessionKeyForToolCall(e),o=e=>d.getMentionsForToolCall(e),a=(e,r)=>{d.broadcastNotification(e,r)},n=new TaskHandleRegistry;registerA2ATools(e,{agentGatewayUrl:t.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},n,r,o,a,()=>d?.getDefaultPlatformAgent()?.instanceId),e.logger.info?.("[beeos-claw] A2A tools registered (beeos_discover_agents, beeos_call_agent) via REST")}if(t.canvasEnabled){sharedCanvasState||(sharedCanvasState=new CanvasStateManager);const r=sharedCanvasState;if(t.agentGatewayUrl&&agentKeys){let o;r.setLogger(e.logger);{const e=t.agentGatewayUrl,r=agentKeys;o=async()=>{const o=await agentFetch(`${e}/api/v1/canvas/token`,r,{method:"POST"});if(!o.ok){const e=await o.text().catch(()=>"");throw new Error(`Canvas token returned ${o.status}: ${e}`)}return o.json()}}const a=a=>{const n=new CanvasRelayClient({canvasRelayUrl:t.canvasRelayUrl,agentKeys:agentKeys,agentId:a.id,displayName:a.displayName,avatarUrl:a.avatarUrl,tokenProvider:o,onReconnect:()=>d.onRelayReconnected(),logger:e.logger});n.connect(),r.setRelayClient(n),r.setAgentId(a.id),d.wireRelayActionHandler(),d.flushPendingCanvasLinks(),e.logger.info?.(`[beeos-claw] Canvas Relay multiplexed client connected (agentId=${a.id}, name=${a.name??"none"})`)};e.registerService({id:"beeos-canvas-relay",async start(){try{await d.waitForAgentMapping();const r=d.getDefaultPlatformAgent();if(r?.id)return void a(r);e.logger.info?.("[beeos-claw] Canvas Relay deferred: waiting for late agent sync..."),d.onceAgentMappingReady(()=>{const r=d.getDefaultPlatformAgent();if(r?.id)try{a(r)}catch(r){e.logger.warn?.(`[beeos-claw] Deferred Canvas Relay start failed: ${r}`)}else e.logger.warn?.("[beeos-claw] Agent mapping ready but no platform agent; Canvas Relay stays disabled")})}catch(r){e.logger.warn?.(`[beeos-claw] Failed to start Canvas Relay client: ${r}`)}},stop(){r.getRelayClient()?.disconnect()}}),e.logger.info?.("[beeos-claw] Canvas Relay configured")}else e.logger.warn?.("[beeos-claw] Canvas Relay unavailable: agentGatewayUrl or agentKeys missing");const o={stateManager:r,logger:e.logger,writeObs:e=>a(e),isCanvasActive:e=>!!e&&d.isCanvasActiveForGatewayKey(e),getActiveSessionId:e=>d.getSessionIdForToolCall(e),getPlatformAgentId:e=>d.getPlatformAgentIdForToolCall(e),getGatewaySessionKey:e=>d.getGatewaySessionKeyForToolCall(e),waitForAgentSync:()=>d.waitForAgentMapping()},s=new FormSchemaRegistry,i=buildCronSchemas(n,{capMs:t.cronMaxDurationCapMs});s.register(i.create),s.register(i.update);const l=new ResourceDescriptorRegistry;l.register(CRON_RESOURCE);const c=e=>async(r,o,t)=>(d.preMapToolCallSession(r),e(r,o));e.registerTool({name:RENDER_UI_TOOL_NAME,description:RENDER_UI_TOOL_DESCRIPTION,parameters:RENDER_UI_TOOL_SCHEMA,execute:c(createRenderUiHandler(o))}),e.registerTool({name:CLOSE_UI_TOOL_NAME,description:CLOSE_UI_TOOL_DESCRIPTION,parameters:CLOSE_UI_TOOL_SCHEMA,execute:c(createCloseUiHandler(o))}),d.setCanvasState(r,{schemaRegistry:s,descriptorRegistry:l});const g=d.getFormBuilder();g&&e.registerTool({name:RENDER_FORM_TOOL_NAME,description:buildRenderFormDescription(s),parameters:RENDER_FORM_TOOL_SCHEMA,execute:c(createRenderFormHandler({registry:s,builder:g,logger:e.logger}))});const u=d.getResourceRenderer();if(u){generateActionTools(l,e,async(e,r)=>d.gatewayRequest(e,r),u,e.logger)}const p=`\n\nTool selection guidance:\n- browser tool: Use when the user wants to OPEN, VISIT, NAVIGATE to a webpage, take SCREENSHOTS, interact with elements (click, type), or visually inspect pages.\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", "screenshot", or mentions seeing/viewing a webpage, ALWAYS prefer the browser tool.`,m=new Set([RENDER_UI_TOOL_NAME,CLOSE_UI_TOOL_NAME,RENDER_FORM_TOOL_NAME]),f=256,y=new Map;if("function"==typeof e.on){try{e.on("before_prompt_build",(e,r)=>{if(r?.sessionKey){if(y.size>=f&&!y.has(r.sessionKey)){const e=y.keys().next().value;void 0!==e&&y.delete(e)}y.set(r.sessionKey,r.channelId)}if(isThirdPartyChannel(n,{sessionKey:r?.sessionKey,channelId:r?.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(!r?.sessionKey)return{appendSystemContext:p};return d.isCanvasActiveForGatewayKey(r.sessionKey)?{appendSystemContext:CANVAS_SYSTEM_PROMPT+p}:{appendSystemContext:"Canvas is closed by user. Do NOT use render_ui, close_ui, or render_form. Respond with plain text only."+p}}),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,r)=>{if(r?.sessionKey&&d.setPendingToolCallSessionKey(r.sessionKey),!m.has(e.toolName))return;const o=r?.sessionKey?y.get(r.sessionKey):void 0;return isThirdPartyChannel(n,{sessionKey:r?.sessionKey,channelId:o})?{block:!0,blockReason:"Canvas tools are not available in this channel. Respond with plain text only."}:r?.sessionKey?d.isCanvasActiveForGatewayKey(r.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(t.bridge.shell.enabled&&!0===t.legacyTerminalWsEnabled){const r=createTerminalWebSocketService({config:{port:t.terminalWsPort,token:agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):"",allowedOrigins:t.terminalWsAllowedOrigins},terminalMgr:sharedTerminalManager,logger:e.logger,writeObs:a});e.registerService({id:"beeos-terminal-ws",start(){r.start(),e.logger.info?.(`[beeos-claw] Terminal WS started on port ${t.terminalWsPort}`)},stop(){r.stop(),e.logger.info?.("[beeos-claw] Terminal WS stopped")}})}else t.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(r.openclawConfig){const o=mirrorOpenClawConfig(r.openclawConfig);o.copied?e.logger.info?.(`[beeos-claw] mirrored openclaw config to ${o.destinationPath}`):e.logger.debug?.(`[beeos-claw] openclaw config mirror skipped: ${o.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 r=initializePlugin(e);if(!r)return;const o={current:null};installUnhandledRejectionObserver(e,r.writeObs,o),registerUploadTools(e,r,o),registerArouterMediaProviders(e),registerServices(e,r,o),_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{MessageClient}from"@beeos-ai/message-sdk";import{hasRuntimeReplyApi}from"./runtime/reply-router.js";import{PromptRunStore}from"./runtime/prompt-run-store.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{MessageServiceRecoveryScanner}from"./im-recovery-scanner.js";import{createTokenProvider}from"@beeos-ai/message-sdk/auth";import{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_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{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{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{InProcessMcpToolBridge}from"./acp-gateway/mcp-tool-bridge.js";import{LocalEncryptedCredentialReferenceVault}from"./acp-gateway/mcp-credential-reference-port.js";import{AgentGatewayGrantJwksReadinessAdapter,RuntimeServiceTransportReadinessAdapter,createRuntimeServiceProductionCandidate}from"./runtime/runtime-service-production-candidate.js";import{MessageServiceRuntimeAuthenticatedFetch}from"./runtime/message-service-runtime-authenticated-fetch.js";const PLUGIN_VERSION=resolvePluginVersion();let agentKeys=null,sharedCanvasState=null,sharedAcpServer=null;const sharedSessionWriteGate=new SessionWriteGate,sharedPromptRunStore=new PromptRunStore;let sharedSessionMutationCoordinator,sharedRuntimeAuthority,sharedMcpToolBridge,sharedMcpCredentialVault;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 r=>{const{createPrivateKey:o,sign:t}=await import("node:crypto"),a=o({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(t(null,Buffer.from(r),a))}}}let unhandledRejectionObserverInstalled=!1;function installUnhandledRejectionObserver(e,r,o){unhandledRejectionObserverInstalled||(unhandledRejectionObserverInstalled=!0,process.on("unhandledRejection",o=>{const t=o instanceof Error?o.message:String(o),a=o instanceof Error?o.stack:void 0;e.logger.error?.(`[beeos-claw] unhandledRejection: ${t}${a?`\n${a}`:""}`),r({component:"plugin",domain:"fatal",name:"unhandled_rejection",severity:"error",summary:t,error:{code:"unhandled_rejection",message:t,...a?{stack:a}:{}}})}),process.on("uncaughtException",o=>{const t=o instanceof Error?o.message:String(o);e.logger.error?.("[beeos-claw] uncaughtException:",o),r({component:"plugin",domain:"fatal",name:"uncaught_exception",severity:"error",summary:t,error:{code:"uncaught_exception",message:t}})}))}function initializePlugin(e){let r,o;try{r=e.runtime?.config?.loadConfig?.()}catch{}try{o=resolveConfigWithMeta({pluginConfig:e.pluginConfig??{},openclawConfig:r})}catch(r){return e.logger.error?.("[beeos-claw] Failed to resolve config:",r),null}const t=o.config,a=buildThirdPartyChannelSet(r);a.size>0&&e.logger.info?.(`[beeos-claw] Third-party channels detected: ${[...a].join(", ")}`);const n=t.log.enabled?createObsEventWriter({dir:t.log.dir,redaction:{gatewayToken:t.gateway.token},logger:e.logger}):()=>{};n({component:"plugin",domain:"lifecycle",name:"lifecycle.startup",severity:"info",payload:{pluginVersion:PLUGIN_VERSION,pid:process.pid,nodeVersion:process.version,cwd:process.cwd()}}),n({component:"plugin",domain:"config",name:"config.resolved",severity:"info",payload:{config:t,sources:o.sources,openclawConfigPath:o.openclawConfigPath}});for(const r of o.validation)n({component:"plugin",domain:"config",name:"config.validation",severity:r.severity,summary:r.message,error:{code:r.code,message:r.message,nextSteps:r.nextSteps}}),"error"===r.severity?e.logger.error?.(`[beeos-claw] ${r.message}`):e.logger.warn?.(`[beeos-claw] ${r.message}`);if(o.validation.some(e=>"error"===e.severity)&&e.logger.error?.("[beeos-claw] Plugin has configuration errors — some features may not work."),t.keyFile)try{agentKeys=loadAgentKeyPair(t.keyFile);const r=Buffer.from(agentKeys.publicKey).toString("base64");e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (publicKey: ${r})`)}catch(r){e.logger.warn?.(`[beeos-claw] Failed to load agent key pair: ${r}`),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&&t.agentGatewayUrl&&(setUploadDeps({gatewayUrl:t.agentGatewayUrl,keys:agentKeys,logger:e.logger}),registerLocalFileExtractor(browserScreenshotExtractor),e.logger.info?.("[beeos-claw] Upload deps + browser screenshot extractor registered")),{cfg:t,writeObs:n,thirdPartyChannels:a,openclawConfig:r}}function registerUploadTools(e,r,o){const{cfg:t,writeObs:a}=r;cleanupUploadToolResultCache().catch(r=>{e.logger.warn?.(`[beeos_upload_file] cache cleanup failed on startup: ${String(r)}`)}),e.registerTool({name:BEEOS_UPLOAD_TOOL_NAME,description:BEEOS_UPLOAD_TOOL_DESCRIPTION,parameters:BEEOS_UPLOAD_TOOL_SCHEMA,async execute(r,o,n){e.logger.debug?.(`[beeos_upload_file] toolCallId=${r} params=${JSON.stringify(o)}`);const s=await executeUpload(o,t,agentKeys,n);return e.logger.debug?.(`[beeos_upload_file] toolCallId=${r} ok=${s.ok}`),a({component:"upload-tool",domain:"tool",name:s.ok?"upload.success":"upload.failure",severity:s.ok?"info":"warn",payload:{toolCallId:r,ok:s.ok,fileCount:s.files?.length??0,error:s.error}}),(async o=>{try{await writeUploadToolResult(r,BEEOS_UPLOAD_TOOL_NAME,o),await cleanupUploadToolResultCache()}catch(o){e.logger.warn?.(`[beeos_upload_file] persist cache failed toolCallId=${r}: ${String(o)}`)}return o})(s)}}),e.registerHttpRoute({path:"/beeos/files/stage",auth:"gateway",async handler(e,o){await handleFileStage(e,o,r.cfg)}}),e.registerHttpRoute({path:"/beeos/status",auth:"plugin",handler(e,r){handleStatusRequest(e,r,{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(r,o,n){e.logger.debug?.(`[beeos_list_files] toolCallId=${r} params=${JSON.stringify(o)}`);const s=await executeListFiles(o,t,agentKeys,n);return a({component:"file-list-tool",domain:"tool",name:s.ok?"list_files.success":"list_files.failure",severity:s.ok?"info":"warn",payload:{toolCallId:r,ok:s.ok,fileCount:s.files?.length??0,error:s.error}}),s}}),e.registerTool({name:BEEOS_FETCH_FILE_TOOL_NAME,description:BEEOS_FETCH_FILE_TOOL_DESCRIPTION,parameters:BEEOS_FETCH_FILE_TOOL_SCHEMA,async execute(r,o,n){e.logger.debug?.(`[beeos_fetch_file] toolCallId=${r} params=${JSON.stringify(o)}`);const s=await executeFetchFile(o,t,agentKeys,{signal:n,resolvePath:e.resolvePath});return a({component:"file-fetch-tool",domain:"tool",name:s.ok?"fetch_file.success":"fetch_file.failure",severity:s.ok?"info":"warn",payload:{toolCallId:r,ok:s.ok,path:s.path,size:s.size,error:s.error}}),s}})}function registerServices(e,r,o){const{cfg:t,writeObs:a,thirdPartyChannels:n}=r,s=agentKeys?(e,r)=>agentFetch(e,agentKeys,r):void 0,i=agentKeys?(e,r)=>agentBoundFetch(e,agentKeys,r):void 0,l=agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):void 0,c=join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos");sharedMcpToolBridge??=new InProcessMcpToolBridge,!sharedMcpCredentialVault&&agentKeys&&l&&(sharedMcpCredentialVault=new LocalEncryptedCredentialReferenceVault(join(c,"mcp-credential-vault.json"),l,agentKeys.privateKey)),sharedMcpToolBridge.registerTools(e),!sharedRuntimeAuthority&&agentKeys&&t.agentGatewayUrl&&s&&i&&(sharedRuntimeAuthority=new RuntimeProductionAuthority({stateDirectory:c,agentGatewayUrl:t.agentGatewayUrl,agentFetch:s,boundAgentFetch:i,keys:agentKeys,logger:e.logger})),sharedSessionMutationCoordinator??=new SessionMutationCoordinator(sharedSessionWriteGate,new OpenClawSessionWriteQuiescence(e.logger,{agentId:t.gateway.agentId||"main"}),e.logger,{onRecoveryRequired:r=>{scheduleProcessRuntimeRestart(r,e.logger)}});const g=sharedSessionMutationCoordinator;sharedAcpServer||(sharedAcpServer=new ACPServer(t,e.logger,g,a,s,l,n,sharedTerminalManager,sharedRuntimeAuthority,void 0,sharedMcpToolBridge,sharedMcpCredentialVault,i));const d=sharedAcpServer;o.current=d;const u=sharedPromptRunStore,{enabled:p,url:m,principalId:f,a2aEnabled:y}=t.messageBus;let w;t.agentGatewayUrl&&agentKeys&&(w=createTokenProvider({agentGatewayUrl:t.agentGatewayUrl,identity:agentKeyPairToIdentity(agentKeys)}));const h=w?new MessageServiceRuntimeAuthenticatedFetch({tokenProvider:w,...t.messageBus.serviceUrl?{configuredServiceUrl:t.messageBus.serviceUrl}:{}}):void 0;e.registerService({id:"beeos-acp",async start(){const r=Date.now(),o=_pluginRegisteredAt?r-_pluginRegisteredAt:-1;e.logger.info?.(`[beeos-claw] beeos-acp service start() begin, sincePluginRegister=${o}ms, bridge.url=${t.bridge.url||"(empty)"}`);const a=await createRuntimeServiceProductionCandidate({...t.agentGatewayUrl&&s?{authorizationAdapter:new AgentGatewayGrantJwksReadinessAdapter({agentGatewayUrl:t.agentGatewayUrl})}:{},...h&&t.agentGatewayUrl&&i&&process.env.MESSAGE_RUNTIME_DELIVERY_KEY?.trim()?{transportAdapter:new RuntimeServiceTransportReadinessAdapter({agentGatewayUrl:t.agentGatewayUrl,messageService:h,boundAgentFetch:i,scopedDeliveryKey:process.env.MESSAGE_RUNTIME_DELIVERY_KEY.trim()})}:{},commandJournal:sharedRuntimeAuthority?.journal??new FileRuntimeCommandJournal(join(c,"runtime-command-journal.json")),poisonJournal:new FileRuntimePoisonDeliveryJournal(join(c,"runtime-poison-deliveries.json")),onConsumerDeliveryError:r=>{const o=r instanceof Error?`${r.name}: ${r.message}${"status"in r?` status=${r.status}`:""}${"code"in r?` code=${r.code}`:""}\n${r.stack??""}`:String(r);e.logger.error?.(`[runtime-service] delivery worker failed before acknowledgement; delivery retained for retry: ${o}`)},onConsumerRequestFailure:r=>e.logger.error?.(`[runtime-service] request failed operationId=${r.operationId} method=${r.method}: ${r.error instanceof Error?r.error.stack??r.error.message:String(r.error)}`)});if(sharedRuntimeAuthority?.setRuntimeServiceMethods("ready"===a.status?a.serviceMethods:[]),sharedRuntimeAuthority?.setRuntimeResourceBindingContractRevision("ready"===a.status?a.resourceBindingContractRevision:void 0),d.setRuntimeRpcConsumerFactory("ready"===a.status?a.factory:void 0),"unavailable"===a.status?e.logger.warn?.("[runtime-service] capability fenced: %s",a.blockers.map(e=>e.id).join(",")):a.capabilityBlockers.length&&e.logger.warn?.("[runtime-service] method capability fenced: %s",a.capabilityBlockers.map(e=>e.method).join(",")),!t.bridge.url&&t.agentGatewayUrl&&s){const r=Date.now(),o=await discoverBridgeFromGW(t.agentGatewayUrl,s);if(e.logger.info?.(`[beeos-claw] bridge discovery took ${Date.now()-r}ms result=${o?.url??"null"}`),o)t.bridge.url=o.url,writeDiscoveryCache({bridge:{url:o.url,discoveredAt:Date.now()}}),e.logger.info?.(`[beeos-claw] Bridge URL discovered from Agent Gateway: ${o.url}`);else{const r=readDiscoveryCache();r?.bridge?.url?(t.bridge.url=r.bridge.url,e.logger.info?.(`[beeos-claw] Bridge URL from cache (GW unreachable): ${r.bridge.url}`)):e.logger.debug?.("[beeos-claw] Bridge URL not available (no GW, no cache) — bridge disabled")}}await d.start(t.bridge.url||void 0),e.logger.info?.(`[beeos-claw] ACP Server started (total service start: ${Date.now()-r}ms)`)},async stop(){await d.stop(),e.logger.info?.("[beeos-claw] ACP Server stopped")}});const b=p&&(w||t.messageBus.serviceUrl),_=b?d.getMessageClient():null;if(b&&_){_.on("disconnect",r=>{e.logger.warn?.("[beeos-claw] message bus disconnected (code=%s, reason=%s) — SDK will auto-reconnect",r?.code??"?",r?.reason??"?")}),_.on("error",r=>{e.logger.warn?.(`[beeos-claw] message bus error: ${r instanceof Error?r.message:String(r)}`)});let r=f||process.env.BEEOS_PRINCIPAL_ID||"";const o={serviceUrl:t.messageBus.serviceUrl||"https://msg.invalid",centrifugoUrl:m,tokenProvider:w,logger:e.logger},n=new Map;let s,i=!1;const l=async()=>{if(r||!w)return r;try{const o=await w("");r=o.identityId||"",r&&e.logger.info?.("[beeos-claw] Message bus principal resolved from Agent Gateway token: %s",r)}catch(r){e.logger.warn?.("[beeos-claw] Failed to resolve message bus principal from Agent Gateway token: %s",r instanceof Error?r.message:String(r))}return r},c=()=>{if(r)return[{localAgentId:d.getLocalAgentId(),principalId:r}];const e=d.getPlatformAgentMappings().map(({localAgentId:e,platformAgent:r})=>({localAgentId:e,principalId:r.instanceId||""})).filter(e=>e.principalId);if(e.length>0)return e;const o=d.getDefaultPlatformAgent()?.instanceId||"";return o?[{localAgentId:d.getLocalAgentId(),principalId:o}]:[]},p=e=>({getLocalAgentId:()=>e.localAgentId,getDefaultPlatformAgent:()=>({instanceId:e.principalId}),ensureImInboundSession:(e,r,o)=>{d.ensureImInboundSession(e,r,o)},chatAbort:(e,r)=>d.chatAbort(e,r)}),y=async(r,s)=>{if(n.has(r.principalId))return;const i=s?_:new MessageClient(o);s&&d.setMessageClient(o,r.principalId);const l=createImInboundHandler({runtime:e.runtime,pluginConfig:e.pluginConfig,client:i,localServer:p(r),promptRunStore:u,sessionMutationCoordinator:g,logger:e.logger,writeObs:a,principalIdOverride:r.principalId,promptTimeoutMs:t.promptTimeoutMs,activeRunAuthority:d.getActiveRunAuthority()}),c=new MessageServiceRecoveryScanner({client:i,imInbound:l,logger:e.logger});i.on("connect",()=>{c.trigger("reconnect"),e.logger.info?.("[beeos-claw] MessageClient connected (localAgentId=%s, principalId=%s, via=%s)",r.localAgentId,r.principalId,w?"gateway":"direct")}),i.on("message",r=>{(async()=>{"chat_message"===r.type?await l.onChatMessage(r):"chat_cancel"===r.type&&await l.onChatCancel(r)})().catch(r=>{e.logger.warn?.(`[beeos-claw] message handler failed: ${r instanceof Error?r.message:String(r)}`)})}),n.set(r.principalId,{client:i,scanner:c,primary:s});try{await i.connect({identityId:r.principalId}),c.start(r.principalId)}catch(e){throw n.delete(r.principalId),c.stop(),s||await i.disconnect().catch(()=>{}),e}},h=async r=>{if(i)if(s)await s;else{s=(async()=>{const o=c();if(0===o.length)return void e.logger.warn?.("[beeos-claw] No principalId resolved, message bus disabled");const t=new Set(o.map(e=>e.principalId));let a=!1;for(const[o,s]of n)t.has(o)||(s.primary&&(a=!0),s.scanner.stop(),await s.client.disconnect(),n.delete(o),e.logger.info?.("[beeos-claw] MessageClient disconnected stale identity principalId=%s reason=%s",o,r));if(a&&n.size>0)for(const[o,t]of n)t.scanner.stop(),await t.client.disconnect(),n.delete(o),e.logger.info?.("[beeos-claw] MessageClient disconnected for primary rebalance principalId=%s reason=%s",o,r);let s=[...n.values()].some(e=>e.primary);for(const e of o){const r=!s;await y(e,r),r&&(s=!0)}})();try{await s}finally{s=void 0}}};e.registerService({id:"beeos-message-bus",async start(){if(i=!0,d.onAgentMappingUpdated(()=>{h("agent-mapping-updated").catch(r=>{e.logger.error?.("[beeos-claw] Message bus mapping sync failed:",r)})}),await l(),!r)try{await d.waitForAgentMapping()}catch(r){e.logger.warn?.("[beeos-claw] Failed to resolve principalId from agent sync:",r)}await h("startup")},async stop(){i=!1;const r=[...n.values()];n.clear();for(const e of r)e.scanner.stop(),await e.client.disconnect();e.logger.info?.("[beeos-claw] MessageClient disconnected")}})}else p?e.logger.warn?.("[beeos-claw] Message bus enabled but MESSAGE_SERVICE_URL not set — disabled"):e.logger.info?.("[beeos-claw] Message bus disabled (enabled=%s)",p);if(!1!==y&&t.agentGatewayUrl){const r=e=>d.getGatewaySessionKeyForToolCall(e),o=e=>d.getMentionsForToolCall(e),a=(e,r)=>{d.broadcastNotification(e,r)},n=new TaskHandleRegistry;registerA2ATools(e,{agentGatewayUrl:t.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},n,r,o,a,()=>d?.getDefaultPlatformAgent()?.instanceId),e.logger.info?.("[beeos-claw] A2A tools registered (beeos_discover_agents, beeos_call_agent) via REST")}if(t.canvasEnabled){sharedCanvasState||(sharedCanvasState=new CanvasStateManager);const r=sharedCanvasState;if(t.agentGatewayUrl&&agentKeys){let o;r.setLogger(e.logger);{const e=t.agentGatewayUrl,r=agentKeys;o=async()=>{const o=await agentFetch(`${e}/api/v1/canvas/token`,r,{method:"POST"});if(!o.ok){const e=await o.text().catch(()=>"");throw new Error(`Canvas token returned ${o.status}: ${e}`)}return o.json()}}const a=a=>{const n=new CanvasRelayClient({canvasRelayUrl:t.canvasRelayUrl,agentKeys:agentKeys,agentId:a.id,displayName:a.displayName,avatarUrl:a.avatarUrl,tokenProvider:o,onReconnect:()=>d.onRelayReconnected(),logger:e.logger});n.connect(),r.setRelayClient(n),r.setAgentId(a.id),d.wireRelayActionHandler(),d.flushPendingCanvasLinks(),e.logger.info?.(`[beeos-claw] Canvas Relay multiplexed client connected (agentId=${a.id}, name=${a.name??"none"})`)};e.registerService({id:"beeos-canvas-relay",async start(){try{await d.waitForAgentMapping();const r=d.getDefaultPlatformAgent();if(r?.id)return void a(r);e.logger.info?.("[beeos-claw] Canvas Relay deferred: waiting for late agent sync..."),d.onceAgentMappingReady(()=>{const r=d.getDefaultPlatformAgent();if(r?.id)try{a(r)}catch(r){e.logger.warn?.(`[beeos-claw] Deferred Canvas Relay start failed: ${r}`)}else e.logger.warn?.("[beeos-claw] Agent mapping ready but no platform agent; Canvas Relay stays disabled")})}catch(r){e.logger.warn?.(`[beeos-claw] Failed to start Canvas Relay client: ${r}`)}},stop(){r.getRelayClient()?.disconnect()}}),e.logger.info?.("[beeos-claw] Canvas Relay configured")}else e.logger.warn?.("[beeos-claw] Canvas Relay unavailable: agentGatewayUrl or agentKeys missing");const o={stateManager:r,logger:e.logger,writeObs:e=>a(e),isCanvasActive:e=>!!e&&d.isCanvasActiveForGatewayKey(e),getActiveSessionId:e=>d.getSessionIdForToolCall(e),getPlatformAgentId:e=>d.getPlatformAgentIdForToolCall(e),getGatewaySessionKey:e=>d.getGatewaySessionKeyForToolCall(e),waitForAgentSync:()=>d.waitForAgentMapping()},s=new FormSchemaRegistry,i=buildCronSchemas(n,{capMs:t.cronMaxDurationCapMs});s.register(i.create),s.register(i.update);const l=new ResourceDescriptorRegistry;l.register(CRON_RESOURCE);const c=e=>async(r,o,t)=>(d.preMapToolCallSession(r),e(r,o));e.registerTool({name:RENDER_UI_TOOL_NAME,description:RENDER_UI_TOOL_DESCRIPTION,parameters:RENDER_UI_TOOL_SCHEMA,execute:c(createRenderUiHandler(o))}),e.registerTool({name:CLOSE_UI_TOOL_NAME,description:CLOSE_UI_TOOL_DESCRIPTION,parameters:CLOSE_UI_TOOL_SCHEMA,execute:c(createCloseUiHandler(o))}),d.setCanvasState(r,{schemaRegistry:s,descriptorRegistry:l});const g=d.getFormBuilder();g&&e.registerTool({name:RENDER_FORM_TOOL_NAME,description:buildRenderFormDescription(s),parameters:RENDER_FORM_TOOL_SCHEMA,execute:c(createRenderFormHandler({registry:s,builder:g,logger:e.logger}))});const u=d.getResourceRenderer();if(u){generateActionTools(l,e,async(e,r)=>d.gatewayRequest(e,r),u,e.logger)}const p=`\n\nTool selection guidance:\n- browser tool: Use when the user wants to OPEN, VISIT, NAVIGATE to a webpage, take SCREENSHOTS, interact with elements (click, type), or visually inspect pages.\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", "screenshot", or mentions seeing/viewing a webpage, ALWAYS prefer the browser tool.`,m=new Set([RENDER_UI_TOOL_NAME,CLOSE_UI_TOOL_NAME,RENDER_FORM_TOOL_NAME]),f=256,y=new Map;if("function"==typeof e.on){try{e.on("before_prompt_build",(e,r)=>{if(r?.sessionKey){if(y.size>=f&&!y.has(r.sessionKey)){const e=y.keys().next().value;void 0!==e&&y.delete(e)}y.set(r.sessionKey,r.channelId)}if(isThirdPartyChannel(n,{sessionKey:r?.sessionKey,channelId:r?.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(!r?.sessionKey)return{appendSystemContext:p};return d.isCanvasActiveForGatewayKey(r.sessionKey)?{appendSystemContext:CANVAS_SYSTEM_PROMPT+p}:{appendSystemContext:"Canvas is closed by user. Do NOT use render_ui, close_ui, or render_form. Respond with plain text only."+p}}),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,r)=>{if(r?.sessionKey&&d.setPendingToolCallSessionKey(r.sessionKey),!m.has(e.toolName))return;const o=r?.sessionKey?y.get(r.sessionKey):void 0;return isThirdPartyChannel(n,{sessionKey:r?.sessionKey,channelId:o})?{block:!0,blockReason:"Canvas tools are not available in this channel. Respond with plain text only."}:r?.sessionKey?d.isCanvasActiveForGatewayKey(r.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(t.bridge.shell.enabled&&!0===t.legacyTerminalWsEnabled){const r=createTerminalWebSocketService({config:{port:t.terminalWsPort,token:agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):"",allowedOrigins:t.terminalWsAllowedOrigins},terminalMgr:sharedTerminalManager,logger:e.logger,writeObs:a});e.registerService({id:"beeos-terminal-ws",start(){r.start(),e.logger.info?.(`[beeos-claw] Terminal WS started on port ${t.terminalWsPort}`)},stop(){r.stop(),e.logger.info?.("[beeos-claw] Terminal WS stopped")}})}else t.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(r.openclawConfig){const o=mirrorOpenClawConfig(r.openclawConfig);o.copied?e.logger.info?.(`[beeos-claw] mirrored openclaw config to ${o.destinationPath}`):e.logger.debug?.(`[beeos-claw] openclaw config mirror skipped: ${o.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 r=initializePlugin(e);if(!r)return;const o={current:null};installUnhandledRejectionObserver(e,r.writeObs,o),registerUploadTools(e,r,o),registerArouterMediaProviders(e),registerServices(e,r,o),_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 +1 @@
1
- import{RUNTIME_ERROR_DESCRIPTORS,RUNTIME_HASH_DOMAINS,RUNTIME_INVOCATION_PARITY_ENTRIES,RUNTIME_INVOCATION_RECOVERY_CLASSES,SERVICE_CANONICAL_COMMAND_NAMES,mapCanonicalResultToService}from"@beeos-ai/beeos-types/runtime";import{reduceRuntimeRecoveryPolicy}from"@beeos-ai/beeos-types/runtime/internal";import{verifyAndCreateMessageServiceCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{awaitRuntimeProjection,hasEffectReceiptHandoff,RuntimeCommandError}from"./runtime-command-dispatcher.js";import{createRuntimeAppendTimingRecorder}from"./runtime-append-timing.js";import{RUNTIME_MESSAGE_TYPES,runtimeMessageBase,runtimeMessageHash,semanticEqual,validateRuntimeCancelEnvelope,validateRuntimeRequestEnvelope}from"./runtime-message-channel-contract.js";import{RuntimeResourceResolutionError}from"./runtime-operation-resource-resolver.js";import{RuntimeTransportFailurePolicy}from"./runtime-transport-failure-policy.js";export class MsRuntimeMessageChannelAdapter{dispatcher;channel;verifier;ports;journal;getLease;options;controller;running;workers=new Map;poisonJournal;renewTransportFailurePolicy=new RuntimeTransportFailurePolicy;receiptHistory=this;constructor(e,t,o,n,a,r,i){if(this.dispatcher=e,this.channel=t,this.verifier=o,this.ports=n,this.journal=a,this.getLease=r,this.options=i,!i.poisonJournal)throw new Error("durable poison journal is required");if(this.poisonJournal=i.poisonJournal,!Number.isSafeInteger(i.poisonDeliveryLimit??3)||(i.poisonDeliveryLimit??3)<=0)throw new Error("poison delivery limit must be a positive integer")}start(){if(this.running)return;const e=new AbortController;this.controller=e;const t=this.loop(e.signal).catch(t=>{e.signal.aborted||this.observeDeliveryError(t)}).finally(()=>{this.running===t&&(this.running=void 0)});this.running=t}async stop(e){this.controller?.abort();for(const e of this.workers.values())e.cancel?.();this.running&&await abortable(this.running,e)}async readOperationHistory(e){const t=this.getLease();if(!t)return{status:"unavailable"};try{return await this.channel.history({operationId:e.operationId,lease:t})}catch{return{status:"unavailable"}}}async loop(e){let t;(()=>{t||(t=setInterval(()=>{this.renewActive().catch(e=>this.observeDeliveryError(e))},this.options.renewIntervalMs??2e4),t.unref?.())})();try{for(;!e.aborted;){const t=this.getLease();if(!t){await delay(this.options.idleDelayMs??500,e);continue}let o;try{o=await this.channel.read({maxCount:this.options.readCount??32,blockMs:this.options.blockMs??5e3,lease:t,signal:e})}catch(t){if(e.aborted)return;this.observeDeliveryError(t),await delay(this.options.idleDelayMs??500,e);continue}for(const n of o){if(e.aborted)break;if(n.message.type===RUNTIME_MESSAGE_TYPES.cancel){await this.handleCancel(n,t).catch(e=>this.observeDeliveryError(e));continue}const o=n.message.channel_id;if(this.workers.has(o))continue;const a={deliveryId:n.deliveryId,claimedLease:t,completion:Promise.resolve()};a.completion=this.handleRequest(n,t,e,e=>{a.cancel=e}).catch(e=>this.observeDeliveryError(e)).finally(()=>{this.workers.delete(o)}),this.workers.set(o,a)}}}finally{t&&clearInterval(t),await Promise.allSettled([...this.workers.values()].map(e=>e.completion))}}observeDeliveryError(e){try{this.options.onDeliveryError?.(e)}catch{}}observeRequestFailure(e,t,o){try{this.options.onRequestFailure?.({operationId:e,method:t,error:o})}catch{}}async renewActive(){const e=this.getLease();if(!e)return;const t=[];for(const o of this.workers.values())sameLeaseBinding(o.claimedLease,e)?t.push(o.deliveryId):o.cancel?.();if(!t.length)return;let o;try{o=await this.channel.renew({deliveryIds:t,lease:e})}catch(e){if(this.observeDeliveryError(e),this.renewTransportFailurePolicy.recordFailure()){const e=new Set(t);for(const t of this.workers.values())e.has(t.deliveryId)&&t.cancel?.()}return}if(this.renewTransportFailurePolicy.recordSuccess(),o.notPending.length){const e=new Set(o.notPending);for(const t of this.workers.values())e.has(t.deliveryId)&&t.cancel?.()}}async handleCancel(e,t){const o=validateRuntimeCancelEnvelope(e.message);if(!o.ok)return void await this.handlePoison(e,`invalid_cancel:${o.reason}`,t);const n=o.envelope.payload,a=await this.channel.history({operationId:n.operationId,lease:t});if("found"===a.status&&a.snapshot.terminal&&"final_message"===a.snapshot.terminalSource)return void await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:t});if("not_found"===a.status||"expired"===a.status)return void await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:t});const r=a.snapshot.request.payload;r.operationId===n.operationId&&r.requestMessageId===n.requestMessageId&&r.requestHash===n.requestHash&&r.dispatchEpoch===n.dispatchEpoch&&r.method===n.method?(await this.journal.observeCancellation({operationId:n.operationId,requestMessageId:n.requestMessageId,requestHash:n.requestHash,eventId:n.eventId}),this.workers.get(n.operationId)?.cancel?.(),await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:t})):await this.handlePoison(e,"cancel_history_mismatch",t)}async handleRequest(e,t,o,n){const a=validateRuntimeRequestEnvelope(e.message);if(!a.ok)return void await this.handlePoison(e,`invalid_request:${a.reason}`,t);const r=a.envelope.payload,i=e.executionGrant,s=createRuntimeAppendTimingRecorder("runtime-op",{operationId:r.operationId,method:r.method}),c=new AbortController;n(()=>c.abort());const d=async()=>{await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:this.currentLease(t)}),s.mark("ack")},l=await this.channel.history({operationId:r.operationId,lease:t});if(s.mark("history"),"not_found"===l.status||"expired"===l.status)return void await d();if(l.snapshot.terminal&&"final_message"===l.snapshot.terminalSource)return void await d();if(this.options.allowedMethods&&!this.options.allowedMethods.has(r.method))return await this.reject(r,"unsupported_method",t),void await d();if("deadline"===l.snapshot.terminalSource||Date.parse(r.executionDeadlineAt)<=Date.now()||!l.snapshot.started&&Date.parse(r.claimDeadlineAt)<=Date.now())return await this.reject(r,"execution_deadline_exceeded",t),void await d();const p=await this.journal.getCancellation(r.operationId);s.mark("journal-cancel-read");const u=l.snapshot.cancelRequested||null!==p;if(u&&"queued"===l.snapshot.status)return await this.reject(r,"cancelled_before_dispatch",t),void await d();if(r.instanceId!==t.instanceId||r.runtimeHandlerIdentity!==t.handlerIdentity||r.dispatchEpoch!==t.runtimeEpoch)return await this.reject(r,"authority_mismatch",t),void await d();const m=l.snapshot.request.payload;if(l.snapshot.operationId!==r.operationId||l.snapshot.requestMessageId!==r.requestMessageId||l.snapshot.requestHash!==r.requestHash||!semanticEqual(m,r))return void await this.handlePoison(e,"request_history_mismatch",t);const h=l.snapshot.started?.payload,I=void 0!==h&&h.operationId===r.operationId&&h.requestMessageId===r.requestMessageId&&h.requestHash===r.requestHash&&h.executionGrantHash===r.executionGrantHash&&h.handlerIdentity===t.handlerIdentity&&h.dispatchEpoch===t.runtimeEpoch&&h.method===r.method,E=await this.journal.get(r.operationId);s.mark("journal-prior");const f=await this.journal.acceptMessageRequest({operationId:r.operationId,command:r.command.name,paramsHash:r.paramsHash,runtimeEpoch:r.dispatchEpoch,requestMessageId:r.requestMessageId,requestHash:r.requestHash});s.mark("journal-accept");const v=I?E:f,R=decideMessageServiceRecovery(l.snapshot,r,v,I);if("already_terminal"===R)return void await d();if(!CORE_RECOVERY_DECISIONS.has(R))return await this.markOutcomeUnknownIfMutable(f),await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:failurePayload(r,new RuntimeCommandError("OUTCOME_UNKNOWN",`Types recovery policy requires ${R}; this runtime cannot safely replay Core`),(new Date).toISOString())}),void await d();if(await this.verifier.refresh(),s.mark("verifier-refresh"),!i)return I?await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:failurePayload(r,new RuntimeCommandError("OUTCOME_UNKNOWN","retained started proof has no live execution grant"),(new Date).toISOString())}):await this.reject(r,"invalid_execution_grant",t),void await d();this.verifier.verifyAndDecodeGrant(i)||await this.verifier.refresh(!0);const _=()=>c.abort();let y;o.addEventListener("abort",_,{once:!0});try{y=await verifyAndCreateMessageServiceCoreExecutionAttempt({request:r,executionGrant:i,runtimeEpoch:t.runtimeEpoch,lease:t,verifier:this.verifier,allowExpiredGrant:I,abortSignal:c.signal,resolvePorts:async()=>await this.ports.resolve({request:r,executionGrant:i,command:r.command,getLease:()=>this.currentLease(t),journalStoreId:this.options.journalStoreId,journalGeneration:this.options.journalGeneration,journalRecord:f,abortSignal:c.signal})})}catch(e){if(o.removeEventListener("abort",_),c.abort(),!(e instanceof RuntimeResourceResolutionError))throw e;if("transient"===e.kind)return;return I?(await this.markOutcomeUnknownIfMutable(f),await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:failurePayload(r,new RuntimeCommandError("OUTCOME_UNKNOWN","eager resources cannot be reconstructed after retained started proof"),(new Date).toISOString())})):await this.reject(r,"integrity_mismatch"===e.kind?"resource_integrity_mismatch":"resource_resolution_rejected",t),void await d()}if(!y.ok)return o.removeEventListener("abort",_),c.abort(),I?(await this.markOutcomeUnknownIfMutable(f),await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:failurePayload(r,new RuntimeCommandError("OUTCOME_UNKNOWN","execution grant cannot be revalidated after retained started proof"),(new Date).toISOString())})):await this.reject(r,"invalid_execution_grant",t),void await d();if(!u||"prepared"!==f.phase&&"started"!==f.phase||c.abort(),c.signal.aborted&&!I)return o.removeEventListener("abort",_),await this.reject(r,"cancelled_before_dispatch",t),void await d();s.mark("attempt"),I?this.currentLease(t):(await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),executionGrant:i,type:RUNTIME_MESSAGE_TYPES.started,payload:{...runtimeMessageBase(r),executionMode:"start",executionGrantHash:r.executionGrantHash,handlerIdentity:t.handlerIdentity,claimedAt:f.messageBinding?.acceptedAt??f.updatedAt}}),s.mark("append-started"));const M=this.dispatcher.dispatch(r.command,y.context);M.completion.catch(()=>{}),n(()=>{c.abort(),M.cancel()}),c.signal.aborted&&M.cancel();const w=this.forwardProgress(r,M.progress,t).catch(()=>{});let g,S=!1;try{if(("mcp/set"===r.method||"mcp/unset"===r.method)&&hasEffectReceiptHandoff(M)){const e=await M.effectCommitted;if(e){if(e.operationId!==r.operationId||e.method!==r.command.name)throw new RuntimeCommandError("OUTCOME_UNKNOWN","Core MCP receipt correlation mismatch");const o=runtimeMessageHash(RUNTIME_HASH_DOMAINS.coreResult,{method:r.method,result:e.result}),n={schemaVersion:1,operationId:r.operationId,method:r.method,coreResultRef:`sha256:${o}`,coreResultHash:o};g=runtimeMessageHash(RUNTIME_HASH_DOMAINS.effectRecord,n),S=!0,await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.effectCommitted,payload:{...runtimeMessageBase(r),effectRecord:n,effectRecordHash:g,committedAt:e.committedAt}}),await M.acknowledgeEffectReceipt({operationId:r.operationId,method:r.command.name,effectRecordHash:g})}}const o=await M.completion;await w,s.mark("core");const n=await this.journal.get(r.operationId);if(s.mark("journal-after-core"),!n||!isCommittedCorePhase(n.phase))throw new RuntimeCommandError("OUTCOME_UNKNOWN","Core completed without a committed local journal record");const a=n.updatedAt;let i,c;if("query"!==RUNTIME_INVOCATION_RECOVERY_CLASSES[r.method]){const e=isAgentProjectionMethod(r.method),c=M.getSnapshot().projection,l=n.agentProjectionIntent??n.agentProjectionPreparationFailure;if(e&&!l)throw new RuntimeCommandError("OUTCOME_UNKNOWN","agent effect committed without an atomic projection preparation record");if(!g){const e=l?.coreResultHash??runtimeMessageHash(RUNTIME_HASH_DOMAINS.coreResult,{method:r.method,result:o}),i=n.agentProjectionIntent?.projectionRevision??(methodRequiresProjectionRevision(r.method)?c?.revision??`${Math.max(0,n.revision)}`:void 0),d={schemaVersion:1,operationId:r.operationId,method:r.method,coreResultRef:`sha256:${e}`,coreResultHash:e,...i?{projectionRevision:i}:{}};g=runtimeMessageHash(RUNTIME_HASH_DOMAINS.effectRecord,d),S=!0,await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.effectCommitted,payload:{...runtimeMessageBase(r),effectRecord:d,effectRecordHash:g,committedAt:a}}),s.mark("append-effect")}let p=e?await awaitRuntimeProjection(M):c;if("session/set_model"===r.method&&"synced"!==p?.state&&(p=await this.projectSessionModelAfterEffect(r,y.context,n,p),s.mark("set-model-projection")),e&&n.agentProjectionIntent&&(!p||p.revision!==n.agentProjectionIntent.projectionRevision))throw new RuntimeCommandError("OUTCOME_UNKNOWN","agent projection receipt revision mismatch");if("pending"===p?.state)return void await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.projectionPending,payload:{...runtimeMessageBase(r),revision:p.revision}});if("synced"===p?.state&&await this.publishProjectionSynced(r,t,{...p,state:"synced"}),"blocked"===p?.state){await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.projectionBlocked,payload:{...runtimeMessageBase(r),revision:p.revision,errorCode:"PROJECTION_BLOCKED"}});const e=RUNTIME_ERROR_DESCRIPTORS.PROJECTION_BLOCKED;return await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:{...runtimeMessageBase(r),outcome:"projection_blocked",response:{jsonrpc:"2.0",id:r.request.id,error:{code:e.jsonRpcCode,message:"PROJECTION_BLOCKED",data:{code:"PROJECTION_BLOCKED",operationId:r.operationId,effectState:"committed",retryMode:e.retryMode}}},effectRecordHash:g,completedAt:a}}),void await d()}i=e?p?.platformAgentId:void 0}try{c=sanitizeRuntimeServiceResult(r.method,o,y.context,i)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed Core result cannot be mapped safely",void 0,{cause:e})}const l=runtimeMessageHash(RUNTIME_HASH_DOMAINS.serviceResult,{method:r.method,result:c});S=!0;const p=await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:{...runtimeMessageBase(r),outcome:"succeeded",response:{jsonrpc:"2.0",id:r.request.id,result:c},serviceResultHash:l,...g?{effectRecordHash:g}:{},completedAt:a}});if(s.mark("append-final"),this.observeRequestTerminalAccepted(e.deliveryId,p.message),("mcp/set"===r.method||"mcp/unset"===r.method)&&hasEffectReceiptHandoff(M)&&"function"==typeof M.acknowledgeTerminalReceipt){if(!g)throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP terminal response lacks its committed effect hash");await M.acknowledgeTerminalReceipt({operationId:r.operationId,method:r.command.name,effectRecordHash:g,terminalMessageId:p.message.message_id})}await d(),this.observeRequestAcknowledged(e.deliveryId,p.message)}catch(e){const o=await this.journal.get(r.operationId);if(o&&isCommittedCorePhase(o.phase)&&S)throw e;const n=!o||!isCommittedCorePhase(o.phase)||e instanceof RuntimeCommandError&&"OUTCOME_UNKNOWN"===e.code?e:new RuntimeCommandError("OUTCOME_UNKNOWN","Core effect committed before terminal mapping",void 0,{cause:e});this.observeRequestFailure(r.operationId,r.method,n);const a=failurePayload(r,n,o?.updatedAt??(new Date).toISOString());await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:a}),s.mark("append-terminal-failure"),await d()}finally{s.finish(),c.abort(),o.removeEventListener("abort",_)}}async handlePoison(e,t,o){this.currentLease(o);(await this.poisonJournal.record({delivery:e,reason:durablePoisonReason(t),limit:this.options.poisonDeliveryLimit??3})).terminal&&await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:this.currentLease(o)})}observeRequestTerminalAccepted(e,t){try{this.options.onRequestTerminalAccepted?.({deliveryId:e,message:t})}catch{}}observeRequestAcknowledged(e,t){try{this.options.onRequestAcknowledged?.({deliveryId:e,message:t})}catch{}}async forwardProgress(e,t,o){for await(const n of t)await this.channel.append({operationId:e.operationId,lease:this.currentLease(o),type:RUNTIME_MESSAGE_TYPES.progress,payload:{...runtimeMessageBase(e),phase:n.phase,progress:n}})}async reject(e,t,o){await this.channel.append({operationId:e.operationId,lease:this.currentLease(o),type:RUNTIME_MESSAGE_TYPES.final,payload:prestartFailurePayload(e,t)})}async markOutcomeUnknownIfMutable(e){"prepared"!==e.phase&&"started"!==e.phase||await this.journal.transition({operationId:e.operationId,expectedRevision:e.revision,phase:"outcome_unknown",errorCode:"OUTCOME_UNKNOWN"})}async projectSessionModelAfterEffect(e,t,o,n){const a=Object.freeze({state:"pending",revision:n?.revision??`${Math.max(0,o.revision)}`}),r=e.command;if("session.setModel"!==r.name||"conversation"!==r.context.scope)return a;const i=t.sessionModelProjection;if(!i)return a;try{await i.project({conversationId:r.context.conversation.runtimeConversationUid,modelOverrideId:r.params.modelOverrideId});const e=Object.freeze({state:"synced",revision:a.revision});try{const t=await this.journal.get(o.operationId)??o;if("projection_committed"===t.phase&&"synced"===t.projectionReceipt?.snapshot.state)return Object.freeze({state:"synced",revision:t.projectionReceipt.snapshot.revision});await this.journal.transition({operationId:t.operationId,expectedRevision:t.revision,phase:"projection_committed",projectionSnapshot:e})}catch{}return e}catch(e){return this.observeDeliveryError(e),a}}async publishProjectionSynced(e,t,o){const n=this.currentLease(t),a=await this.channel.history({operationId:e.operationId,lease:n});if("found"===a.status&&"synced"===a.snapshot.projectionState)return;const r={...runtimeMessageBase(e),revision:o.revision};("found"!==a.status||"reserved"===a.snapshot.projectionState||"pending"!==a.snapshot.projectionState&&void 0===a.snapshot.projectionPending)&&await this.channel.append({operationId:e.operationId,lease:n,type:RUNTIME_MESSAGE_TYPES.projectionPending,payload:r}),await this.channel.append({operationId:e.operationId,lease:n,type:RUNTIME_MESSAGE_TYPES.projectionSynced,payload:{...r,...o.platformAgentId?{platformAgentId:o.platformAgentId}:{}}})}currentLease(e){const t=this.getLease();if(!t||t.instanceId!==e.instanceId||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch||t.handlerIdentity!==e.handlerIdentity||!Number.isFinite(Date.parse(t.leaseExpiresAt))||Date.parse(t.leaseExpiresAt)<=Date.now())throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime message lease changed");return t}}function durablePoisonReason(e){const t=e.replace(/[^a-z0-9_:.-]+/gi,"_").replace(/^_+|_+$/g,"").slice(0,256);if(!t)throw new Error("poison reason is empty");return t}function isCommittedCorePhase(e){return"effect_committed"===e||"projection_committed"===e}const CORE_RECOVERY_DECISIONS=new Set(["execute","resume","replay_effect_committed","resume_projection","publish_committed_result","publish_projected_result","publish_terminal_result"]);function decideMessageServiceRecovery(e,t,o,n){const a=RUNTIME_INVOCATION_PARITY_ENTRIES.find(e=>e.acpMethod===t.method),r=e.terminal?"terminal":"blocked"===e.projectionState?"projection_blocked":"synced"===e.projectionState?"projection_synced":"pending"===e.projectionState?"projection_pending":e.effectCommitted?"runtime_committed":n?"running":"queued",i="failed"===o?.phase||"outcome_unknown"===o?.phase?"terminal":o?.phase,s=o&&o.runtimeEpoch===t.dispatchEpoch&&(void 0===o.messageBinding||o.messageBinding.requestMessageId===t.requestMessageId&&o.messageBinding.requestHash===t.requestHash)?"continuous":"discontinuous";return reduceRuntimeRecoveryPolicy({recoveryClass:a?.recoveryClass??RUNTIME_INVOCATION_RECOVERY_CLASSES[t.method],projectionRequired:"mcp/set"===t.method||"mcp/unset"===t.method||(a?.projectionRequired??!1),messageServicePhase:r,executionBoundaryCrossed:n,lineageContinuity:s,journalState:o?"available":"unavailable",...i?{journalPhase:i}:{}})}export function sanitizeRuntimeServiceResult(e,t,o,n){if("session/clear"===e)return serviceClearResult(t,o?.historyBoundary);if("session/set_model"===e)return serviceSetModelResult(t,o);if(!(e in SERVICE_CANONICAL_COMMAND_NAMES))return t;const a="cron/status"===e&&record(t)?{enabled:t.enabled,jobCount:t.jobCount,..."number"==typeof t.nextWakeAtMs?{nextWakeAtMs:t.nextWakeAtMs}:{}}:t;return mapCanonicalResultToService(e,a,{...n?{platformAgentId:n}:{},classifySkillOrigin:e=>e.bundled?{origin:"bundled",slug:e.slug}:{origin:"legacy_runtime",slug:e.slug},resolveMcpView:e=>({name:e.name,transport:e.transport,origin:"legacy_runtime",...void 0===e.connected?{}:{connected:e.connected},...void 0===e.connectionTimeoutMs?{}:{connectionTimeoutMs:e.connectionTimeoutMs}}),sanitizeDiagnostic:e=>/^[A-Z][A-Z0-9_]{0,63}$/.test(e)?e:"RUNTIME_EXECUTION_FAILED"})}function serviceClearResult(e,t){if(!record(e)||!0!==e.cleared||"string"!=typeof e.historyGeneration||!t||"message-service"!==t.kind)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed clear result lacks its Message Service receipt");const o=t;if(!o.committedIntentId)throw new RuntimeCommandError("OUTCOME_UNKNOWN","Message Service clear receipt is unavailable");return{...e,clearIntentId:o.committedIntentId()}}function serviceSetModelResult(e,t){if(!record(e)||"string"!=typeof e.effectiveModelId||!t)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed set_model result is invalid");const o=t.verifiedMessageGrant?.claims.command;if(!o||"session.setModel"!==o.name||"conversation"!==o.context.scope)throw new RuntimeCommandError("OUTCOME_UNKNOWN","set_model Service target receipt is unavailable");const n=o.params.modelOverrideId;return{conversationId:o.context.conversation.runtimeConversationUid,...null===n?{defaultModelId:e.effectiveModelId}:{},modelOverrideId:n,effectiveModelId:e.effectiveModelId,effectState:"committed",projectionState:"synced",operationId:t.operationId}}function isAgentProjectionMethod(e){return"agent/create"===e||"agent/update"===e||"agent/delete"===e||"agent/applyTemplate"===e}function methodRequiresProjectionRevision(e){return isAgentProjectionMethod(e)||"session/set_model"===e}function record(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}function sameLeaseBinding(e,t){return e.instanceId===t.instanceId&&e.leaseId===t.leaseId&&e.handlerIdentity===t.handlerIdentity&&e.runtimeEpoch===t.runtimeEpoch}function failurePayload(e,t,o){const n=t instanceof RuntimeCommandError?t.code:"RUNTIME_EXECUTION_FAILED",a=n in RUNTIME_ERROR_DESCRIPTORS?n:"RUNTIME_EXECUTION_FAILED",r="OUTCOME_UNKNOWN"===a||"INSTANCE_EPOCH_MISMATCH"===a?"outcome_unknown":"COMMAND_EXPIRED"===a?"expired":"OPERATION_CANCELLED"===a?"cancelled":"failed",i="outcome_unknown"===r?"OUTCOME_UNKNOWN":a,s=RUNTIME_ERROR_DESCRIPTORS[i],c="outcome_unknown"===r?"outcome_unknown":r;return{...runtimeMessageBase(e),outcome:r,response:{jsonrpc:"2.0",id:e.request.id,error:{code:s.jsonRpcCode,message:i,data:{code:i,operationId:e.operationId,effectState:c,retryMode:s.retryMode}}},completedAt:o}}function prestartFailurePayload(e,t){const o="invalid_execution_grant"===t?"INVALID_EXECUTION_GRANT":"unsupported_method"===t?"METHOD_NOT_SUPPORTED":"authority_mismatch"===t?"INSTANCE_EPOCH_MISMATCH":"execution_deadline_exceeded"===t?"COMMAND_EXPIRED":"cancelled_before_dispatch"===t?"OPERATION_CANCELLED":"resource_resolution_rejected"===t?"INVALID_INVOCATION":"RUNTIME_EXECUTION_FAILED",n=RUNTIME_ERROR_DESCRIPTORS[o in RUNTIME_ERROR_DESCRIPTORS?o:"RUNTIME_EXECUTION_FAILED"],a="COMMAND_EXPIRED"===o?"expired":"OPERATION_CANCELLED"===o?"cancelled":"failed";return{...runtimeMessageBase(e),outcome:a,response:{jsonrpc:"2.0",id:e.request.id,error:{code:n.jsonRpcCode,message:o,data:{code:o,operationId:e.operationId,effectState:a,retryMode:n.retryMode}}},completedAt:(new Date).toISOString()}}function abortable(e,t){return t?t.aborted?Promise.resolve():new Promise((o,n)=>{const a=()=>o();t.addEventListener("abort",a,{once:!0}),e.then(()=>{t.removeEventListener("abort",a),o()},e=>{t.removeEventListener("abort",a),n(e)})}):e}function delay(e,t){return new Promise(o=>{if(t?.aborted)return o();const n=()=>{clearTimeout(a),o()},a=setTimeout(()=>{t?.removeEventListener("abort",n),o()},e);t?.addEventListener("abort",n,{once:!0})})}
1
+ import{RUNTIME_ERROR_DESCRIPTORS,RUNTIME_HASH_DOMAINS,RUNTIME_INVOCATION_PARITY_ENTRIES,RUNTIME_INVOCATION_RECOVERY_CLASSES,SERVICE_CANONICAL_COMMAND_NAMES,mapCanonicalResultToService}from"@beeos-ai/beeos-types/runtime";import{reduceRuntimeRecoveryPolicy}from"@beeos-ai/beeos-types/runtime/internal";import{verifyAndCreateMessageServiceCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{awaitRuntimeProjection,hasEffectReceiptHandoff,RuntimeCommandError}from"./runtime-command-dispatcher.js";import{createRuntimeAppendTimingRecorder}from"./runtime-append-timing.js";import{RUNTIME_MESSAGE_TYPES,runtimeMessageBase,runtimeMessageHash,semanticEqual,validateRuntimeCancelEnvelope,validateRuntimeRequestEnvelope}from"./runtime-message-channel-contract.js";import{RuntimeResourceResolutionError}from"./runtime-operation-resource-resolver.js";import{RuntimeTransportFailurePolicy}from"./runtime-transport-failure-policy.js";export class MsRuntimeMessageChannelAdapter{dispatcher;channel;verifier;ports;journal;getLease;options;controller;running;workers=new Map;poisonJournal;renewTransportFailurePolicy=new RuntimeTransportFailurePolicy;receiptHistory=this;constructor(e,t,o,n,a,r,i){if(this.dispatcher=e,this.channel=t,this.verifier=o,this.ports=n,this.journal=a,this.getLease=r,this.options=i,!i.poisonJournal)throw new Error("durable poison journal is required");if(this.poisonJournal=i.poisonJournal,!Number.isSafeInteger(i.poisonDeliveryLimit??3)||(i.poisonDeliveryLimit??3)<=0)throw new Error("poison delivery limit must be a positive integer")}start(){if(this.running)return;const e=new AbortController;this.controller=e;const t=this.loop(e.signal).catch(t=>{e.signal.aborted||this.observeDeliveryError(t)}).finally(()=>{this.running===t&&(this.running=void 0)});this.running=t}async stop(e){this.controller?.abort();for(const e of this.workers.values())e.cancel?.();this.running&&await abortable(this.running,e)}async readOperationHistory(e){const t=this.getLease();if(!t)return{status:"unavailable"};try{return await this.channel.history({operationId:e.operationId,lease:t})}catch{return{status:"unavailable"}}}async loop(e){let t;(()=>{t||(t=setInterval(()=>{this.renewActive().catch(e=>this.observeDeliveryError(e))},this.options.renewIntervalMs??2e4),t.unref?.())})();try{for(;!e.aborted;){const t=this.getLease();if(!t){await delay(this.options.idleDelayMs??500,e);continue}let o;try{o=await this.channel.read({maxCount:this.options.readCount??32,blockMs:this.options.blockMs??5e3,lease:t,signal:e})}catch(t){if(e.aborted)return;this.observeDeliveryError(t),await delay(this.options.idleDelayMs??500,e);continue}for(const n of o){if(e.aborted)break;if(n.message.type===RUNTIME_MESSAGE_TYPES.cancel){await this.handleCancel(n,t).catch(e=>this.observeDeliveryError(e));continue}const o=n.message.channel_id;if(this.workers.has(o))continue;const a={deliveryId:n.deliveryId,claimedLease:t,completion:Promise.resolve()};a.completion=this.handleRequest(n,t,e,e=>{a.cancel=e}).catch(e=>this.observeDeliveryError(e)).finally(()=>{this.workers.delete(o)}),this.workers.set(o,a)}}}finally{t&&clearInterval(t),await Promise.allSettled([...this.workers.values()].map(e=>e.completion))}}observeDeliveryError(e){try{this.options.onDeliveryError?.(e)}catch{}}observeRequestFailure(e,t,o){try{this.options.onRequestFailure?.({operationId:e,method:t,error:o})}catch{}}async renewActive(){const e=this.getLease();if(!e)return;const t=[];for(const o of this.workers.values())sameLeaseBinding(o.claimedLease,e)?t.push(o.deliveryId):o.cancel?.();if(!t.length)return;let o;try{o=await this.channel.renew({deliveryIds:t,lease:e})}catch(e){if(this.observeDeliveryError(e),this.renewTransportFailurePolicy.recordFailure()){const e=new Set(t);for(const t of this.workers.values())e.has(t.deliveryId)&&t.cancel?.()}return}if(this.renewTransportFailurePolicy.recordSuccess(),o.notPending.length){const e=new Set(o.notPending);for(const t of this.workers.values())e.has(t.deliveryId)&&t.cancel?.()}}async handleCancel(e,t){const o=validateRuntimeCancelEnvelope(e.message);if(!o.ok)return void await this.handlePoison(e,`invalid_cancel:${o.reason}`,t);const n=o.envelope.payload,a=await this.channel.history({operationId:n.operationId,lease:t});if("found"===a.status&&a.snapshot.terminal&&"final_message"===a.snapshot.terminalSource)return void await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:t});if("not_found"===a.status||"expired"===a.status)return void await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:t});const r=a.snapshot.request.payload;r.operationId===n.operationId&&r.requestMessageId===n.requestMessageId&&r.requestHash===n.requestHash&&r.dispatchEpoch===n.dispatchEpoch&&r.method===n.method?(await this.journal.observeCancellation({operationId:n.operationId,requestMessageId:n.requestMessageId,requestHash:n.requestHash,eventId:n.eventId}),this.workers.get(n.operationId)?.cancel?.(),await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:t})):await this.handlePoison(e,"cancel_history_mismatch",t)}async handleRequest(e,t,o,n){const a=validateRuntimeRequestEnvelope(e.message);if(!a.ok)return void await this.handlePoison(e,`invalid_request:${a.reason}`,t);const r=a.envelope.payload,i=e.executionGrant,s=createRuntimeAppendTimingRecorder("runtime-op",{operationId:r.operationId,method:r.method}),c=new AbortController;n(()=>c.abort());const d=async()=>{await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:this.currentLease(t)}),s.mark("ack")},l=await this.channel.history({operationId:r.operationId,lease:t});if(s.mark("history"),"not_found"===l.status||"expired"===l.status)return void await d();if(l.snapshot.terminal&&"final_message"===l.snapshot.terminalSource)return void await d();if(this.options.allowedMethods&&!this.options.allowedMethods.has(r.method))return await this.reject(r,"unsupported_method",t),void await d();if("deadline"===l.snapshot.terminalSource||Date.parse(r.executionDeadlineAt)<=Date.now()||!l.snapshot.started&&Date.parse(r.claimDeadlineAt)<=Date.now())return await this.reject(r,"execution_deadline_exceeded",t),void await d();const u=await this.journal.getCancellation(r.operationId);s.mark("journal-cancel-read");const p=l.snapshot.cancelRequested||null!==u;if(p&&"queued"===l.snapshot.status)return await this.reject(r,"cancelled_before_dispatch",t),void await d();if(r.instanceId!==t.instanceId||r.runtimeHandlerIdentity!==t.handlerIdentity||r.dispatchEpoch!==t.runtimeEpoch)return await this.reject(r,"authority_mismatch",t),void await d();const m=l.snapshot.request.payload;if(l.snapshot.operationId!==r.operationId||l.snapshot.requestMessageId!==r.requestMessageId||l.snapshot.requestHash!==r.requestHash||!semanticEqual(m,r))return void await this.handlePoison(e,"request_history_mismatch",t);const h=l.snapshot.started?.payload,I=void 0!==h&&h.operationId===r.operationId&&h.requestMessageId===r.requestMessageId&&h.requestHash===r.requestHash&&h.executionGrantHash===r.executionGrantHash&&h.handlerIdentity===t.handlerIdentity&&h.dispatchEpoch===t.runtimeEpoch&&h.method===r.method,E=await this.journal.get(r.operationId);s.mark("journal-prior");const f=await this.journal.acceptMessageRequest({operationId:r.operationId,command:r.command.name,paramsHash:r.paramsHash,runtimeEpoch:r.dispatchEpoch,requestMessageId:r.requestMessageId,requestHash:r.requestHash});s.mark("journal-accept");const v=I?E:f,R=decideMessageServiceRecovery(l.snapshot,r,v,I);if("already_terminal"===R)return void await d();if(!CORE_RECOVERY_DECISIONS.has(R))return await this.markOutcomeUnknownIfMutable(f),await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:failurePayload(r,new RuntimeCommandError("OUTCOME_UNKNOWN",`Types recovery policy requires ${R}; this runtime cannot safely replay Core`),(new Date).toISOString())}),void await d();if(await this.verifier.refresh(),s.mark("verifier-refresh"),!i)return I?await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:failurePayload(r,new RuntimeCommandError("OUTCOME_UNKNOWN","retained started proof has no live execution grant"),(new Date).toISOString())}):await this.reject(r,"invalid_execution_grant",t),void await d();this.verifier.verifyAndDecodeGrant(i)||await this.verifier.refresh(!0);const _=()=>c.abort();let y;o.addEventListener("abort",_,{once:!0});try{y=await verifyAndCreateMessageServiceCoreExecutionAttempt({request:r,executionGrant:i,runtimeEpoch:t.runtimeEpoch,lease:t,verifier:this.verifier,allowExpiredGrant:I,abortSignal:c.signal,resolvePorts:async()=>await this.ports.resolve({request:r,executionGrant:i,command:r.command,getLease:()=>this.currentLease(t),...this.journalLineage(),journalRecord:f,abortSignal:c.signal})})}catch(e){if(o.removeEventListener("abort",_),c.abort(),!(e instanceof RuntimeResourceResolutionError))throw e;if("transient"===e.kind)return;return I?(await this.markOutcomeUnknownIfMutable(f),await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:failurePayload(r,new RuntimeCommandError("OUTCOME_UNKNOWN","eager resources cannot be reconstructed after retained started proof"),(new Date).toISOString())})):await this.reject(r,"integrity_mismatch"===e.kind?"resource_integrity_mismatch":"resource_resolution_rejected",t),void await d()}if(!y.ok)return o.removeEventListener("abort",_),c.abort(),I?(await this.markOutcomeUnknownIfMutable(f),await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:failurePayload(r,new RuntimeCommandError("OUTCOME_UNKNOWN","execution grant cannot be revalidated after retained started proof"),(new Date).toISOString())})):await this.reject(r,"invalid_execution_grant",t),void await d();if(!p||"prepared"!==f.phase&&"started"!==f.phase||c.abort(),c.signal.aborted&&!I)return o.removeEventListener("abort",_),await this.reject(r,"cancelled_before_dispatch",t),void await d();s.mark("attempt"),I?this.currentLease(t):(await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),executionGrant:i,type:RUNTIME_MESSAGE_TYPES.started,payload:{...runtimeMessageBase(r),executionMode:"start",executionGrantHash:r.executionGrantHash,handlerIdentity:t.handlerIdentity,claimedAt:f.messageBinding?.acceptedAt??f.updatedAt}}),s.mark("append-started"));const g=this.dispatcher.dispatch(r.command,y.context);g.completion.catch(()=>{}),n(()=>{c.abort(),g.cancel()}),c.signal.aborted&&g.cancel();const M=this.forwardProgress(r,g.progress,t).catch(()=>{});let w,S=!1;try{if(("mcp/set"===r.method||"mcp/unset"===r.method)&&hasEffectReceiptHandoff(g)){const e=await g.effectCommitted;if(e){if(e.operationId!==r.operationId||e.method!==r.command.name)throw new RuntimeCommandError("OUTCOME_UNKNOWN","Core MCP receipt correlation mismatch");const o=runtimeMessageHash(RUNTIME_HASH_DOMAINS.coreResult,{method:r.method,result:e.result}),n={schemaVersion:1,operationId:r.operationId,method:r.method,coreResultRef:`sha256:${o}`,coreResultHash:o};w=runtimeMessageHash(RUNTIME_HASH_DOMAINS.effectRecord,n),S=!0,await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.effectCommitted,payload:{...runtimeMessageBase(r),effectRecord:n,effectRecordHash:w,committedAt:e.committedAt}}),await g.acknowledgeEffectReceipt({operationId:r.operationId,method:r.command.name,effectRecordHash:w})}}const o=await g.completion;await M,s.mark("core");const n=await this.journal.get(r.operationId);if(s.mark("journal-after-core"),!n||!isCommittedCorePhase(n.phase))throw new RuntimeCommandError("OUTCOME_UNKNOWN","Core completed without a committed local journal record");const a=n.updatedAt;let i,c;if("query"!==RUNTIME_INVOCATION_RECOVERY_CLASSES[r.method]){const e=isAgentProjectionMethod(r.method),c=g.getSnapshot().projection,l=n.agentProjectionIntent??n.agentProjectionPreparationFailure;if(e&&!l)throw new RuntimeCommandError("OUTCOME_UNKNOWN","agent effect committed without an atomic projection preparation record");if(!w){const e=l?.coreResultHash??runtimeMessageHash(RUNTIME_HASH_DOMAINS.coreResult,{method:r.method,result:o}),i=n.agentProjectionIntent?.projectionRevision??(methodRequiresProjectionRevision(r.method)?c?.revision??`${Math.max(0,n.revision)}`:void 0),d={schemaVersion:1,operationId:r.operationId,method:r.method,coreResultRef:`sha256:${e}`,coreResultHash:e,...i?{projectionRevision:i}:{}};w=runtimeMessageHash(RUNTIME_HASH_DOMAINS.effectRecord,d),S=!0,await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.effectCommitted,payload:{...runtimeMessageBase(r),effectRecord:d,effectRecordHash:w,committedAt:a}}),s.mark("append-effect")}let u=e?await awaitRuntimeProjection(g):c;if("session/set_model"===r.method&&"synced"!==u?.state&&(u=await this.projectSessionModelAfterEffect(r,y.context,n,u),s.mark("set-model-projection")),e&&n.agentProjectionIntent&&(!u||u.revision!==n.agentProjectionIntent.projectionRevision))throw new RuntimeCommandError("OUTCOME_UNKNOWN","agent projection receipt revision mismatch");if("pending"===u?.state)return void await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.projectionPending,payload:{...runtimeMessageBase(r),revision:u.revision}});if("synced"===u?.state&&await this.publishProjectionSynced(r,t,{...u,state:"synced"}),"blocked"===u?.state){await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.projectionBlocked,payload:{...runtimeMessageBase(r),revision:u.revision,errorCode:"PROJECTION_BLOCKED"}});const e=RUNTIME_ERROR_DESCRIPTORS.PROJECTION_BLOCKED;return await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:{...runtimeMessageBase(r),outcome:"projection_blocked",response:{jsonrpc:"2.0",id:r.request.id,error:{code:e.jsonRpcCode,message:"PROJECTION_BLOCKED",data:{code:"PROJECTION_BLOCKED",operationId:r.operationId,effectState:"committed",retryMode:e.retryMode}}},effectRecordHash:w,completedAt:a}}),void await d()}i=e?u?.platformAgentId:void 0}try{c=sanitizeRuntimeServiceResult(r.method,o,y.context,i)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed Core result cannot be mapped safely",void 0,{cause:e})}const l=runtimeMessageHash(RUNTIME_HASH_DOMAINS.serviceResult,{method:r.method,result:c});S=!0;const u=await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:{...runtimeMessageBase(r),outcome:"succeeded",response:{jsonrpc:"2.0",id:r.request.id,result:c},serviceResultHash:l,...w?{effectRecordHash:w}:{},completedAt:a}});if(s.mark("append-final"),this.observeRequestTerminalAccepted(e.deliveryId,u.message),("mcp/set"===r.method||"mcp/unset"===r.method)&&hasEffectReceiptHandoff(g)&&"function"==typeof g.acknowledgeTerminalReceipt){if(!w)throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP terminal response lacks its committed effect hash");await g.acknowledgeTerminalReceipt({operationId:r.operationId,method:r.command.name,effectRecordHash:w,terminalMessageId:u.message.message_id})}await d(),this.observeRequestAcknowledged(e.deliveryId,u.message)}catch(e){const o=await this.journal.get(r.operationId);if(o&&isCommittedCorePhase(o.phase)&&S)throw e;const n=!o||!isCommittedCorePhase(o.phase)||e instanceof RuntimeCommandError&&"OUTCOME_UNKNOWN"===e.code?e:new RuntimeCommandError("OUTCOME_UNKNOWN","Core effect committed before terminal mapping",void 0,{cause:e});this.observeRequestFailure(r.operationId,r.method,n);const a=failurePayload(r,n,o?.updatedAt??(new Date).toISOString());await this.channel.append({operationId:r.operationId,lease:this.currentLease(t),type:RUNTIME_MESSAGE_TYPES.final,payload:a}),s.mark("append-terminal-failure"),await d()}finally{s.finish(),c.abort(),o.removeEventListener("abort",_)}}async handlePoison(e,t,o){this.currentLease(o);(await this.poisonJournal.record({delivery:e,reason:durablePoisonReason(t),limit:this.options.poisonDeliveryLimit??3})).terminal&&await this.channel.acknowledge({deliveryIds:[e.deliveryId],lease:this.currentLease(o)})}observeRequestTerminalAccepted(e,t){try{this.options.onRequestTerminalAccepted?.({deliveryId:e,message:t})}catch{}}observeRequestAcknowledged(e,t){try{this.options.onRequestAcknowledged?.({deliveryId:e,message:t})}catch{}}async forwardProgress(e,t,o){for await(const n of t)await this.channel.append({operationId:e.operationId,lease:this.currentLease(o),type:RUNTIME_MESSAGE_TYPES.progress,payload:{...runtimeMessageBase(e),phase:n.phase,progress:n}})}async reject(e,t,o){await this.channel.append({operationId:e.operationId,lease:this.currentLease(o),type:RUNTIME_MESSAGE_TYPES.final,payload:prestartFailurePayload(e,t)})}async markOutcomeUnknownIfMutable(e){"prepared"!==e.phase&&"started"!==e.phase||await this.journal.transition({operationId:e.operationId,expectedRevision:e.revision,phase:"outcome_unknown",errorCode:"OUTCOME_UNKNOWN"})}async projectSessionModelAfterEffect(e,t,o,n){const a=Object.freeze({state:"pending",revision:n?.revision??`${Math.max(0,o.revision)}`}),r=e.command;if("session.setModel"!==r.name||"conversation"!==r.context.scope)return a;const i=t.sessionModelProjection;if(!i)return a;try{await i.project({conversationId:r.context.conversation.runtimeConversationUid,modelOverrideId:r.params.modelOverrideId});const e=Object.freeze({state:"synced",revision:a.revision});try{const t=await this.journal.get(o.operationId)??o;if("projection_committed"===t.phase&&"synced"===t.projectionReceipt?.snapshot.state)return Object.freeze({state:"synced",revision:t.projectionReceipt.snapshot.revision});await this.journal.transition({operationId:t.operationId,expectedRevision:t.revision,phase:"projection_committed",projectionSnapshot:e})}catch{}return e}catch(e){return this.observeDeliveryError(e),a}}async publishProjectionSynced(e,t,o){const n=this.currentLease(t),a=await this.channel.history({operationId:e.operationId,lease:n});if("found"===a.status&&"synced"===a.snapshot.projectionState)return;const r={...runtimeMessageBase(e),revision:o.revision};("found"!==a.status||"reserved"===a.snapshot.projectionState||"pending"!==a.snapshot.projectionState&&void 0===a.snapshot.projectionPending)&&await this.channel.append({operationId:e.operationId,lease:n,type:RUNTIME_MESSAGE_TYPES.projectionPending,payload:r}),await this.channel.append({operationId:e.operationId,lease:n,type:RUNTIME_MESSAGE_TYPES.projectionSynced,payload:{...r,...o.platformAgentId?{platformAgentId:o.platformAgentId}:{}}})}journalLineage(){return this.options.getJournalLineage?.()??{journalStoreId:this.options.journalStoreId,journalGeneration:this.options.journalGeneration}}currentLease(e){const t=this.getLease();if(!t||t.instanceId!==e.instanceId||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch||t.handlerIdentity!==e.handlerIdentity||!Number.isFinite(Date.parse(t.leaseExpiresAt))||Date.parse(t.leaseExpiresAt)<=Date.now())throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime message lease changed");return t}}function durablePoisonReason(e){const t=e.replace(/[^a-z0-9_:.-]+/gi,"_").replace(/^_+|_+$/g,"").slice(0,256);if(!t)throw new Error("poison reason is empty");return t}function isCommittedCorePhase(e){return"effect_committed"===e||"projection_committed"===e}const CORE_RECOVERY_DECISIONS=new Set(["execute","resume","replay_effect_committed","resume_projection","publish_committed_result","publish_projected_result","publish_terminal_result"]);function decideMessageServiceRecovery(e,t,o,n){const a=RUNTIME_INVOCATION_PARITY_ENTRIES.find(e=>e.acpMethod===t.method),r=e.terminal?"terminal":"blocked"===e.projectionState?"projection_blocked":"synced"===e.projectionState?"projection_synced":"pending"===e.projectionState?"projection_pending":e.effectCommitted?"runtime_committed":n?"running":"queued",i="failed"===o?.phase||"outcome_unknown"===o?.phase?"terminal":o?.phase,s=o&&o.runtimeEpoch===t.dispatchEpoch&&(void 0===o.messageBinding||o.messageBinding.requestMessageId===t.requestMessageId&&o.messageBinding.requestHash===t.requestHash)?"continuous":"discontinuous";return reduceRuntimeRecoveryPolicy({recoveryClass:a?.recoveryClass??RUNTIME_INVOCATION_RECOVERY_CLASSES[t.method],projectionRequired:"mcp/set"===t.method||"mcp/unset"===t.method||(a?.projectionRequired??!1),messageServicePhase:r,executionBoundaryCrossed:n,lineageContinuity:s,journalState:o?"available":"unavailable",...i?{journalPhase:i}:{}})}export function sanitizeRuntimeServiceResult(e,t,o,n){if("session/clear"===e)return serviceClearResult(t,o?.historyBoundary);if("session/set_model"===e)return serviceSetModelResult(t,o);if(!(e in SERVICE_CANONICAL_COMMAND_NAMES))return t;const a="cron/status"===e&&record(t)?{enabled:t.enabled,jobCount:t.jobCount,..."number"==typeof t.nextWakeAtMs?{nextWakeAtMs:t.nextWakeAtMs}:{}}:t;return mapCanonicalResultToService(e,a,{...n?{platformAgentId:n}:{},classifySkillOrigin:e=>e.bundled?{origin:"bundled",slug:e.slug}:{origin:"legacy_runtime",slug:e.slug},resolveMcpView:e=>({name:e.name,transport:e.transport,origin:"legacy_runtime",...void 0===e.connected?{}:{connected:e.connected},...void 0===e.connectionTimeoutMs?{}:{connectionTimeoutMs:e.connectionTimeoutMs}}),sanitizeDiagnostic:e=>/^[A-Z][A-Z0-9_]{0,63}$/.test(e)?e:"RUNTIME_EXECUTION_FAILED"})}function serviceClearResult(e,t){if(!record(e)||!0!==e.cleared||"string"!=typeof e.historyGeneration||!t||"message-service"!==t.kind)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed clear result lacks its Message Service receipt");const o=t;if(!o.committedIntentId)throw new RuntimeCommandError("OUTCOME_UNKNOWN","Message Service clear receipt is unavailable");return{...e,clearIntentId:o.committedIntentId()}}function serviceSetModelResult(e,t){if(!record(e)||"string"!=typeof e.effectiveModelId||!t)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed set_model result is invalid");const o=t.verifiedMessageGrant?.claims.command;if(!o||"session.setModel"!==o.name||"conversation"!==o.context.scope)throw new RuntimeCommandError("OUTCOME_UNKNOWN","set_model Service target receipt is unavailable");const n=o.params.modelOverrideId;return{conversationId:o.context.conversation.runtimeConversationUid,...null===n?{defaultModelId:e.effectiveModelId}:{},modelOverrideId:n,effectiveModelId:e.effectiveModelId,effectState:"committed",projectionState:"synced",operationId:t.operationId}}function isAgentProjectionMethod(e){return"agent/create"===e||"agent/update"===e||"agent/delete"===e||"agent/applyTemplate"===e}function methodRequiresProjectionRevision(e){return isAgentProjectionMethod(e)||"session/set_model"===e}function record(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}function sameLeaseBinding(e,t){return e.instanceId===t.instanceId&&e.leaseId===t.leaseId&&e.handlerIdentity===t.handlerIdentity&&e.runtimeEpoch===t.runtimeEpoch}function failurePayload(e,t,o){const n=t instanceof RuntimeCommandError?t.code:"RUNTIME_EXECUTION_FAILED",a=n in RUNTIME_ERROR_DESCRIPTORS?n:"RUNTIME_EXECUTION_FAILED",r="OUTCOME_UNKNOWN"===a||"INSTANCE_EPOCH_MISMATCH"===a?"outcome_unknown":"COMMAND_EXPIRED"===a?"expired":"OPERATION_CANCELLED"===a?"cancelled":"failed",i="outcome_unknown"===r?"OUTCOME_UNKNOWN":a,s=RUNTIME_ERROR_DESCRIPTORS[i],c="outcome_unknown"===r?"outcome_unknown":r;return{...runtimeMessageBase(e),outcome:r,response:{jsonrpc:"2.0",id:e.request.id,error:{code:s.jsonRpcCode,message:i,data:{code:i,operationId:e.operationId,effectState:c,retryMode:s.retryMode}}},completedAt:o}}function prestartFailurePayload(e,t){const o="invalid_execution_grant"===t?"INVALID_EXECUTION_GRANT":"unsupported_method"===t?"METHOD_NOT_SUPPORTED":"authority_mismatch"===t?"INSTANCE_EPOCH_MISMATCH":"execution_deadline_exceeded"===t?"COMMAND_EXPIRED":"cancelled_before_dispatch"===t?"OPERATION_CANCELLED":"resource_resolution_rejected"===t?"INVALID_INVOCATION":"RUNTIME_EXECUTION_FAILED",n=RUNTIME_ERROR_DESCRIPTORS[o in RUNTIME_ERROR_DESCRIPTORS?o:"RUNTIME_EXECUTION_FAILED"],a="COMMAND_EXPIRED"===o?"expired":"OPERATION_CANCELLED"===o?"cancelled":"failed";return{...runtimeMessageBase(e),outcome:a,response:{jsonrpc:"2.0",id:e.request.id,error:{code:n.jsonRpcCode,message:o,data:{code:o,operationId:e.operationId,effectState:a,retryMode:n.retryMode}}},completedAt:(new Date).toISOString()}}function abortable(e,t){return t?t.aborted?Promise.resolve():new Promise((o,n)=>{const a=()=>o();t.addEventListener("abort",a,{once:!0}),e.then(()=>{t.removeEventListener("abort",a),o()},e=>{t.removeEventListener("abort",a),n(e)})}):e}function delay(e,t){return new Promise(o=>{if(t?.aborted)return o();const n=()=>{clearTimeout(a),o()},a=setTimeout(()=>{t?.removeEventListener("abort",n),o()},e);t?.addEventListener("abort",n,{once:!0})})}
@@ -1 +1 @@
1
- import{MsRuntimeMessageChannelAdapter}from"./ms-runtime-message-channel-adapter.js";export class MsRuntimeMessageChannelConsumerFactory{deps;constructor(e){this.deps=e}async create(e){return new MsRuntimeMessageChannelAdapter(e.dispatcher,this.deps.channel,this.deps.verifier,this.deps.ports,this.deps.journal,()=>e.getLease(),{...this.deps.options,allowedMethods:this.deps.allowedMethods,journalStoreId:e.binding.journalStoreId,journalGeneration:e.binding.journalGeneration})}}
1
+ import{MsRuntimeMessageChannelAdapter}from"./ms-runtime-message-channel-adapter.js";export class MsRuntimeMessageChannelConsumerFactory{deps;constructor(e){this.deps=e}async create(e){return new MsRuntimeMessageChannelAdapter(e.dispatcher,this.deps.channel,this.deps.verifier,this.deps.ports,this.deps.journal,()=>e.getLease(),{...this.deps.options,allowedMethods:this.deps.allowedMethods,journalStoreId:e.binding.journalStoreId,journalGeneration:e.binding.journalGeneration,...e.getBinding?{getJournalLineage:()=>{const n=e.getBinding();return{journalStoreId:n.journalStoreId,journalGeneration:n.journalGeneration}}}:{}})}}
@@ -1 +1 @@
1
- import{randomUUID}from"node:crypto";import{verifyAndCreateServiceCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";import{RuntimeClaimAuthorityProtocolError}from"./message-service-runtime-claim-authority.js";import{sessionSetModelMappingMatches}from"./session-set-model-mapping.js";import{RuntimeTransportFailurePolicy}from"./runtime-transport-failure-policy.js";export class MsRuntimeRpcAdapter{dispatcher;authority;authorizationVerifier;attemptPorts;authorizationJournal;getLease;options;controller;running;constructor(e,t,r,a,n,o,i){this.dispatcher=e,this.authority=t,this.authorizationVerifier=r,this.attemptPorts=a,this.authorizationJournal=n,this.getLease=o,this.options=i}start(){if(this.running)return;this.controller=new AbortController;const e=this.loop(this.controller.signal).finally(()=>{this.running===e&&(this.running=void 0)});this.running=e}async stop(e){const t=this.running;if(this.controller?.abort(),!t)return;await waitForAbort(t,e)===aborted&&this.running===t&&(this.running=void 0)}async loop(e){let t=randomUUID(),r="",a="";for(;!e.aborted;){const n=this.getLease();if(!n){await delay(this.options.pollDelayMs??1e3,e);continue}const o=`${n.instanceId}\n${n.runtimeEpoch}\n${this.options.handlerIdentity}\n${this.options.journalStoreId}\n${this.options.journalGeneration}`;if(r&&r!==o&&(t=randomUUID()),r=o,a!==o)try{const t=await this.authorizationJournal.listRecoverable(n.runtimeEpoch);for(const r of t){if(e.aborted||this.getLease()?.leaseId!==n.leaseId)break;if(!await this.recoverExact({operationId:r.operationId,requestMessageId:r.requestMessageId,dispatchEpoch:r.runtimeEpoch},n,e))throw new Error("durable runtime invocation is still owned by another claim")}a=o}catch{await delay(this.options.pollDelayMs??1e3,e);continue}let i;try{const r=this.authority.claimNext({claimRequestId:t,instanceId:n.instanceId,handlerIdentity:this.options.handlerIdentity,runtimeEpoch:n.runtimeEpoch,journalStoreId:this.options.journalStoreId,journalGeneration:this.options.journalGeneration,runtimeLeaseCredential:n.runtimeLeaseCredential}),a=await waitForAbort(r,e);if(a===aborted)return;i=a}catch(t){if(e.aborted)return;if(t instanceof RuntimeClaimAuthorityProtocolError)return;await delay(this.options.pollDelayMs??1e3,e);continue}if(e.aborted)return;t=randomUUID(),"empty"!==i.status?"fenced"!==i.status?await this.handleClaimed(i,n,e,"start").catch(()=>{}):await delay(this.options.pollDelayMs??1e3,e):await delay(Math.max(i.retryAfterMs,this.options.pollDelayMs??0),e)}}async recoverExact(e,t,r){if(e.dispatchEpoch!==t.runtimeEpoch)throw new Error("recovery candidate runtime epoch mismatch");const a=await this.authority.claimExact({operationId:e.operationId,requestMessageId:e.requestMessageId,handlerIdentity:this.options.handlerIdentity,dispatchEpoch:e.dispatchEpoch,runtimeEpoch:t.runtimeEpoch,journalStoreId:this.options.journalStoreId,journalGeneration:this.options.journalGeneration,runtimeLeaseCredential:t.runtimeLeaseCredential});return"terminal"===a.status?(await this.authorizationJournal.markTerminal(e.operationId).catch(()=>{}),!0):"claimed"===a.status&&(await this.handleClaimed(a,t,r,"recovery"),!0)}async handleClaimed(e,t,r,a="start"){const n={operationId:e.request.operationId,requestMessageId:e.request.messageId,dispatchEpoch:e.request.payload.command.context.dispatchEpoch},o=async()=>{await this.authorizationJournal.markTerminal(n.operationId).catch(()=>{})},i=async e=>{await this.authority.commitFailure(e),await o()},s=()=>{const r=this.getLease(),a=r?.leaseId===t.leaseId&&r.runtimeEpoch===t.runtimeEpoch?r.runtimeLeaseCredential:t.runtimeLeaseCredential;return{operationId:n.operationId,requestMessageId:n.requestMessageId,...e.fence,runtimeLeaseCredential:a}},c=async e=>{try{await i(e)}finally{await this.authority.releaseClaim({...s(),reason:"claim_rejected"}).catch(()=>{})}};if(e.cancelRequested)return await i({operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:prestartAttempt(e.request),outcome:"cancelled",code:"OPERATION_CANCELLED"}),void await this.authority.releaseClaim({...s(),reason:"cancelled"});let u;try{u=await this.authorizationJournal.prepare({operationId:n.operationId,requestMessageId:n.requestMessageId,method:e.request.payload.request.method,commandHash:e.request.payload.commandHash,runtimeEpoch:t.runtimeEpoch,journalStoreId:e.fence.journalStoreId,journalGeneration:e.fence.journalGeneration})}catch{return void await c({operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:prestartAttempt(e.request),outcome:"failed",code:"UNAUTHORIZED_TARGET"}).catch(()=>{})}const d=await this.authority.authorize({request:e.request,fence:e.fence,lease:t,journal:u});if("authorized"!==d.status)return"terminal"===d.status?(await o(),void await this.authority.releaseClaim({...s(),reason:"completed"}).catch(()=>{})):void await c({operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:prestartAttempt(e.request),outcome:"expired"===d.status?"expired":"cancelled"===d.status?"cancelled":"failed",code:"expired"===d.status?"COMMAND_EXPIRED":"cancelled"===d.status?"OPERATION_CANCELLED":"fenced"===d.status?"INSTANCE_EPOCH_MISMATCH":"UNAUTHORIZED_TARGET"});const m={operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:d.attempt},l={...m,attempt:prestartAttempt(e.request)};let h;const p=new AbortController;let E;try{assertCommandCorrelation(e.request,d.attempt.command,t.runtimeEpoch),h=await verifyAndCreateServiceCoreExecutionAttempt({operationId:n.operationId,runtimeEpoch:t.runtimeEpoch,fence:e.fence,authorization:d.attempt.authorization,derivationContext:{...d.attempt.derivationContext,localJournalRevision:u.journalRevisionBeforeAttempt,localPreparedEntryHash:u.preparedEntryHash},verifier:this.authorizationVerifier,abortSignal:p.signal,recordVerifiedAttempt:async t=>{const r=t.request.binding;await this.authorizationJournal.recordBoundary({operationId:n.operationId,expectedRevision:u.revision,preparedEntryHash:u.preparedEntryHash,boundary:{executionBoundaryId:r.executionBoundaryId,boundaryAssertionHash:r.boundaryAssertionHash,executionMode:r.executionMode,fence:e.fence,...r.handoffId?{handoffId:r.handoffId}:{}}})},resolvePorts:async()=>E??=await this.attemptPorts.resolve(m,{getRuntimeLeaseCredential:()=>this.currentRuntimeLeaseCredential(t)})})}catch{return p.abort(),void await c({...l,outcome:"failed",code:"UNAUTHORIZED_TARGET"})}if(!h.ok)return p.abort(),void await c({...l,outcome:"failed",code:"UNAUTHORIZED_TARGET"});let I=!1,C=!1;const f=new RuntimeTransportFailurePolicy;let y,w,M,v,N=!1,A=!1,R=!1,O=!1,T=!1,_=!1,b=!1;const g=new Promise(e=>{v=e}),L=e=>{b||(b=!0,v(e))};let x;const U=()=>{T=!0,p.abort(),M&&!x&&(x=M.cancel().catch(()=>({status:"not_cancellable"})))},q=()=>{I=!0,L("fenced"),U()},S=()=>q();r.addEventListener("abort",S,{once:!0});const P=()=>{const e=this.getLease();e&&e.leaseId===t.leaseId&&e.runtimeEpoch===t.runtimeEpoch||q()},H=setInterval(P,100);H.unref?.();const D=setInterval(()=>{this.authority.renewClaim(s()).then(e=>{f.recordSuccess(),"renewed"===e.status&&e.cancelRequested?(C=!0,L("cancel_requested"),U()):"renewed"!==e.status&&q()}).catch(()=>{f.recordFailure()&&q()})},this.options.claimRenewIntervalMs??2e4);D.unref?.();const j=Date.parse(d.attempt.command.context.executionDeadline)-Date.now(),G=setTimeout(()=>{_=!0,L("deadline"),U()},Math.max(0,Math.min(j,2147483647)));G.unref?.();const z=async e=>{if(!y)return;const r=this.currentConversationRunBoundaryLease(t);await this.finishConversationRunBoundary(m,r,y,e,"recovery"===a?"recovery":"runtime"),y=void 0};try{if(P(),I||r.aborted)return void await i({...m,outcome:"outcome_unknown",code:"INSTANCE_EPOCH_MISMATCH"});if(await this.authority.appendStarted(m),I||r.aborted)return void await i({...m,outcome:"outcome_unknown",code:"INSTANCE_EPOCH_MISMATCH"});if(_)return void await i({...m,outcome:"expired",code:"COMMAND_EXPIRED"});if(C)return void await i({...m,outcome:"cancelled",code:"OPERATION_CANCELLED"});const e=await this.startConversationRunBoundary(m,t,E);if(e?.rejected)return void await i({...m,outcome:"failed",code:e.code});if(y=e,I||r.aborted)throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime authority fenced after the conversation run boundary started");if("recovery"===a&&y)throw new RuntimeCommandError("OUTCOME_UNKNOWN","session.prompt crossed its durable run boundary before recovery");if(_)throw new RuntimeCommandError("COMMAND_EXPIRED","session.prompt expired after the conversation run boundary started");if(C)throw new RuntimeCommandError("OPERATION_CANCELLED","session.prompt was cancelled after the conversation run boundary started");let n;M=this.dispatcher.dispatch(d.attempt.command,h.context),A=!0,T&&U();const s=this.forwardProgress(M.progress,m,()=>I||r.aborted||C||_).catch(e=>{n=e}),c=M.completion.then(e=>(clearTimeout(G),{status:"completed",result:e}),e=>(clearTimeout(G),{status:"failed",error:e}));let u=await Promise.race([c,g.then(e=>({status:"interrupted",reason:e}))]);if("interrupted"===u.status){const e=u.reason;U();if("cancel_requested"===(x?await Promise.race([x,delay(this.options.cancellationGraceMs??1e3).then(()=>({status:"not_cancellable"}))]):{status:"not_cancellable"}).status&&(u=await Promise.race([c,delay(this.options.cancellationGraceMs??1e3).then(()=>({status:"interrupted",reason:e}))])),"interrupted"===u.status)throw new RuntimeCommandError("fenced"===u.reason?"INSTANCE_EPOCH_MISMATCH":"OUTCOME_UNKNOWN","session.prompt execution did not converge after interruption")}if(R=!0,"failed"===u.status)throw u.error;const l=u.result;N=!0;const p="session.prompt"===d.attempt.command.name&&!0===l.requestAborted;if(w=p?"cancelled":"completed",await s,n)throw n;if(await z(w),I||r.aborted)return void await i({...m,outcome:"outcome_unknown",code:"INSTANCE_EPOCH_MISMATCH"});if(_)return void await i({...m,outcome:"outcome_unknown",code:"OUTCOME_UNKNOWN"});if(C&&!p)throw new RuntimeCommandError("OUTCOME_UNKNOWN","cancel request raced a locally completed runtime effect");await this.authority.commitResult({...m,command:d.attempt.command,result:sanitizeServiceResult(d.attempt.command.name,l)}),await o()}catch(e){let t=e;if(y&&(!A||R))try{await z(w??(C||_?"cancelled":"failed"))}catch(r){t=new RuntimeCommandError("OUTCOME_UNKNOWN",`conversation run terminal outcome is unknown: ${errorText(r)}`,void 0,{cause:e})}if(N&&!isUncertainRuntimeError(t)&&(t=new RuntimeCommandError("OUTCOME_UNKNOWN",`runtime effect completed before its terminal receipt: ${errorText(t)}`,void 0,{cause:e})),y)return void(O=!0);const r=serviceRuntimeFailureCode(t),a="OUTCOME_UNKNOWN"===r||"INSTANCE_EPOCH_MISMATCH"===r||I||A&&(C||_),n="COMMAND_EXPIRED"===r||!A&&_,o=!A&&C;await i({...m,outcome:a?"outcome_unknown":n?"expired":o?"cancelled":"failed",code:"INSTANCE_EPOCH_MISMATCH"===r||I?"INSTANCE_EPOCH_MISMATCH":a?"OUTCOME_UNKNOWN":n?"COMMAND_EXPIRED":o?"OPERATION_CANCELLED":r??"RUNTIME_EXECUTION_FAILED"})}finally{clearInterval(H),clearInterval(D),clearTimeout(G),r.removeEventListener("abort",S),p.abort(),await this.authority.releaseClaim({...s(),reason:r.aborted||O?"worker_shutdown":"completed"}).catch(()=>{})}}async startConversationRunBoundary(e,t,r){const a=e.attempt.command;if("session.prompt"!==a.name)return;const n=r?.conversationRunBoundary;if(!n)throw new RuntimeCommandError("INVALID_PARAMS","Service session.prompt lacks its conversation run boundary");const o=a.context.conversation,i=await n.boundary.start({operationId:e.operationId,requestMessageId:e.requestMessageId,conversationId:o.runtimeConversationUid,historyGeneration:n.historyGeneration,runtimeRunId:a.params.runtimeRunId,lease:exactRunBoundaryLease(t)});if("rejected"===i.status)return{rejected:!0,code:i.reason};if(i.historyGeneration!==n.historyGeneration)throw new RuntimeCommandError("OUTCOME_UNKNOWN","conversation run boundary changed history generation");return{boundary:n.boundary,historyGeneration:n.historyGeneration,activeRunRegistrationId:i.activeRunRegistrationId,runtimeRunId:a.params.runtimeRunId}}currentRuntimeLeaseCredential(e){const t=this.getLease();if(!t||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch)throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime lease changed before deferred resource redemption");return t.runtimeLeaseCredential}currentConversationRunBoundaryLease(e){const t=this.getLease();if(!t||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch||t.handlerIdentity!==e.handlerIdentity)throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime lease changed before conversation run convergence");return t}async finishConversationRunBoundary(e,t,r,a,n){const o=e.attempt.command.context;if("conversation"!==o.scope)throw new RuntimeCommandError("OUTCOME_UNKNOWN","conversation run boundary lost its target");const i=await r.boundary.finish({activeRunRegistrationId:r.activeRunRegistrationId,operationId:e.operationId,requestMessageId:e.requestMessageId,conversationId:o.conversation.runtimeConversationUid,historyGeneration:r.historyGeneration,runtimeRunId:r.runtimeRunId,terminalState:a,source:n,lease:exactRunBoundaryLease(t)});if("rejected"===i.status||i.activeRunRegistrationId!==r.activeRunRegistrationId)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`conversation run finish rejected: ${"rejected"===i.status?i.reason:"registration_mismatch"}`)}async forwardProgress(e,t,r){for await(const a of e){if(r())return;await this.authority.appendProgress({...t,progress:a})}}}function exactRunBoundaryLease(e){return{leaseId:e.leaseId,handlerIdentity:e.handlerIdentity,runtimeEpoch:e.runtimeEpoch,runtimeLeaseCredential:e.runtimeLeaseCredential}}function serviceRuntimeFailureCode(e){const t=e?.code;return t&&["OUTCOME_UNKNOWN","INSTANCE_EPOCH_MISMATCH","COMMAND_EXPIRED","OPERATION_CANCELLED","IDEMPOTENCY_CONFLICT","UNAUTHORIZED_TARGET","AGENT_DEFAULT_MODEL_NOT_CONFIGURED"].includes(t)?t:void 0}function isUncertainRuntimeError(e){return e instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===e.code||"INSTANCE_EPOCH_MISMATCH"===e.code)}function errorText(e){return e instanceof Error?e.message:String(e)}export function sanitizeServiceResult(e,t){if("cron.status"!==e||!t||"object"!=typeof t)return t;const r=t;return{enabled:r.enabled,jobCount:r.jobCount,..."number"==typeof r.nextWakeAtMs?{nextWakeAtMs:r.nextWakeAtMs}:{}}}function assertCommandCorrelation(e,t,r){if(e.operationId!==t.context.operationId||e.payload.paramsHash!==t.context.paramsHash||t.context.dispatchEpoch!==r||e.payload.command.name!==t.name||!sessionSetModelMappingMatches(e.payload.request,t))throw new Error("runtime invocation command correlation failed")}function prestartAttempt(e){return{command:e.payload.command,executionBoundaryId:"prestart",boundaryAssertionHash:e.payload.commandHash,executionMode:"start",validationContext:{},authorization:{},derivationContext:{}}}const aborted=Symbol("aborted");function waitForAbort(e,t){return t?t.aborted?(e.catch(()=>{}),Promise.resolve(aborted)):new Promise((r,a)=>{let n=!1;const o=()=>{n||(n=!0,r(aborted))};t.addEventListener("abort",o,{once:!0}),e.then(e=>{n||(n=!0,t.removeEventListener("abort",o),r(e))},e=>{n||(n=!0,t.removeEventListener("abort",o),a(e))})}):e}const delay=(e,t)=>new Promise(r=>{if(t?.aborted)return r();const a=()=>{clearTimeout(n),r()},n=setTimeout(()=>{t?.removeEventListener("abort",a),r()},e);t?.addEventListener("abort",a,{once:!0})});
1
+ import{randomUUID}from"node:crypto";import{verifyAndCreateServiceCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";import{RuntimeClaimAuthorityProtocolError}from"./message-service-runtime-claim-authority.js";import{sessionSetModelMappingMatches}from"./session-set-model-mapping.js";import{RuntimeTransportFailurePolicy}from"./runtime-transport-failure-policy.js";export class MsRuntimeRpcAdapter{dispatcher;authority;authorizationVerifier;attemptPorts;authorizationJournal;getLease;options;controller;running;constructor(e,t,r,a,n,o,i){this.dispatcher=e,this.authority=t,this.authorizationVerifier=r,this.attemptPorts=a,this.authorizationJournal=n,this.getLease=o,this.options=i}start(){if(this.running)return;this.controller=new AbortController;const e=this.loop(this.controller.signal).finally(()=>{this.running===e&&(this.running=void 0)});this.running=e}async stop(e){const t=this.running;if(this.controller?.abort(),!t)return;await waitForAbort(t,e)===aborted&&this.running===t&&(this.running=void 0)}journalLineage(){return this.options.getJournalLineage?.()??{journalStoreId:this.options.journalStoreId,journalGeneration:this.options.journalGeneration}}async loop(e){let t=randomUUID(),r="",a="";for(;!e.aborted;){const n=this.getLease();if(!n){await delay(this.options.pollDelayMs??1e3,e);continue}const o=this.journalLineage(),i=`${n.instanceId}\n${n.runtimeEpoch}\n${this.options.handlerIdentity}\n${o.journalStoreId}\n${o.journalGeneration}`;if(r&&r!==i&&(t=randomUUID()),r=i,a!==i)try{const t=await this.authorizationJournal.listRecoverable(n.runtimeEpoch);for(const r of t){if(e.aborted||this.getLease()?.leaseId!==n.leaseId)break;if(!await this.recoverExact({operationId:r.operationId,requestMessageId:r.requestMessageId,dispatchEpoch:r.runtimeEpoch},n,e))throw new Error("durable runtime invocation is still owned by another claim")}a=i}catch{await delay(this.options.pollDelayMs??1e3,e);continue}let s;try{const r=this.authority.claimNext({claimRequestId:t,instanceId:n.instanceId,handlerIdentity:this.options.handlerIdentity,runtimeEpoch:n.runtimeEpoch,journalStoreId:o.journalStoreId,journalGeneration:o.journalGeneration,runtimeLeaseCredential:n.runtimeLeaseCredential}),a=await waitForAbort(r,e);if(a===aborted)return;s=a}catch(t){if(e.aborted)return;if(t instanceof RuntimeClaimAuthorityProtocolError)return;await delay(this.options.pollDelayMs??1e3,e);continue}if(e.aborted)return;t=randomUUID(),"empty"!==s.status?"fenced"!==s.status?await this.handleClaimed(s,n,e,"start").catch(()=>{}):await delay(this.options.pollDelayMs??1e3,e):await delay(Math.max(s.retryAfterMs,this.options.pollDelayMs??0),e)}}async recoverExact(e,t,r){if(e.dispatchEpoch!==t.runtimeEpoch)throw new Error("recovery candidate runtime epoch mismatch");const a=this.journalLineage(),n=await this.authority.claimExact({operationId:e.operationId,requestMessageId:e.requestMessageId,handlerIdentity:this.options.handlerIdentity,dispatchEpoch:e.dispatchEpoch,runtimeEpoch:t.runtimeEpoch,journalStoreId:a.journalStoreId,journalGeneration:a.journalGeneration,runtimeLeaseCredential:t.runtimeLeaseCredential});return"terminal"===n.status?(await this.authorizationJournal.markTerminal(e.operationId).catch(()=>{}),!0):"claimed"===n.status&&(await this.handleClaimed(n,t,r,"recovery"),!0)}async handleClaimed(e,t,r,a="start"){const n={operationId:e.request.operationId,requestMessageId:e.request.messageId,dispatchEpoch:e.request.payload.command.context.dispatchEpoch},o=async()=>{await this.authorizationJournal.markTerminal(n.operationId).catch(()=>{})},i=async e=>{await this.authority.commitFailure(e),await o()},s=()=>{const r=this.getLease(),a=r?.leaseId===t.leaseId&&r.runtimeEpoch===t.runtimeEpoch?r.runtimeLeaseCredential:t.runtimeLeaseCredential;return{operationId:n.operationId,requestMessageId:n.requestMessageId,...e.fence,runtimeLeaseCredential:a}},c=async e=>{try{await i(e)}finally{await this.authority.releaseClaim({...s(),reason:"claim_rejected"}).catch(()=>{})}};if(e.cancelRequested)return await i({operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:prestartAttempt(e.request),outcome:"cancelled",code:"OPERATION_CANCELLED"}),void await this.authority.releaseClaim({...s(),reason:"cancelled"});let u;try{u=await this.authorizationJournal.prepare({operationId:n.operationId,requestMessageId:n.requestMessageId,method:e.request.payload.request.method,commandHash:e.request.payload.commandHash,runtimeEpoch:t.runtimeEpoch,journalStoreId:e.fence.journalStoreId,journalGeneration:e.fence.journalGeneration})}catch{return void await c({operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:prestartAttempt(e.request),outcome:"failed",code:"UNAUTHORIZED_TARGET"}).catch(()=>{})}const d=await this.authority.authorize({request:e.request,fence:e.fence,lease:t,journal:u});if("authorized"!==d.status)return"terminal"===d.status?(await o(),void await this.authority.releaseClaim({...s(),reason:"completed"}).catch(()=>{})):void await c({operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:prestartAttempt(e.request),outcome:"expired"===d.status?"expired":"cancelled"===d.status?"cancelled":"failed",code:"expired"===d.status?"COMMAND_EXPIRED":"cancelled"===d.status?"OPERATION_CANCELLED":"fenced"===d.status?"INSTANCE_EPOCH_MISMATCH":"UNAUTHORIZED_TARGET"});const m={operationId:n.operationId,requestMessageId:n.requestMessageId,request:e.request,fence:e.fence,claimLeaseExpiresAt:e.leaseExpiresAt,runtimeLeaseCredential:t.runtimeLeaseCredential,attempt:d.attempt},l={...m,attempt:prestartAttempt(e.request)};let h;const p=new AbortController;let E;try{assertCommandCorrelation(e.request,d.attempt.command,t.runtimeEpoch),h=await verifyAndCreateServiceCoreExecutionAttempt({operationId:n.operationId,runtimeEpoch:t.runtimeEpoch,fence:e.fence,authorization:d.attempt.authorization,derivationContext:{...d.attempt.derivationContext,localJournalRevision:u.journalRevisionBeforeAttempt,localPreparedEntryHash:u.preparedEntryHash},verifier:this.authorizationVerifier,abortSignal:p.signal,recordVerifiedAttempt:async t=>{const r=t.request.binding;await this.authorizationJournal.recordBoundary({operationId:n.operationId,expectedRevision:u.revision,preparedEntryHash:u.preparedEntryHash,boundary:{executionBoundaryId:r.executionBoundaryId,boundaryAssertionHash:r.boundaryAssertionHash,executionMode:r.executionMode,fence:e.fence,...r.handoffId?{handoffId:r.handoffId}:{}}})},resolvePorts:async()=>E??=await this.attemptPorts.resolve(m,{getRuntimeLeaseCredential:()=>this.currentRuntimeLeaseCredential(t)})})}catch{return p.abort(),void await c({...l,outcome:"failed",code:"UNAUTHORIZED_TARGET"})}if(!h.ok)return p.abort(),void await c({...l,outcome:"failed",code:"UNAUTHORIZED_TARGET"});let I=!1,C=!1;const f=new RuntimeTransportFailurePolicy;let y,w,M,v,N=!1,A=!1,R=!1,O=!1,T=!1,_=!1,g=!1;const b=new Promise(e=>{v=e}),L=e=>{g||(g=!0,v(e))};let x;const U=()=>{T=!0,p.abort(),M&&!x&&(x=M.cancel().catch(()=>({status:"not_cancellable"})))},q=()=>{I=!0,L("fenced"),U()},S=()=>q();r.addEventListener("abort",S,{once:!0});const P=()=>{const e=this.getLease();e&&e.leaseId===t.leaseId&&e.runtimeEpoch===t.runtimeEpoch||q()},H=setInterval(P,100);H.unref?.();const D=setInterval(()=>{this.authority.renewClaim(s()).then(e=>{f.recordSuccess(),"renewed"===e.status&&e.cancelRequested?(C=!0,L("cancel_requested"),U()):"renewed"!==e.status&&q()}).catch(()=>{f.recordFailure()&&q()})},this.options.claimRenewIntervalMs??2e4);D.unref?.();const j=Date.parse(d.attempt.command.context.executionDeadline)-Date.now(),G=setTimeout(()=>{_=!0,L("deadline"),U()},Math.max(0,Math.min(j,2147483647)));G.unref?.();const z=async e=>{if(!y)return;const r=this.currentConversationRunBoundaryLease(t);await this.finishConversationRunBoundary(m,r,y,e,"recovery"===a?"recovery":"runtime"),y=void 0};try{if(P(),I||r.aborted)return void await i({...m,outcome:"outcome_unknown",code:"INSTANCE_EPOCH_MISMATCH"});if(await this.authority.appendStarted(m),I||r.aborted)return void await i({...m,outcome:"outcome_unknown",code:"INSTANCE_EPOCH_MISMATCH"});if(_)return void await i({...m,outcome:"expired",code:"COMMAND_EXPIRED"});if(C)return void await i({...m,outcome:"cancelled",code:"OPERATION_CANCELLED"});const e=await this.startConversationRunBoundary(m,t,E);if(e?.rejected)return void await i({...m,outcome:"failed",code:e.code});if(y=e,I||r.aborted)throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime authority fenced after the conversation run boundary started");if("recovery"===a&&y)throw new RuntimeCommandError("OUTCOME_UNKNOWN","session.prompt crossed its durable run boundary before recovery");if(_)throw new RuntimeCommandError("COMMAND_EXPIRED","session.prompt expired after the conversation run boundary started");if(C)throw new RuntimeCommandError("OPERATION_CANCELLED","session.prompt was cancelled after the conversation run boundary started");let n;M=this.dispatcher.dispatch(d.attempt.command,h.context),A=!0,T&&U();const s=this.forwardProgress(M.progress,m,()=>I||r.aborted||C||_).catch(e=>{n=e}),c=M.completion.then(e=>(clearTimeout(G),{status:"completed",result:e}),e=>(clearTimeout(G),{status:"failed",error:e}));let u=await Promise.race([c,b.then(e=>({status:"interrupted",reason:e}))]);if("interrupted"===u.status){const e=u.reason;U();if("cancel_requested"===(x?await Promise.race([x,delay(this.options.cancellationGraceMs??1e3).then(()=>({status:"not_cancellable"}))]):{status:"not_cancellable"}).status&&(u=await Promise.race([c,delay(this.options.cancellationGraceMs??1e3).then(()=>({status:"interrupted",reason:e}))])),"interrupted"===u.status)throw new RuntimeCommandError("fenced"===u.reason?"INSTANCE_EPOCH_MISMATCH":"OUTCOME_UNKNOWN","session.prompt execution did not converge after interruption")}if(R=!0,"failed"===u.status)throw u.error;const l=u.result;N=!0;const p="session.prompt"===d.attempt.command.name&&!0===l.requestAborted;if(w=p?"cancelled":"completed",await s,n)throw n;if(await z(w),I||r.aborted)return void await i({...m,outcome:"outcome_unknown",code:"INSTANCE_EPOCH_MISMATCH"});if(_)return void await i({...m,outcome:"outcome_unknown",code:"OUTCOME_UNKNOWN"});if(C&&!p)throw new RuntimeCommandError("OUTCOME_UNKNOWN","cancel request raced a locally completed runtime effect");await this.authority.commitResult({...m,command:d.attempt.command,result:sanitizeServiceResult(d.attempt.command.name,l)}),await o()}catch(e){let t=e;if(y&&(!A||R))try{await z(w??(C||_?"cancelled":"failed"))}catch(r){t=new RuntimeCommandError("OUTCOME_UNKNOWN",`conversation run terminal outcome is unknown: ${errorText(r)}`,void 0,{cause:e})}if(N&&!isUncertainRuntimeError(t)&&(t=new RuntimeCommandError("OUTCOME_UNKNOWN",`runtime effect completed before its terminal receipt: ${errorText(t)}`,void 0,{cause:e})),y)return void(O=!0);const r=serviceRuntimeFailureCode(t),a="OUTCOME_UNKNOWN"===r||"INSTANCE_EPOCH_MISMATCH"===r||I||A&&(C||_),n="COMMAND_EXPIRED"===r||!A&&_,o=!A&&C;await i({...m,outcome:a?"outcome_unknown":n?"expired":o?"cancelled":"failed",code:"INSTANCE_EPOCH_MISMATCH"===r||I?"INSTANCE_EPOCH_MISMATCH":a?"OUTCOME_UNKNOWN":n?"COMMAND_EXPIRED":o?"OPERATION_CANCELLED":r??"RUNTIME_EXECUTION_FAILED"})}finally{clearInterval(H),clearInterval(D),clearTimeout(G),r.removeEventListener("abort",S),p.abort(),await this.authority.releaseClaim({...s(),reason:r.aborted||O?"worker_shutdown":"completed"}).catch(()=>{})}}async startConversationRunBoundary(e,t,r){const a=e.attempt.command;if("session.prompt"!==a.name)return;const n=r?.conversationRunBoundary;if(!n)throw new RuntimeCommandError("INVALID_PARAMS","Service session.prompt lacks its conversation run boundary");const o=a.context.conversation,i=await n.boundary.start({operationId:e.operationId,requestMessageId:e.requestMessageId,conversationId:o.runtimeConversationUid,historyGeneration:n.historyGeneration,runtimeRunId:a.params.runtimeRunId,lease:exactRunBoundaryLease(t)});if("rejected"===i.status)return{rejected:!0,code:i.reason};if(i.historyGeneration!==n.historyGeneration)throw new RuntimeCommandError("OUTCOME_UNKNOWN","conversation run boundary changed history generation");return{boundary:n.boundary,historyGeneration:n.historyGeneration,activeRunRegistrationId:i.activeRunRegistrationId,runtimeRunId:a.params.runtimeRunId}}currentRuntimeLeaseCredential(e){const t=this.getLease();if(!t||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch)throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime lease changed before deferred resource redemption");return t.runtimeLeaseCredential}currentConversationRunBoundaryLease(e){const t=this.getLease();if(!t||t.leaseId!==e.leaseId||t.runtimeEpoch!==e.runtimeEpoch||t.handlerIdentity!==e.handlerIdentity)throw new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime lease changed before conversation run convergence");return t}async finishConversationRunBoundary(e,t,r,a,n){const o=e.attempt.command.context;if("conversation"!==o.scope)throw new RuntimeCommandError("OUTCOME_UNKNOWN","conversation run boundary lost its target");const i=await r.boundary.finish({activeRunRegistrationId:r.activeRunRegistrationId,operationId:e.operationId,requestMessageId:e.requestMessageId,conversationId:o.conversation.runtimeConversationUid,historyGeneration:r.historyGeneration,runtimeRunId:r.runtimeRunId,terminalState:a,source:n,lease:exactRunBoundaryLease(t)});if("rejected"===i.status||i.activeRunRegistrationId!==r.activeRunRegistrationId)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`conversation run finish rejected: ${"rejected"===i.status?i.reason:"registration_mismatch"}`)}async forwardProgress(e,t,r){for await(const a of e){if(r())return;await this.authority.appendProgress({...t,progress:a})}}}function exactRunBoundaryLease(e){return{leaseId:e.leaseId,handlerIdentity:e.handlerIdentity,runtimeEpoch:e.runtimeEpoch,runtimeLeaseCredential:e.runtimeLeaseCredential}}function serviceRuntimeFailureCode(e){const t=e?.code;return t&&["OUTCOME_UNKNOWN","INSTANCE_EPOCH_MISMATCH","COMMAND_EXPIRED","OPERATION_CANCELLED","IDEMPOTENCY_CONFLICT","UNAUTHORIZED_TARGET","AGENT_DEFAULT_MODEL_NOT_CONFIGURED"].includes(t)?t:void 0}function isUncertainRuntimeError(e){return e instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===e.code||"INSTANCE_EPOCH_MISMATCH"===e.code)}function errorText(e){return e instanceof Error?e.message:String(e)}export function sanitizeServiceResult(e,t){if("cron.status"!==e||!t||"object"!=typeof t)return t;const r=t;return{enabled:r.enabled,jobCount:r.jobCount,..."number"==typeof r.nextWakeAtMs?{nextWakeAtMs:r.nextWakeAtMs}:{}}}function assertCommandCorrelation(e,t,r){if(e.operationId!==t.context.operationId||e.payload.paramsHash!==t.context.paramsHash||t.context.dispatchEpoch!==r||e.payload.command.name!==t.name||!sessionSetModelMappingMatches(e.payload.request,t))throw new Error("runtime invocation command correlation failed")}function prestartAttempt(e){return{command:e.payload.command,executionBoundaryId:"prestart",boundaryAssertionHash:e.payload.commandHash,executionMode:"start",validationContext:{},authorization:{},derivationContext:{}}}const aborted=Symbol("aborted");function waitForAbort(e,t){return t?t.aborted?(e.catch(()=>{}),Promise.resolve(aborted)):new Promise((r,a)=>{let n=!1;const o=()=>{n||(n=!0,r(aborted))};t.addEventListener("abort",o,{once:!0}),e.then(e=>{n||(n=!0,t.removeEventListener("abort",o),r(e))},e=>{n||(n=!0,t.removeEventListener("abort",o),a(e))})}):e}const delay=(e,t)=>new Promise(r=>{if(t?.aborted)return r();const a=()=>{clearTimeout(n),r()},n=setTimeout(()=>{t?.removeEventListener("abort",a),r()},e);t?.addEventListener("abort",a,{once:!0})});
@@ -1 +1 @@
1
- import{MsRuntimeRpcAdapter}from"./ms-runtime-rpc-adapter.js";export class MsRuntimeRpcConsumerFactory{deps;constructor(e){this.deps=e}async create(e){return new MsRuntimeRpcAdapter(e.dispatcher,this.deps.authority,this.deps.authorizationVerifier,this.deps.attemptPorts,this.deps.authorizationJournal,e.getLease,{handlerIdentity:e.binding.handlerIdentity,journalStoreId:e.binding.journalStoreId,journalGeneration:e.binding.journalGeneration,...void 0===this.deps.pollDelayMs?{}:{pollDelayMs:this.deps.pollDelayMs}})}}
1
+ import{MsRuntimeRpcAdapter}from"./ms-runtime-rpc-adapter.js";export class MsRuntimeRpcConsumerFactory{deps;constructor(e){this.deps=e}async create(e){return new MsRuntimeRpcAdapter(e.dispatcher,this.deps.authority,this.deps.authorizationVerifier,this.deps.attemptPorts,this.deps.authorizationJournal,e.getLease,{handlerIdentity:e.binding.handlerIdentity,journalStoreId:e.binding.journalStoreId,journalGeneration:e.binding.journalGeneration,...e.getBinding?{getJournalLineage:()=>{const t=e.getBinding();return{journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration}}}:{},...void 0===this.deps.pollDelayMs?{}:{pollDelayMs:this.deps.pollDelayMs}})}}
@@ -1 +1 @@
1
- import{createHash,createPrivateKey,randomUUID}from"node:crypto";import{join}from"node:path";import{CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES,RUNTIME_METHOD_PARITY_MANIFEST,RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION,RUNTIME_RPC_CONTRACT_REVISION}from"@beeos-ai/beeos-types/runtime";import{agentBoundFetch}from"../agent-auth.js";import{FileRuntimeCommandJournal}from"./runtime-command-journal.js";import{FileRuntimeAgentIdentityRegistry}from"./runtime-agent-identity-registry.js";import{RuntimeRegistrationIdentityStore}from"./runtime-registration-identity-store.js";import{InstanceAuthorityRuntimeRegistrationTransport,RuntimeRegistrationCoordinator,RuntimeRegistrationJournalAdvanceRequiredError}from"./runtime-registration.js";import{verifiedRuntimeParityManifestDigest}from"./runtime-parity-manifest-digest.js";import{intersectRuntimeServiceMethodsWithBackendCapabilities}from"./runtime-service-contract-compatibility.js";export class RuntimeProductionAuthority{options;journal;identities;identityStore;handlerIdentity;listeners=new Set;coordinator;unsubscribeCoordinator;starting;rolling=new Map;refreshing;stoppedCoordinators=new WeakSet;coordinatorGeneration=0;lineage;identity;preparing;runtimeConsumerLive=!1;runtimeServiceMethods=Object.freeze([]);realtimeCapabilityReady=new Map;resourceBindingContractRevision;manifestDigest=verifiedRuntimeParityManifestDigest();started=!1;lifecycleVersion=0;authorityLeasePending=!1;boundAgentFetch;constructor(t){this.options=t,this.handlerIdentity=t.handlerIdentity??(process.env.HOSTNAME?.trim()||process.env.POD_NAME?.trim()||randomUUID()),this.journal=new FileRuntimeCommandJournal(join(t.stateDirectory,"runtime-command-journal.json")),this.identities=new FileRuntimeAgentIdentityRegistry(join(t.stateDirectory,"runtime-agent-identities.json")),this.boundAgentFetch=t.boundAgentFetch??((e,i)=>agentBoundFetch(e,t.keys,i)),this.identityStore=new RuntimeRegistrationIdentityStore(join(t.stateDirectory,"runtime-registration-identity.json"))}get agentKeys(){return this.options.keys}get stateDirectory(){return this.options.stateDirectory}get authorityLease(){return this.coordinator?.authorityLease??null}subscribeAuthority(t){return this.listeners.add(t),()=>{this.listeners.delete(t)}}async prepareConsumerBinding(){return this.preparing||(this.preparing=(async()=>{const t=await this.identityStore.acquire(this.handlerIdentity,this.journal),e={journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration};return this.lineage=e,this.identity=t,this.consumerBinding(t,e)})().finally(()=>{this.preparing=void 0})),await this.preparing}setRuntimeConsumerLive(t){if(this.started)throw new Error("runtime consumer availability is immutable while registered");this.runtimeConsumerLive=t}setRuntimeServiceMethods(t){if(this.started)throw new Error("runtime Service methods are immutable while registered");this.runtimeServiceMethods=intersectRuntimeServiceMethodsWithBackendCapabilities(t)}setRuntimeRealtimeCapabilityReady(t,e){const i=this.realtimeCapabilityReady.get(t)!==e;if(this.realtimeCapabilityReady.set(t,e),!i||!this.started)return;const r=this.lifecycleVersion;this.refreshRealtimeCapabilities(r).catch(t=>this.options.logger?.warn?.(`[runtime-authority] realtime capability refresh fenced: ${t instanceof Error?t.message:t}`))}setRuntimeResourceBindingContractRevision(t){if(this.started)throw new Error("runtime resource binding revision is immutable while registered");if(void 0!==t&&t!==RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION)throw new Error("unsupported runtime resource binding revision");this.resourceBindingContractRevision=t}async start(t){if(this.starting)return await this.starting;if(this.started)return;if(t?.aborted)return;const e=this.lifecycleVersion,i=this.startOnce(e,t).finally(()=>{this.starting===i&&(this.starting=void 0)});this.starting=i,await i}stop(){this.lifecycleVersion++,this.starting=void 0,this.started=!1;const t=this.coordinator;t&&this.detachCoordinator(t),this.publish(null)}async startOnce(t,e){if(await this.prepareConsumerBinding(),t!==this.lifecycleVersion||e?.aborted)return;this.lineage;const i=this.identity;this.started=!0,await this.activate(i,t)}async activate(t,e){if(!this.isLifecycleCurrent(e))return;if(this.resourceBindingContractRevision&&!this.runtimeConsumerLive)throw new Error("resource binding revision requires a live Message Service consumer");if(!this.runtimeConsumerLive||0===this.runtimeServiceMethods.length)throw new Error(`runtime registration refused: consumerLive=${this.runtimeConsumerLive} methods=${this.runtimeServiceMethods.length}`);const i=[...this.runtimeServiceMethods],r=runtimeCapabilities(this.runtimeServiceMethods,this.realtimeCapabilityReady);this.options.logger?.info?.(`[runtime-authority] advertising ${i.length} methods (renew-safe)`);const n=++this.coordinatorGeneration,o=new RuntimeRegistrationCoordinator(new InstanceAuthorityRuntimeRegistrationTransport({agentGatewayUrl:this.options.agentGatewayUrl,registrationId:t.registrationId,agentFetch:this.options.agentFetch}),{registrationId:t.registrationId,handlerIdentity:t.handlerIdentity,contractRevision:RUNTIME_RPC_CONTRACT_REVISION,runtimeMethods:i,capabilities:r,manifestDigest:this.manifestDigest,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration,instanceIdentityKeyId:createHash("sha256").update(this.options.keys.publicKey).digest("hex"),privateKey:createPrivateKey({key:privateKeyDer(this.options.keys.privateKey),format:"der",type:"pkcs8"})},(t,i)=>{if(!this.isCoordinatorCurrent(e,n,o))return;const r=i instanceof Error?i.message:void 0!==i?String(i):"";this.options.logger?.warn?.(r?`[runtime-authority] lease lost: ${t}: ${r}`:`[runtime-authority] lease lost: ${t}`);const s=i instanceof RuntimeRegistrationJournalAdvanceRequiredError?i:void 0;("expired"===t||s)&&this.rollExpired(e,n,o,s).catch(t=>this.options.logger?.warn?.(`[runtime-authority] expired registration roll fenced: ${t instanceof Error?t.message:t}`))});this.coordinator=o,this.unsubscribeCoordinator=o.subscribeAuthority(t=>{this.isCoordinatorCurrent(e,n,o)&&(t&&this.authorityLeasePending?(this.authorityLeasePending=!1,this.options.logger?.info?.("[runtime-authority] registration active (recovered after handoff)")):t||(this.authorityLeasePending=!0),this.publish(t))});const s=await o.start();this.isCoordinatorCurrent(e,n,o)?(this.options.logger?.info?.("[runtime-authority] registration "+(s?"active":"pending")),s||(this.authorityLeasePending=!0)):this.stopCoordinatorOnce(o)}async rollExpired(t,e,i,r){if(!this.lineage||!this.isCoordinatorCurrent(t,e,i))return;const n=this.rolling.get(e);if(n)return await n;const o=(async()=>{if(!this.isCoordinatorCurrent(t,e,i))return;this.detachCoordinator(i);const n=this.identity;if(!n)return;const o=await this.identityStore.roll(n,this.journal,{authorityCurrentGeneration:r?.currentJournalGeneration,expectedJournalGeneration:r?.expectedJournalGeneration});this.isLifecycleCurrent(t)&&void 0===this.coordinator&&(this.identity=o,this.lineage={journalStoreId:o.journalStoreId,journalGeneration:o.journalGeneration},await this.activate(o,t))})().finally(()=>{this.rolling.get(e)===o&&this.rolling.delete(e)});this.rolling.set(e,o),await o}async refreshRealtimeCapabilities(t){if(!this.isLifecycleCurrent(t))return;const e=this.refreshing;if(e)return await e;const i=(async()=>{if(!this.isLifecycleCurrent(t))return;const e=this.identity,i=this.coordinator;e&&i&&(i.authorityLease?(this.detachCoordinator(i),this.isLifecycleCurrent(t)&&void 0===this.coordinator&&await this.activate(e,t)):this.options.logger?.info?.("[runtime-authority] deferring realtime capability refresh until registration is active"))})().finally(()=>{this.refreshing===i&&(this.refreshing=void 0)});this.refreshing=i,await i}isLifecycleCurrent(t){return this.started&&t===this.lifecycleVersion}isCoordinatorCurrent(t,e,i){return this.isLifecycleCurrent(t)&&this.coordinatorGeneration===e&&this.coordinator===i}detachCoordinator(t){this.coordinator===t&&(this.unsubscribeCoordinator?.(),this.unsubscribeCoordinator=void 0,this.coordinator=void 0),this.stopCoordinatorOnce(t)}stopCoordinatorOnce(t){this.stoppedCoordinators.has(t)||(this.stoppedCoordinators.add(t),t.stop())}publish(t){for(const e of this.listeners)try{e(t)}catch{}}consumerBinding(t,e){return Object.freeze({handlerIdentity:t.handlerIdentity,journalStoreId:e.journalStoreId,journalGeneration:e.journalGeneration})}}const ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex"),privateKeyDer=t=>32===t.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(t)]):Buffer.from(t),REALTIME_CAPABILITY_NAMES=["terminal","canvas"];function runtimeCapabilities(t,e){const i=new Set(t),r=RUNTIME_METHOD_PARITY_MANIFEST.filter(t=>t.serviceAllowed&&void 0!==runtimeInvocationMethod(t)&&i.has(runtimeInvocationMethod(t))).map(t=>t.capability),n=REALTIME_CAPABILITY_NAMES.filter(t=>!0===e.get(t));return[...new Set([...r,...n])]}function runtimeInvocationMethod(t){return"methods"===t.serviceRoute&&t.serviceMethod?t.serviceMethod:Object.entries(CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES).find(([,e])=>e===t.canonicalCommand)?.[0]}
1
+ import{createHash,createPrivateKey,randomUUID}from"node:crypto";import{join}from"node:path";import{CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES,RUNTIME_METHOD_PARITY_MANIFEST,RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION,RUNTIME_RPC_CONTRACT_REVISION}from"@beeos-ai/beeos-types/runtime";import{agentBoundFetch}from"../agent-auth.js";import{FileRuntimeCommandJournal}from"./runtime-command-journal.js";import{FileRuntimeAgentIdentityRegistry}from"./runtime-agent-identity-registry.js";import{RuntimeRegistrationIdentityStore}from"./runtime-registration-identity-store.js";import{InstanceAuthorityRuntimeRegistrationTransport,RuntimeRegistrationCoordinator,RuntimeRegistrationJournalAdvanceRequiredError}from"./runtime-registration.js";import{verifiedRuntimeParityManifestDigest}from"./runtime-parity-manifest-digest.js";import{intersectRuntimeServiceMethodsWithBackendCapabilities}from"./runtime-service-contract-compatibility.js";export class RuntimeProductionAuthority{options;journal;identities;identityStore;handlerIdentity;listeners=new Set;coordinator;unsubscribeCoordinator;starting;rolling=new Map;refreshing;stoppedCoordinators=new WeakSet;coordinatorGeneration=0;lineage;identity;preparing;runtimeConsumerLive=!1;runtimeServiceMethods=Object.freeze([]);realtimeCapabilityReady=new Map;resourceBindingContractRevision;manifestDigest=verifiedRuntimeParityManifestDigest();started=!1;lifecycleVersion=0;authorityLeasePending=!1;boundAgentFetch;constructor(t){this.options=t,this.handlerIdentity=t.handlerIdentity??(process.env.HOSTNAME?.trim()||process.env.POD_NAME?.trim()||randomUUID()),this.journal=new FileRuntimeCommandJournal(join(t.stateDirectory,"runtime-command-journal.json")),this.identities=new FileRuntimeAgentIdentityRegistry(join(t.stateDirectory,"runtime-agent-identities.json")),this.boundAgentFetch=t.boundAgentFetch??((i,e)=>agentBoundFetch(i,t.keys,e)),this.identityStore=new RuntimeRegistrationIdentityStore(join(t.stateDirectory,"runtime-registration-identity.json"))}get agentKeys(){return this.options.keys}get stateDirectory(){return this.options.stateDirectory}get authorityLease(){return this.coordinator?.authorityLease??null}subscribeAuthority(t){return this.listeners.add(t),()=>{this.listeners.delete(t)}}currentConsumerBinding(){return this.identity&&this.lineage?this.consumerBinding(this.identity,this.lineage):null}async prepareConsumerBinding(){return this.preparing||(this.preparing=(async()=>{const t=await this.identityStore.acquire(this.handlerIdentity,this.journal),i={journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration};return this.lineage=i,this.identity=t,this.consumerBinding(t,i)})().finally(()=>{this.preparing=void 0})),await this.preparing}setRuntimeConsumerLive(t){if(this.started)throw new Error("runtime consumer availability is immutable while registered");this.runtimeConsumerLive=t}setRuntimeServiceMethods(t){if(this.started)throw new Error("runtime Service methods are immutable while registered");this.runtimeServiceMethods=intersectRuntimeServiceMethodsWithBackendCapabilities(t)}setRuntimeRealtimeCapabilityReady(t,i){const e=this.realtimeCapabilityReady.get(t)!==i;if(this.realtimeCapabilityReady.set(t,i),!e||!this.started)return;const r=this.lifecycleVersion;this.refreshRealtimeCapabilities(r).catch(t=>this.options.logger?.warn?.(`[runtime-authority] realtime capability refresh fenced: ${t instanceof Error?t.message:t}`))}setRuntimeResourceBindingContractRevision(t){if(this.started)throw new Error("runtime resource binding revision is immutable while registered");if(void 0!==t&&t!==RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION)throw new Error("unsupported runtime resource binding revision");this.resourceBindingContractRevision=t}async start(t){if(this.starting)return await this.starting;if(this.started)return;if(t?.aborted)return;const i=this.lifecycleVersion,e=this.startOnce(i,t).finally(()=>{this.starting===e&&(this.starting=void 0)});this.starting=e,await e}stop(){this.lifecycleVersion++,this.starting=void 0,this.started=!1;const t=this.coordinator;t&&this.detachCoordinator(t),this.publish(null)}async startOnce(t,i){if(await this.prepareConsumerBinding(),t!==this.lifecycleVersion||i?.aborted)return;this.lineage;const e=this.identity;this.started=!0,await this.activate(e,t)}async activate(t,i){if(!this.isLifecycleCurrent(i))return;if(this.resourceBindingContractRevision&&!this.runtimeConsumerLive)throw new Error("resource binding revision requires a live Message Service consumer");if(!this.runtimeConsumerLive||0===this.runtimeServiceMethods.length)throw new Error(`runtime registration refused: consumerLive=${this.runtimeConsumerLive} methods=${this.runtimeServiceMethods.length}`);const e=[...this.runtimeServiceMethods],r=runtimeCapabilities(this.runtimeServiceMethods,this.realtimeCapabilityReady);this.options.logger?.info?.(`[runtime-authority] advertising ${e.length} methods (renew-safe)`);const n=++this.coordinatorGeneration,o=new RuntimeRegistrationCoordinator(new InstanceAuthorityRuntimeRegistrationTransport({agentGatewayUrl:this.options.agentGatewayUrl,registrationId:t.registrationId,agentFetch:this.options.agentFetch}),{registrationId:t.registrationId,handlerIdentity:t.handlerIdentity,contractRevision:RUNTIME_RPC_CONTRACT_REVISION,runtimeMethods:e,capabilities:r,manifestDigest:this.manifestDigest,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration,instanceIdentityKeyId:createHash("sha256").update(this.options.keys.publicKey).digest("hex"),privateKey:createPrivateKey({key:privateKeyDer(this.options.keys.privateKey),format:"der",type:"pkcs8"})},(t,e)=>{if(!this.isCoordinatorCurrent(i,n,o))return;const r=e instanceof Error?e.message:void 0!==e?String(e):"";this.options.logger?.warn?.(r?`[runtime-authority] lease lost: ${t}: ${r}`:`[runtime-authority] lease lost: ${t}`);const s=e instanceof RuntimeRegistrationJournalAdvanceRequiredError?e:void 0;("expired"===t||s)&&this.rollExpired(i,n,o,s).catch(t=>this.options.logger?.warn?.(`[runtime-authority] expired registration roll fenced: ${t instanceof Error?t.message:t}`))});this.coordinator=o,this.unsubscribeCoordinator=o.subscribeAuthority(t=>{this.isCoordinatorCurrent(i,n,o)&&(t&&this.authorityLeasePending?(this.authorityLeasePending=!1,this.options.logger?.info?.("[runtime-authority] registration active (recovered after handoff)")):t||(this.authorityLeasePending=!0),this.publish(t))});const s=await o.start();this.isCoordinatorCurrent(i,n,o)?(this.options.logger?.info?.("[runtime-authority] registration "+(s?"active":"pending")),s||(this.authorityLeasePending=!0)):this.stopCoordinatorOnce(o)}async rollExpired(t,i,e,r){if(!this.lineage||!this.isCoordinatorCurrent(t,i,e))return;const n=this.rolling.get(i);if(n)return await n;const o=(async()=>{if(!this.isCoordinatorCurrent(t,i,e))return;this.detachCoordinator(e);const n=this.identity;if(!n)return;const o=await this.identityStore.roll(n,this.journal,{authorityCurrentGeneration:r?.currentJournalGeneration,expectedJournalGeneration:r?.expectedJournalGeneration});this.isLifecycleCurrent(t)&&void 0===this.coordinator&&(this.identity=o,this.lineage={journalStoreId:o.journalStoreId,journalGeneration:o.journalGeneration},await this.activate(o,t))})().finally(()=>{this.rolling.get(i)===o&&this.rolling.delete(i)});this.rolling.set(i,o),await o}async refreshRealtimeCapabilities(t){if(!this.isLifecycleCurrent(t))return;const i=this.refreshing;if(i)return await i;const e=(async()=>{if(!this.isLifecycleCurrent(t))return;const i=this.identity,e=this.coordinator;i&&e&&(e.authorityLease?(this.detachCoordinator(e),this.isLifecycleCurrent(t)&&void 0===this.coordinator&&await this.activate(i,t)):this.options.logger?.info?.("[runtime-authority] deferring realtime capability refresh until registration is active"))})().finally(()=>{this.refreshing===e&&(this.refreshing=void 0)});this.refreshing=e,await e}isLifecycleCurrent(t){return this.started&&t===this.lifecycleVersion}isCoordinatorCurrent(t,i,e){return this.isLifecycleCurrent(t)&&this.coordinatorGeneration===i&&this.coordinator===e}detachCoordinator(t){this.coordinator===t&&(this.unsubscribeCoordinator?.(),this.unsubscribeCoordinator=void 0,this.coordinator=void 0),this.stopCoordinatorOnce(t)}stopCoordinatorOnce(t){this.stoppedCoordinators.has(t)||(this.stoppedCoordinators.add(t),t.stop())}publish(t){for(const i of this.listeners)try{i(t)}catch{}}consumerBinding(t,i){return Object.freeze({handlerIdentity:t.handlerIdentity,journalStoreId:i.journalStoreId,journalGeneration:i.journalGeneration})}}const ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex"),privateKeyDer=t=>32===t.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(t)]):Buffer.from(t),REALTIME_CAPABILITY_NAMES=["terminal","canvas"];function runtimeCapabilities(t,i){const e=new Set(t),r=RUNTIME_METHOD_PARITY_MANIFEST.filter(t=>t.serviceAllowed&&void 0!==runtimeInvocationMethod(t)&&e.has(runtimeInvocationMethod(t))).map(t=>t.capability),n=REALTIME_CAPABILITY_NAMES.filter(t=>!0===i.get(t));return[...new Set([...r,...n])]}function runtimeInvocationMethod(t){return"methods"===t.serviceRoute&&t.serviceMethod?t.serviceMethod:Object.entries(CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES).find(([,i])=>i===t.canonicalCommand)?.[0]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beeos-claw",
3
- "version": "0.6.0",
3
+ "version": "0.6.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>",