beeos-claw 0.6.55-staging.1 → 0.6.55-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveConfigWithMeta,mirrorOpenClawConfig}from"./config.js";import{hasRuntimeReplyApi}from"./runtime/reply-router.js";import{PromptRunStore}from"./runtime/prompt-run-store.js";import{createMcpNativeProjectionNudge,fingerprintMcpServers}from"./acp-gateway/mcp-native-projection-nudge.js";import{createImHotPathDeferral}from"./runtime/im-hot-path-deferral.js";import{SessionWriteGate}from"./acp-gateway/session-write-gate.js";import{OpenClawSessionWriteQuiescence}from"./runtime/openclaw-session-write-quiescence.js";import{SessionMutationCoordinator}from"./runtime/session-mutation-coordinator.js";import{scheduleProcessRuntimeRestart}from"./runtime/runtime-restart-scheduler.js";import{createImInboundHandler}from"./im-inbound-handler.js";import{createTokenProvider}from"@beeos-ai/message-sdk/auth";import{BeeosClawMessageClientProcessOwner}from"./message-client-composition.js";import{MessageProjectionInboundConsumer}from"./message-projection-inbound-consumer.js";import{MessageClientTerminalStateRecovery}from"./runtime/message-client-terminal-state-recovery.js";import{RuntimeAuthorityMessageBusLifecycle}from"./runtime/runtime-authority-message-bus-lifecycle.js";import{FileInboundExecutionJournal}from"./inbound-execution-journal.js";import{completeA2ATask,registerA2ATools,TaskHandleRegistry}from"./a2a/tools.js";import{BEEOS_UPLOAD_TOOL_NAME,BEEOS_UPLOAD_TOOL_SCHEMA,BEEOS_UPLOAD_TOOL_DESCRIPTION,executeUpload}from"./upload/tool.js";import{BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,executeDesktopScreenshot}from"./upload/desktop-screenshot-tool.js";import{BEEOS_LIST_FILES_TOOL_NAME,BEEOS_LIST_FILES_TOOL_SCHEMA,BEEOS_LIST_FILES_TOOL_DESCRIPTION,executeListFiles}from"./files/list-tool.js";import{BEEOS_FETCH_FILE_TOOL_NAME,BEEOS_FETCH_FILE_TOOL_SCHEMA,BEEOS_FETCH_FILE_TOOL_DESCRIPTION,executeFetchFile}from"./files/fetch-tool.js";import{writeUploadToolResult,cleanupUploadToolResultCache}from"./upload/result-cache.js";import{handleFileStage}from"./upload/file-stage.js";import{handleStatusRequest}from"./status.js";import{ACPServer}from"./acp-server.js";import{GatewayClient}from"./gateway-client.js";import{createObsEventWriter}from"./observability.js";import{resolvePluginVersion}from"./plugin-version.js";import{createTerminalWebSocketService}from"./terminal/websocket.js";import{TerminalManager}from"./terminal/session-manager.js";import{loadAgentKeyPair,agentBoundFetch,agentFetch}from"./agent-auth.js";import{readDiscoveryCache,writeDiscoveryCache}from"./discovery-cache.js";import{discoverBridgeFromGW}from"./bridge-discovery.js";import{setUploadDeps}from"./upload/upload-file.js";import{registerLocalFileExtractor}from"./upload/local-file-extractors.js";import{browserScreenshotExtractor}from"./browser/screenshot-extractor.js";import{BEEOS_FILE_RESOURCE_ROUTING_RULE}from"./files/beeos-file-uri.js";import{CONNECTOR_MCP_PROXY_PATH,handleConnectorMcpRequest}from"./mcp-connector-proxy.js";import{CanvasStateManager}from"./canvas/state.js";import{CanvasRelayClient}from"./canvas/relay-client.js";import{RENDER_UI_TOOL_NAME,RENDER_UI_TOOL_DESCRIPTION,RENDER_UI_TOOL_SCHEMA,CLOSE_UI_TOOL_NAME,CLOSE_UI_TOOL_DESCRIPTION,CLOSE_UI_TOOL_SCHEMA,createRenderUiHandler,createCloseUiHandler,CANVAS_SYSTEM_PROMPT}from"./canvas/tools.js";import{FormSchemaRegistry}from"./surface/form-schema-registry.js";import{buildCronSchemas}from"./surface/cron-schemas.js";import{RENDER_FORM_TOOL_NAME,RENDER_FORM_TOOL_SCHEMA,buildRenderFormDescription,createRenderFormHandler}from"./surface/render-form-tool.js";import{ResourceDescriptorRegistry}from"./surface/resource-descriptor-registry.js";import{CRON_RESOURCE}from"./surface/cron-resource.js";import{generateActionTools}from"./surface/action-tool-generator.js";import{buildThirdPartyChannelSet,isThirdPartyChannel}from"./channel-detection.js";import{registerArouterMediaProviders}from"./media/index.js";import{homedir}from"node:os";import{join}from"node:path";import{RuntimeProductionAuthority}from"./runtime/runtime-production-authority.js";import{FileRuntimeCommandJournal}from"./runtime/runtime-command-journal.js";import{FileRuntimePoisonDeliveryJournal}from"./runtime/runtime-poison-delivery-journal.js";import{OpenClawNativeMcpProjection}from"./acp-gateway/openclaw-native-mcp-projection.js";import{LocalEncryptedCredentialReferenceVault}from"./acp-gateway/mcp-credential-reference-port.js";import{AgentGatewayGrantJwksReadinessAdapter,RuntimeServiceTransportReadinessAdapter,createRuntimeServiceProductionCandidate}from"./runtime/runtime-service-production-candidate.js";import{MessageServiceCronDeliveryHttpAuthority}from"./runtime/message-service-cron-delivery-http-authority.js";import{AgentMappingRegistry}from"./runtime/agent-mapping-registry.js";import{OpenClawRuntimeProcessOwner}from"./runtime/openclaw-runtime-process-owner.js";import{AgentGatewayHttpProjectionTransport,AgentGatewayTemplateOriginReporter,AgentProjectionProduction,OpenClawAgentProjectionSource}from"./runtime/agent-projection-production.js";import{MessageServiceRunOutputChannel}from"./runtime/message-service-run-output-channel.js";import{AssistantMessageCommitRouter}from"./runtime/assistant-message-commit-router.js";import{ToolResultObservationRouter}from"./runtime/tool-result-observation-router.js";import{ModelCallObservationRouter,registerModelCallObservationHooks}from"./runtime/model-call-observability.js";import{registerArouterLatencyRouting}from"./runtime/arouter-latency-routing.js";import{createPeerRunOutputRouterFactory}from"./runtime/run-output-composition.js";const PLUGIN_VERSION=resolvePluginVersion();let agentKeys=null,sharedCanvasState=null,sharedAcpServer=null;const sharedSessionWriteGate=new SessionWriteGate,sharedPromptRunStore=new PromptRunStore;let lastOpenclawConfig;const sharedMcpNativeProjectionNudge=createMcpNativeProjectionNudge({getActiveImRunCount:()=>sharedPromptRunStore.size(),nudge:()=>{sharedAcpServer?.nudgeMcpNativeProjection()}}),sharedImHotPathDeferral=createImHotPathDeferral({getActiveImRunCount:()=>sharedPromptRunStore.size()});sharedPromptRunStore.setOnBecameIdle(()=>{sharedMcpNativeProjectionNudge.flushIfIdle({fingerprint:fingerprintMcpServers(lastOpenclawConfig)})});const sharedAssistantMessageCommitRouter=new AssistantMessageCommitRouter,sharedToolResultObservationRouter=new ToolResultObservationRouter,sharedModelCallObservationRouter=new ModelCallObservationRouter;let sharedSessionMutationCoordinator,sharedRuntimeAuthority;const sharedAgentMappings=new AgentMappingRegistry;let sharedGatewayClient,sharedRuntimeOwner,sharedAgentProjection,sharedRuntimeAgentCapabilities,sharedMcpProjection,sharedMcpCredentialVault;const sharedMessageClientOwner=new BeeosClawMessageClientProcessOwner;let sharedActiveMessageConsumer,sharedInboundExecutionJournal;const sharedTerminalManager=new TerminalManager;let _initializing=!1,_pluginRegisteredAt=0;const ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex");function agentKeyPairToIdentity(e){return{publicKeyBase64:()=>Buffer.from(e.publicKey).toString("base64"),sign:async t=>{const{createPrivateKey:r,sign:o}=await import("node:crypto"),n=r({key:32===e.privateKey.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(e.privateKey)]):Buffer.from(e.privateKey),format:"der",type:"pkcs8"});return new Uint8Array(o(null,Buffer.from(t),n))}}}let unhandledRejectionObserverInstalled=!1;function installUnhandledRejectionObserver(e,t,r){unhandledRejectionObserverInstalled||(unhandledRejectionObserverInstalled=!0,process.on("unhandledRejection",r=>{const o=r instanceof Error?r.message:String(r),n=r instanceof Error?r.stack:void 0;e.logger.error?.(`[beeos-claw] unhandledRejection: ${o}${n?`\n${n}`:""}`),t({component:"plugin",domain:"fatal",name:"unhandled_rejection",severity:"error",summary:o,error:{code:"unhandled_rejection",message:o,...n?{stack:n}:{}}})}),process.on("uncaughtException",r=>{const o=r instanceof Error?r.message:String(r);e.logger.error?.(`[beeos-claw] uncaughtException: ${r instanceof Error?r.stack??r.message:String(r)}`),t({component:"plugin",domain:"fatal",name:"uncaught_exception",severity:"error",summary:o,error:{code:"uncaught_exception",message:o}})}))}function initializePlugin(e){let t,r;try{t=e.runtime?.config?.loadConfig?.()}catch{}try{r=resolveConfigWithMeta({pluginConfig:e.pluginConfig??{},openclawConfig:t})}catch(t){return e.logger.error?.(`[beeos-claw] Failed to resolve config: ${t instanceof Error?t.message:String(t)}`),null}const o=r.config,n=buildThirdPartyChannelSet(t);n.size>0&&e.logger.info?.(`[beeos-claw] Third-party channels detected: ${[...n].join(", ")}`);const a=o.log.enabled?createObsEventWriter({dir:o.log.dir,redaction:{gatewayToken:o.gateway.token},logger:e.logger}):()=>{};a({component:"plugin",domain:"lifecycle",name:"lifecycle.startup",severity:"info",payload:{pluginVersion:PLUGIN_VERSION,pid:process.pid,nodeVersion:process.version,cwd:process.cwd()}}),a({component:"plugin",domain:"config",name:"config.resolved",severity:"info",payload:{config:o,sources:r.sources,openclawConfigPath:r.openclawConfigPath}});for(const t of r.validation)a({component:"plugin",domain:"config",name:"config.validation",severity:t.severity,summary:t.message,error:{code:t.code,message:t.message,nextSteps:t.nextSteps}}),"error"===t.severity?e.logger.error?.(`[beeos-claw] ${t.message}`):e.logger.warn?.(`[beeos-claw] ${t.message}`);if(r.validation.some(e=>"error"===e.severity)&&e.logger.error?.("[beeos-claw] Plugin has configuration errors — some features may not work."),o.keyFile)try{agentKeys=loadAgentKeyPair(o.keyFile);const t=Buffer.from(agentKeys.publicKey).toString("base64");e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (publicKey: ${t})`)}catch(t){e.logger.warn?.(`[beeos-claw] Failed to load agent key pair: ${t}`),e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no key pair — signed requests disabled)`)}else e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no keyFile configured)`);return agentKeys&&o.agentGatewayUrl&&(setUploadDeps({gatewayUrl:o.agentGatewayUrl,keys:agentKeys,logger:e.logger}),registerLocalFileExtractor(browserScreenshotExtractor),e.logger.info?.("[beeos-claw] Upload deps + browser screenshot extractor registered")),lastOpenclawConfig=t,{cfg:o,writeObs:a,thirdPartyChannels:n,openclawConfig:t}}function registerUploadTools(e,t,r){const{cfg:o,writeObs:n}=t;cleanupUploadToolResultCache().catch(t=>{e.logger.warn?.(`[beeos_upload_file] cache cleanup failed on startup: ${String(t)}`)}),e.registerTool({name:BEEOS_UPLOAD_TOOL_NAME,description:BEEOS_UPLOAD_TOOL_DESCRIPTION,parameters:BEEOS_UPLOAD_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_upload_file] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeUpload(r,o,agentKeys,a);return e.logger.debug?.(`[beeos_upload_file] toolCallId=${t} ok=${i.ok}`),n({component:"upload-tool",domain:"tool",name:i.ok?"upload.success":"upload.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,fileCount:i.files?.length??0,error:i.error}}),(async r=>{try{await writeUploadToolResult(t,BEEOS_UPLOAD_TOOL_NAME,r),await cleanupUploadToolResultCache()}catch(r){e.logger.warn?.(`[beeos_upload_file] persist cache failed toolCallId=${t}: ${String(r)}`)}return r})(i)}}),e.registerTool({name:BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,description:BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,parameters:BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeDesktopScreenshot(r,o,agentKeys,a);return e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} ok=${i.ok}`),n({component:"desktop-screenshot-tool",domain:"tool",name:i.ok?"desktop_screenshot.success":"desktop_screenshot.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,path:i.path,fileCount:i.files?.length??0,error:i.error}}),(async r=>{try{await writeUploadToolResult(t,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,r),await cleanupUploadToolResultCache()}catch(r){e.logger.warn?.(`[beeos_desktop_screenshot] persist cache failed toolCallId=${t}: ${String(r)}`)}return r})(i)}}),e.registerHttpRoute({path:"/beeos/files/stage",auth:"gateway",async handler(e,r){await handleFileStage(e,r,t.cfg)}}),e.registerHttpRoute({path:"/beeos/status",auth:"plugin",handler(e,t){handleStatusRequest(e,t,{publicKey:agentKeys?.publicKey??null,pluginVersion:PLUGIN_VERSION})}}),e.registerTool({name:BEEOS_LIST_FILES_TOOL_NAME,description:BEEOS_LIST_FILES_TOOL_DESCRIPTION,parameters:BEEOS_LIST_FILES_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_list_files] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeListFiles(r,o,agentKeys,a);return n({component:"file-list-tool",domain:"tool",name:i.ok?"list_files.success":"list_files.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,fileCount:i.files?.length??0,error:i.error}}),i}}),e.registerTool({name:BEEOS_FETCH_FILE_TOOL_NAME,description:BEEOS_FETCH_FILE_TOOL_DESCRIPTION,parameters:BEEOS_FETCH_FILE_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_fetch_file] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeFetchFile(r,o,agentKeys,{signal:a,resolvePath:e.resolvePath});return n({component:"file-fetch-tool",domain:"tool",name:i.ok?"fetch_file.success":"fetch_file.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,path:i.path,size:i.size,error:i.error}}),i}})}function registerConnectorMcpProxy(e,t){e.registerHttpRoute({path:CONNECTOR_MCP_PROXY_PATH,auth:"plugin",async handler(r,o){await handleConnectorMcpRequest(r,o,{agentGatewayUrl:t.cfg.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger})}})}function registerServices(e,t,r){const{cfg:o,writeObs:n,thirdPartyChannels:a}=t;if("function"!=typeof e.on){if(o.messageBus.enabled)throw new Error("openclaw_before_message_write_hook_unavailable")}else registerModelCallObservationHooks((t,r)=>e.on(t,r),sharedModelCallObservationRouter),e.on("before_message_write",(t,r)=>{const o=t.sessionKey??r.sessionKey;if(!o)return void e.logger.debug?.("[before_message_write] ignored assistant write without sessionKey");sharedAssistantMessageCommitRouter.route(o,t.message)||e.logger.debug?.(`[before_message_write] no active IM commit target sessionKey=${o} active_targets=${sharedAssistantMessageCommitRouter.size()}`)}),e.on("after_tool_call",(t,r)=>{const o=r.sessionKey,n=t.toolCallId??r.toolCallId,a=t.toolName||r.toolName;if(!o||!n||!a)return void e.logger.debug?.(`[after_tool_call] ignored structured result without correlation sessionKey=${o??"missing"} toolCallId=${n??"missing"}`);sharedToolResultObservationRouter.route(o,{toolCallId:n,toolName:a,rawResult:t.result,isError:Boolean(t.error)})||e.logger.debug?.(`[after_tool_call] no active IM observation target sessionKey=${o} active_targets=${sharedToolResultObservationRouter.size()}`)});const i=agentKeys?(e,t)=>agentFetch(e,agentKeys,t):void 0,s=agentKeys?(e,t)=>agentBoundFetch(e,agentKeys,t):void 0,l=agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):void 0,c=join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos"),g=sharedInboundExecutionJournal??=new FileInboundExecutionJournal(join(c,"inbound-execution-journal.v1.jsonl"));if(sharedMcpProjection??=new OpenClawNativeMcpProjection,!sharedMcpCredentialVault&&agentKeys&&l&&(sharedMcpCredentialVault=new LocalEncryptedCredentialReferenceVault(join(c,"mcp-credential-vault.json"),l,agentKeys.privateKey)),!sharedRuntimeAuthority&&agentKeys&&o.agentGatewayUrl&&i&&s&&(sharedRuntimeAuthority=new RuntimeProductionAuthority({stateDirectory:c,agentGatewayUrl:o.agentGatewayUrl,agentFetch:i,boundAgentFetch:s,keys:agentKeys,logger:e.logger})),sharedGatewayClient??=new GatewayClient({url:o.gateway.url,token:o.gateway.token||"",clientId:o.gateway.clientId,openclawCanvasCompat:!!o.openclawCanvasCompat,retry:{baseMs:o.retry.baseMs,maxMs:o.retry.maxMs,maxAttempts:o.retry.maxAttempts}}),sharedRuntimeAgentCapabilities??={canvas:o.canvasEnabled,terminal:!1,webSsh:!1,fileSystem:!0,image:!0,audio:!1,mcp:!0,cron:!0,streaming:!0,pushNotifications:o.messageBus.enabled},!sharedAgentProjection&&sharedRuntimeAuthority&&i&&o.agentGatewayUrl){const t=sharedRuntimeAuthority;sharedAgentProjection=new AgentProjectionProduction({authority:t,stateFile:join(t.stateDirectory,"runtime-agent-projection.json"),journal:t.journal,identities:t.identities,source:new OpenClawAgentProjectionSource(()=>sharedGatewayClient),transport:new AgentGatewayHttpProjectionTransport({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:t.boundAgentFetch,getRuntimeLeaseCredential:e=>{const r=t.authorityLease;if(!r||r.instanceId!==e.instanceId||r.leaseId!==e.leaseId||r.runtimeEpoch!==e.runtimeEpoch||r.handlerIdentity!==e.handlerIdentity)throw new Error("agent projection fenced by current runtime lease");return r.runtimeLeaseCredential},onAccepted:t=>{0!==t.size&&sharedAgentMappings.replace(t).catch(t=>{e.logger.warn?.("[agent-projection] mapping publication failed: "+(t instanceof Error?t.message:String(t)))})}}),capabilities:sharedRuntimeAgentCapabilities,logger:e.logger})}sharedRuntimeOwner??=new OpenClawRuntimeProcessOwner(sharedGatewayClient,sharedAgentProjection,e.logger);const d=sharedRuntimeOwner;sharedSessionMutationCoordinator??=new SessionMutationCoordinator(sharedSessionWriteGate,new OpenClawSessionWriteQuiescence(e.logger,{agentId:o.gateway.agentId||"main"}),e.logger,{onRecoveryRequired:t=>{scheduleProcessRuntimeRestart(t,e.logger)}});const u=sharedSessionMutationCoordinator;if(!sharedAcpServer){const t=sharedRuntimeAuthority&&o.agentGatewayUrl?new AgentGatewayTemplateOriginReporter({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:sharedRuntimeAuthority.boundAgentFetch}):void 0;sharedAcpServer=new ACPServer(o,e.logger,u,n,i,l,a,sharedTerminalManager,sharedRuntimeAuthority,void 0,sharedMcpProjection,sharedMcpCredentialVault,s,{gateway:d.client,agentProjection:sharedAgentProjection,markAgentProjectionDirty:()=>sharedAgentProjection?.markDirty(),templateOriginReporter:t,agentMappings:sharedAgentMappings,agentCapabilities:sharedRuntimeAgentCapabilities,createRunOutputRouter:createPeerRunOutputRouterFactory(new MessageServiceRunOutputChannel({getClient:()=>sharedMessageClientOwner.current?.client??null,logger:e.logger,writeObs:n})),waitForChatIdle:t=>sharedImHotPathDeferral.waitIfBusy(t,e.logger).then(()=>{})})}const p=sharedAcpServer,m=()=>sharedAgentMappings.snapshot()?.values().next().value;r.current=p,sharedMcpNativeProjectionNudge.request({registrationMode:e.registrationMode,fingerprint:fingerprintMcpServers(t.openclawConfig),logger:e.logger});const y=sharedPromptRunStore,{enabled:h,principalId:f,a2aEnabled:w}=o.messageBus;let v;o.agentGatewayUrl&&agentKeys&&(v=createTokenProvider({agentGatewayUrl:o.agentGatewayUrl,identity:agentKeyPairToIdentity(agentKeys)}));const b=process.env.MESSAGE_RUNTIME_DELIVERY_KEY?.trim(),C=f||process.env.BEEOS_PRINCIPAL_ID||"",_=v?sharedMessageClientOwner.configure({tokenProvider:v,identityId:C,...o.messageBus.serviceUrl?{serviceUrl:o.messageBus.serviceUrl}:{},logger:e.logger,...sharedRuntimeAuthority&&b?{runtimeDelivery:{scopedDeliveryKey:b,authority:{currentLease:()=>{const e=sharedRuntimeAuthority?.authorityLease,t=sharedRuntimeAuthority?.currentConsumerBinding();return e&&t?{...e,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration}:null}}}}:{}}):void 0,R=sharedRuntimeAuthority,O=o.messageBus.serviceUrl;if(_&&R&&O&&b){const e=_.nodeTransport;p.setCronConversationDelivery({deliver:async t=>{const r=R.authorityLease;if(!r)throw new Error("current runtime authority lease unavailable for cron delivery");const o=new MessageServiceCronDeliveryHttpAuthority({messageServiceUrl:O,fetchAuthorized:async(t,o)=>await e.fetchRuntimeChannel(t,{runtimeLeaseCredential:r.runtimeLeaseCredential},o)});return await o.deliver({...t,runtimeLeaseCredential:r.runtimeLeaseCredential})}})}else p.setCronConversationDelivery(void 0);const E=sharedRuntimeAuthority?.journal??new FileRuntimeCommandJournal(join(c,"runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:t=>e.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=threshold error=${t instanceof Error?t.message:String(t)}`)}),S=d.createRegistrationToken();e.registerService({id:"beeos-acp",async start(){const t=Date.now(),r=_pluginRegisteredAt?t-_pluginRegisteredAt:-1;e.logger.info?.(`[beeos-claw] beeos-acp service start() begin, sincePluginRegister=${r}ms, bridge.url=${o.bridge.url||"(empty)"}`);const n=await createRuntimeServiceProductionCandidate({...o.agentGatewayUrl&&i?{authorizationAdapter:new AgentGatewayGrantJwksReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl})}:{},..._&&o.agentGatewayUrl&&s&&b?{transportAdapter:new RuntimeServiceTransportReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl,messageService:_.nodeTransport,messageClient:_.client,configuredServiceUrl:o.messageBus.serviceUrl,boundAgentFetch:s,scopedDeliveryKey:b})}:{},commandJournal:E,poisonJournal:new FileRuntimePoisonDeliveryJournal(join(c,"runtime-poison-deliveries.json")),onConsumerDeliveryError:t=>{const r=t instanceof Error?`${t.name}: ${t.message}${"status"in t?` status=${t.status}`:""}${"code"in t?` code=${t.code}`:""}\n${t.stack??""}`:String(t);e.logger.error?.(`[runtime-service] delivery worker failed before acknowledgement; delivery retained for retry: ${r}`)},onConsumerLeaseStarvation:({starvedForMs:t,recovered:r})=>{const o=Math.round(t/1e3);r?e.logger.info?.(`[runtime-service] runtime authority lease restored after ${o}s without delivery reads`):e.logger.warn?.(`[runtime-service] no runtime authority lease for ${o}s; delivery reads suspended, queued runtime operations (model switch, session control) will not be consumed`)},onConsumerRequestFailure:t=>e.logger.error?.(`[runtime-service] request failed operationId=${t.operationId} method=${t.method}: ${t.error instanceof Error?t.error.stack??t.error.message:String(t.error)}`)});if(sharedRuntimeAuthority?.setRuntimeServiceMethods("ready"===n.status?n.serviceMethods:[]),sharedRuntimeAuthority?.setRuntimeResourceBindingContractRevision("ready"===n.status?n.resourceBindingContractRevision:void 0),p.setRuntimeRpcConsumerFactory("ready"===n.status?n.factory:void 0),"unavailable"===n.status?e.logger.warn?.("[runtime-service] capability fenced: "+n.blockers.map(e=>e.id).join(",")):n.capabilityBlockers.length&&e.logger.warn?.("[runtime-service] method capability fenced: "+n.capabilityBlockers.map(e=>e.method).join(",")),!o.bridge.url&&o.agentGatewayUrl&&i){const t=Date.now(),r=await discoverBridgeFromGW(o.agentGatewayUrl,i);if(e.logger.info?.(`[beeos-claw] bridge discovery took ${Date.now()-t}ms result=${r?.url??"null"}`),r)o.bridge.url=r.url,writeDiscoveryCache({bridge:{url:r.url,discoveredAt:Date.now()}}),e.logger.info?.(`[beeos-claw] Bridge URL discovered from Agent Gateway: ${r.url}`);else{const t=readDiscoveryCache();t?.bridge?.url?(o.bridge.url=t.bridge.url,e.logger.info?.(`[beeos-claw] Bridge URL from cache (GW unreachable): ${t.bridge.url}`)):e.logger.debug?.("[beeos-claw] Bridge URL not available (no GW, no cache) — bridge disabled")}}await d.acquire(S);try{await p.start(o.bridge.url||void 0)}catch(e){throw await d.release(S),e}e.logger.info?.(`[beeos-claw] ACP Server started (total service start: ${Date.now()-t}ms)`)},async stop(){try{await d.release(S)}finally{await p.stop()}await E.checkpoint(),e.logger.info?.("[runtime-service] command journal checkpoint completed"),e.logger.info?.("[beeos-claw] ACP Server stopped")}});if(h&&void 0!==v&&v){const t=sharedMessageClientOwner.createRegistrationToken(),r=async(e,t)=>{const r=await v(e);if(!r.identityId||r.identityId!==e)throw new Error("Message SDK token identity did not match the current runtime authority");return{principalId:e,runtimeEpoch:t}},a=(e,t,r)=>({getLocalAgentId:()=>t,resolveLocalAgentId:e=>e===r?t:void 0,getDefaultPlatformAgent:()=>({instanceId:e.principalId}),ensureImInboundSession:(e,t,r,o)=>{p.ensureImInboundSession(e,t,r,o)},chatAbort:(e,t)=>p.chatAbort(e,t)}),i=sharedRuntimeAuthority?new RuntimeAuthorityMessageBusLifecycle({authority:sharedRuntimeAuthority,logger:e.logger,bus:{connect:async i=>{await sharedMessageClientOwner.acquire(t,async t=>{const s=await r(i.instanceId,i.runtimeEpoch),l=t.client,c=new Map,d=new MessageProjectionInboundConsumer({client:l,bindings:()=>{const t=sharedRuntimeAuthority?.authorityLease;if(!t||t.instanceId!==s.principalId||t.runtimeEpoch!==s.runtimeEpoch)return[];const r=sharedAgentMappings.snapshot();return r?[...r.entries()].flatMap(([r,i])=>{if(!i.id||i.instanceId!==s.principalId)return[];let g=c.get(i.id);return g&&g.localAgentId===r||(g={localAgentId:r,inbound:createImInboundHandler({runtime:e.runtime,pluginConfig:e.pluginConfig,client:l,localServer:a(s,r,i.id),promptRunStore:y,assistantMessageCommitRouter:sharedAssistantMessageCommitRouter,toolResultObservationRouter:sharedToolResultObservationRouter,modelCallObservationRouter:sharedModelCallObservationRouter,sessionMutationCoordinator:u,logger:e.logger,writeObs:n,principalIdOverride:s.principalId,promptTimeoutMs:o.promptTimeoutMs,activeRunAuthority:p.getActiveRunAuthority(),...o.agentGatewayUrl&&agentKeys?{completeA2ATask:(t,r,n)=>completeA2ATask({agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},t,r,n)}:{}})},c.set(i.id,g)),[{principalId:s.principalId,targetAgentId:i.id,runtimeEpoch:t.runtimeEpoch,inbound:g.inbound}]}):[]},executionJournal:g,logger:e.logger}),m=sharedAgentMappings.subscribe(()=>{sharedActiveMessageConsumer===d&&d.refresh()}),h=sharedRuntimeAuthority?.subscribeAuthority(()=>{sharedActiveMessageConsumer===d&&d.refresh()})??(()=>{}),f=new MessageClientTerminalStateRecovery({logger:e.logger,isCurrent:()=>{if(sharedActiveMessageConsumer!==d)return!1;const e=sharedRuntimeAuthority?.authorityLease;return null!=e&&e.instanceId===s.principalId&&e.runtimeEpoch===s.runtimeEpoch&&e.leaseId===i.leaseId&&e.handlerIdentity===i.handlerIdentity},reconnect:async()=>{if("failed"===l.getSnapshot().connection){await l.connect();try{await p.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient recovery: ${t instanceof Error?t.message:String(t)}`)}}}});let w=l.getSnapshot().connection;const v=l.subscribe(()=>{const t=l.getSnapshot();t.connection!==w&&(w=t.connection,"connected"===t.connection?(f.notifyConnected(),e.logger.info?.(`[beeos-claw] connection transport=message_service_realtime state=connected localAgentId=${p.getLocalAgentId()} principalId=${s.principalId}`)):"failed"===t.connection?(e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=failed principalId=${s.principalId} recoveryError=${t.recoveryError??"unknown"}`),f.notifyFailed()):"disconnected"!==t.connection&&"reconnecting"!==t.connection||e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=${t.connection} principalId=${s.principalId}; SDK owns reconnect/recovery`))});sharedActiveMessageConsumer=d,d.start();try{await l.connect()}catch(e){throw sharedActiveMessageConsumer===d&&(sharedActiveMessageConsumer=void 0),f.dispose(),await d.stop(),m(),h(),v(),await l.disconnect().catch(()=>{}),e}try{await p.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient connect: ${t instanceof Error?t.message:String(t)}`)}return async()=>{sharedActiveMessageConsumer===d&&(sharedActiveMessageConsumer=void 0),f.dispose(),await d.stop(),h(),v(),await l.disconnect(),e.logger.info?.("[beeos-claw] MessageClient disconnected")}})},disconnect:async()=>{await sharedMessageClientOwner.release(t)}}}):void 0;e.registerService({id:"beeos-message-bus",async start(){i?i.start():e.logger.warn?.("[beeos-claw] Message bus requires runtime authority; waiting is unavailable without Agent Gateway runtime identity")},async stop(){await(i?.stop())}})}else h?e.logger.warn?.("[beeos-claw] Message bus enabled but Agent Gateway token provider is unavailable — disabled"):e.logger.info?.(`[beeos-claw] Message bus disabled (enabled=${h})`);if(!1!==w&&o.agentGatewayUrl){const t=e=>p.getGatewaySessionKeyForToolCall(e),r=e=>p.getMentionsForToolCall(e),n=(e,t)=>{p.broadcastNotification(e,t)},a=new TaskHandleRegistry;registerA2ATools(e,{agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},a,t,r,n,()=>m()?.instanceId),e.logger.info?.("[beeos-claw] A2A tools registered (beeos_discover_agents, beeos_call_agent) via REST")}if(o.canvasEnabled){sharedCanvasState||(sharedCanvasState=new CanvasStateManager);const t=sharedCanvasState;if(o.agentGatewayUrl&&agentKeys){let r;t.setLogger(e.logger);{const e=o.agentGatewayUrl,t=agentKeys;r=async()=>{const r=await agentFetch(`${e}/api/v1/canvas/token`,t,{method:"POST"});if(!r.ok){const e=await r.text().catch(()=>"");throw new Error(`Canvas token returned ${r.status}: ${e}`)}return r.json()}}const n=n=>{const a=new CanvasRelayClient({canvasRelayUrl:o.canvasRelayUrl,agentKeys:agentKeys,agentId:n.id,displayName:n.displayName,avatarUrl:n.avatarUrl,tokenProvider:r,onConnected:()=>p.flushPendingCanvasLinks(),onReconnect:()=>p.onRelayReconnected(),logger:e.logger});t.setRelayClient(a),t.setAgentId(n.id),p.wireRelayActionHandler(),a.connect(),e.logger.info?.(`[beeos-claw] Canvas Relay multiplexed client connected (agentId=${n.id}, name=${n.name??"none"})`)};e.registerService({id:"beeos-canvas-relay",async start(){try{await sharedAgentMappings.waitForSynchronization();const t=m();if(t?.id)return void n(t);e.logger.info?.("[beeos-claw] Canvas Relay deferred: waiting for late agent sync..."),sharedAgentMappings.onceReady(()=>{const t=m();if(t?.id)try{n(t)}catch(t){e.logger.warn?.(`[beeos-claw] Deferred Canvas Relay start failed: ${t}`)}else e.logger.warn?.("[beeos-claw] Agent mapping ready but no platform agent; Canvas Relay stays disabled")})}catch(t){e.logger.warn?.(`[beeos-claw] Failed to start Canvas Relay client: ${t}`)}},stop(){t.getRelayClient()?.disconnect()}}),e.logger.info?.("[beeos-claw] Canvas Relay configured")}else e.logger.warn?.("[beeos-claw] Canvas Relay unavailable: agentGatewayUrl or agentKeys missing");const r={stateManager:t,logger:e.logger,writeObs:e=>n(e),isCanvasActive:e=>!!e&&p.isCanvasActiveForGatewayKey(e),getActiveSessionId:e=>p.getSessionIdForToolCall(e),getPlatformAgentId:e=>p.getPlatformAgentIdForToolCall(e),getGatewaySessionKey:e=>p.getGatewaySessionKeyForToolCall(e),waitForAgentSync:()=>sharedAgentMappings.waitForSynchronization()},i=new FormSchemaRegistry,s=buildCronSchemas(a,{capMs:o.cronMaxDurationCapMs});i.register(s.create),i.register(s.update);const l=new ResourceDescriptorRegistry;l.register(CRON_RESOURCE);const c=e=>async(t,r,o)=>(p.preMapToolCallSession(t),e(t,r));e.registerTool({name:RENDER_UI_TOOL_NAME,description:RENDER_UI_TOOL_DESCRIPTION,parameters:RENDER_UI_TOOL_SCHEMA,execute:c(createRenderUiHandler(r))}),e.registerTool({name:CLOSE_UI_TOOL_NAME,description:CLOSE_UI_TOOL_DESCRIPTION,parameters:CLOSE_UI_TOOL_SCHEMA,execute:c(createCloseUiHandler(r))}),p.setCanvasState(t,{schemaRegistry:i,descriptorRegistry:l});const g=p.getFormBuilder();g&&e.registerTool({name:RENDER_FORM_TOOL_NAME,description:buildRenderFormDescription(i),parameters:RENDER_FORM_TOOL_SCHEMA,execute:c(createRenderFormHandler({registry:i,builder:g,logger:e.logger}))});const d=p.getResourceRenderer();if(d){generateActionTools(l,e,async(e,t)=>p.gatewayRequest(e,t),d,e.logger)}const u=`\n\nTool selection guidance:\n- browser tool: Use when the user wants to OPEN, VISIT, NAVIGATE to a webpage, take SCREENSHOTS of web pages, interact with elements (click, type), or visually inspect pages.\n- ${BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME}: Use to capture the agent desktop (XFCE/VNC DISPLAY). Prefer this over shell/python X11 dumps; never invent raw Xlib/PIL capture.\n- beeos_search: Use ONLY for text-based information retrieval (fact-finding, research queries). Never for page interaction or screenshots.\n- beeos_fetch: Use ONLY to extract article text from a known URL. Never for visual content or screenshots.\n- ${BEEOS_FILE_RESOURCE_ROUTING_RULE}\nWhen the user says "open", "visit", "go to", or mentions seeing/viewing a webpage, ALWAYS prefer the browser tool. When they ask for a desktop / screen / VNC capture of the agent workspace, use beeos_desktop_screenshot.`,y=new Set([RENDER_UI_TOOL_NAME,CLOSE_UI_TOOL_NAME,RENDER_FORM_TOOL_NAME]),h=256,f=new Map;if("function"==typeof e.on){try{e.on("before_prompt_build",(e,t)=>{if(t?.sessionKey){if(f.size>=h&&!f.has(t.sessionKey)){const e=f.keys().next().value;void 0!==e&&f.delete(e)}f.set(t.sessionKey,t.channelId)}if(isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:t?.channelId}))return{appendSystemContext:'This conversation is in a third-party channel (Lark/Telegram/Discord/Slack); Canvas surfaces are not visible here. Do NOT use render_ui, close_ui, or render_form. Use built-in tools directly (e.g. the `cron` tool with `action:"add"` for scheduled tasks) and respond with plain text.'};if(!t?.sessionKey)return{appendSystemContext:u};return p.isCanvasActiveForGatewayKey(t.sessionKey)?{appendSystemContext:CANVAS_SYSTEM_PROMPT+u}:{appendSystemContext:"Canvas is closed by user. Do NOT use render_ui, close_ui, or render_form. Respond with plain text only."+u}}),e.logger.debug?.("[beeos-claw] Canvas system prompt hook registered: before_prompt_build")}catch{e.logger.debug?.("[beeos-claw] before_prompt_build hook not supported")}try{e.on("before_tool_call",(e,t)=>{if(t?.sessionKey&&p.setPendingToolCallSessionKey(t.sessionKey),!y.has(e.toolName))return;const r=t?.sessionKey?f.get(t.sessionKey):void 0;return isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:r})?{block:!0,blockReason:"Canvas tools are not available in this channel. Respond with plain text only."}:t?.sessionKey?p.isCanvasActiveForGatewayKey(t.sessionKey)?void 0:{block:!0,blockReason:"Canvas is closed by user. Respond with plain text instead."}:{block:!0,blockReason:"Canvas session unknown. Respond with plain text instead."}}),e.logger.debug?.("[beeos-claw] Canvas tool guard registered: before_tool_call")}catch{e.logger.debug?.("[beeos-claw] before_tool_call hook not supported")}}e.logger.info?.("[beeos-claw] Canvas tools registered (render_ui, close_ui, render_form + auto-action tools)")}else e.logger.info?.("[beeos-claw] Canvas disabled by config (canvasEnabled=false)");if(o.bridge.shell.enabled&&!0===o.legacyTerminalWsEnabled){const t=createTerminalWebSocketService({config:{port:o.terminalWsPort,token:agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):"",allowedOrigins:o.terminalWsAllowedOrigins},terminalMgr:sharedTerminalManager,logger:e.logger,writeObs:n});e.registerService({id:"beeos-terminal-ws",start(){t.start(),e.logger.info?.(`[beeos-claw] Terminal WS started on port ${o.terminalWsPort}`)},stop(){t.stop(),e.logger.info?.("[beeos-claw] Terminal WS stopped")}})}else o.bridge.shell.enabled?e.logger.info?.("[beeos-claw] Legacy static-token Terminal WS disabled (default)"):e.logger.info?.("[beeos-claw] Terminal/shell disabled by config (bridge.shell.enabled=false)");if(t.openclawConfig){const r=mirrorOpenClawConfig(t.openclawConfig);r.copied?e.logger.info?.(`[beeos-claw] mirrored openclaw config to ${r.destinationPath}`):e.logger.debug?.(`[beeos-claw] openclaw config mirror skipped: ${r.reason}`)}}const CAPABILITY_REGISTRATION_MODES=new Set(["full","discovery","tool-discovery"]),plugin={id:"beeos-claw",name:"beeos-claw",description:"BeeOS platform bridge plugin for OpenClaw. Provides file upload/download, ACP bridge, and observability.",version:PLUGIN_VERSION,register(e){if(!_initializing)if(!e.registrationMode||CAPABILITY_REGISTRATION_MODES.has(e.registrationMode)){_initializing=!0;try{if(!hasRuntimeReplyApi(e.runtime))throw e.logger.error?.("[beeos-claw] FATAL: OpenClaw runtime.channel.reply.dispatchReplyFromConfig not exposed. Upgrade OpenClaw or revert beeos-claw."),new Error("openclaw_runtime_reply_api_unavailable");const t=initializePlugin(e);if(!t)return;const r={current:null};installUnhandledRejectionObserver(e,t.writeObs,r),registerUploadTools(e,t,r),registerConnectorMcpProxy(e,t),registerArouterLatencyRouting(e),registerArouterMediaProviders(e),registerServices(e,t,r),_pluginRegisteredAt=Date.now(),e.logger.info?.("[beeos-claw] Plugin registered successfully")}finally{_initializing=!1}}else e.logger.debug?.(`[beeos-claw] Skipping registration (mode=${e.registrationMode})`)}};export default plugin;
|
|
1
|
+
import{resolveConfigWithMeta,mirrorOpenClawConfig}from"./config.js";import{hasRuntimeReplyApi}from"./runtime/reply-router.js";import{PromptRunStore}from"./runtime/prompt-run-store.js";import{createMcpNativeProjectionNudge,fingerprintMcpServers}from"./acp-gateway/mcp-native-projection-nudge.js";import{createImHotPathDeferral}from"./runtime/im-hot-path-deferral.js";import{SessionWriteGate}from"./acp-gateway/session-write-gate.js";import{OpenClawSessionWriteQuiescence}from"./runtime/openclaw-session-write-quiescence.js";import{SessionMutationCoordinator}from"./runtime/session-mutation-coordinator.js";import{scheduleProcessRuntimeRestart}from"./runtime/runtime-restart-scheduler.js";import{createImInboundHandler}from"./im-inbound-handler.js";import{createTokenProvider}from"@beeos-ai/message-sdk/auth";import{BeeosClawMessageClientProcessOwner}from"./message-client-composition.js";import{MessageProjectionInboundConsumer}from"./message-projection-inbound-consumer.js";import{MessageClientTerminalStateRecovery}from"./runtime/message-client-terminal-state-recovery.js";import{RuntimeAuthorityMessageBusLifecycle}from"./runtime/runtime-authority-message-bus-lifecycle.js";import{FileInboundExecutionJournal}from"./inbound-execution-journal.js";import{completeA2ATask,registerA2ATools,TaskHandleRegistry}from"./a2a/tools.js";import{BEEOS_UPLOAD_TOOL_NAME,BEEOS_UPLOAD_TOOL_SCHEMA,BEEOS_UPLOAD_TOOL_DESCRIPTION,executeUpload}from"./upload/tool.js";import{BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,executeDesktopScreenshot}from"./upload/desktop-screenshot-tool.js";import{BEEOS_LIST_FILES_TOOL_NAME,BEEOS_LIST_FILES_TOOL_SCHEMA,BEEOS_LIST_FILES_TOOL_DESCRIPTION,executeListFiles}from"./files/list-tool.js";import{BEEOS_FETCH_FILE_TOOL_NAME,BEEOS_FETCH_FILE_TOOL_SCHEMA,BEEOS_FETCH_FILE_TOOL_DESCRIPTION,executeFetchFile}from"./files/fetch-tool.js";import{writeUploadToolResult,cleanupUploadToolResultCache}from"./upload/result-cache.js";import{handleFileStage}from"./upload/file-stage.js";import{handleStatusRequest}from"./status.js";import{ACPServer}from"./acp-server.js";import{GatewayClient}from"./gateway-client.js";import{createObsEventWriter}from"./observability.js";import{resolvePluginVersion}from"./plugin-version.js";import{createTerminalWebSocketService}from"./terminal/websocket.js";import{TerminalManager}from"./terminal/session-manager.js";import{loadAgentKeyPair,agentBoundFetch,agentFetch}from"./agent-auth.js";import{readDiscoveryCache,writeDiscoveryCache}from"./discovery-cache.js";import{discoverBridgeFromGW}from"./bridge-discovery.js";import{setUploadDeps}from"./upload/upload-file.js";import{registerLocalFileExtractor}from"./upload/local-file-extractors.js";import{browserScreenshotExtractor}from"./browser/screenshot-extractor.js";import{BEEOS_FILE_RESOURCE_ROUTING_RULE}from"./files/beeos-file-uri.js";import{CONNECTOR_MCP_PROXY_PATH,handleConnectorMcpRequest}from"./mcp-connector-proxy.js";import{CanvasStateManager}from"./canvas/state.js";import{CanvasRelayClient}from"./canvas/relay-client.js";import{RENDER_UI_TOOL_NAME,RENDER_UI_TOOL_DESCRIPTION,RENDER_UI_TOOL_SCHEMA,CLOSE_UI_TOOL_NAME,CLOSE_UI_TOOL_DESCRIPTION,CLOSE_UI_TOOL_SCHEMA,createRenderUiHandler,createCloseUiHandler,CANVAS_SYSTEM_PROMPT}from"./canvas/tools.js";import{FormSchemaRegistry}from"./surface/form-schema-registry.js";import{buildCronSchemas}from"./surface/cron-schemas.js";import{RENDER_FORM_TOOL_NAME,RENDER_FORM_TOOL_SCHEMA,buildRenderFormDescription,createRenderFormHandler}from"./surface/render-form-tool.js";import{ResourceDescriptorRegistry}from"./surface/resource-descriptor-registry.js";import{CRON_RESOURCE}from"./surface/cron-resource.js";import{generateActionTools}from"./surface/action-tool-generator.js";import{buildThirdPartyChannelSet,isThirdPartyChannel}from"./channel-detection.js";import{registerArouterMediaProviders}from"./media/index.js";import{homedir}from"node:os";import{join}from"node:path";import{RuntimeProductionAuthority}from"./runtime/runtime-production-authority.js";import{FileRuntimeCommandJournal}from"./runtime/runtime-command-journal.js";import{FileRuntimePoisonDeliveryJournal}from"./runtime/runtime-poison-delivery-journal.js";import{OpenClawNativeMcpProjection}from"./acp-gateway/openclaw-native-mcp-projection.js";import{LocalEncryptedCredentialReferenceVault}from"./acp-gateway/mcp-credential-reference-port.js";import{AgentGatewayGrantJwksReadinessAdapter,RuntimeServiceTransportReadinessAdapter,createRuntimeServiceProductionCandidate}from"./runtime/runtime-service-production-candidate.js";import{MessageServiceCronDeliveryHttpAuthority}from"./runtime/message-service-cron-delivery-http-authority.js";import{AgentMappingRegistry}from"./runtime/agent-mapping-registry.js";import{OpenClawRuntimeProcessOwner}from"./runtime/openclaw-runtime-process-owner.js";import{AgentGatewayHttpProjectionTransport,AgentGatewayTemplateOriginReporter,AgentProjectionProduction,OpenClawAgentProjectionSource}from"./runtime/agent-projection-production.js";import{MessageServiceRunOutputChannel}from"./runtime/message-service-run-output-channel.js";import{AssistantMessageCommitRouter}from"./runtime/assistant-message-commit-router.js";import{ToolResultObservationRouter}from"./runtime/tool-result-observation-router.js";import{ModelCallObservationRouter,registerModelCallObservationHooks}from"./runtime/model-call-observability.js";import{registerArouterLatencyRouting}from"./runtime/arouter-latency-routing.js";import{createPeerRunOutputRouterFactory}from"./runtime/run-output-composition.js";const PLUGIN_VERSION=resolvePluginVersion();let agentKeys=null,sharedCanvasState=null,sharedAcpServer=null;const sharedSessionWriteGate=new SessionWriteGate,sharedPromptRunStore=new PromptRunStore;let lastOpenclawConfig;const sharedMcpNativeProjectionNudge=createMcpNativeProjectionNudge({getActiveImRunCount:()=>sharedPromptRunStore.size(),nudge:()=>{sharedAcpServer?.nudgeMcpNativeProjection()}}),sharedImHotPathDeferral=createImHotPathDeferral({getActiveImRunCount:()=>sharedPromptRunStore.size()});sharedPromptRunStore.setOnBecameIdle(()=>{sharedMcpNativeProjectionNudge.flushIfIdle({fingerprint:fingerprintMcpServers(lastOpenclawConfig)}),sharedImHotPathDeferral.notifyIdle()});const sharedAssistantMessageCommitRouter=new AssistantMessageCommitRouter,sharedToolResultObservationRouter=new ToolResultObservationRouter,sharedModelCallObservationRouter=new ModelCallObservationRouter;let sharedSessionMutationCoordinator,sharedRuntimeAuthority;const sharedAgentMappings=new AgentMappingRegistry;let sharedGatewayClient,sharedRuntimeOwner,sharedAgentProjection,sharedRuntimeAgentCapabilities,sharedMcpProjection,sharedMcpCredentialVault;const sharedMessageClientOwner=new BeeosClawMessageClientProcessOwner;let sharedActiveMessageConsumer,sharedInboundExecutionJournal;const sharedTerminalManager=new TerminalManager;let _initializing=!1,_pluginRegisteredAt=0;const ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex");function agentKeyPairToIdentity(e){return{publicKeyBase64:()=>Buffer.from(e.publicKey).toString("base64"),sign:async t=>{const{createPrivateKey:r,sign:o}=await import("node:crypto"),n=r({key:32===e.privateKey.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(e.privateKey)]):Buffer.from(e.privateKey),format:"der",type:"pkcs8"});return new Uint8Array(o(null,Buffer.from(t),n))}}}let unhandledRejectionObserverInstalled=!1;function installUnhandledRejectionObserver(e,t,r){unhandledRejectionObserverInstalled||(unhandledRejectionObserverInstalled=!0,process.on("unhandledRejection",r=>{const o=r instanceof Error?r.message:String(r),n=r instanceof Error?r.stack:void 0;e.logger.error?.(`[beeos-claw] unhandledRejection: ${o}${n?`\n${n}`:""}`),t({component:"plugin",domain:"fatal",name:"unhandled_rejection",severity:"error",summary:o,error:{code:"unhandled_rejection",message:o,...n?{stack:n}:{}}})}),process.on("uncaughtException",r=>{const o=r instanceof Error?r.message:String(r);e.logger.error?.(`[beeos-claw] uncaughtException: ${r instanceof Error?r.stack??r.message:String(r)}`),t({component:"plugin",domain:"fatal",name:"uncaught_exception",severity:"error",summary:o,error:{code:"uncaught_exception",message:o}})}))}function initializePlugin(e){let t,r;try{t=e.runtime?.config?.loadConfig?.()}catch{}try{r=resolveConfigWithMeta({pluginConfig:e.pluginConfig??{},openclawConfig:t})}catch(t){return e.logger.error?.(`[beeos-claw] Failed to resolve config: ${t instanceof Error?t.message:String(t)}`),null}const o=r.config,n=buildThirdPartyChannelSet(t);n.size>0&&e.logger.info?.(`[beeos-claw] Third-party channels detected: ${[...n].join(", ")}`);const a=o.log.enabled?createObsEventWriter({dir:o.log.dir,redaction:{gatewayToken:o.gateway.token},logger:e.logger}):()=>{};a({component:"plugin",domain:"lifecycle",name:"lifecycle.startup",severity:"info",payload:{pluginVersion:PLUGIN_VERSION,pid:process.pid,nodeVersion:process.version,cwd:process.cwd()}}),a({component:"plugin",domain:"config",name:"config.resolved",severity:"info",payload:{config:o,sources:r.sources,openclawConfigPath:r.openclawConfigPath}});for(const t of r.validation)a({component:"plugin",domain:"config",name:"config.validation",severity:t.severity,summary:t.message,error:{code:t.code,message:t.message,nextSteps:t.nextSteps}}),"error"===t.severity?e.logger.error?.(`[beeos-claw] ${t.message}`):e.logger.warn?.(`[beeos-claw] ${t.message}`);if(r.validation.some(e=>"error"===e.severity)&&e.logger.error?.("[beeos-claw] Plugin has configuration errors — some features may not work."),o.keyFile)try{agentKeys=loadAgentKeyPair(o.keyFile);const t=Buffer.from(agentKeys.publicKey).toString("base64");e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (publicKey: ${t})`)}catch(t){e.logger.warn?.(`[beeos-claw] Failed to load agent key pair: ${t}`),e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no key pair — signed requests disabled)`)}else e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no keyFile configured)`);return agentKeys&&o.agentGatewayUrl&&(setUploadDeps({gatewayUrl:o.agentGatewayUrl,keys:agentKeys,logger:e.logger}),registerLocalFileExtractor(browserScreenshotExtractor),e.logger.info?.("[beeos-claw] Upload deps + browser screenshot extractor registered")),lastOpenclawConfig=t,{cfg:o,writeObs:a,thirdPartyChannels:n,openclawConfig:t}}function registerUploadTools(e,t,r){const{cfg:o,writeObs:n}=t;cleanupUploadToolResultCache().catch(t=>{e.logger.warn?.(`[beeos_upload_file] cache cleanup failed on startup: ${String(t)}`)}),e.registerTool({name:BEEOS_UPLOAD_TOOL_NAME,description:BEEOS_UPLOAD_TOOL_DESCRIPTION,parameters:BEEOS_UPLOAD_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_upload_file] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeUpload(r,o,agentKeys,a);return e.logger.debug?.(`[beeos_upload_file] toolCallId=${t} ok=${i.ok}`),n({component:"upload-tool",domain:"tool",name:i.ok?"upload.success":"upload.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,fileCount:i.files?.length??0,error:i.error}}),(async r=>{try{await writeUploadToolResult(t,BEEOS_UPLOAD_TOOL_NAME,r),await cleanupUploadToolResultCache()}catch(r){e.logger.warn?.(`[beeos_upload_file] persist cache failed toolCallId=${t}: ${String(r)}`)}return r})(i)}}),e.registerTool({name:BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,description:BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,parameters:BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeDesktopScreenshot(r,o,agentKeys,a);return e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} ok=${i.ok}`),n({component:"desktop-screenshot-tool",domain:"tool",name:i.ok?"desktop_screenshot.success":"desktop_screenshot.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,path:i.path,fileCount:i.files?.length??0,error:i.error}}),(async r=>{try{await writeUploadToolResult(t,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,r),await cleanupUploadToolResultCache()}catch(r){e.logger.warn?.(`[beeos_desktop_screenshot] persist cache failed toolCallId=${t}: ${String(r)}`)}return r})(i)}}),e.registerHttpRoute({path:"/beeos/files/stage",auth:"gateway",async handler(e,r){await handleFileStage(e,r,t.cfg)}}),e.registerHttpRoute({path:"/beeos/status",auth:"plugin",handler(e,t){handleStatusRequest(e,t,{publicKey:agentKeys?.publicKey??null,pluginVersion:PLUGIN_VERSION})}}),e.registerTool({name:BEEOS_LIST_FILES_TOOL_NAME,description:BEEOS_LIST_FILES_TOOL_DESCRIPTION,parameters:BEEOS_LIST_FILES_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_list_files] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeListFiles(r,o,agentKeys,a);return n({component:"file-list-tool",domain:"tool",name:i.ok?"list_files.success":"list_files.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,fileCount:i.files?.length??0,error:i.error}}),i}}),e.registerTool({name:BEEOS_FETCH_FILE_TOOL_NAME,description:BEEOS_FETCH_FILE_TOOL_DESCRIPTION,parameters:BEEOS_FETCH_FILE_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_fetch_file] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeFetchFile(r,o,agentKeys,{signal:a,resolvePath:e.resolvePath});return n({component:"file-fetch-tool",domain:"tool",name:i.ok?"fetch_file.success":"fetch_file.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,path:i.path,size:i.size,error:i.error}}),i}})}function registerConnectorMcpProxy(e,t){e.registerHttpRoute({path:CONNECTOR_MCP_PROXY_PATH,auth:"plugin",async handler(r,o){await handleConnectorMcpRequest(r,o,{agentGatewayUrl:t.cfg.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger})}})}function registerServices(e,t,r){const{cfg:o,writeObs:n,thirdPartyChannels:a}=t;if("function"!=typeof e.on){if(o.messageBus.enabled)throw new Error("openclaw_before_message_write_hook_unavailable")}else registerModelCallObservationHooks((t,r)=>e.on(t,r),sharedModelCallObservationRouter),e.on("before_message_write",(t,r)=>{const o=t.sessionKey??r.sessionKey;if(!o)return void e.logger.debug?.("[before_message_write] ignored assistant write without sessionKey");sharedAssistantMessageCommitRouter.route(o,t.message)||e.logger.debug?.(`[before_message_write] no active IM commit target sessionKey=${o} active_targets=${sharedAssistantMessageCommitRouter.size()}`)}),e.on("after_tool_call",(t,r)=>{const o=r.sessionKey,n=t.toolCallId??r.toolCallId,a=t.toolName||r.toolName;if(!o||!n||!a)return void e.logger.debug?.(`[after_tool_call] ignored structured result without correlation sessionKey=${o??"missing"} toolCallId=${n??"missing"}`);sharedToolResultObservationRouter.route(o,{toolCallId:n,toolName:a,rawResult:t.result,isError:Boolean(t.error)})||e.logger.debug?.(`[after_tool_call] no active IM observation target sessionKey=${o} active_targets=${sharedToolResultObservationRouter.size()}`)});const i=agentKeys?(e,t)=>agentFetch(e,agentKeys,t):void 0,s=agentKeys?(e,t)=>agentBoundFetch(e,agentKeys,t):void 0,l=agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):void 0,c=join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos"),g=sharedInboundExecutionJournal??=new FileInboundExecutionJournal(join(c,"inbound-execution-journal.v1.jsonl"));if(sharedMcpProjection??=new OpenClawNativeMcpProjection,!sharedMcpCredentialVault&&agentKeys&&l&&(sharedMcpCredentialVault=new LocalEncryptedCredentialReferenceVault(join(c,"mcp-credential-vault.json"),l,agentKeys.privateKey)),!sharedRuntimeAuthority&&agentKeys&&o.agentGatewayUrl&&i&&s&&(sharedRuntimeAuthority=new RuntimeProductionAuthority({stateDirectory:c,agentGatewayUrl:o.agentGatewayUrl,agentFetch:i,boundAgentFetch:s,keys:agentKeys,logger:e.logger})),sharedGatewayClient??=new GatewayClient({url:o.gateway.url,token:o.gateway.token||"",clientId:o.gateway.clientId,openclawCanvasCompat:!!o.openclawCanvasCompat,retry:{baseMs:o.retry.baseMs,maxMs:o.retry.maxMs,maxAttempts:o.retry.maxAttempts}}),sharedRuntimeAgentCapabilities??={canvas:o.canvasEnabled,terminal:!1,webSsh:!1,fileSystem:!0,image:!0,audio:!1,mcp:!0,cron:!0,streaming:!0,pushNotifications:o.messageBus.enabled},!sharedAgentProjection&&sharedRuntimeAuthority&&i&&o.agentGatewayUrl){const t=sharedRuntimeAuthority;sharedAgentProjection=new AgentProjectionProduction({authority:t,stateFile:join(t.stateDirectory,"runtime-agent-projection.json"),journal:t.journal,identities:t.identities,source:new OpenClawAgentProjectionSource(()=>sharedGatewayClient),transport:new AgentGatewayHttpProjectionTransport({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:t.boundAgentFetch,getRuntimeLeaseCredential:e=>{const r=t.authorityLease;if(!r||r.instanceId!==e.instanceId||r.leaseId!==e.leaseId||r.runtimeEpoch!==e.runtimeEpoch||r.handlerIdentity!==e.handlerIdentity)throw new Error("agent projection fenced by current runtime lease");return r.runtimeLeaseCredential},onAccepted:t=>{0!==t.size&&sharedAgentMappings.replace(t).catch(t=>{e.logger.warn?.("[agent-projection] mapping publication failed: "+(t instanceof Error?t.message:String(t)))})}}),capabilities:sharedRuntimeAgentCapabilities,logger:e.logger})}sharedRuntimeOwner??=new OpenClawRuntimeProcessOwner(sharedGatewayClient,sharedAgentProjection,e.logger);const d=sharedRuntimeOwner;sharedSessionMutationCoordinator??=new SessionMutationCoordinator(sharedSessionWriteGate,new OpenClawSessionWriteQuiescence(e.logger,{agentId:o.gateway.agentId||"main"}),e.logger,{onRecoveryRequired:t=>{scheduleProcessRuntimeRestart(t,e.logger)}});const u=sharedSessionMutationCoordinator;if(!sharedAcpServer){const t=sharedRuntimeAuthority&&o.agentGatewayUrl?new AgentGatewayTemplateOriginReporter({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:sharedRuntimeAuthority.boundAgentFetch}):void 0;sharedAcpServer=new ACPServer(o,e.logger,u,n,i,l,a,sharedTerminalManager,sharedRuntimeAuthority,void 0,sharedMcpProjection,sharedMcpCredentialVault,s,{gateway:d.client,agentProjection:sharedAgentProjection,markAgentProjectionDirty:()=>sharedAgentProjection?.markDirty(),templateOriginReporter:t,agentMappings:sharedAgentMappings,agentCapabilities:sharedRuntimeAgentCapabilities,createRunOutputRouter:createPeerRunOutputRouterFactory(new MessageServiceRunOutputChannel({getClient:()=>sharedMessageClientOwner.current?.client??null,logger:e.logger,writeObs:n})),waitForChatIdle:(t,r)=>sharedImHotPathDeferral.waitIfBusy(t,e.logger,r).then(()=>{})})}const p=sharedAcpServer,m=()=>sharedAgentMappings.snapshot()?.values().next().value;r.current=p,sharedMcpNativeProjectionNudge.request({registrationMode:e.registrationMode,fingerprint:fingerprintMcpServers(t.openclawConfig),logger:e.logger});const y=sharedPromptRunStore,{enabled:h,principalId:f,a2aEnabled:w}=o.messageBus;let v;o.agentGatewayUrl&&agentKeys&&(v=createTokenProvider({agentGatewayUrl:o.agentGatewayUrl,identity:agentKeyPairToIdentity(agentKeys)}));const b=process.env.MESSAGE_RUNTIME_DELIVERY_KEY?.trim(),C=f||process.env.BEEOS_PRINCIPAL_ID||"",_=v?sharedMessageClientOwner.configure({tokenProvider:v,identityId:C,...o.messageBus.serviceUrl?{serviceUrl:o.messageBus.serviceUrl}:{},logger:e.logger,...sharedRuntimeAuthority&&b?{runtimeDelivery:{scopedDeliveryKey:b,authority:{currentLease:()=>{const e=sharedRuntimeAuthority?.authorityLease,t=sharedRuntimeAuthority?.currentConsumerBinding();return e&&t?{...e,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration}:null}}}}:{}}):void 0,R=sharedRuntimeAuthority,O=o.messageBus.serviceUrl;if(_&&R&&O&&b){const e=_.nodeTransport;p.setCronConversationDelivery({deliver:async t=>{const r=R.authorityLease;if(!r)throw new Error("current runtime authority lease unavailable for cron delivery");const o=new MessageServiceCronDeliveryHttpAuthority({messageServiceUrl:O,fetchAuthorized:async(t,o)=>await e.fetchRuntimeChannel(t,{runtimeLeaseCredential:r.runtimeLeaseCredential},o)});return await o.deliver({...t,runtimeLeaseCredential:r.runtimeLeaseCredential})}})}else p.setCronConversationDelivery(void 0);const E=sharedRuntimeAuthority?.journal??new FileRuntimeCommandJournal(join(c,"runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:t=>e.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=threshold error=${t instanceof Error?t.message:String(t)}`)}),S=d.createRegistrationToken();e.registerService({id:"beeos-acp",async start(){const t=Date.now(),r=_pluginRegisteredAt?t-_pluginRegisteredAt:-1;e.logger.info?.(`[beeos-claw] beeos-acp service start() begin, sincePluginRegister=${r}ms, bridge.url=${o.bridge.url||"(empty)"}`);const n=await createRuntimeServiceProductionCandidate({...o.agentGatewayUrl&&i?{authorizationAdapter:new AgentGatewayGrantJwksReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl})}:{},..._&&o.agentGatewayUrl&&s&&b?{transportAdapter:new RuntimeServiceTransportReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl,messageService:_.nodeTransport,messageClient:_.client,configuredServiceUrl:o.messageBus.serviceUrl,boundAgentFetch:s,scopedDeliveryKey:b})}:{},commandJournal:E,poisonJournal:new FileRuntimePoisonDeliveryJournal(join(c,"runtime-poison-deliveries.json")),onConsumerDeliveryError:t=>{const r=t instanceof Error?`${t.name}: ${t.message}${"status"in t?` status=${t.status}`:""}${"code"in t?` code=${t.code}`:""}\n${t.stack??""}`:String(t);e.logger.error?.(`[runtime-service] delivery worker failed before acknowledgement; delivery retained for retry: ${r}`)},onConsumerLeaseStarvation:({starvedForMs:t,recovered:r})=>{const o=Math.round(t/1e3);r?e.logger.info?.(`[runtime-service] runtime authority lease restored after ${o}s without delivery reads`):e.logger.warn?.(`[runtime-service] no runtime authority lease for ${o}s; delivery reads suspended, queued runtime operations (model switch, session control) will not be consumed`)},onConsumerRequestFailure:t=>e.logger.error?.(`[runtime-service] request failed operationId=${t.operationId} method=${t.method}: ${t.error instanceof Error?t.error.stack??t.error.message:String(t.error)}`)});if(sharedRuntimeAuthority?.setRuntimeServiceMethods("ready"===n.status?n.serviceMethods:[]),sharedRuntimeAuthority?.setRuntimeResourceBindingContractRevision("ready"===n.status?n.resourceBindingContractRevision:void 0),p.setRuntimeRpcConsumerFactory("ready"===n.status?n.factory:void 0),"unavailable"===n.status?e.logger.warn?.("[runtime-service] capability fenced: "+n.blockers.map(e=>e.id).join(",")):n.capabilityBlockers.length&&e.logger.warn?.("[runtime-service] method capability fenced: "+n.capabilityBlockers.map(e=>e.method).join(",")),!o.bridge.url&&o.agentGatewayUrl&&i){const t=Date.now(),r=await discoverBridgeFromGW(o.agentGatewayUrl,i);if(e.logger.info?.(`[beeos-claw] bridge discovery took ${Date.now()-t}ms result=${r?.url??"null"}`),r)o.bridge.url=r.url,writeDiscoveryCache({bridge:{url:r.url,discoveredAt:Date.now()}}),e.logger.info?.(`[beeos-claw] Bridge URL discovered from Agent Gateway: ${r.url}`);else{const t=readDiscoveryCache();t?.bridge?.url?(o.bridge.url=t.bridge.url,e.logger.info?.(`[beeos-claw] Bridge URL from cache (GW unreachable): ${t.bridge.url}`)):e.logger.debug?.("[beeos-claw] Bridge URL not available (no GW, no cache) — bridge disabled")}}await d.acquire(S);try{await p.start(o.bridge.url||void 0)}catch(e){throw await d.release(S),e}e.logger.info?.(`[beeos-claw] ACP Server started (total service start: ${Date.now()-t}ms)`)},async stop(){try{await d.release(S)}finally{await p.stop()}await E.checkpoint(),e.logger.info?.("[runtime-service] command journal checkpoint completed"),e.logger.info?.("[beeos-claw] ACP Server stopped")}});if(h&&void 0!==v&&v){const t=sharedMessageClientOwner.createRegistrationToken(),r=async(e,t)=>{const r=await v(e);if(!r.identityId||r.identityId!==e)throw new Error("Message SDK token identity did not match the current runtime authority");return{principalId:e,runtimeEpoch:t}},a=(e,t,r)=>({getLocalAgentId:()=>t,resolveLocalAgentId:e=>e===r?t:void 0,getDefaultPlatformAgent:()=>({instanceId:e.principalId}),ensureImInboundSession:(e,t,r,o)=>{p.ensureImInboundSession(e,t,r,o)},chatAbort:(e,t)=>p.chatAbort(e,t)}),i=sharedRuntimeAuthority?new RuntimeAuthorityMessageBusLifecycle({authority:sharedRuntimeAuthority,logger:e.logger,bus:{connect:async i=>{await sharedMessageClientOwner.acquire(t,async t=>{const s=await r(i.instanceId,i.runtimeEpoch),l=t.client,c=new Map,d=new MessageProjectionInboundConsumer({client:l,bindings:()=>{const t=sharedRuntimeAuthority?.authorityLease;if(!t||t.instanceId!==s.principalId||t.runtimeEpoch!==s.runtimeEpoch)return[];const r=sharedAgentMappings.snapshot();return r?[...r.entries()].flatMap(([r,i])=>{if(!i.id||i.instanceId!==s.principalId)return[];let g=c.get(i.id);return g&&g.localAgentId===r||(g={localAgentId:r,inbound:createImInboundHandler({runtime:e.runtime,pluginConfig:e.pluginConfig,client:l,localServer:a(s,r,i.id),promptRunStore:y,assistantMessageCommitRouter:sharedAssistantMessageCommitRouter,toolResultObservationRouter:sharedToolResultObservationRouter,modelCallObservationRouter:sharedModelCallObservationRouter,sessionMutationCoordinator:u,logger:e.logger,writeObs:n,principalIdOverride:s.principalId,promptTimeoutMs:o.promptTimeoutMs,activeRunAuthority:p.getActiveRunAuthority(),...o.agentGatewayUrl&&agentKeys?{completeA2ATask:(t,r,n)=>completeA2ATask({agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},t,r,n)}:{}})},c.set(i.id,g)),[{principalId:s.principalId,targetAgentId:i.id,runtimeEpoch:t.runtimeEpoch,inbound:g.inbound}]}):[]},executionJournal:g,logger:e.logger}),m=sharedAgentMappings.subscribe(()=>{sharedActiveMessageConsumer===d&&d.refresh()}),h=sharedRuntimeAuthority?.subscribeAuthority(()=>{sharedActiveMessageConsumer===d&&d.refresh()})??(()=>{}),f=new MessageClientTerminalStateRecovery({logger:e.logger,isCurrent:()=>{if(sharedActiveMessageConsumer!==d)return!1;const e=sharedRuntimeAuthority?.authorityLease;return null!=e&&e.instanceId===s.principalId&&e.runtimeEpoch===s.runtimeEpoch&&e.leaseId===i.leaseId&&e.handlerIdentity===i.handlerIdentity},reconnect:async()=>{if("failed"===l.getSnapshot().connection){await l.connect();try{await p.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient recovery: ${t instanceof Error?t.message:String(t)}`)}}}});let w=l.getSnapshot().connection;const v=l.subscribe(()=>{const t=l.getSnapshot();t.connection!==w&&(w=t.connection,"connected"===t.connection?(f.notifyConnected(),e.logger.info?.(`[beeos-claw] connection transport=message_service_realtime state=connected localAgentId=${p.getLocalAgentId()} principalId=${s.principalId}`)):"failed"===t.connection?(e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=failed principalId=${s.principalId} recoveryError=${t.recoveryError??"unknown"}`),f.notifyFailed()):"disconnected"!==t.connection&&"reconnecting"!==t.connection||e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=${t.connection} principalId=${s.principalId}; SDK owns reconnect/recovery`))});sharedActiveMessageConsumer=d,d.start();try{await l.connect()}catch(e){throw sharedActiveMessageConsumer===d&&(sharedActiveMessageConsumer=void 0),f.dispose(),await d.stop(),m(),h(),v(),await l.disconnect().catch(()=>{}),e}try{await p.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient connect: ${t instanceof Error?t.message:String(t)}`)}return async()=>{sharedActiveMessageConsumer===d&&(sharedActiveMessageConsumer=void 0),f.dispose(),await d.stop(),h(),v(),await l.disconnect(),e.logger.info?.("[beeos-claw] MessageClient disconnected")}})},disconnect:async()=>{await sharedMessageClientOwner.release(t)}}}):void 0;e.registerService({id:"beeos-message-bus",async start(){i?i.start():e.logger.warn?.("[beeos-claw] Message bus requires runtime authority; waiting is unavailable without Agent Gateway runtime identity")},async stop(){await(i?.stop())}})}else h?e.logger.warn?.("[beeos-claw] Message bus enabled but Agent Gateway token provider is unavailable — disabled"):e.logger.info?.(`[beeos-claw] Message bus disabled (enabled=${h})`);if(!1!==w&&o.agentGatewayUrl){const t=e=>p.getGatewaySessionKeyForToolCall(e),r=e=>p.getMentionsForToolCall(e),n=(e,t)=>{p.broadcastNotification(e,t)},a=new TaskHandleRegistry;registerA2ATools(e,{agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},a,t,r,n,()=>m()?.instanceId),e.logger.info?.("[beeos-claw] A2A tools registered (beeos_discover_agents, beeos_call_agent) via REST")}if(o.canvasEnabled){sharedCanvasState||(sharedCanvasState=new CanvasStateManager);const t=sharedCanvasState;if(o.agentGatewayUrl&&agentKeys){let r;t.setLogger(e.logger);{const e=o.agentGatewayUrl,t=agentKeys;r=async()=>{const r=await agentFetch(`${e}/api/v1/canvas/token`,t,{method:"POST"});if(!r.ok){const e=await r.text().catch(()=>"");throw new Error(`Canvas token returned ${r.status}: ${e}`)}return r.json()}}const n=n=>{const a=new CanvasRelayClient({canvasRelayUrl:o.canvasRelayUrl,agentKeys:agentKeys,agentId:n.id,displayName:n.displayName,avatarUrl:n.avatarUrl,tokenProvider:r,onConnected:()=>p.flushPendingCanvasLinks(),onReconnect:()=>p.onRelayReconnected(),logger:e.logger});t.setRelayClient(a),t.setAgentId(n.id),p.wireRelayActionHandler(),a.connect(),e.logger.info?.(`[beeos-claw] Canvas Relay multiplexed client connected (agentId=${n.id}, name=${n.name??"none"})`)};e.registerService({id:"beeos-canvas-relay",async start(){try{await sharedAgentMappings.waitForSynchronization();const t=m();if(t?.id)return void n(t);e.logger.info?.("[beeos-claw] Canvas Relay deferred: waiting for late agent sync..."),sharedAgentMappings.onceReady(()=>{const t=m();if(t?.id)try{n(t)}catch(t){e.logger.warn?.(`[beeos-claw] Deferred Canvas Relay start failed: ${t}`)}else e.logger.warn?.("[beeos-claw] Agent mapping ready but no platform agent; Canvas Relay stays disabled")})}catch(t){e.logger.warn?.(`[beeos-claw] Failed to start Canvas Relay client: ${t}`)}},stop(){t.getRelayClient()?.disconnect()}}),e.logger.info?.("[beeos-claw] Canvas Relay configured")}else e.logger.warn?.("[beeos-claw] Canvas Relay unavailable: agentGatewayUrl or agentKeys missing");const r={stateManager:t,logger:e.logger,writeObs:e=>n(e),isCanvasActive:e=>!!e&&p.isCanvasActiveForGatewayKey(e),getActiveSessionId:e=>p.getSessionIdForToolCall(e),getPlatformAgentId:e=>p.getPlatformAgentIdForToolCall(e),getGatewaySessionKey:e=>p.getGatewaySessionKeyForToolCall(e),waitForAgentSync:()=>sharedAgentMappings.waitForSynchronization()},i=new FormSchemaRegistry,s=buildCronSchemas(a,{capMs:o.cronMaxDurationCapMs});i.register(s.create),i.register(s.update);const l=new ResourceDescriptorRegistry;l.register(CRON_RESOURCE);const c=e=>async(t,r,o)=>(p.preMapToolCallSession(t),e(t,r));e.registerTool({name:RENDER_UI_TOOL_NAME,description:RENDER_UI_TOOL_DESCRIPTION,parameters:RENDER_UI_TOOL_SCHEMA,execute:c(createRenderUiHandler(r))}),e.registerTool({name:CLOSE_UI_TOOL_NAME,description:CLOSE_UI_TOOL_DESCRIPTION,parameters:CLOSE_UI_TOOL_SCHEMA,execute:c(createCloseUiHandler(r))}),p.setCanvasState(t,{schemaRegistry:i,descriptorRegistry:l});const g=p.getFormBuilder();g&&e.registerTool({name:RENDER_FORM_TOOL_NAME,description:buildRenderFormDescription(i),parameters:RENDER_FORM_TOOL_SCHEMA,execute:c(createRenderFormHandler({registry:i,builder:g,logger:e.logger}))});const d=p.getResourceRenderer();if(d){generateActionTools(l,e,async(e,t)=>p.gatewayRequest(e,t),d,e.logger)}const u=`\n\nTool selection guidance:\n- browser tool: Use when the user wants to OPEN, VISIT, NAVIGATE to a webpage, take SCREENSHOTS of web pages, interact with elements (click, type), or visually inspect pages.\n- ${BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME}: Use to capture the agent desktop (XFCE/VNC DISPLAY). Prefer this over shell/python X11 dumps; never invent raw Xlib/PIL capture.\n- beeos_search: Use ONLY for text-based information retrieval (fact-finding, research queries). Never for page interaction or screenshots.\n- beeos_fetch: Use ONLY to extract article text from a known URL. Never for visual content or screenshots.\n- ${BEEOS_FILE_RESOURCE_ROUTING_RULE}\nWhen the user says "open", "visit", "go to", or mentions seeing/viewing a webpage, ALWAYS prefer the browser tool. When they ask for a desktop / screen / VNC capture of the agent workspace, use beeos_desktop_screenshot.`,y=new Set([RENDER_UI_TOOL_NAME,CLOSE_UI_TOOL_NAME,RENDER_FORM_TOOL_NAME]),h=256,f=new Map;if("function"==typeof e.on){try{e.on("before_prompt_build",(e,t)=>{if(t?.sessionKey){if(f.size>=h&&!f.has(t.sessionKey)){const e=f.keys().next().value;void 0!==e&&f.delete(e)}f.set(t.sessionKey,t.channelId)}if(isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:t?.channelId}))return{appendSystemContext:'This conversation is in a third-party channel (Lark/Telegram/Discord/Slack); Canvas surfaces are not visible here. Do NOT use render_ui, close_ui, or render_form. Use built-in tools directly (e.g. the `cron` tool with `action:"add"` for scheduled tasks) and respond with plain text.'};if(!t?.sessionKey)return{appendSystemContext:u};return p.isCanvasActiveForGatewayKey(t.sessionKey)?{appendSystemContext:CANVAS_SYSTEM_PROMPT+u}:{appendSystemContext:"Canvas is closed by user. Do NOT use render_ui, close_ui, or render_form. Respond with plain text only."+u}}),e.logger.debug?.("[beeos-claw] Canvas system prompt hook registered: before_prompt_build")}catch{e.logger.debug?.("[beeos-claw] before_prompt_build hook not supported")}try{e.on("before_tool_call",(e,t)=>{if(t?.sessionKey&&p.setPendingToolCallSessionKey(t.sessionKey),!y.has(e.toolName))return;const r=t?.sessionKey?f.get(t.sessionKey):void 0;return isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:r})?{block:!0,blockReason:"Canvas tools are not available in this channel. Respond with plain text only."}:t?.sessionKey?p.isCanvasActiveForGatewayKey(t.sessionKey)?void 0:{block:!0,blockReason:"Canvas is closed by user. Respond with plain text instead."}:{block:!0,blockReason:"Canvas session unknown. Respond with plain text instead."}}),e.logger.debug?.("[beeos-claw] Canvas tool guard registered: before_tool_call")}catch{e.logger.debug?.("[beeos-claw] before_tool_call hook not supported")}}e.logger.info?.("[beeos-claw] Canvas tools registered (render_ui, close_ui, render_form + auto-action tools)")}else e.logger.info?.("[beeos-claw] Canvas disabled by config (canvasEnabled=false)");if(o.bridge.shell.enabled&&!0===o.legacyTerminalWsEnabled){const t=createTerminalWebSocketService({config:{port:o.terminalWsPort,token:agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):"",allowedOrigins:o.terminalWsAllowedOrigins},terminalMgr:sharedTerminalManager,logger:e.logger,writeObs:n});e.registerService({id:"beeos-terminal-ws",start(){t.start(),e.logger.info?.(`[beeos-claw] Terminal WS started on port ${o.terminalWsPort}`)},stop(){t.stop(),e.logger.info?.("[beeos-claw] Terminal WS stopped")}})}else o.bridge.shell.enabled?e.logger.info?.("[beeos-claw] Legacy static-token Terminal WS disabled (default)"):e.logger.info?.("[beeos-claw] Terminal/shell disabled by config (bridge.shell.enabled=false)");if(t.openclawConfig){const r=mirrorOpenClawConfig(t.openclawConfig);r.copied?e.logger.info?.(`[beeos-claw] mirrored openclaw config to ${r.destinationPath}`):e.logger.debug?.(`[beeos-claw] openclaw config mirror skipped: ${r.reason}`)}}const CAPABILITY_REGISTRATION_MODES=new Set(["full","discovery","tool-discovery"]),plugin={id:"beeos-claw",name:"beeos-claw",description:"BeeOS platform bridge plugin for OpenClaw. Provides file upload/download, ACP bridge, and observability.",version:PLUGIN_VERSION,register(e){if(!_initializing)if(!e.registrationMode||CAPABILITY_REGISTRATION_MODES.has(e.registrationMode)){_initializing=!0;try{if(!hasRuntimeReplyApi(e.runtime))throw e.logger.error?.("[beeos-claw] FATAL: OpenClaw runtime.channel.reply.dispatchReplyFromConfig not exposed. Upgrade OpenClaw or revert beeos-claw."),new Error("openclaw_runtime_reply_api_unavailable");const t=initializePlugin(e);if(!t)return;const r={current:null};installUnhandledRejectionObserver(e,t.writeObs,r),registerUploadTools(e,t,r),registerConnectorMcpProxy(e,t),registerArouterLatencyRouting(e),registerArouterMediaProviders(e),registerServices(e,t,r),_pluginRegisteredAt=Date.now(),e.logger.info?.("[beeos-claw] Plugin registered successfully")}finally{_initializing=!1}}else e.logger.debug?.(`[beeos-claw] Skipping registration (mode=${e.registrationMode})`)}};export default plugin;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export class ImHotPathAbortedError extends Error{reason;constructor(t){super(`hot-path wait aborted reason=${t}`),this.name="ImHotPathAbortedError",this.reason=t}}export const createImHotPathDeferral=t=>{const e=new Set;return{waitIfBusy:async(r,o,n)=>{if(t.getActiveImRunCount()<=0)return"idle";if(n?.aborted)throw new ImHotPathAbortedError(r);return o?.info?.(`[hot-path] deferred_wait reason=${r} active_im_runs=${t.getActiveImRunCount()}`),await new Promise((o,a)=>{const s=()=>{e.delete(i),n?.removeEventListener("abort",d)},i=()=>{s(),o()},d=()=>{s(),a(new ImHotPathAbortedError(r))};e.add(i),n?.addEventListener("abort",d,{once:!0}),t.getActiveImRunCount()<=0&&i()}),o?.info?.(`[hot-path] flushed reason=${r}`),"deferred_wait"},notifyIdle:()=>{if(0===e.size)return;const t=[...e];e.clear();for(const e of t)e()}}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash,randomUUID}from"node:crypto";import{mkdir,writeFile}from"node:fs/promises";import{homedir}from"node:os";import path,{join,relative,resolve}from"node:path";import{RUNTIME_HASH_DOMAINS,RUNTIME_METHOD_PARITY_MANIFEST,runtimeHashPreimage}from"@beeos-ai/beeos-types/runtime";import{reduceRuntimeRecoveryPolicy}from"@beeos-ai/beeos-types/runtime/internal";import{assertTrustedCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{InMemoryRuntimeCommandJournal,RuntimeCommandJournalConflictError,runtimeCommandIsReplayableQuery,runtimeCommandResultHash}from"./runtime-command-journal.js";import{RuntimeProjectionTransportError}from"./agent-projection-worker.js";const DEFAULT_AGENT_ID="main",AGENT_VALID_ID_RE=/^[a-z0-9][a-z0-9_-]{0,63}$/i,AGENT_INVALID_CHARS_RE=/[^a-z0-9_-]+/g;export function normalizeRuntimeAgentLocalKey(e){const t=(e??"").trim();return t?AGENT_VALID_ID_RE.test(t)?t.toLowerCase():t.toLowerCase().replace(AGENT_INVALID_CHARS_RE,"-").replace(/^-+/,"").replace(/-+$/,"").slice(0,64)||"main":"main"}export async function awaitRuntimeProjection(e){const t=e;return await(t.awaitProjection?.()??Promise.resolve(e.getSnapshot().projection))}export function hasEffectReceiptHandoff(e){const t=e;return t.effectCommitted instanceof Promise&&"function"==typeof t.acknowledgeEffectReceipt}export class RuntimeCommandError extends Error{code;param;constructor(e,t,r,i){super(t,i),this.code=e,this.param=r,this.name="RuntimeCommandError"}}const emptyProgress=async function*(){};function deferred(){let e;return{promise:new Promise(t=>{e=t}),resolve:e}}async function waitForEffectReceipt(e,t){if(!t)return await e;if(t.aborted)throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt was not durably published before the Service attempt ended");await new Promise((r,i)=>{const n=()=>i(new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt was not durably published before the Service attempt ended"));t.addEventListener("abort",n,{once:!0}),e.then(()=>{t.removeEventListener("abort",n),r()},e=>{t.removeEventListener("abort",n),i(e)})})}export class OpenClawRuntimeCommandDispatcher{deps;logger;getOpenclawHome;createAgentId;journal;capabilities;constructor(e){this.deps=e,this.logger=e.logger??{},this.getOpenclawHome=e.getOpenclawHome??(()=>process.env.OPENCLAW_HOME||join(homedir(),".openclaw")),this.createAgentId=e.createAgentId??(()=>`agent-${randomUUID().replace(/-/g,"").slice(0,12)}`),this.journal=e.journal??new InMemoryRuntimeCommandJournal,this.capabilities=e.capabilities}setCapabilities(e){this.capabilities=e}dispatch(e,t){const r=e;assertTrustedCoreExecutionAttempt(t,{operationId:r.context.operationId,runtimeEpoch:r.context.dispatchEpoch});let i,n,a,o="running";const s=deferred(),c=deferred();let m;const d="service"===t.origin&&("mcp.set"===r.name||"mcp.unset"===r.name),p=this.executeWithLease(r,t,(e,t)=>{o="committed",m={operationId:r.context.operationId,method:r.name,record:t},s.resolve({...m,result:e,committedAt:t.updatedAt})},async()=>{d?await waitForEffectReceipt(c.promise,t.abortSignal):"acp"!==t.origin||"mcp.set"!==r.name&&"mcp.unset"!==r.name||!m?.record.mcpDesiredStateCommit||(await this.journal.releaseMcpLiveEffect({operationId:r.context.operationId,source:"acp_local"}),await this.journal.promoteMcpDesiredStateActive({operationId:r.context.operationId,source:"acp_local"}))},d,e=>{i=e},e=>{n=e}).then(e=>(o="committed",e),e=>{throw o=e instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===e.code||"INSTANCE_EPOCH_MISMATCH"===e.code)?"outcome_unknown":"failed",s.resolve(void 0),e});return{operationId:r.context.operationId,getSnapshot:()=>({effectState:o,...i?{projection:i}:{}}),completion:p,progress:emptyProgress(),effectCommitted:s.promise,acknowledgeEffectReceipt:async e=>{if(!m||e.operationId!==m.operationId||e.method!==m.method)throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt does not match this operation");if(!/^[a-f0-9]{64}$/.test(e.effectRecordHash))throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt hash is invalid");await this.journal.releaseMcpLiveEffect({operationId:e.operationId,source:"service",effectRecordHash:e.effectRecordHash}),c.resolve()},acknowledgeTerminalReceipt:async e=>{if(!(d&&m&&e.operationId===m.operationId&&e.method===m.method&&/^[a-f0-9]{64}$/.test(e.effectRecordHash)&&e.terminalMessageId))throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP terminal receipt does not match this operation");await this.journal.promoteMcpDesiredStateActive({operationId:e.operationId,source:"service",effectRecordHash:e.effectRecordHash,terminalMessageId:e.terminalMessageId});const a=await this.requireReleasedRecord(e.operationId);if(void 0===a.result)throw new RuntimeCommandError("OUTCOME_UNKNOWN","active MCP desired state lost its committed result");await this.afterCommittedEffect(r,a.result,a,t,e=>{i=e},e=>{n=e})},cancel:async()=>"running"===o?{status:"cancel_requested"===await(this.capabilities?.cancel?.(r.context.operationId))?"cancel_requested":"not_cancellable"}:{status:"already_terminal"},awaitProjection:async()=>n?(a??=n(),await a):i}}async executeWithLease(e,t,r,i,n,a,o){const s=await this.deps.leases.claim(e.context.operationId,e.context.dispatchEpoch);if(s.runtimeEpoch!==e.context.dispatchEpoch)throw await this.deps.leases.release(s),new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime operation lease epoch mismatch");const c=this.deps.leases.startHeartbeat(s);try{let c;await this.recoverInterruptedCreates(e.context.operationId);try{c=await this.journal.prepare({operationId:e.context.operationId,command:e.name,paramsHash:e.context.paramsHash,runtimeEpoch:s.runtimeEpoch})}catch(e){if(e instanceof RuntimeCommandJournalConflictError)throw new RuntimeCommandError("OUTCOME_UNKNOWN",e.message,void 0,{cause:e});throw e}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&void 0!==c.result){if(r(c.result,c),await i(),c=await this.requireReleasedRecord(c.operationId),void 0===c.result)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed result disappeared from command journal");return n||await this.afterCommittedEffect(e,c.result,c,t,a,o),c.result}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&"hash_only"===c.resultReceipt?.policy&&runtimeCommandIsReplayableQuery(e.name)){const n=await this.execute(e,s,t);if(runtimeCommandResultHash(n)!==c.resultReceipt.resultHash)throw new RuntimeCommandError("OUTCOME_UNKNOWN","redacted committed query result changed");return r(n,c),await i(),n}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&"hash_only"===c.resultReceipt?.policy){if(r(void 0,c),await i(),c=await this.requireReleasedRecord(c.operationId),!n){if(isRuntimeAgentMutation(e)&&(c.agentProjectionIntent||c.agentProjectionPreparationFailure)){const r=agentProjectionRecoveryResult(c.agentProjectionIntent??c.agentProjectionPreparationFailure);return await this.afterCommittedEffect(e,r,c,t,a,o),r}await this.runAfterEffect(e,void 0,c,t,a)}throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed mutation result is redacted")}if("effect_committed"===c.phase||"projection_committed"===c.phase)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed result receipt is unavailable");if("outcome_unknown"===c.phase)throw new RuntimeCommandError("OUTCOME_UNKNOWN","previous execution outcome is unknown");if("started"===c.phase&&isRuntimeAgentMutation(e)){let n;try{n=await this.recoverStartedAgentMutation(e,s,t)}catch(e){throw await this.markJournalUnknown(c),e instanceof RuntimeCommandError?e:new RuntimeCommandError("OUTCOME_UNKNOWN",`agent desired-state recovery could not prove its effect: ${errorText(e)}`,void 0,{cause:e})}const m=this.deps.projection?await this.prepareAgentProjectionCommit(e,n,t):void 0,d=await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:"effect_committed",result:n,...m?{agentProjection:m}:{}});return r(n,d),await i(),await this.afterCommittedEffect(e,n,d,t,a,o),this.scheduleJournalCompaction(),n}if("started"===c.phase&&"outcome_unknown"===this.recoveryDecision(e.name,c.phase))throw await this.markJournalUnknown(c),new RuntimeCommandError("OUTCOME_UNKNOWN","imperative action crossed its execution boundary");if("failed"===c.phase)throw new RuntimeCommandError("EXECUTION_FAILED","previous execution failed");"prepared"===c.phase&&(c=await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:"started"}));let m=!1;try{const d=await this.execute(e,s,t);m=!0;const p=isRuntimeAgentMutation(e)&&this.deps.projection?await this.prepareAgentProjectionCommit(e,d,t):void 0;let u;try{const e=await this.requireReleasedRecord(c.operationId);u=await this.journal.transition({operationId:e.operationId,expectedRevision:e.revision,phase:"effect_committed",result:d,...p?{agentProjection:p}:{}})}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`effect completed before its journal commit: ${errorText(e)}`,void 0,{cause:e})}return r(d,u),await i(),u=await this.requireReleasedRecord(u.operationId),n||await this.afterCommittedEffect(e,d,u,t,a,o),this.scheduleJournalCompaction(),d}catch(t){if(m)throw t;const r=t instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===t.code||"INSTANCE_EPOCH_MISMATCH"===t.code);if(r&&"clear-saga"===recoveryMetadata(e.name).recoveryClass)throw t;try{await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:r?"outcome_unknown":"failed",errorCode:r?"OUTCOME_UNKNOWN":"EXECUTION_FAILED"})}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`command and journal outcome unknown: ${errorText(e)}`,void 0,{cause:t})}throw t}}finally{try{await c.stop(),await this.deps.leases.release(s)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`runtime operation lease finalization unknown: ${errorText(e)}`,void 0,{cause:e})}}}scheduleJournalCompaction(){this.journal.compact().catch(e=>this.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=effect_committed error=${errorText(e)}`))}async execute(e,t,r){switch(e.name){case"agent.create":return await this.createAgent(e,t,r);case"agent.update":return await this.updateAgent(e);case"agent.delete":return await this.deleteAgent(e,t);case"agent.applyTemplate":return await this.applyTemplate(e,t,r)}if(!this.capabilities)throw new RuntimeCommandError("INVALID_PARAMS",`Unsupported runtime command: ${e.name}`);return await this.capabilities.execute(e,t,r)}async recoverStartedAgentMutation(e,t,r){if("agent.create"===e.name||"agent.applyTemplate"===e.name){let i=await this.bindCreateOperation(e,t,!1);if(i||(i=await this.findCreateRecoveryIdentity(e,!1),"allocated"===i.state&&void 0===i.createOperation?.paramsHash&&await this.bindCreateOperation(e,t,!1),i=await this.findCreateRecoveryIdentity(e)),"active"===i.state)return await this.recoverActiveCreateEffect(i);if(await this.runtimeAgentExists(i.observedRuntimeLocalKey??i.runtimeLocalKey)){const n={runtimeLocalKey:i.observedRuntimeLocalKey??i.runtimeLocalKey,workspacePath:join(this.getOpenclawHome(),`workspace-${i.runtimeLocalKey}`)};return"agent.create"===e.name?await this.completeCreatedAgent(e,t,r,n):await this.completeTemplateAgent(e,t,r,n)}return await this.execute(e,t,r)}const i=e.context.agent,n=await this.runtimeAgentExists(i.runtimeLocalKey);if("agent.delete"===e.name&&!n)return await this.deps.identities.commitDeleteTombstone(i.runtimeAgentUid,i.runtimeLocalKey,t),{runtimeAgentUid:i.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey};if("agent.update"===e.name&&!n)throw new RuntimeCommandError("OUTCOME_UNKNOWN","update recovery target is absent");return await this.execute(e,t,r)}async runtimeAgentExists(e){const t=await this.requireGateway().request("agents.list",{});if(!Array.isArray(t?.agents))throw new RuntimeCommandError("OUTCOME_UNKNOWN","runtime agent directory query is incomplete");return t.agents.some(t=>null!==t&&"object"==typeof t&&(t.id===e||t.name===e))}async findCreateRecoveryIdentity(e,t=!0){const r={operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash,runtimeAgentUid:e.params.runtimeAgentUid},i=void 0!==this.deps.identities.findCreateOperation,n=i?await this.deps.identities.findCreateOperation(r):(await this.deps.identities.pendingAllocations()).filter(e=>e.operationId===r.operationId&&e.command===r.command&&e.runtimeEpoch===r.runtimeEpoch&&e.runtimeAgentUid===r.runtimeAgentUid);if(1!==n.length)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity binding is missing or ambiguous");const a=n[0];if(i&&(!a.createOperation||t&&a.createOperation.paramsHash!==r.paramsHash||a.createOperation.operationId!==r.operationId||a.createOperation.command!==r.command||a.createOperation.runtimeEpoch!==r.runtimeEpoch))throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity hash binding conflicts");return a}async bindCreateOperation(e,t,r=!0){if(!this.deps.identities.findCreateOperation||!this.deps.identities.bindCreateOperation)return;const i={operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash,runtimeAgentUid:e.params.runtimeAgentUid};try{let n=await this.deps.identities.findCreateOperation(i);if(0===n.length){if(!r)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity binding is missing");if((await this.deps.identities.allocateCreate(t,e.name,e.params.runtimeAgentUid)).runtimeAgentUid!==e.params.runtimeAgentUid)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create allocation UID does not match the canonical command");n=await this.deps.identities.findCreateOperation(i)}if(1!==n.length)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create identity binding is missing or ambiguous");const a=n[0],o=await this.deps.identities.bindCreateOperation(a.runtimeAgentUid,a.runtimeLocalKey,{operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash});if(!o.createOperation||o.runtimeAgentUid!==e.params.runtimeAgentUid||o.createOperation.operationId!==i.operationId||o.createOperation.command!==i.command||o.createOperation.runtimeEpoch!==i.runtimeEpoch||o.createOperation.paramsHash!==i.paramsHash)throw new Error("create operation identity binding changed during compare-and-set");if("tombstoned"===o.state)throw new Error("create operation identity is tombstoned");return o}catch(e){if(e instanceof RuntimeCommandError)throw e;throw new RuntimeCommandError("OUTCOME_UNKNOWN",`create operation identity binding is corrupt: ${errorText(e)}`,void 0,{cause:e})}}async recoverActiveCreateEffect(e){const t=e.observedRuntimeLocalKey??e.runtimeLocalKey;if(!await this.runtimeAgentExists(t))throw new RuntimeCommandError("OUTCOME_UNKNOWN","active create identity is absent from the runtime directory");return{runtimeAgentUid:e.runtimeAgentUid,runtimeLocalKey:t,workspacePath:join(this.getOpenclawHome(),`workspace-${e.runtimeLocalKey}`)}}async markJournalUnknown(e){try{await this.journal.transition({operationId:e.operationId,expectedRevision:e.revision,phase:"outcome_unknown",errorCode:"OUTCOME_UNKNOWN"})}catch{}}async requireReleasedRecord(e){const t=await this.journal.get(e);if(!t)throw new RuntimeCommandError("OUTCOME_UNKNOWN","command journal record disappeared");return t}async afterCommittedEffect(e,t,r,i,n,a){if(!isRuntimeAgentMutation(e))return void await this.runAfterEffect(e,t,r,i,n);const o=await this.prepareAgentProjection(e,t,r,i,n);o&&(a(o),"acp"===i.origin&&o().catch(e=>this.logger.warn?.(`[runtime-core] deferred ACP agent projection failed: ${errorText(e)}`)))}async prepareAgentProjectionCommit(e,t,r){const i=agentProjectionRecoverySummary(e,t,r);try{const r=await this.deps.projection.prepareCommittedMutation({command:e,result:t,...i.expectedPlatformAgentId?{expectedPlatformAgentId:i.expectedPlatformAgentId}:{}});return Object.freeze({...r,...i})}catch(e){const t=!(e instanceof RuntimeProjectionTransportError)||e.retryable;return this.logger.warn?.(`[runtime-core] agent projection preparation ${t?"deferred":"blocked"}: ${errorText(e)}`),Object.freeze({...i,state:t?"pending":"blocked",errorKind:t?"snapshot_retryable":"snapshot_nonretryable"})}}async prepareAgentProjection(e,t,r,i,n){if("projection_committed"===r.phase)return n(r.projectionReceipt?.snapshot??Object.freeze({state:"synced",revision:`${Math.max(0,r.revision-1)}`})),r.agentProjectionIntent&&await this.compactAgentProjectionReference(r.agentProjectionIntent),void await this.runPostCommit(e,t);if("effect_committed"!==r.phase||!this.deps.projection)return;let a=r,o=a.agentProjectionIntent;const s=a.agentProjectionPreparationFailure;if(o&&"blocked"===a.agentProjectionTerminal?.state)return n(Object.freeze({state:"blocked",revision:a.agentProjectionTerminal.revision})),await this.compactAgentProjectionReference(o),void await this.runPostCommit(e,t);let c;return o&&n(Object.freeze({state:"pending",revision:o.projectionRevision})),async()=>(c??=(async()=>{if(!o){if(!s||"blocked"===s.state)return Object.freeze({state:"blocked",revision:`${Math.max(0,a.revision)}`});const t=await this.prepareAgentProjectionCommit(e,agentProjectionRecoveryResult(s),i);if(!isProjectionIntent(t))return"blocked"===t.state?Object.freeze({state:"blocked",revision:`${Math.max(0,a.revision)}`}):Object.freeze({state:"pending",revision:`${Math.max(0,a.revision)}`});o=t;try{a=await this.journal.recordAgentProjectionIntent({operationId:a.operationId,expectedRevision:a.revision,intent:o})}catch(e){return void this.logger.warn?.(`[runtime-core] agent projection reservation recovery deferred: ${errorText(e)}`)}n(Object.freeze({state:"pending",revision:o.projectionRevision}))}const r=Object.freeze({state:"pending",revision:o.projectionRevision});let c;try{c=await this.deps.projection.applyCommittedMutation(o)}catch(e){this.logger.warn?.(`[runtime-core] agent projection receipt deferred: ${errorText(e)}`),c=r}if(c.revision!==o.projectionRevision&&(c=Object.freeze({state:"blocked",revision:o.projectionRevision})),"synced"===c.state)try{a=await this.journal.transition({operationId:a.operationId,expectedRevision:a.revision,phase:"projection_committed",projectionSnapshot:c}),await this.compactAgentProjectionReference(o)}catch(e){this.logger.warn?.(`[runtime-core] projection phase commit deferred: ${errorText(e)}`),c=Object.freeze({state:"pending",revision:o.projectionRevision})}if("blocked"===c.state)try{a=await this.journal.recordAgentProjectionBlocked({operationId:a.operationId,expectedRevision:a.revision,revision:o.projectionRevision}),await this.compactAgentProjectionReference(o)}catch(e){this.logger.warn?.(`[runtime-core] blocked projection terminal commit deferred: ${errorText(e)}`),c=r}return"synced"===c.state&&c.platformAgentId&&"projection_committed"===a.phase&&this.reportTemplateOriginBestEffort(e,c.platformAgentId),n(c),await this.runPostCommit(e,t),c})(),await c)}async compactAgentProjectionReference(e){if(this.deps.projection)try{await this.deps.projection.compactCommittedMutation(e)}catch(e){this.logger.warn?.(`[runtime-core] projection reference compaction deferred: ${errorText(e)}`)}}reportTemplateOriginBestEffort(e,t){if("agent.applyTemplate"!==e.name||!this.deps.templateOriginReporter)return;const r=e.params.templateId;r&&this.deps.templateOriginReporter.report({platformAgentId:t,templateId:r}).catch(e=>{this.logger.warn?.(`[runtime-core] active template origin report failed, client fallback remains: ${errorText(e)}`)})}async runAfterEffect(e,t,r,i,n){if("projection_committed"===r.phase)return n(r.projectionReceipt?.snapshot??Object.freeze({state:"synced",revision:`${Math.max(0,r.revision-1)}`})),void await this.runPostCommit(e,t);const a="effect_committed"===r.phase&&await this.runProjection(e,t,i),o=!0===a?Object.freeze({state:"synced",revision:`${r.revision}`}):a&&"object"==typeof a?Object.freeze({...a,revision:`${r.revision}`}):"session.setModel"===e.name?Object.freeze({state:"pending",revision:`${r.revision}`}):void 0;if("synced"===o?.state)try{const e=o;await this.journal.transition({operationId:r.operationId,expectedRevision:r.revision,phase:"projection_committed",projectionSnapshot:e}),n(o)}catch(e){return this.logger.warn?.(`[runtime-core] projection phase commit deferred: ${errorText(e)}`),void n(Object.freeze({revision:o.revision,state:"pending"}))}else o&&n(o);await this.runPostCommit(e,t)}async runPostCommit(e,t){if(this.capabilities?.afterCommit)try{await this.capabilities.afterCommit(e,t)}catch(e){this.logger.warn?.(`[runtime-core] post-commit action failed: ${errorText(e)}`)}}async runProjection(e,t,r){if(!recoveryMetadata(e.name).projectionRequired)return!1;try{return!isRuntimeAgentMutation(e)&&(await(this.capabilities?.projectCommitted?.(e,t,r))??!1)}catch(e){return this.logger.warn?.(`[runtime-core] agent projection deferred: ${errorText(e)}`),!1}}recoveryDecision(e,t){const r=recoveryMetadata(e);return reduceRuntimeRecoveryPolicy({...r,messageServicePhase:"queued",executionBoundaryCrossed:"prepared"!==t,lineageContinuity:"continuous",journalState:"available",journalPhase:t})}requireGateway(){const e=this.deps.getGateway();if(!e?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");return e}async createAgentShell(e,t,r,i,n,a){const o=this.requireGateway(),s=(await this.deps.identities.pendingAllocations()).find(t=>t.runtimeAgentUid===e),c=a??s?.runtimeLocalKey??this.createAgentId(),m=join(this.getOpenclawHome(),`workspace-${c}`);let d;try{d=await o.request("agents.create",{name:c,workspace:m,...n?{model:n}:{},...i?.emoji?{emoji:i.emoji}:{},...i?.avatar?{avatar:i.avatar}:{}})}catch(r){if(isDeterministicAgentsCreateParamsRejection(r)&&(void 0!==a||void 0!==s))try{await this.deps.identities.tombstone(e,c,t)}catch(e){this.logger.warn?.(`[runtime-core] rejected-create allocation cleanup deferred: ${errorText(e)}`)}throw r}const p=d?.agentId??c,u=d?.workspace??m;if(await this.deps.identities.observeEffectKey(e,c,p,t),p!==c)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Create agent outcome unknown: gateway changed reserved local key to ${p}`);try{await o.request("agents.update",{agentId:p,name:r})}catch(e){this.logger.warn?.(`[runtime-core] display-name update failed agent=${p}: ${errorText(e)}`)}return{runtimeLocalKey:p,workspacePath:u}}async createAgent(e,t,r){const i=e.params.name.trim();if(!i)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");try{const n=await this.bindCreateOperation(e,t);if("active"===n?.state)return await this.recoverActiveCreateEffect(n);const a=await this.createAgentShell(e.params.runtimeAgentUid,t,i,e.params.identity,e.params.defaultModelId,n?.runtimeLocalKey);return await this.completeCreatedAgent(e,t,r,a)}catch(e){throw this.executionError("Create agent",e)}}async updateAgent(e){const{runtimeAgentUid:t,runtimeLocalKey:r}=e.context.agent;if(!r.trim())throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");const i=e.params,n=i.name?.trim(),a=i.defaultModelId?.trim(),o=i.identity?.emoji?.trim(),s=i.identity?.avatar?.trim();if(!(n||a||o||s||void 0!==i.description||void 0!==i.version||void 0!==i.capabilities||void 0!==i.skills||void 0!==i.avatarUrl||void 0!==i.homepage||void 0!==i.status||void 0!==i.identity?.vibe))throw new RuntimeCommandError("INVALID_PARAMS","No updatable fields provided (name/model/identity)");try{return await this.deps.identities.requireActive(t,r),await this.requireGateway().request("agents.update",{agentId:r,...n?{name:n}:{},...a?{model:a}:{},...o?{emoji:o}:{},...s?{avatar:s}:{},...i.identity?.vibe?{vibe:i.identity.vibe}:{},...void 0!==i.description?{description:i.description}:{},...void 0!==i.version?{version:i.version}:{},...void 0!==i.capabilities?{capabilities:i.capabilities}:{},...void 0!==i.skills?{skills:i.skills}:{},...void 0!==i.avatarUrl?{avatarUrl:i.avatarUrl}:{},...void 0!==i.homepage?{homepage:i.homepage}:{},...void 0!==i.status?{status:i.status}:{}}),{runtimeAgentUid:t,runtimeLocalKey:r}}catch(e){throw this.executionError("Update agent",e)}}async deleteAgent(e,t){const{runtimeAgentUid:r,runtimeLocalKey:i}=e.context.agent;if(!i.trim())throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");if("main"===normalizeRuntimeAgentLocalKey(i))throw new RuntimeCommandError("INVALID_PARAMS",'"main" cannot be deleted; delete the instance instead');try{if("tombstoned"===await this.deps.identities.requireDeleteTarget(r,i))return{runtimeAgentUid:r,runtimeLocalKey:i};if(!await this.runtimeAgentExists(i))return await this.deps.identities.commitDeleteTombstone(r,i,t),{runtimeAgentUid:r,runtimeLocalKey:i};const n=requireCommittedOpenClawDeleteAck(await this.requireGateway().request("agents.delete",{agentId:i,deleteFiles:e.params.deleteFiles??!0}),i);try{await this.deps.identities.commitDeleteTombstone(r,i,t)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Delete agent outcome unknown: identity tombstone failed: ${errorText(e)}`,void 0,{cause:e})}return{runtimeAgentUid:r,runtimeLocalKey:i,...void 0===n.removedBindings?{}:{removedBindings:n.removedBindings}}}catch(e){throw this.executionError("Delete agent",e)}}async applyTemplate(e,t,r){await(this.deps.waitForChatIdle?.("applyTemplate"));const i=e.params.name.trim();if(!i)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");this.requireGateway();try{const n=await this.bindCreateOperation(e,t);if("active"===n?.state)return await this.recoverActiveCreateEffect(n);const a=await r.resources.readVerifiedArtifact(e.params.artifact),o=await this.createAgentShell(e.params.runtimeAgentUid,t,i,e.params.identity,e.params.defaultModelId,n?.runtimeLocalKey);return await this.completeTemplateAgent(e,t,r,o,a)}catch(e){throw this.executionError("Apply template",e)}}async completeCreatedAgent(e,t,r,i){for(const t of e.params.bootstrapFiles??[]){if("IDENTITY.md"===t.name){this.logger.warn?.("[runtime-core] agent.create ignoring IDENTITY.md; identity is structured");continue}const e=await r.resources.readContent(t.contentRef);try{await this.requireGateway().request("agents.files.set",{agentId:i.runtimeLocalKey,name:t.name,content:e})}catch(e){this.logger.warn?.(`[runtime-core] bootstrap write failed file=${t.name}: ${errorText(e)}`)}}try{await this.deps.identities.activate(e.params.runtimeAgentUid,i.runtimeLocalKey,t)}catch(r){throw await this.compensateCreatedAgent(e.params.runtimeAgentUid,i.runtimeLocalKey,t,r,"Create agent"),new RuntimeCommandError("EXECUTION_FAILED",`Create agent failed after compensation: ${errorText(r)}`)}return{runtimeAgentUid:e.params.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,workspacePath:i.workspacePath}}async completeTemplateAgent(e,t,r,i,n){const a=n??await r.resources.readVerifiedArtifact(e.params.artifact);try{for(const[e,t]of a.workspaceFiles){const r=resolve(i.workspacePath,e),n=relative(i.workspacePath,r);if(""===n||n.startsWith("..")||path.isAbsolute(n))throw new Error(`refusing to write outside workspace: ${e}`);await(this.deps.mkdirWorkspace??mkdir)(path.dirname(r),{recursive:!0}),await(this.deps.writeWorkspaceFile??writeFile)(r,t)}await this.deps.identities.activate(e.params.runtimeAgentUid,i.runtimeLocalKey,t)}catch(r){throw await this.compensateCreatedAgent(e.params.runtimeAgentUid,i.runtimeLocalKey,t,r,"Apply template"),new RuntimeCommandError("EXECUTION_FAILED",`Apply template failed after compensation: ${errorText(r)}`,void 0,{cause:r})}return{runtimeAgentUid:e.params.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,workspacePath:i.workspacePath}}executionError(e,t){return t instanceof RuntimeCommandError?t:(this.logger.error?.(`[runtime-core] ${e.toLowerCase()} failed: ${errorText(t)}`),new RuntimeCommandError("EXECUTION_FAILED",`${e} failed: ${errorText(t)}`,void 0,{cause:t}))}async compensateCreatedAgent(e,t,r,i,n){try{const i=this.requireGateway();requireCommittedOpenClawDeleteAck(await i.request("agents.delete",{agentId:t,deleteFiles:!0}),t),await this.deps.identities.tombstone(e,t,r)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`${n} outcome unknown after ${errorText(i)}; compensation unproven: ${errorText(e)}`,void 0,{cause:e})}}async recoverInterruptedCreates(e){const t=await this.deps.identities.pendingAllocations();for(const r of t){if(r.operationId===e)continue;const t=await this.deps.leases.claimExpiredForRecovery(r.operationId);if(!t)continue;const i=this.deps.leases.startHeartbeat(t);try{await this.deps.identities.adoptRecoveryLease(r.runtimeAgentUid,r.runtimeLocalKey,t);const e=r.observedRuntimeLocalKey;if(!e){const e=r.createOperation?.operationId??r.operationId,i=await this.journal.get(e);if("failed"===i?.phase&&i.command===r.command&&!await this.runtimeAgentExists(r.runtimeLocalKey)){await this.deps.identities.tombstone(r.runtimeAgentUid,r.runtimeLocalKey,t);continue}throw new Error("no persisted gateway effect key; absence cannot be proven")}requireCommittedOpenClawDeleteAck(await this.requireGateway().request("agents.delete",{agentId:e,deleteFiles:!0}),e),await this.deps.identities.tombstone(r.runtimeAgentUid,r.runtimeLocalKey,t)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Interrupted ${r.command} recovery outcome unknown for operation ${r.operationId}: ${errorText(e)}`,void 0,{cause:e})}finally{let e;try{await i.stop()}catch(t){e=t}try{await this.deps.leases.release(t)}catch(t){e??=t}if(e)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Interrupted recovery lease finalization unknown: ${errorText(e)}`,void 0,{cause:e})}}}}function isRuntimeAgentMutation(e){return"agent.create"===e.name||"agent.update"===e.name||"agent.delete"===e.name||"agent.applyTemplate"===e.name}function servicePlatformAgentId(e){const t=e.verifiedMessageGrant?.claims;return"agent"===t?.scope?t.platformAgentId:void 0}function agentCoreResultHash(e,t){return createHash("sha256").update(runtimeHashPreimage(RUNTIME_HASH_DOMAINS.coreResult,{method:e.name.replace(".","/"),result:t})).digest("hex")}function agentProjectionRecoverySummary(e,t,r){const i="agent.update"===e.name||"agent.delete"===e.name?e.context.agent:createProjectionTarget(e,t),n=t;return Object.freeze({schemaVersion:1,operationId:e.context.operationId,method:e.name,runtimeEpoch:e.context.dispatchEpoch,runtimeAgentUid:i.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,coreResultHash:agentCoreResultHash(e,t),...servicePlatformAgentId(r)?{expectedPlatformAgentId:servicePlatformAgentId(r)}:{},..."agent.delete"===e.name&&Number.isSafeInteger(n?.removedBindings)?{removedBindings:n.removedBindings}:{}})}function createProjectionTarget(e,t){const r=t;if(!r||r.runtimeAgentUid!==e.params.runtimeAgentUid||"string"!=typeof r.runtimeLocalKey||!r.runtimeLocalKey)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed agent effect lacks a recoverable local identity");return{runtimeAgentUid:r.runtimeAgentUid,runtimeLocalKey:r.runtimeLocalKey}}function agentProjectionRecoveryResult(e){return Object.freeze({runtimeAgentUid:e.runtimeAgentUid,runtimeLocalKey:e.runtimeLocalKey,...void 0===e.removedBindings?{}:{removedBindings:e.removedBindings}})}function isProjectionIntent(e){return"projectionRevision"in e}function recoveryMetadata(e){const t=RUNTIME_METHOD_PARITY_MANIFEST.filter(t=>t.serviceAllowed&&t.canonicalCommand===e),r=t[0]?.recoveryClass;return r?{recoveryClass:r,projectionRequired:"mcp.set"===e||"mcp.unset"===e||t.some(e=>!0===e.projectionRequired)}:{recoveryClass:"imperative-ambiguous",projectionRequired:!1}}export function isDeterministicAgentsCreateParamsRejection(e){return e instanceof Error&&/^invalid agents\.create params\b/.test(e.message)}export function requireCommittedOpenClawDeleteAck(e,t){if(!e||"object"!=typeof e)throw new Error("gateway delete lacks the committed target acknowledgement");const r=e,i=Object.keys(r).every(e=>"ok"===e||"agentId"===e||"removedBindings"===e),n=void 0===r.removedBindings||Number.isSafeInteger(r.removedBindings)&&r.removedBindings>=0;if(!i||!0!==r.ok||r.agentId!==t||!n)throw new Error("gateway delete lacks the committed target acknowledgement");return{ok:!0,agentId:t,...void 0===r.removedBindings?{}:{removedBindings:r.removedBindings}}}function errorText(e){return e instanceof Error?e.message:String(e)}
|
|
1
|
+
import{createHash,randomUUID}from"node:crypto";import{mkdir,writeFile}from"node:fs/promises";import{homedir}from"node:os";import path,{join,relative,resolve}from"node:path";import{RUNTIME_HASH_DOMAINS,RUNTIME_METHOD_PARITY_MANIFEST,runtimeHashPreimage}from"@beeos-ai/beeos-types/runtime";import{reduceRuntimeRecoveryPolicy}from"@beeos-ai/beeos-types/runtime/internal";import{assertTrustedCoreExecutionAttempt}from"./core-execution-attempt-context.js";import{InMemoryRuntimeCommandJournal,RuntimeCommandJournalConflictError,runtimeCommandIsReplayableQuery,runtimeCommandResultHash}from"./runtime-command-journal.js";import{RuntimeProjectionTransportError}from"./agent-projection-worker.js";import{ImHotPathAbortedError}from"./im-hot-path-deferral.js";const DEFAULT_AGENT_ID="main",AGENT_VALID_ID_RE=/^[a-z0-9][a-z0-9_-]{0,63}$/i,AGENT_INVALID_CHARS_RE=/[^a-z0-9_-]+/g;export function normalizeRuntimeAgentLocalKey(e){const t=(e??"").trim();return t?AGENT_VALID_ID_RE.test(t)?t.toLowerCase():t.toLowerCase().replace(AGENT_INVALID_CHARS_RE,"-").replace(/^-+/,"").replace(/-+$/,"").slice(0,64)||"main":"main"}export async function awaitRuntimeProjection(e){const t=e;return await(t.awaitProjection?.()??Promise.resolve(e.getSnapshot().projection))}export function hasEffectReceiptHandoff(e){const t=e;return t.effectCommitted instanceof Promise&&"function"==typeof t.acknowledgeEffectReceipt}export class RuntimeCommandError extends Error{code;param;constructor(e,t,r,i){super(t,i),this.code=e,this.param=r,this.name="RuntimeCommandError"}}const emptyProgress=async function*(){};function deferred(){let e;return{promise:new Promise(t=>{e=t}),resolve:e}}async function waitForEffectReceipt(e,t){if(!t)return await e;if(t.aborted)throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt was not durably published before the Service attempt ended");await new Promise((r,i)=>{const n=()=>i(new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt was not durably published before the Service attempt ended"));t.addEventListener("abort",n,{once:!0}),e.then(()=>{t.removeEventListener("abort",n),r()},e=>{t.removeEventListener("abort",n),i(e)})})}export class OpenClawRuntimeCommandDispatcher{deps;logger;getOpenclawHome;createAgentId;journal;capabilities;constructor(e){this.deps=e,this.logger=e.logger??{},this.getOpenclawHome=e.getOpenclawHome??(()=>process.env.OPENCLAW_HOME||join(homedir(),".openclaw")),this.createAgentId=e.createAgentId??(()=>`agent-${randomUUID().replace(/-/g,"").slice(0,12)}`),this.journal=e.journal??new InMemoryRuntimeCommandJournal,this.capabilities=e.capabilities}setCapabilities(e){this.capabilities=e}dispatch(e,t){const r=e;assertTrustedCoreExecutionAttempt(t,{operationId:r.context.operationId,runtimeEpoch:r.context.dispatchEpoch});let i,n,a,o="running";const s=deferred(),c=deferred();let m;const d="service"===t.origin&&("mcp.set"===r.name||"mcp.unset"===r.name),p=this.executeWithLease(r,t,(e,t)=>{o="committed",m={operationId:r.context.operationId,method:r.name,record:t},s.resolve({...m,result:e,committedAt:t.updatedAt})},async()=>{d?await waitForEffectReceipt(c.promise,t.abortSignal):"acp"!==t.origin||"mcp.set"!==r.name&&"mcp.unset"!==r.name||!m?.record.mcpDesiredStateCommit||(await this.journal.releaseMcpLiveEffect({operationId:r.context.operationId,source:"acp_local"}),await this.journal.promoteMcpDesiredStateActive({operationId:r.context.operationId,source:"acp_local"}))},d,e=>{i=e},e=>{n=e}).then(e=>(o="committed",e),e=>{throw o=e instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===e.code||"INSTANCE_EPOCH_MISMATCH"===e.code)?"outcome_unknown":"failed",s.resolve(void 0),e});return{operationId:r.context.operationId,getSnapshot:()=>({effectState:o,...i?{projection:i}:{}}),completion:p,progress:emptyProgress(),effectCommitted:s.promise,acknowledgeEffectReceipt:async e=>{if(!m||e.operationId!==m.operationId||e.method!==m.method)throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt does not match this operation");if(!/^[a-f0-9]{64}$/.test(e.effectRecordHash))throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP effect receipt hash is invalid");await this.journal.releaseMcpLiveEffect({operationId:e.operationId,source:"service",effectRecordHash:e.effectRecordHash}),c.resolve()},acknowledgeTerminalReceipt:async e=>{if(!(d&&m&&e.operationId===m.operationId&&e.method===m.method&&/^[a-f0-9]{64}$/.test(e.effectRecordHash)&&e.terminalMessageId))throw new RuntimeCommandError("OUTCOME_UNKNOWN","MCP terminal receipt does not match this operation");await this.journal.promoteMcpDesiredStateActive({operationId:e.operationId,source:"service",effectRecordHash:e.effectRecordHash,terminalMessageId:e.terminalMessageId});const a=await this.requireReleasedRecord(e.operationId);if(void 0===a.result)throw new RuntimeCommandError("OUTCOME_UNKNOWN","active MCP desired state lost its committed result");await this.afterCommittedEffect(r,a.result,a,t,e=>{i=e},e=>{n=e})},cancel:async()=>"running"===o?{status:"cancel_requested"===await(this.capabilities?.cancel?.(r.context.operationId))?"cancel_requested":"not_cancellable"}:{status:"already_terminal"},awaitProjection:async()=>n?(a??=n(),await a):i}}async executeWithLease(e,t,r,i,n,a,o){const s=await this.deps.leases.claim(e.context.operationId,e.context.dispatchEpoch);if(s.runtimeEpoch!==e.context.dispatchEpoch)throw await this.deps.leases.release(s),new RuntimeCommandError("INSTANCE_EPOCH_MISMATCH","runtime operation lease epoch mismatch");const c=this.deps.leases.startHeartbeat(s);try{let c;await this.recoverInterruptedCreates(e.context.operationId);try{c=await this.journal.prepare({operationId:e.context.operationId,command:e.name,paramsHash:e.context.paramsHash,runtimeEpoch:s.runtimeEpoch})}catch(e){if(e instanceof RuntimeCommandJournalConflictError)throw new RuntimeCommandError("OUTCOME_UNKNOWN",e.message,void 0,{cause:e});throw e}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&void 0!==c.result){if(r(c.result,c),await i(),c=await this.requireReleasedRecord(c.operationId),void 0===c.result)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed result disappeared from command journal");return n||await this.afterCommittedEffect(e,c.result,c,t,a,o),c.result}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&"hash_only"===c.resultReceipt?.policy&&runtimeCommandIsReplayableQuery(e.name)){const n=await this.execute(e,s,t);if(runtimeCommandResultHash(n)!==c.resultReceipt.resultHash)throw new RuntimeCommandError("OUTCOME_UNKNOWN","redacted committed query result changed");return r(n,c),await i(),n}if(("effect_committed"===c.phase||"projection_committed"===c.phase)&&"hash_only"===c.resultReceipt?.policy){if(r(void 0,c),await i(),c=await this.requireReleasedRecord(c.operationId),!n){if(isRuntimeAgentMutation(e)&&(c.agentProjectionIntent||c.agentProjectionPreparationFailure)){const r=agentProjectionRecoveryResult(c.agentProjectionIntent??c.agentProjectionPreparationFailure);return await this.afterCommittedEffect(e,r,c,t,a,o),r}await this.runAfterEffect(e,void 0,c,t,a)}throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed mutation result is redacted")}if("effect_committed"===c.phase||"projection_committed"===c.phase)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed result receipt is unavailable");if("outcome_unknown"===c.phase)throw new RuntimeCommandError("OUTCOME_UNKNOWN","previous execution outcome is unknown");if("started"===c.phase&&isRuntimeAgentMutation(e)){let n;try{n=await this.recoverStartedAgentMutation(e,s,t)}catch(e){throw await this.markJournalUnknown(c),e instanceof RuntimeCommandError?e:new RuntimeCommandError("OUTCOME_UNKNOWN",`agent desired-state recovery could not prove its effect: ${errorText(e)}`,void 0,{cause:e})}const m=this.deps.projection?await this.prepareAgentProjectionCommit(e,n,t):void 0,d=await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:"effect_committed",result:n,...m?{agentProjection:m}:{}});return r(n,d),await i(),await this.afterCommittedEffect(e,n,d,t,a,o),this.scheduleJournalCompaction(),n}if("started"===c.phase&&"outcome_unknown"===this.recoveryDecision(e.name,c.phase))throw await this.markJournalUnknown(c),new RuntimeCommandError("OUTCOME_UNKNOWN","imperative action crossed its execution boundary");if("failed"===c.phase)throw new RuntimeCommandError("EXECUTION_FAILED","previous execution failed");"prepared"===c.phase&&(c=await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:"started"}));let m=!1;try{const d=await this.execute(e,s,t);m=!0;const p=isRuntimeAgentMutation(e)&&this.deps.projection?await this.prepareAgentProjectionCommit(e,d,t):void 0;let u;try{const e=await this.requireReleasedRecord(c.operationId);u=await this.journal.transition({operationId:e.operationId,expectedRevision:e.revision,phase:"effect_committed",result:d,...p?{agentProjection:p}:{}})}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`effect completed before its journal commit: ${errorText(e)}`,void 0,{cause:e})}return r(d,u),await i(),u=await this.requireReleasedRecord(u.operationId),n||await this.afterCommittedEffect(e,d,u,t,a,o),this.scheduleJournalCompaction(),d}catch(t){if(m)throw t;const r=t instanceof RuntimeCommandError&&("OUTCOME_UNKNOWN"===t.code||"INSTANCE_EPOCH_MISMATCH"===t.code);if(r&&"clear-saga"===recoveryMetadata(e.name).recoveryClass)throw t;try{await this.journal.transition({operationId:c.operationId,expectedRevision:c.revision,phase:r?"outcome_unknown":"failed",errorCode:r?"OUTCOME_UNKNOWN":"EXECUTION_FAILED"})}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`command and journal outcome unknown: ${errorText(e)}`,void 0,{cause:t})}throw t}}finally{try{await c.stop(),await this.deps.leases.release(s)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`runtime operation lease finalization unknown: ${errorText(e)}`,void 0,{cause:e})}}}scheduleJournalCompaction(){this.journal.compact().catch(e=>this.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=effect_committed error=${errorText(e)}`))}async execute(e,t,r){switch(e.name){case"agent.create":return await this.createAgent(e,t,r);case"agent.update":return await this.updateAgent(e);case"agent.delete":return await this.deleteAgent(e,t);case"agent.applyTemplate":return await this.applyTemplate(e,t,r)}if(!this.capabilities)throw new RuntimeCommandError("INVALID_PARAMS",`Unsupported runtime command: ${e.name}`);return await this.capabilities.execute(e,t,r)}async recoverStartedAgentMutation(e,t,r){if("agent.create"===e.name||"agent.applyTemplate"===e.name){let i=await this.bindCreateOperation(e,t,!1);if(i||(i=await this.findCreateRecoveryIdentity(e,!1),"allocated"===i.state&&void 0===i.createOperation?.paramsHash&&await this.bindCreateOperation(e,t,!1),i=await this.findCreateRecoveryIdentity(e)),"active"===i.state)return await this.recoverActiveCreateEffect(i);if(await this.runtimeAgentExists(i.observedRuntimeLocalKey??i.runtimeLocalKey)){const n={runtimeLocalKey:i.observedRuntimeLocalKey??i.runtimeLocalKey,workspacePath:join(this.getOpenclawHome(),`workspace-${i.runtimeLocalKey}`)};return"agent.create"===e.name?await this.completeCreatedAgent(e,t,r,n):await this.completeTemplateAgent(e,t,r,n)}return await this.execute(e,t,r)}const i=e.context.agent,n=await this.runtimeAgentExists(i.runtimeLocalKey);if("agent.delete"===e.name&&!n)return await this.deps.identities.commitDeleteTombstone(i.runtimeAgentUid,i.runtimeLocalKey,t),{runtimeAgentUid:i.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey};if("agent.update"===e.name&&!n)throw new RuntimeCommandError("OUTCOME_UNKNOWN","update recovery target is absent");return await this.execute(e,t,r)}async runtimeAgentExists(e){const t=await this.requireGateway().request("agents.list",{});if(!Array.isArray(t?.agents))throw new RuntimeCommandError("OUTCOME_UNKNOWN","runtime agent directory query is incomplete");return t.agents.some(t=>null!==t&&"object"==typeof t&&(t.id===e||t.name===e))}async findCreateRecoveryIdentity(e,t=!0){const r={operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash,runtimeAgentUid:e.params.runtimeAgentUid},i=void 0!==this.deps.identities.findCreateOperation,n=i?await this.deps.identities.findCreateOperation(r):(await this.deps.identities.pendingAllocations()).filter(e=>e.operationId===r.operationId&&e.command===r.command&&e.runtimeEpoch===r.runtimeEpoch&&e.runtimeAgentUid===r.runtimeAgentUid);if(1!==n.length)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity binding is missing or ambiguous");const a=n[0];if(i&&(!a.createOperation||t&&a.createOperation.paramsHash!==r.paramsHash||a.createOperation.operationId!==r.operationId||a.createOperation.command!==r.command||a.createOperation.runtimeEpoch!==r.runtimeEpoch))throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity hash binding conflicts");return a}async bindCreateOperation(e,t,r=!0){if(!this.deps.identities.findCreateOperation||!this.deps.identities.bindCreateOperation)return;const i={operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash,runtimeAgentUid:e.params.runtimeAgentUid};try{let n=await this.deps.identities.findCreateOperation(i);if(0===n.length){if(!r)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create recovery identity binding is missing");if((await this.deps.identities.allocateCreate(t,e.name,e.params.runtimeAgentUid)).runtimeAgentUid!==e.params.runtimeAgentUid)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create allocation UID does not match the canonical command");n=await this.deps.identities.findCreateOperation(i)}if(1!==n.length)throw new RuntimeCommandError("OUTCOME_UNKNOWN","create identity binding is missing or ambiguous");const a=n[0],o=await this.deps.identities.bindCreateOperation(a.runtimeAgentUid,a.runtimeLocalKey,{operationId:e.context.operationId,command:e.name,runtimeEpoch:e.context.dispatchEpoch,paramsHash:e.context.paramsHash});if(!o.createOperation||o.runtimeAgentUid!==e.params.runtimeAgentUid||o.createOperation.operationId!==i.operationId||o.createOperation.command!==i.command||o.createOperation.runtimeEpoch!==i.runtimeEpoch||o.createOperation.paramsHash!==i.paramsHash)throw new Error("create operation identity binding changed during compare-and-set");if("tombstoned"===o.state)throw new Error("create operation identity is tombstoned");return o}catch(e){if(e instanceof RuntimeCommandError)throw e;throw new RuntimeCommandError("OUTCOME_UNKNOWN",`create operation identity binding is corrupt: ${errorText(e)}`,void 0,{cause:e})}}async recoverActiveCreateEffect(e){const t=e.observedRuntimeLocalKey??e.runtimeLocalKey;if(!await this.runtimeAgentExists(t))throw new RuntimeCommandError("OUTCOME_UNKNOWN","active create identity is absent from the runtime directory");return{runtimeAgentUid:e.runtimeAgentUid,runtimeLocalKey:t,workspacePath:join(this.getOpenclawHome(),`workspace-${e.runtimeLocalKey}`)}}async markJournalUnknown(e){try{await this.journal.transition({operationId:e.operationId,expectedRevision:e.revision,phase:"outcome_unknown",errorCode:"OUTCOME_UNKNOWN"})}catch{}}async requireReleasedRecord(e){const t=await this.journal.get(e);if(!t)throw new RuntimeCommandError("OUTCOME_UNKNOWN","command journal record disappeared");return t}async afterCommittedEffect(e,t,r,i,n,a){if(!isRuntimeAgentMutation(e))return void await this.runAfterEffect(e,t,r,i,n);const o=await this.prepareAgentProjection(e,t,r,i,n);o&&(a(o),"acp"===i.origin&&o().catch(e=>this.logger.warn?.(`[runtime-core] deferred ACP agent projection failed: ${errorText(e)}`)))}async prepareAgentProjectionCommit(e,t,r){const i=agentProjectionRecoverySummary(e,t,r);try{const r=await this.deps.projection.prepareCommittedMutation({command:e,result:t,...i.expectedPlatformAgentId?{expectedPlatformAgentId:i.expectedPlatformAgentId}:{}});return Object.freeze({...r,...i})}catch(e){const t=!(e instanceof RuntimeProjectionTransportError)||e.retryable;return this.logger.warn?.(`[runtime-core] agent projection preparation ${t?"deferred":"blocked"}: ${errorText(e)}`),Object.freeze({...i,state:t?"pending":"blocked",errorKind:t?"snapshot_retryable":"snapshot_nonretryable"})}}async prepareAgentProjection(e,t,r,i,n){if("projection_committed"===r.phase)return n(r.projectionReceipt?.snapshot??Object.freeze({state:"synced",revision:`${Math.max(0,r.revision-1)}`})),r.agentProjectionIntent&&await this.compactAgentProjectionReference(r.agentProjectionIntent),void await this.runPostCommit(e,t);if("effect_committed"!==r.phase||!this.deps.projection)return;let a=r,o=a.agentProjectionIntent;const s=a.agentProjectionPreparationFailure;if(o&&"blocked"===a.agentProjectionTerminal?.state)return n(Object.freeze({state:"blocked",revision:a.agentProjectionTerminal.revision})),await this.compactAgentProjectionReference(o),void await this.runPostCommit(e,t);let c;return o&&n(Object.freeze({state:"pending",revision:o.projectionRevision})),async()=>(c??=(async()=>{if(!o){if(!s||"blocked"===s.state)return Object.freeze({state:"blocked",revision:`${Math.max(0,a.revision)}`});const t=await this.prepareAgentProjectionCommit(e,agentProjectionRecoveryResult(s),i);if(!isProjectionIntent(t))return"blocked"===t.state?Object.freeze({state:"blocked",revision:`${Math.max(0,a.revision)}`}):Object.freeze({state:"pending",revision:`${Math.max(0,a.revision)}`});o=t;try{a=await this.journal.recordAgentProjectionIntent({operationId:a.operationId,expectedRevision:a.revision,intent:o})}catch(e){return void this.logger.warn?.(`[runtime-core] agent projection reservation recovery deferred: ${errorText(e)}`)}n(Object.freeze({state:"pending",revision:o.projectionRevision}))}const r=Object.freeze({state:"pending",revision:o.projectionRevision});let c;try{c=await this.deps.projection.applyCommittedMutation(o)}catch(e){this.logger.warn?.(`[runtime-core] agent projection receipt deferred: ${errorText(e)}`),c=r}if(c.revision!==o.projectionRevision&&(c=Object.freeze({state:"blocked",revision:o.projectionRevision})),"synced"===c.state)try{a=await this.journal.transition({operationId:a.operationId,expectedRevision:a.revision,phase:"projection_committed",projectionSnapshot:c}),await this.compactAgentProjectionReference(o)}catch(e){this.logger.warn?.(`[runtime-core] projection phase commit deferred: ${errorText(e)}`),c=Object.freeze({state:"pending",revision:o.projectionRevision})}if("blocked"===c.state)try{a=await this.journal.recordAgentProjectionBlocked({operationId:a.operationId,expectedRevision:a.revision,revision:o.projectionRevision}),await this.compactAgentProjectionReference(o)}catch(e){this.logger.warn?.(`[runtime-core] blocked projection terminal commit deferred: ${errorText(e)}`),c=r}return"synced"===c.state&&c.platformAgentId&&"projection_committed"===a.phase&&this.reportTemplateOriginBestEffort(e,c.platformAgentId),n(c),await this.runPostCommit(e,t),c})(),await c)}async compactAgentProjectionReference(e){if(this.deps.projection)try{await this.deps.projection.compactCommittedMutation(e)}catch(e){this.logger.warn?.(`[runtime-core] projection reference compaction deferred: ${errorText(e)}`)}}reportTemplateOriginBestEffort(e,t){if("agent.applyTemplate"!==e.name||!this.deps.templateOriginReporter)return;const r=e.params.templateId;r&&this.deps.templateOriginReporter.report({platformAgentId:t,templateId:r}).catch(e=>{this.logger.warn?.(`[runtime-core] active template origin report failed, client fallback remains: ${errorText(e)}`)})}async runAfterEffect(e,t,r,i,n){if("projection_committed"===r.phase)return n(r.projectionReceipt?.snapshot??Object.freeze({state:"synced",revision:`${Math.max(0,r.revision-1)}`})),void await this.runPostCommit(e,t);const a="effect_committed"===r.phase&&await this.runProjection(e,t,i),o=!0===a?Object.freeze({state:"synced",revision:`${r.revision}`}):a&&"object"==typeof a?Object.freeze({...a,revision:`${r.revision}`}):"session.setModel"===e.name?Object.freeze({state:"pending",revision:`${r.revision}`}):void 0;if("synced"===o?.state)try{const e=o;await this.journal.transition({operationId:r.operationId,expectedRevision:r.revision,phase:"projection_committed",projectionSnapshot:e}),n(o)}catch(e){return this.logger.warn?.(`[runtime-core] projection phase commit deferred: ${errorText(e)}`),void n(Object.freeze({revision:o.revision,state:"pending"}))}else o&&n(o);await this.runPostCommit(e,t)}async runPostCommit(e,t){if(this.capabilities?.afterCommit)try{await this.capabilities.afterCommit(e,t)}catch(e){this.logger.warn?.(`[runtime-core] post-commit action failed: ${errorText(e)}`)}}async runProjection(e,t,r){if(!recoveryMetadata(e.name).projectionRequired)return!1;try{return!isRuntimeAgentMutation(e)&&(await(this.capabilities?.projectCommitted?.(e,t,r))??!1)}catch(e){return this.logger.warn?.(`[runtime-core] agent projection deferred: ${errorText(e)}`),!1}}recoveryDecision(e,t){const r=recoveryMetadata(e);return reduceRuntimeRecoveryPolicy({...r,messageServicePhase:"queued",executionBoundaryCrossed:"prepared"!==t,lineageContinuity:"continuous",journalState:"available",journalPhase:t})}requireGateway(){const e=this.deps.getGateway();if(!e?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");return e}async createAgentShell(e,t,r,i,n,a){const o=this.requireGateway(),s=(await this.deps.identities.pendingAllocations()).find(t=>t.runtimeAgentUid===e),c=a??s?.runtimeLocalKey??this.createAgentId(),m=join(this.getOpenclawHome(),`workspace-${c}`);let d;try{d=await o.request("agents.create",{name:c,workspace:m,...n?{model:n}:{},...i?.emoji?{emoji:i.emoji}:{},...i?.avatar?{avatar:i.avatar}:{}})}catch(r){if(isDeterministicAgentsCreateParamsRejection(r)&&(void 0!==a||void 0!==s))try{await this.deps.identities.tombstone(e,c,t)}catch(e){this.logger.warn?.(`[runtime-core] rejected-create allocation cleanup deferred: ${errorText(e)}`)}throw r}const p=d?.agentId??c,u=d?.workspace??m;if(await this.deps.identities.observeEffectKey(e,c,p,t),p!==c)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Create agent outcome unknown: gateway changed reserved local key to ${p}`);try{await o.request("agents.update",{agentId:p,name:r})}catch(e){this.logger.warn?.(`[runtime-core] display-name update failed agent=${p}: ${errorText(e)}`)}return{runtimeLocalKey:p,workspacePath:u}}async createAgent(e,t,r){const i=e.params.name.trim();if(!i)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");try{const n=await this.bindCreateOperation(e,t);if("active"===n?.state)return await this.recoverActiveCreateEffect(n);const a=await this.createAgentShell(e.params.runtimeAgentUid,t,i,e.params.identity,e.params.defaultModelId,n?.runtimeLocalKey);return await this.completeCreatedAgent(e,t,r,a)}catch(e){throw this.executionError("Create agent",e)}}async updateAgent(e){const{runtimeAgentUid:t,runtimeLocalKey:r}=e.context.agent;if(!r.trim())throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");const i=e.params,n=i.name?.trim(),a=i.defaultModelId?.trim(),o=i.identity?.emoji?.trim(),s=i.identity?.avatar?.trim();if(!(n||a||o||s||void 0!==i.description||void 0!==i.version||void 0!==i.capabilities||void 0!==i.skills||void 0!==i.avatarUrl||void 0!==i.homepage||void 0!==i.status||void 0!==i.identity?.vibe))throw new RuntimeCommandError("INVALID_PARAMS","No updatable fields provided (name/model/identity)");try{return await this.deps.identities.requireActive(t,r),await this.requireGateway().request("agents.update",{agentId:r,...n?{name:n}:{},...a?{model:a}:{},...o?{emoji:o}:{},...s?{avatar:s}:{},...i.identity?.vibe?{vibe:i.identity.vibe}:{},...void 0!==i.description?{description:i.description}:{},...void 0!==i.version?{version:i.version}:{},...void 0!==i.capabilities?{capabilities:i.capabilities}:{},...void 0!==i.skills?{skills:i.skills}:{},...void 0!==i.avatarUrl?{avatarUrl:i.avatarUrl}:{},...void 0!==i.homepage?{homepage:i.homepage}:{},...void 0!==i.status?{status:i.status}:{}}),{runtimeAgentUid:t,runtimeLocalKey:r}}catch(e){throw this.executionError("Update agent",e)}}async deleteAgent(e,t){const{runtimeAgentUid:r,runtimeLocalKey:i}=e.context.agent;if(!i.trim())throw new RuntimeCommandError("MISSING_PARAM","Missing agentId","agentId");if("main"===normalizeRuntimeAgentLocalKey(i))throw new RuntimeCommandError("INVALID_PARAMS",'"main" cannot be deleted; delete the instance instead');try{if("tombstoned"===await this.deps.identities.requireDeleteTarget(r,i))return{runtimeAgentUid:r,runtimeLocalKey:i};if(!await this.runtimeAgentExists(i))return await this.deps.identities.commitDeleteTombstone(r,i,t),{runtimeAgentUid:r,runtimeLocalKey:i};const n=requireCommittedOpenClawDeleteAck(await this.requireGateway().request("agents.delete",{agentId:i,deleteFiles:e.params.deleteFiles??!0}),i);try{await this.deps.identities.commitDeleteTombstone(r,i,t)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Delete agent outcome unknown: identity tombstone failed: ${errorText(e)}`,void 0,{cause:e})}return{runtimeAgentUid:r,runtimeLocalKey:i,...void 0===n.removedBindings?{}:{removedBindings:n.removedBindings}}}catch(e){throw this.executionError("Delete agent",e)}}async applyTemplate(e,t,r){try{await(this.deps.waitForChatIdle?.("applyTemplate",r.abortSignal))}catch(e){if(e instanceof ImHotPathAbortedError)throw new RuntimeCommandError("INVALID_PARAMS","Apply template aborted while waiting for in-flight chat");throw e}const i=e.params.name.trim();if(!i)throw new RuntimeCommandError("INVALID_PARAMS","Missing agent name");this.requireGateway();try{const n=await this.bindCreateOperation(e,t);if("active"===n?.state)return await this.recoverActiveCreateEffect(n);const a=await r.resources.readVerifiedArtifact(e.params.artifact),o=await this.createAgentShell(e.params.runtimeAgentUid,t,i,e.params.identity,e.params.defaultModelId,n?.runtimeLocalKey);return await this.completeTemplateAgent(e,t,r,o,a)}catch(e){throw this.executionError("Apply template",e)}}async completeCreatedAgent(e,t,r,i){for(const t of e.params.bootstrapFiles??[]){if("IDENTITY.md"===t.name){this.logger.warn?.("[runtime-core] agent.create ignoring IDENTITY.md; identity is structured");continue}const e=await r.resources.readContent(t.contentRef);try{await this.requireGateway().request("agents.files.set",{agentId:i.runtimeLocalKey,name:t.name,content:e})}catch(e){this.logger.warn?.(`[runtime-core] bootstrap write failed file=${t.name}: ${errorText(e)}`)}}try{await this.deps.identities.activate(e.params.runtimeAgentUid,i.runtimeLocalKey,t)}catch(r){throw await this.compensateCreatedAgent(e.params.runtimeAgentUid,i.runtimeLocalKey,t,r,"Create agent"),new RuntimeCommandError("EXECUTION_FAILED",`Create agent failed after compensation: ${errorText(r)}`)}return{runtimeAgentUid:e.params.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,workspacePath:i.workspacePath}}async completeTemplateAgent(e,t,r,i,n){const a=n??await r.resources.readVerifiedArtifact(e.params.artifact);try{for(const[e,t]of a.workspaceFiles){const r=resolve(i.workspacePath,e),n=relative(i.workspacePath,r);if(""===n||n.startsWith("..")||path.isAbsolute(n))throw new Error(`refusing to write outside workspace: ${e}`);await(this.deps.mkdirWorkspace??mkdir)(path.dirname(r),{recursive:!0}),await(this.deps.writeWorkspaceFile??writeFile)(r,t)}await this.deps.identities.activate(e.params.runtimeAgentUid,i.runtimeLocalKey,t)}catch(r){throw await this.compensateCreatedAgent(e.params.runtimeAgentUid,i.runtimeLocalKey,t,r,"Apply template"),new RuntimeCommandError("EXECUTION_FAILED",`Apply template failed after compensation: ${errorText(r)}`,void 0,{cause:r})}return{runtimeAgentUid:e.params.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,workspacePath:i.workspacePath}}executionError(e,t){return t instanceof RuntimeCommandError?t:(this.logger.error?.(`[runtime-core] ${e.toLowerCase()} failed: ${errorText(t)}`),new RuntimeCommandError("EXECUTION_FAILED",`${e} failed: ${errorText(t)}`,void 0,{cause:t}))}async compensateCreatedAgent(e,t,r,i,n){try{const i=this.requireGateway();requireCommittedOpenClawDeleteAck(await i.request("agents.delete",{agentId:t,deleteFiles:!0}),t),await this.deps.identities.tombstone(e,t,r)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`${n} outcome unknown after ${errorText(i)}; compensation unproven: ${errorText(e)}`,void 0,{cause:e})}}async recoverInterruptedCreates(e){const t=await this.deps.identities.pendingAllocations();for(const r of t){if(r.operationId===e)continue;const t=await this.deps.leases.claimExpiredForRecovery(r.operationId);if(!t)continue;const i=this.deps.leases.startHeartbeat(t);try{await this.deps.identities.adoptRecoveryLease(r.runtimeAgentUid,r.runtimeLocalKey,t);const e=r.observedRuntimeLocalKey;if(!e){const e=r.createOperation?.operationId??r.operationId,i=await this.journal.get(e);if("failed"===i?.phase&&i.command===r.command&&!await this.runtimeAgentExists(r.runtimeLocalKey)){await this.deps.identities.tombstone(r.runtimeAgentUid,r.runtimeLocalKey,t);continue}throw new Error("no persisted gateway effect key; absence cannot be proven")}requireCommittedOpenClawDeleteAck(await this.requireGateway().request("agents.delete",{agentId:e,deleteFiles:!0}),e),await this.deps.identities.tombstone(r.runtimeAgentUid,r.runtimeLocalKey,t)}catch(e){throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Interrupted ${r.command} recovery outcome unknown for operation ${r.operationId}: ${errorText(e)}`,void 0,{cause:e})}finally{let e;try{await i.stop()}catch(t){e=t}try{await this.deps.leases.release(t)}catch(t){e??=t}if(e)throw new RuntimeCommandError("OUTCOME_UNKNOWN",`Interrupted recovery lease finalization unknown: ${errorText(e)}`,void 0,{cause:e})}}}}function isRuntimeAgentMutation(e){return"agent.create"===e.name||"agent.update"===e.name||"agent.delete"===e.name||"agent.applyTemplate"===e.name}function servicePlatformAgentId(e){const t=e.verifiedMessageGrant?.claims;return"agent"===t?.scope?t.platformAgentId:void 0}function agentCoreResultHash(e,t){return createHash("sha256").update(runtimeHashPreimage(RUNTIME_HASH_DOMAINS.coreResult,{method:e.name.replace(".","/"),result:t})).digest("hex")}function agentProjectionRecoverySummary(e,t,r){const i="agent.update"===e.name||"agent.delete"===e.name?e.context.agent:createProjectionTarget(e,t),n=t;return Object.freeze({schemaVersion:1,operationId:e.context.operationId,method:e.name,runtimeEpoch:e.context.dispatchEpoch,runtimeAgentUid:i.runtimeAgentUid,runtimeLocalKey:i.runtimeLocalKey,coreResultHash:agentCoreResultHash(e,t),...servicePlatformAgentId(r)?{expectedPlatformAgentId:servicePlatformAgentId(r)}:{},..."agent.delete"===e.name&&Number.isSafeInteger(n?.removedBindings)?{removedBindings:n.removedBindings}:{}})}function createProjectionTarget(e,t){const r=t;if(!r||r.runtimeAgentUid!==e.params.runtimeAgentUid||"string"!=typeof r.runtimeLocalKey||!r.runtimeLocalKey)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed agent effect lacks a recoverable local identity");return{runtimeAgentUid:r.runtimeAgentUid,runtimeLocalKey:r.runtimeLocalKey}}function agentProjectionRecoveryResult(e){return Object.freeze({runtimeAgentUid:e.runtimeAgentUid,runtimeLocalKey:e.runtimeLocalKey,...void 0===e.removedBindings?{}:{removedBindings:e.removedBindings}})}function isProjectionIntent(e){return"projectionRevision"in e}function recoveryMetadata(e){const t=RUNTIME_METHOD_PARITY_MANIFEST.filter(t=>t.serviceAllowed&&t.canonicalCommand===e),r=t[0]?.recoveryClass;return r?{recoveryClass:r,projectionRequired:"mcp.set"===e||"mcp.unset"===e||t.some(e=>!0===e.projectionRequired)}:{recoveryClass:"imperative-ambiguous",projectionRequired:!1}}export function isDeterministicAgentsCreateParamsRejection(e){return e instanceof Error&&/^invalid agents\.create params\b/.test(e.message)}export function requireCommittedOpenClawDeleteAck(e,t){if(!e||"object"!=typeof e)throw new Error("gateway delete lacks the committed target acknowledgement");const r=e,i=Object.keys(r).every(e=>"ok"===e||"agentId"===e||"removedBindings"===e),n=void 0===r.removedBindings||Number.isSafeInteger(r.removedBindings)&&r.removedBindings>=0;if(!i||!0!==r.ok||r.agentId!==t||!n)throw new Error("gateway delete lacks the committed target acknowledgement");return{ok:!0,agentId:t,...void 0===r.removedBindings?{}:{removedBindings:r.removedBindings}}}function errorText(e){return e instanceof Error?e.message:String(e)}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beeos-claw",
|
|
3
|
-
"version": "0.6.55-staging.
|
|
3
|
+
"version": "0.6.55-staging.2",
|
|
4
4
|
"description": "OpenClaw plugin that bridges the BeeOS platform with the local OpenClaw Gateway, providing file transfer, ACP bridge, and observability.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "BeeOS <dev@beeos.ai>",
|
|
@@ -87,5 +87,5 @@
|
|
|
87
87
|
"fast-uri": ">=3.1.4",
|
|
88
88
|
"brace-expansion": ">=5.0.9"
|
|
89
89
|
},
|
|
90
|
-
"gitHead": "
|
|
90
|
+
"gitHead": "73c309ca647a327cd591f14721c267b552dfa655"
|
|
91
91
|
}
|