beeos-claw 0.6.66-cloud.1 → 0.6.66-cloud.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.
Files changed (33) hide show
  1. package/README.md +13 -0
  2. package/dist/acp-gateway/bridge-core.js +1 -1
  3. package/dist/contracts/question/codec.js +1 -0
  4. package/dist/gateway-client.js +1 -1
  5. package/dist/im-inbound-handler.js +1 -1
  6. package/dist/im-runtime-dispatch.js +1 -1
  7. package/dist/index.js +1 -1
  8. package/dist/message-projection-inbound-consumer.js +1 -1
  9. package/dist/runtime/agent-projection-production.js +1 -1
  10. package/dist/runtime/core-execution-attempt-context.js +1 -1
  11. package/dist/runtime/im-reply-sink.js +1 -1
  12. package/dist/runtime/message-service-runtime-invocation-authority.js +1 -1
  13. package/dist/runtime/message-service-session-reasoning-http-authority.js +1 -0
  14. package/dist/runtime/ms-runtime-message-channel-adapter.js +1 -1
  15. package/dist/runtime/openclaw-native-sessions.js +1 -1
  16. package/dist/runtime/openclaw-question-adapter.js +1 -0
  17. package/dist/runtime/openclaw-runtime-capabilities.js +1 -1
  18. package/dist/runtime/question-wire.js +1 -0
  19. package/dist/runtime/runtime-command-dispatcher.js +1 -1
  20. package/dist/runtime/runtime-command-journal.js +1 -1
  21. package/dist/runtime/runtime-parity-manifest-digest.js +1 -1
  22. package/dist/runtime/runtime-parity-manifest.js +1 -1
  23. package/dist/runtime/runtime-production-authority.js +1 -1
  24. package/dist/runtime/runtime-reasoning.js +1 -0
  25. package/dist/runtime/runtime-service-contract-compatibility.js +1 -1
  26. package/dist/runtime/runtime-service-production-candidate.js +1 -1
  27. package/dist/runtime/runtime-session-control-service.js +1 -1
  28. package/dist/runtime/session-set-model-mapping.js +1 -1
  29. package/dist/terminal/runtime-terminal-outbound-registration.js +1 -1
  30. package/dist/terminal/runtime-terminal-service.js +1 -1
  31. package/dist/terminal/terminal-lease-bound-bridge-client.js +1 -1
  32. package/package.json +7 -6
  33. package/runtime-service-wire-identity.v1.json +12 -12
@@ -1 +1 @@
1
- import{WebSocket}from"ws";import{EventEmitter}from"node:events";import{randomUUID,createHash}from"node:crypto";import{loadOrCreateDeviceIdentity,buildDeviceAuthPayload,signDeviceAuth,ensureDevicePaired,warmupCryptoSign}from"./device-identity.js";const MIN_PROTOCOL_VERSION=3,MAX_PROTOCOL_VERSION=4,CONNECT_TIMEOUT_MS=3e4,HEARTBEAT_INTERVAL_MS=15e3,LIVENESS_TIMEOUT_MS=6e4,CHAT_HISTORY_PAGE_TIMEOUT_MS=1e4,AUTH_FAILURE_CLOSE_CODE=4001,EL_LAG_INTERVAL_MS=200,EL_LAG_THRESHOLD_MS=500,RECONNECT_NOTIFY_MIN_MS=6e4,RECONNECT_NOTIFY_MAX_MS=18e4,RECONNECT_NOTIFY_METHOD="server/reconnect";export class GatewayHandshakeError extends Error{code;retryable;retryAfterMs;constructor(e){super(e?.message||"Gateway connect rejected"),this.name="GatewayHandshakeError",this.code=e?.code,this.retryable=!0===e?.retryable||"UNAVAILABLE"===e?.code,this.retryAfterMs="number"==typeof e?.retryAfterMs?e.retryAfterMs:void 0}}export function decodeChatAbortResult(e){if("object"!=typeof e||null===e||Array.isArray(e))throw new Error("Invalid chat.abort response: expected object");const t=e;if("boolean"!=typeof t.aborted)throw new Error("Invalid chat.abort response: expected boolean aborted");if(!Array.isArray(t.runIds)||t.runIds.some(e=>"string"!=typeof e))throw new Error("Invalid chat.abort response: expected string[] runIds");return{aborted:t.aborted,runIds:[...t.runIds]}}function wsUrlToHttpBase(e){try{const t=new URL(e),n="wss:"===t.protocol?"https:":"ws:"===t.protocol?"http:":null;return n?`${n}//${t.host}`:null}catch{return null}}const MANUAL_RECONNECT_SIGNAL="SIGUSR1";function registerReconnectSignal(e,t,n){detachReconnectSignal(e),e.handler=t;try{process.on("SIGUSR1",t),n?.info?.("[gateway] manual reconnect enabled signal=SIGUSR1")}catch{n?.warn?.("[gateway] manual reconnect signal not supported")}}function detachReconnectSignal(e){if(e.handler){try{process.off("SIGUSR1",e.handler)}catch{}e.handler=null}}export class GatewayClient extends EventEmitter{ws=null;config;pending=new Map;connected=!1;stopped=!1;authFailed=!1;connectPromise=null;reconnectTimer=null;reconnectAttempt=0;nextReconnectDelayHintMs=null;signalState={handler:null};deviceIdentity;heartbeatTimer=null;lastPongTs=0;_connectionSeq=0;_currentConnectionId=null;_lastReadyAt=0;reconnectNotifyTimer=null;constructor(e){super(),this.config=e,this.deviceIdentity=loadOrCreateDeviceIdentity();try{ensureDevicePaired(this.deviceIdentity)}catch(e){}Date.now();warmupCryptoSign(this.deviceIdentity)}async start(){this.stopped=!1,this.reconnectAttempt=0,registerReconnectSignal(this.signalState,()=>{this.stopped||(this.emit("manual-reconnect"),this.reconnectNow())}),await this.connect()}stop(){this.stopped=!0,this.stopHeartbeat(),this.clearReconnectNotify(),detachReconnectSignal(this.signalState),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.close()}async connect(){if(this.connectPromise)return this.connectPromise;this.connectPromise=this._connect();try{await this.connectPromise}finally{this.connectPromise=null}}_connect(){return new Promise((e,t)=>{if(this.authFailed)return void t(new Error("Authentication failed — not retrying"));const n=Date.now();this.emit("transport",{event:"connect_start",url:this.config.url,attempt:this.reconnectAttempt});const s=new WebSocket(this.config.url);this.ws=s;let r,i=null,o=!1;const c=setTimeout(()=>{this.emit("transport",{event:"connect_fail",reason:"timeout",durationMs:Date.now()-n}),s.close(),t(new Error("Gateway connection timeout"))},3e4),a=t=>{clearTimeout(c),o=!0,this.connected=!0,this.reconnectAttempt=0,this.authFailed=!1,this._connectionSeq++,this._currentConnectionId=randomUUID(),this._lastReadyAt=Date.now(),this.startHeartbeat(),this.emit("transport",{event:"connect_ok",connectionSeq:this._connectionSeq,connectionId:this._currentConnectionId,durationMs:Date.now()-n}),this.emit("connected",{...t,connectionSeq:this._connectionSeq,connectionId:this._currentConnectionId}),e()},h=e=>{const r="string"==typeof e?e:e.message;clearTimeout(c),this.authFailed=!0,this.emit("transport",{event:"connect_fail",reason:r,durationMs:Date.now()-n}),this.emit("auth-failed",{reason:r}),s.close(),t("string"==typeof e?new Error(e):e)};s.on("open",()=>{this.emit("transport",{event:"ws_open",durationMs:Date.now()-n})}),s.on("message",e=>{const o="string"==typeof e?e:e.toString("utf-8");if("pong"===o)return void(this.lastPongTs=Date.now());let l;this.emitMessageHash(o);try{l=JSON.parse(o)}catch{return}const d=l.method;d&&!l.id&&(this.isReconnectNotification(d)?this.scheduleReconnectNotify():this.reconnectNotifyTimer&&this.clearReconnectNotify(),this.emit("notification",d,l.params));const u=l.type;if("event"===u&&this.reconnectNotifyTimer&&this.clearReconnectNotify(),"event"===u){const e=l.event,t=l.payload;if("connect.challenge"===e){const e=Date.now();this.emit("transport",{event:"handshake.challenge_recv",durationMs:e-n});const s=t?.nonce;i=this._sendConnect(s),this.emit("transport",{event:"handshake.connect_sent",durationMs:Date.now()-n}),r?.();let o=0;const c=setInterval(()=>{o++},200),a=Date.now(),h=setInterval(()=>{const e=Date.now()-a,t=e-200*o;t>500&&this.emit("transport",{event:"eventloop_lag",lagMs:t,elapsedMs:e,probeSeq:o})},200);let l;const d=()=>{clearInterval(c),clearInterval(h),l&&clearTimeout(l),this.off("connected",d),r===d&&(r=void 0)};r=d,l=setTimeout(d,3e4),this.once("connected",d)}else"chat"===e?this.emit("chat",t):"agent"===e?this.emit("agent",t):"cron"===e?this.emit("cron",t):"canvas"===e?this.emit("canvas",t):"session.message"===e?this.emit("sessionMessage",t):this.emit("gateway-event",e,t)}else if("hello-ok"===u)this.emit("transport",{event:"handshake.hello_ok_recv",durationMs:Date.now()-n}),a(l);else if("hello-error"===u||"connect-error"===u)h("Authentication rejected by Gateway");else if("res"===u){const e=l.id;if(e&&e===i){if(this.emit("transport",{event:"handshake.connect_res_recv",ok:!!l.ok,durationMs:Date.now()-n}),l.ok){const e=l.payload;a(e??l)}else{const e=l.error,r=new GatewayHandshakeError(e);r.retryable?(clearTimeout(c),this.emit("transport",{event:"connect_fail",reason:r.message,retryable:!0,retryAfterMs:r.retryAfterMs,durationMs:Date.now()-n}),"number"==typeof r.retryAfterMs&&r.retryAfterMs>0&&(this.nextReconnectDelayHintMs=r.retryAfterMs),s.close(),t(r)):h(r)}return}const r=this.pending.get(e);if(r)if(this.pending.delete(e),clearTimeout(r.timer),l.ok)r.resolve(l.payload);else{const e=l.error;r.reject(new Error(e?.message||"Gateway request failed"))}else this.emit("response",l)}}),s.on("pong",()=>{this.lastPongTs=Date.now()}),s.on("close",e=>{const s=o;r?.(),this.stopHeartbeat(),this.clearReconnectNotify(),this.emit("transport",{event:"close",code:e,durationMs:Date.now()-n}),this.connected=!1,this.connectPromise=null;for(const[,e]of this.pending)clearTimeout(e.timer),e.reject(new Error("Connection closed"));this.pending.clear(),s||(clearTimeout(c),t(new Error(`Connection closed during handshake (code=${e})`))),4001===e&&(this.authFailed=!0,this.emit("auth-failed",{code:e})),s&&this.emit("disconnected"),this.stopped||this.authFailed||this.scheduleReconnect()}),s.on("error",e=>{this.emit("transport",{event:"error",message:e.message,durationMs:Date.now()-n}),this.stopped?clearTimeout(c):(this.connected||(clearTimeout(c),t(e)),this.emit("error",e))})})}emitMessageHash(e){if(e.length<32)return;const t=createHash("sha256").update(e).digest("hex").slice(0,16);this.emit("transport",{event:"message_recv",hash:t,size:e.length})}startHeartbeat(){this.stopHeartbeat(),this.lastPongTs=Date.now(),this.heartbeatTimer=setInterval(()=>{if(!this.ws||!this.connected)return;try{this.ws.ping()}catch{}const e=Date.now()-this.lastPongTs;e>6e4&&(this.emit("liveness-timeout",{elapsed:e}),this.ws.close())},15e3),"object"==typeof this.heartbeatTimer&&"unref"in this.heartbeatTimer&&this.heartbeatTimer.unref()}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)}isReconnectNotification(e){return"server/reconnect"===e||e.endsWith("/reconnect")}scheduleReconnectNotify(){this.clearReconnectNotify();const e=6e4+12e4*Math.random();this.emit("reconnect-notify-scheduled",{delayMs:Math.round(e)}),this.reconnectNotifyTimer=setTimeout(()=>{this.reconnectNotifyTimer=null,!this.stopped&&this.connected&&(this.emit("reconnect-notify-fired"),this.reconnectNow())},e),"object"==typeof this.reconnectNotifyTimer&&"unref"in this.reconnectNotifyTimer&&this.reconnectNotifyTimer.unref()}clearReconnectNotify(){this.reconnectNotifyTimer&&(clearTimeout(this.reconnectNotifyTimer),this.reconnectNotifyTimer=null)}scheduleReconnect(){if(this.stopped)return;if(this.reconnectTimer)return;const e=this.config.retry??{baseMs:1e3,maxMs:6e5,maxAttempts:0};if(e.maxAttempts>0&&this.reconnectAttempt>=e.maxAttempts)return void this.emit("reconnect-exhausted",this.reconnectAttempt);let t;const n=this.nextReconnectDelayHintMs,s=null!==n&&n>0;s?(t=Math.min(n,e.maxMs),this.nextReconnectDelayHintMs=null):(t=Math.min(e.baseMs*Math.pow(2,this.reconnectAttempt)+500*Math.random(),e.maxMs),this.reconnectAttempt++),this.emit("reconnecting",{attempt:s?this.reconnectAttempt+1:this.reconnectAttempt,delayMs:t}),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.stopped||this.connect().catch(e=>{this.emitReconnectFailure(e)})},t),"object"==typeof this.reconnectTimer&&"unref"in this.reconnectTimer&&this.reconnectTimer.unref()}reconnectNow(){this.stopped||(this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.close(),this.reconnectAttempt=0,this.connect().catch(e=>{this.emitReconnectFailure(e)}))}emitReconnectFailure(e){this.stopped||this.authFailed||this.emit("error",e instanceof Error?e:new Error(String(e)))}_sendConnect(e){const t=Date.now(),n=Date.now(),s="operator",r=["operator.admin","operator.approvals","operator.pairing"],i={id:this.deviceIdentity.deviceId,publicKey:this.deviceIdentity.publicKeyB64url,signedAt:n};if(e){const t=Date.now(),o=buildDeviceAuthPayload({deviceId:this.deviceIdentity.deviceId,clientId:"cli",clientMode:"cli",role:s,scopes:r,signedAtMs:n,token:this.config.token,nonce:e}),c=Date.now()-t,a=Date.now();i.nonce=e,i.signature=signDeviceAuth(this.deviceIdentity,o);const h=Date.now()-a;this.emit("transport",{event:"handshake.sign_done",buildPayloadMs:c,signMs:h})}const o=randomUUID(),c={type:"req",id:o,method:"connect",params:{minProtocol:3,maxProtocol:4,client:{id:"cli",displayName:"BeeOS Platform Bridge",version:"0.1.0",platform:"beeos",mode:"cli"},caps:this.config.openclawCanvasCompat?["tool-events","canvas"]:["tool-events"],role:s,scopes:r,auth:{token:this.config.token},device:i}},a=Date.now(),h=JSON.stringify(c),l=Date.now()-a,d=Date.now();this.ws?.send(h);const u=Date.now()-d;return this.emit("transport",{event:"handshake._sendConnect_done",totalMs:Date.now()-t,serializeMs:l,sendMs:u}),o}async request(e,t,n=6e4){if(!this.ws||!this.connected)throw new Error("Not connected to Gateway");const s=randomUUID(),r={type:"req",id:s,method:e,params:t};return new Promise((t,i)=>{const o=setTimeout(()=>{this.pending.delete(s),i(new Error(`Gateway request timeout: ${e}`))},n);this.pending.set(s,{resolve:t,reject:i,timer:o}),this.ws.send(JSON.stringify(r))})}async agentSend(e,t,n,s,r){const i={agentId:n,sessionKey:e,message:t,deliver:!1,idempotencyKey:randomUUID()};s?.length&&(i.attachments=s),r&&(i.inputProvenance=r);return await this.request("agent",i,12e4)}async chatSend(e,t,n){const s={sessionKey:e,message:t,deliver:!1,idempotencyKey:randomUUID()};n?.length&&(s.attachments=n);return await this.request("chat.send",s,12e4)}async chatAbort(e,t){const n={sessionKey:e};t&&(n.runId=t);return decodeChatAbortResult(await this.request("chat.abort",n))}sendFrame(e){if(!this.ws||!this.connected)return!1;try{return this.ws.send(JSON.stringify(e)),!0}catch{return!1}}agentCancel(e,t){const n={sessionKey:e};t?.runId&&(n.runId=t.runId);const s=`abort_${t?.runId??t?.requestId??e}`;return this.sendFrame({type:"req",id:s,method:"chat.abort",params:n})}async chatHistory(e,t){return this.request("chat.history",{sessionKey:e,limit:t})}async chatHistoryPage(e,t){const n=wsUrlToHttpBase(this.config.url);if(!n)return null;let s;try{s=new URL(`${n}/sessions/${encodeURIComponent(e)}/history`)}catch{return null}null!=t?.limit&&s.searchParams.set("limit",String(t.limit)),t?.cursor&&s.searchParams.set("cursor",t.cursor);try{const e=await fetch(s,{method:"GET",headers:{accept:"application/json",authorization:`Bearer ${this.config.token}`},signal:AbortSignal.timeout(1e4)});if(!e.ok)return null;const t=await e.json(),n=Array.isArray(t.messages)?t.messages:Array.isArray(t.items)?t.items:[];return{messages:n.filter(e=>"object"==typeof e&&null!==e),nextCursor:"string"==typeof t.nextCursor&&t.nextCursor.length>0?t.nextCursor:void 0,hasMore:!0===t.hasMore}}catch{return null}}async sessionsMessagesSubscribe(e){return this.request("sessions.messages.subscribe",{key:e})}async sessionsMessagesUnsubscribe(e){return this.request("sessions.messages.unsubscribe",{key:e})}async skillsStatus(e){return this.request("skills.status",e?{agentId:e}:{})}async skillsUpdate(e,t,n){return this.request("skills.update",{skillKey:e,enabled:t,...n?{agentId:n}:{}})}async sessionsList(){return this.request("sessions.list",{includeDerivedTitles:!0})}async sessionsResolve(e){return this.request("sessions.resolve",{key:e})}async sessionsPatch(e,t){return this.request("sessions.patch",{key:e,...t})}async sessionsDelete(e){return this.request("sessions.delete",{key:e})}async sessionsReset(e){return this.request("sessions.reset",{key:e,reason:"new"})}close(){if(this.ws)try{this.ws.close()}catch{}this.ws=null,this.connected=!1,this.connectPromise=null,this._currentConnectionId=null}get isConnected(){return this.connected}get connectionSeq(){return this._connectionSeq}get currentConnectionId(){return this._currentConnectionId}get lastReadyAt(){return this._lastReadyAt}}
1
+ import{WebSocket}from"ws";import{EventEmitter}from"node:events";import{randomUUID,createHash}from"node:crypto";import{loadOrCreateDeviceIdentity,buildDeviceAuthPayload,signDeviceAuth,ensureDevicePaired,warmupCryptoSign}from"./device-identity.js";const MIN_PROTOCOL_VERSION=3,MAX_PROTOCOL_VERSION=4,CONNECT_TIMEOUT_MS=3e4,HEARTBEAT_INTERVAL_MS=15e3,LIVENESS_TIMEOUT_MS=6e4,CHAT_HISTORY_PAGE_TIMEOUT_MS=1e4,AUTH_FAILURE_CLOSE_CODE=4001,EL_LAG_INTERVAL_MS=200,EL_LAG_THRESHOLD_MS=500,RECONNECT_NOTIFY_MIN_MS=6e4,RECONNECT_NOTIFY_MAX_MS=18e4,RECONNECT_NOTIFY_METHOD="server/reconnect";export class GatewayHandshakeError extends Error{code;retryable;retryAfterMs;constructor(e){super(e?.message||"Gateway connect rejected"),this.name="GatewayHandshakeError",this.code=e?.code,this.retryable=!0===e?.retryable||"UNAVAILABLE"===e?.code,this.retryAfterMs="number"==typeof e?.retryAfterMs?e.retryAfterMs:void 0}}export class GatewayRpcError extends Error{gatewayRejected=!0;code;constructor(e){super(e?.message||"Gateway request failed"),this.name="GatewayRpcError",this.code=e?.code}}export function decodeChatAbortResult(e){if("object"!=typeof e||null===e||Array.isArray(e))throw new Error("Invalid chat.abort response: expected object");const t=e;if("boolean"!=typeof t.aborted)throw new Error("Invalid chat.abort response: expected boolean aborted");if(!Array.isArray(t.runIds)||t.runIds.some(e=>"string"!=typeof e))throw new Error("Invalid chat.abort response: expected string[] runIds");return{aborted:t.aborted,runIds:[...t.runIds]}}function wsUrlToHttpBase(e){try{const t=new URL(e),n="wss:"===t.protocol?"https:":"ws:"===t.protocol?"http:":null;return n?`${n}//${t.host}`:null}catch{return null}}const MANUAL_RECONNECT_SIGNAL="SIGUSR1";function registerReconnectSignal(e,t,n){detachReconnectSignal(e),e.handler=t;try{process.on("SIGUSR1",t),n?.info?.("[gateway] manual reconnect enabled signal=SIGUSR1")}catch{n?.warn?.("[gateway] manual reconnect signal not supported")}}function detachReconnectSignal(e){if(e.handler){try{process.off("SIGUSR1",e.handler)}catch{}e.handler=null}}export class GatewayClient extends EventEmitter{ws=null;config;pending=new Map;connected=!1;stopped=!1;authFailed=!1;connectPromise=null;reconnectTimer=null;reconnectAttempt=0;nextReconnectDelayHintMs=null;signalState={handler:null};deviceIdentity;heartbeatTimer=null;lastPongTs=0;_connectionSeq=0;_currentConnectionId=null;_lastReadyAt=0;reconnectNotifyTimer=null;constructor(e){super(),this.config=e,this.deviceIdentity=loadOrCreateDeviceIdentity();try{ensureDevicePaired(this.deviceIdentity)}catch(e){}Date.now();warmupCryptoSign(this.deviceIdentity)}async start(){this.stopped=!1,this.reconnectAttempt=0,registerReconnectSignal(this.signalState,()=>{this.stopped||(this.emit("manual-reconnect"),this.reconnectNow())}),await this.connect()}stop(){this.stopped=!0,this.stopHeartbeat(),this.clearReconnectNotify(),detachReconnectSignal(this.signalState),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.close()}async connect(){if(this.connectPromise)return this.connectPromise;this.connectPromise=this._connect();try{await this.connectPromise}finally{this.connectPromise=null}}_connect(){return new Promise((e,t)=>{if(this.authFailed)return void t(new Error("Authentication failed — not retrying"));const n=Date.now();this.emit("transport",{event:"connect_start",url:this.config.url,attempt:this.reconnectAttempt});const s=new WebSocket(this.config.url);this.ws=s;let r,i=null,o=!1;const c=setTimeout(()=>{this.emit("transport",{event:"connect_fail",reason:"timeout",durationMs:Date.now()-n}),s.close(),t(new Error("Gateway connection timeout"))},3e4),a=t=>{clearTimeout(c),o=!0,this.connected=!0,this.reconnectAttempt=0,this.authFailed=!1,this._connectionSeq++,this._currentConnectionId=randomUUID(),this._lastReadyAt=Date.now(),this.startHeartbeat(),this.emit("transport",{event:"connect_ok",connectionSeq:this._connectionSeq,connectionId:this._currentConnectionId,durationMs:Date.now()-n}),this.emit("connected",{...t,connectionSeq:this._connectionSeq,connectionId:this._currentConnectionId}),e()},h=e=>{const r="string"==typeof e?e:e.message;clearTimeout(c),this.authFailed=!0,this.emit("transport",{event:"connect_fail",reason:r,durationMs:Date.now()-n}),this.emit("auth-failed",{reason:r}),s.close(),t("string"==typeof e?new Error(e):e)};s.on("open",()=>{this.emit("transport",{event:"ws_open",durationMs:Date.now()-n})}),s.on("message",e=>{const o="string"==typeof e?e:e.toString("utf-8");if("pong"===o)return void(this.lastPongTs=Date.now());let l;this.emitMessageHash(o);try{l=JSON.parse(o)}catch{return}const d=l.method;d&&!l.id&&(this.isReconnectNotification(d)?this.scheduleReconnectNotify():this.reconnectNotifyTimer&&this.clearReconnectNotify(),this.emit("notification",d,l.params));const u=l.type;if("event"===u&&this.reconnectNotifyTimer&&this.clearReconnectNotify(),"event"===u){const e=l.event,t=l.payload;if("connect.challenge"===e){const e=Date.now();this.emit("transport",{event:"handshake.challenge_recv",durationMs:e-n});const s=t?.nonce;i=this._sendConnect(s),this.emit("transport",{event:"handshake.connect_sent",durationMs:Date.now()-n}),r?.();let o=0;const c=setInterval(()=>{o++},200),a=Date.now(),h=setInterval(()=>{const e=Date.now()-a,t=e-200*o;t>500&&this.emit("transport",{event:"eventloop_lag",lagMs:t,elapsedMs:e,probeSeq:o})},200);let l;const d=()=>{clearInterval(c),clearInterval(h),l&&clearTimeout(l),this.off("connected",d),r===d&&(r=void 0)};r=d,l=setTimeout(d,3e4),this.once("connected",d)}else"chat"===e?this.emit("chat",t):"agent"===e?this.emit("agent",t):"cron"===e?this.emit("cron",t):"canvas"===e?this.emit("canvas",t):"session.message"===e?this.emit("sessionMessage",t):this.emit("gateway-event",e,t)}else if("hello-ok"===u)this.emit("transport",{event:"handshake.hello_ok_recv",durationMs:Date.now()-n}),a(l);else if("hello-error"===u||"connect-error"===u)h("Authentication rejected by Gateway");else if("res"===u){const e=l.id;if(e&&e===i){if(this.emit("transport",{event:"handshake.connect_res_recv",ok:!!l.ok,durationMs:Date.now()-n}),l.ok){const e=l.payload;a(e??l)}else{const e=l.error,r=new GatewayHandshakeError(e);r.retryable?(clearTimeout(c),this.emit("transport",{event:"connect_fail",reason:r.message,retryable:!0,retryAfterMs:r.retryAfterMs,durationMs:Date.now()-n}),"number"==typeof r.retryAfterMs&&r.retryAfterMs>0&&(this.nextReconnectDelayHintMs=r.retryAfterMs),s.close(),t(r)):h(r)}return}const r=this.pending.get(e);if(r)if(this.pending.delete(e),clearTimeout(r.timer),l.ok)r.resolve(l.payload);else{const e=l.error;r.reject(new GatewayRpcError(e))}else this.emit("response",l)}}),s.on("pong",()=>{this.lastPongTs=Date.now()}),s.on("close",e=>{const s=o;r?.(),this.stopHeartbeat(),this.clearReconnectNotify(),this.emit("transport",{event:"close",code:e,durationMs:Date.now()-n}),this.connected=!1,this.connectPromise=null;for(const[,e]of this.pending)clearTimeout(e.timer),e.reject(new Error("Connection closed"));this.pending.clear(),s||(clearTimeout(c),t(new Error(`Connection closed during handshake (code=${e})`))),4001===e&&(this.authFailed=!0,this.emit("auth-failed",{code:e})),s&&this.emit("disconnected"),this.stopped||this.authFailed||this.scheduleReconnect()}),s.on("error",e=>{this.emit("transport",{event:"error",message:e.message,durationMs:Date.now()-n}),this.stopped?clearTimeout(c):(this.connected||(clearTimeout(c),t(e)),this.emit("error",e))})})}emitMessageHash(e){if(e.length<32)return;const t=createHash("sha256").update(e).digest("hex").slice(0,16);this.emit("transport",{event:"message_recv",hash:t,size:e.length})}startHeartbeat(){this.stopHeartbeat(),this.lastPongTs=Date.now(),this.heartbeatTimer=setInterval(()=>{if(!this.ws||!this.connected)return;try{this.ws.ping()}catch{}const e=Date.now()-this.lastPongTs;e>6e4&&(this.emit("liveness-timeout",{elapsed:e}),this.ws.close())},15e3),"object"==typeof this.heartbeatTimer&&"unref"in this.heartbeatTimer&&this.heartbeatTimer.unref()}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)}isReconnectNotification(e){return"server/reconnect"===e||e.endsWith("/reconnect")}scheduleReconnectNotify(){this.clearReconnectNotify();const e=6e4+12e4*Math.random();this.emit("reconnect-notify-scheduled",{delayMs:Math.round(e)}),this.reconnectNotifyTimer=setTimeout(()=>{this.reconnectNotifyTimer=null,!this.stopped&&this.connected&&(this.emit("reconnect-notify-fired"),this.reconnectNow())},e),"object"==typeof this.reconnectNotifyTimer&&"unref"in this.reconnectNotifyTimer&&this.reconnectNotifyTimer.unref()}clearReconnectNotify(){this.reconnectNotifyTimer&&(clearTimeout(this.reconnectNotifyTimer),this.reconnectNotifyTimer=null)}scheduleReconnect(){if(this.stopped)return;if(this.reconnectTimer)return;const e=this.config.retry??{baseMs:1e3,maxMs:6e5,maxAttempts:0};if(e.maxAttempts>0&&this.reconnectAttempt>=e.maxAttempts)return void this.emit("reconnect-exhausted",this.reconnectAttempt);let t;const n=this.nextReconnectDelayHintMs,s=null!==n&&n>0;s?(t=Math.min(n,e.maxMs),this.nextReconnectDelayHintMs=null):(t=Math.min(e.baseMs*Math.pow(2,this.reconnectAttempt)+500*Math.random(),e.maxMs),this.reconnectAttempt++),this.emit("reconnecting",{attempt:s?this.reconnectAttempt+1:this.reconnectAttempt,delayMs:t}),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.stopped||this.connect().catch(e=>{this.emitReconnectFailure(e)})},t),"object"==typeof this.reconnectTimer&&"unref"in this.reconnectTimer&&this.reconnectTimer.unref()}reconnectNow(){this.stopped||(this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.close(),this.reconnectAttempt=0,this.connect().catch(e=>{this.emitReconnectFailure(e)}))}emitReconnectFailure(e){this.stopped||this.authFailed||this.emit("error",e instanceof Error?e:new Error(String(e)))}_sendConnect(e){const t=Date.now(),n=Date.now(),s="operator",r=["operator.admin","operator.approvals","operator.pairing"],i={id:this.deviceIdentity.deviceId,publicKey:this.deviceIdentity.publicKeyB64url,signedAt:n};if(e){const t=Date.now(),o=buildDeviceAuthPayload({deviceId:this.deviceIdentity.deviceId,clientId:"cli",clientMode:"cli",role:s,scopes:r,signedAtMs:n,token:this.config.token,nonce:e}),c=Date.now()-t,a=Date.now();i.nonce=e,i.signature=signDeviceAuth(this.deviceIdentity,o);const h=Date.now()-a;this.emit("transport",{event:"handshake.sign_done",buildPayloadMs:c,signMs:h})}const o=randomUUID(),c={type:"req",id:o,method:"connect",params:{minProtocol:3,maxProtocol:4,client:{id:"cli",displayName:"BeeOS Platform Bridge",version:"0.1.0",platform:"beeos",mode:"cli"},caps:this.config.openclawCanvasCompat?["tool-events","canvas"]:["tool-events"],role:s,scopes:r,auth:{token:this.config.token},device:i}},a=Date.now(),h=JSON.stringify(c),l=Date.now()-a,d=Date.now();this.ws?.send(h);const u=Date.now()-d;return this.emit("transport",{event:"handshake._sendConnect_done",totalMs:Date.now()-t,serializeMs:l,sendMs:u}),o}async request(e,t,n=6e4){if(!this.ws||!this.connected)throw new Error("Not connected to Gateway");const s=randomUUID(),r={type:"req",id:s,method:e,params:t};return new Promise((t,i)=>{const o=setTimeout(()=>{this.pending.delete(s),i(new Error(`Gateway request timeout: ${e}`))},n);this.pending.set(s,{resolve:t,reject:i,timer:o}),this.ws.send(JSON.stringify(r))})}async agentSend(e,t,n,s,r){const i={agentId:n,sessionKey:e,message:t,deliver:!1,idempotencyKey:randomUUID()};s?.length&&(i.attachments=s),r&&(i.inputProvenance=r);return await this.request("agent",i,12e4)}async chatSend(e,t,n){const s={sessionKey:e,message:t,deliver:!1,idempotencyKey:randomUUID()};n?.length&&(s.attachments=n);return await this.request("chat.send",s,12e4)}async chatAbort(e,t){const n={sessionKey:e};t&&(n.runId=t);return decodeChatAbortResult(await this.request("chat.abort",n))}sendFrame(e){if(!this.ws||!this.connected)return!1;try{return this.ws.send(JSON.stringify(e)),!0}catch{return!1}}agentCancel(e,t){const n={sessionKey:e};t?.runId&&(n.runId=t.runId);const s=`abort_${t?.runId??t?.requestId??e}`;return this.sendFrame({type:"req",id:s,method:"chat.abort",params:n})}async chatHistory(e,t){return this.request("chat.history",{sessionKey:e,limit:t})}async chatHistoryPage(e,t){const n=wsUrlToHttpBase(this.config.url);if(!n)return null;let s;try{s=new URL(`${n}/sessions/${encodeURIComponent(e)}/history`)}catch{return null}null!=t?.limit&&s.searchParams.set("limit",String(t.limit)),t?.cursor&&s.searchParams.set("cursor",t.cursor);try{const e=await fetch(s,{method:"GET",headers:{accept:"application/json",authorization:`Bearer ${this.config.token}`},signal:AbortSignal.timeout(1e4)});if(!e.ok)return null;const t=await e.json(),n=Array.isArray(t.messages)?t.messages:Array.isArray(t.items)?t.items:[];return{messages:n.filter(e=>"object"==typeof e&&null!==e),nextCursor:"string"==typeof t.nextCursor&&t.nextCursor.length>0?t.nextCursor:void 0,hasMore:!0===t.hasMore}}catch{return null}}async sessionsMessagesSubscribe(e){return this.request("sessions.messages.subscribe",{key:e})}async sessionsMessagesUnsubscribe(e){return this.request("sessions.messages.unsubscribe",{key:e})}async skillsStatus(e){return this.request("skills.status",e?{agentId:e}:{})}async skillsUpdate(e,t,n){return this.request("skills.update",{skillKey:e,enabled:t,...n?{agentId:n}:{}})}async sessionsList(){return this.request("sessions.list",{includeDerivedTitles:!0})}async sessionsResolve(e){return this.request("sessions.resolve",{key:e})}async sessionsPatch(e,t){return this.request("sessions.patch",{key:e,...t})}async sessionsDelete(e){return this.request("sessions.delete",{key:e})}async sessionsReset(e){return this.request("sessions.reset",{key:e,reason:"new"})}close(){if(this.ws)try{this.ws.close()}catch{}this.ws=null,this.connected=!1,this.connectPromise=null,this._currentConnectionId=null}get isConnected(){return this.connected}get connectionSeq(){return this._connectionSeq}get currentConnectionId(){return this._currentConnectionId}get lastReadyAt(){return this._lastReadyAt}}
@@ -1 +1 @@
1
- import{extractChatPrompt}from"@beeos-ai/message-sdk/chat-envelope";import{buildMentionInstruction,parseMentionsParam}from"./delegation/mention-resolver.js";import{dispatchImInboundPrompt}from"./im-runtime-dispatch.js";import{promptRunKey}from"./runtime/prompt-run-store.js";import{decodeWake}from"./automation/wake.js";const MAX_PROTOCOL_MARKER_LEN=64,DEFAULT_ORIGIN="im",resolveOrigin=e=>"string"!=typeof e||0===e.length||e.length>64?"im":e,conversationIdFromMessage=e=>"string"==typeof e.conversationId?e.conversationId:"";export const createImInboundHandler=e=>{const{runtime:t,pluginConfig:n,client:o,localServer:a,promptRunStore:s,waitIfExclusive:i,assistantMessageCommitRouter:r,toolResultObservationRouter:c,modelCallObservationRouter:d,sessionMutationCoordinator:l,logger:m,writeObs:p,principalIdOverride:g,dispatch:u=dispatchImInboundPrompt,completeA2ATask:I,activeRunAuthority:h}=e;return{onChatMessage:async _=>{const v=decodeWake(_);if("invalid"===v.kind||"wake"===v.kind&&!e.automationTurnPolicy?.isReady())return void m.warn?.("[beeos-claw] Automation Wake rejected: invalid contract or unavailable turn policy");const y=_.content??{},f=y.metadata??void 0,b="string"==typeof f?.target_agent_id?f.target_agent_id:"",w=b?a.resolveLocalAgentId?.(b):void 0;if(!b||!w)return void m.warn?.(`[beeos-claw] chat_message target_agent_id=${b||"missing"} not resolvable to a local agent — drop`);const $=extractChatPrompt(_,{source:"chat_message",localAgentId:w});if(!$)return;if(!$.channelId||!$.messageId)return void m.warn?.(`[beeos-claw] chat_message missing channelId/messageId (sessionKey=${$.sessionKey}) — drop`);const R=g||a.getDefaultPlatformAgent()?.instanceId||"";if(!R)return void m.warn?.(`[beeos-claw] chat_message dropped (no principalId resolved, sessionKey=${$.sessionKey})`);const k="string"==typeof f?.delivery_principal?f.delivery_principal:"";if(k&&k!==R)return void m.info?.(`[beeos-claw] chat_message skipped (delivery_principal=${k}, selfPrincipalId=${R}, message_id=${$.messageId})`);const C=parseMentionsParam(y.mentions);a.ensureImInboundSession($.sessionKey,$.channelId,$.messageId,C);const M=buildMentionInstruction(C),K=M?`${M}\n${$.message}`:$.message,O=resolveOrigin(f?.protocol),P=$.messageId,A="a2a"===O&&I?(e,t)=>I(P,e,t):void 0;m.debug?.(`[beeos-claw] chat_message dispatch (sessionKey=${$.sessionKey}, channelId=${$.channelId}, origin=${O})`);const T=e=>{p({component:"plugin",domain:"im_dispatch",name:`im.dispatch.${e.type}`,severity:"dispatch_error"===e.type?"warn":"info",payload:e})};try{await u({runtime:t,titles:e.titles,pluginConfig:n,prompt:{channelId:$.channelId,inboundMessageId:$.messageId,requestId:_.requestId,selfPrincipalId:R,dispatchInput:{message:K,attachments:$.files},sessionKey:$.sessionKey},client:o,logger:m,promptRunStore:s,...i?{waitIfExclusive:i}:{},assistantMessageCommitRouter:r,toolResultObservationRouter:c,modelCallObservationRouter:d,sessionMutationCoordinator:l,abortTransport:a,origin:O,onLifecycleEvent:T,...A?{onTerminalResult:A}:{},activeRunAuthority:h,..."wake"===v.kind?{enterTurn:(t,n)=>e.automationTurnPolicy.enter(t,n,v.context)}:{}})}catch(e){m.error?.(`[beeos-claw] chat_message dispatch failed channelId=${$.channelId} in_reply_to=${$.messageId} error_code=runtime_dispatch_failed`)}},onChatCancel:async e=>{const t=e.content??{},n="string"==typeof t.target_message_id?t.target_message_id:"",o="string"==typeof t.reason?t.reason:"unknown";if(!n)return void m.warn?.(`[beeos-claw] chat_cancel dropped (no target_message_id, reason=${o})`);const a=conversationIdFromMessage(e);if(!a)return void m.warn?.(`[beeos-claw] chat_cancel dropped (no conversation identity, target_message_id=${n}, reason=${o})`);const i=`chat_cancel:${o}`,r=await s.abort(promptRunKey(a,n),i);r.ok?m.info?.(`[beeos-claw] chat_cancel accepted (target_message_id=${n}, requestId=${r.requestId}, channelId=${r.channelId}, reason=${o})`):m.info?.(`[beeos-claw] chat_cancel no-op (target_message_id=${n}, reason=${o}, why=${r.reason})`)}}};
1
+ import{extractChatPrompt}from"@beeos-ai/message-sdk/chat-envelope";import{buildMentionInstruction,parseMentionsParam}from"./delegation/mention-resolver.js";import{dispatchImInboundPrompt}from"./im-runtime-dispatch.js";import{promptRunKey}from"./runtime/prompt-run-store.js";import{decodeWake}from"./automation/wake.js";const MAX_PROTOCOL_MARKER_LEN=64,DEFAULT_ORIGIN="im",resolveOrigin=e=>"string"!=typeof e||0===e.length||e.length>64?"im":e,conversationIdFromMessage=e=>"string"==typeof e.conversationId?e.conversationId:"";export const createImInboundHandler=e=>{const{runtime:t,pluginConfig:n,client:o,localServer:s,promptRunStore:a,waitIfExclusive:i,assistantMessageCommitRouter:r,toolResultObservationRouter:d,modelCallObservationRouter:c,sessionMutationCoordinator:l,logger:m,writeObs:p,principalIdOverride:u,dispatch:g=dispatchImInboundPrompt,completeA2ATask:h,activeRunAuthority:I}=e;return{onChatMessage:async y=>{const _=decodeWake(y);if("invalid"===_.kind||"wake"===_.kind&&!e.automationTurnPolicy?.isReady())return void m.warn?.("[beeos-claw] Automation Wake rejected: invalid contract or unavailable turn policy");const v=y.content??{},f=y.deliveryContext??v.metadata??void 0,b="string"==typeof f?.target_agent_id?f.target_agent_id:"",w=b?s.resolveLocalAgentId?.(b):void 0;if(!b||!w)return void m.warn?.(`[beeos-claw] chat_message target_agent_id=${b||"missing"} not resolvable to a local agent — drop`);const $=w,C=extractChatPrompt(y,{source:"chat_message",localAgentId:$});if(!C)return;if(!C.channelId||!C.messageId)return void m.warn?.(`[beeos-claw] chat_message missing channelId/messageId (sessionKey=${C.sessionKey}) — drop`);const A=u||s.getDefaultPlatformAgent()?.instanceId||"";if(!A)return void m.warn?.(`[beeos-claw] chat_message dropped (no principalId resolved, sessionKey=${C.sessionKey})`);const R="string"==typeof f?.delivery_principal?f.delivery_principal:"";if(R&&R!==A)return void m.info?.(`[beeos-claw] chat_message skipped (delivery_principal=${R}, selfPrincipalId=${A}, message_id=${C.messageId})`);const k=parseMentionsParam(v.mentions);s.ensureImInboundSession(C.sessionKey,C.channelId,C.messageId,k);const M=buildMentionInstruction(k),P=M?`${M}\n${C.message}`:C.message,q=resolveOrigin(v.metadata?.protocol),K=C.messageId,O="a2a"===q&&h?(e,t)=>h(K,e,t):void 0;m.debug?.(`[beeos-claw] chat_message dispatch (sessionKey=${C.sessionKey}, channelId=${C.channelId}, origin=${q})`);const x=e=>{p({component:"plugin",domain:"im_dispatch",name:`im.dispatch.${e.type}`,severity:"dispatch_error"===e.type?"warn":"info",payload:e})};try{await g({runtime:t,titles:e.titles,pluginConfig:n,prompt:{channelId:C.channelId,inboundMessageId:C.messageId,requestId:y.requestId,selfPrincipalId:A,dispatchInput:{message:P,attachments:C.files},sessionKey:C.sessionKey},client:o,logger:m,promptRunStore:a,...i?{waitIfExclusive:i}:{},assistantMessageCommitRouter:r,toolResultObservationRouter:d,modelCallObservationRouter:c,sessionMutationCoordinator:l,abortTransport:s,origin:q,onLifecycleEvent:x,...O?{onTerminalResult:O}:{},activeRunAuthority:I,...e.questionAdapter&&e.questionAuthority&&y.deliveryContext&&"string"==typeof y.senderId&&y.senderId.startsWith("user:")&&f?.runtime_epoch===e.questionAuthority.runtimeEpoch&&f.delivery_principal===A?{questionInput:{adapter:e.questionAdapter,senderId:y.senderId,deliveryContext:y.deliveryContext,historyGeneration:y.historyGeneration,targetAgentId:b,localAgentId:$,runtimeEpoch:e.questionAuthority.runtimeEpoch,assertCurrent:()=>e.questionAuthority.assertCurrent()}}:{},..."wake"===_.kind?{enterTurn:(t,n)=>e.automationTurnPolicy.enter(t,n,_.context)}:{}})}catch(e){m.error?.(`[beeos-claw] chat_message dispatch failed channelId=${C.channelId} in_reply_to=${C.messageId} error_code=runtime_dispatch_failed`)}},onChatCancel:async e=>{const t=e.content??{},n="string"==typeof t.target_message_id?t.target_message_id:"",o="string"==typeof t.reason?t.reason:"unknown";if(!n)return void m.warn?.(`[beeos-claw] chat_cancel dropped (no target_message_id, reason=${o})`);const s=conversationIdFromMessage(e);if(!s)return void m.warn?.(`[beeos-claw] chat_cancel dropped (no conversation identity, target_message_id=${n}, reason=${o})`);const i=`chat_cancel:${o}`,r=await a.abort(promptRunKey(s,n),i);r.ok?m.info?.(`[beeos-claw] chat_cancel accepted (target_message_id=${n}, requestId=${r.requestId}, channelId=${r.channelId}, reason=${o})`):m.info?.(`[beeos-claw] chat_cancel no-op (target_message_id=${n}, reason=${o}, why=${r.reason})`)},onUserContinue:t=>e.questionAdapter?.handleContinue(t)??Promise.resolve()}};
@@ -1 +1 @@
1
- import{createHash,randomUUID}from"node:crypto";import{dispatchRuntimeReplyWithRoute,hasRuntimeReplyApi}from"./runtime/reply-router.js";import{createImReplySink}from"./runtime/im-reply-sink.js";import{SerializedCallbackQueue}from"./runtime/callback-queue.js";import{promptRunKey}from"./runtime/prompt-run-store.js";import{classifyAgentRuntimeFailure}from"./runtime/agent-runtime-failure-classifier.js";import{SESSION_RECOVERY_REQUIRED_CODE,SESSION_RECOVERY_REQUIRED_MESSAGE}from"./runtime/session-mutation-coordinator.js";import{interceptAgentRuntimeFailures,messageStopReasonForRuntimeFailure,SESSION_BUSY_PUBLIC_MESSAGE}from"./runtime/im-runtime-failure-policy.js";import{writeRuntimeFailure}from"./runtime/write-runtime-failure.js";import{maybeRenameImConversationTitle}from"./runtime/conversation-title.js";import{logChatLatency}from"./chat-latency.js";const normalizePositiveTimeoutMs=e=>{if("number"!=typeof e||!Number.isFinite(e))return;const n=Math.max(0,Math.trunc(e));return n>0?n:void 0},ABORT_TERMINAL_GRACE_MS=1e4,PRE_RUN_LOOP_PROBE_MS=100,PRE_RUN_LOOP_LAG_LOG_MS=500,formatErrMessage=e=>{if(e instanceof Error)return e.message;if("string"==typeof e)return e;try{return JSON.stringify(e)}catch{return String(e)}},failMessageStream=async(e,n)=>{void 0!==n.body&&e.setBody(n.body),await e.fail(void 0===n.stopReason?void 0:{stopReason:n.stopReason})};export const deterministicReplyMessageId=(e,n,t,r)=>`${e}_${createHash("sha256").update(`${n}:${t}:${r}`).digest("hex").slice(0,32)}`;export const dispatchImInboundPrompt=async e=>{const{runtime:n,prompt:t,client:r,logger:a,promptRunStore:i,assistantMessageCommitRouter:s,toolResultObservationRouter:o,modelCallObservationRouter:c,sessionMutationCoordinator:d,abortTransport:l,activeRunAuthority:u}=e;if(!hasRuntimeReplyApi(n))return a.warn?.(`[im] runtime dispatch skipped reason=reply_api_unavailable channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"skipped",reason:"reply_api_unavailable",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),!1;const m=t.requestId??t.inboundMessageId,p=randomUUID(),I=Date.now();logChatLatency(a,"claw.inbound.received",m,I,{origin:e.origin??"im",request_id_source:t.requestId?"realtime_correlation":"message_id_fallback"});const g=e.origin??"im",h={protocol:"im",requestId:m,sessionId:t.sessionKey??"agent:main",sourceMessageId:t.inboundMessageId,chatId:t.channelId,origin:g},y=async(n,r)=>{if(e.onTerminalResult)try{await e.onTerminalResult(n,r)}catch(e){a.warn?.(`[im] terminal business callback failed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}},_=promptRunKey(t.channelId,t.inboundMessageId),R=t.sessionKey??"agent:main",f=new AbortController;let b,E,w;const M=new Promise((e,n)=>{w=n});let $;const S=()=>{void 0!==$&&(clearTimeout($),$=void 0)};if(e.waitIfExclusive)try{await e.waitIfExclusive("im_dispatch",f.signal)}catch(n){return a.warn?.(`[im] runtime dispatch aborted waiting for exclusive mutation requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:n instanceof Error?n.message:String(n),cause:"abort"}),!1}if(!i.register(_,{requestId:m,channelId:t.channelId,requestCancel:async e=>{if(E??=e,f.abort(e),!b)return{ok:!0};const n=await d.requestCancel(b,l);return n.ok?n.terminalObservation.then(e=>{e.ok||w(new Error(SESSION_RECOVERY_REQUIRED_CODE))}):w(new Error(SESSION_RECOVERY_REQUIRED_CODE)),{ok:n.ok}},startedAt:I,sessionKey:R}))return a.warn?.(`[im] runtime dispatch rejected duplicate inboundMessageId=${t.inboundMessageId} requestId=${m} channel=${t.channelId}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"duplicate_inbound_message_id",cause:"abort"}),!1;const v=deterministicReplyMessageId("asst",t.channelId,t.inboundMessageId,t.selfPrincipalId),C={conversationId:t.channelId,clientMessageId:v,idempotencyKey:v,replyTo:e.inReplyTo??t.inboundMessageId,requestId:m,type:"agent_reply",content:{}},q=r.messages.startStream(C);let k=!1;try{const e=await q.opened();k="duplicate"===e.outcome,logChatLatency(a,"claw.reply_stream.opened",m,I,{outcome:e.outcome})}catch(n){let s="unconfirmed";try{s=(await r.messages.getEnvelope(t.channelId,v)).state}catch{}return a.warn?.(`[im] runtime reply stream open outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} reply_id=${v} reconciled_state=${s}: ${formatErrMessage(n)}; runtime_not_invoked`),e.onLifecycleEvent?.({type:"skipped",reason:"reply_open_outcome_unknown",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),i.unregister(_),!1}if(k)return a.info?.(`[im] runtime dispatch skipped existing deterministic reply requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"skipped",reason:"idempotent_reply_exists",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),i.unregister(_),!1;const O="string"==typeof t.dispatchInput.message?t.dispatchInput.message:"";let L;const T=new SerializedCallbackQueue,A=createImReplySink({stream:q,abortSignal:f.signal,callbackQueue:T,logger:a,requestId:m,startedAtMs:I,runId:p,channelId:t.channelId,inboundMessageId:t.inboundMessageId,origin:g,sessionKey:R,onAgentRunStart:e=>{d.bindRun(b,e),void 0!==L&&logChatLatency(a,"claw.runtime.pre_run_completed",m,L)}}),D=interceptAgentRuntimeFailures(A,"im_runtime",e=>a.warn?.(`[im] runtime failure reply suppressed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} error_code=${e.errorCode}`)),F=D.sink;let U=!1;const K=async e=>{F.dispatcher.markComplete(e);try{await F.dispatcher.waitForIdle()}catch{}},N=()=>{S();const n=normalizePositiveTimeoutMs(e.abortTerminalGraceMs)??1e4;$=setTimeout(()=>{$=void 0,(async()=>{if(q.isTerminated)return;const e=f.signal.reason??E,n="string"==typeof e&&e.startsWith("chat_cancel");if(await K("failure"),!q.isTerminated)try{n?await q.cancel({stopReason:"user_stop"}):await failMessageStream(q,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}),a.warn?.(`[im] abort settlement watchdog terminalized reply requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} user_cancel=${n}`)}catch(e){a.warn?.(`[im] abort settlement watchdog terminal write failed requestId=${m} channel=${t.channelId}: ${formatErrMessage(e)}`)}})()},n)};f.signal.aborted?N():f.signal.addEventListener("abort",N,{once:!0});const P=()=>{if(!f.signal.aborted)return;const e=f.signal.reason;throw e instanceof Error?e:new Error(String(e??"runtime dispatch aborted"))};let G,Q,j,Y,x,B,V;const z=async e=>{if(!b)throw new Error("session_mutation_not_acquired");const n=await d.converge(b,e);return n.ok?(G=n.permit,!0):(a.error?.(`[im] session mutation recovery required requestId=${m} sessionKey=${R} reason=${n.recovery.reason}`),Q={code:n.recovery.code,reason:n.recovery.reason,sessionKey:n.recovery.sessionKey,operationId:n.recovery.operationId,surface:"message_service",...n.recovery.terminalKind?{terminalKind:n.recovery.terminalKind}:{}},!1)};try{P();const r=d.tryStartInteractive({sessionKey:R,label:"im_chat_message"},"message_service");if(!r.ok){try{await failMessageStream(q,{body:SESSION_BUSY_PUBLIC_MESSAGE,stopReason:"error"})}catch(e){a.warn?.(`[im] session busy terminal write failed channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}return a.warn?.(`[im] session write gate busy sessionKey=${R} channel=${t.channelId} in_reply_to=${t.inboundMessageId} active_operation=${r.activeOperationId??"unknown"}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"session_busy",cause:"runtime_error"}),!1}if(b=r.token,logChatLatency(a,"claw.session_gate.acquired",m,I),d.bindRun(b,p),Y=s.register(R,A),x=o.register(R,A),B=c.register(p,{requestId:m,startedAtMs:I,logger:a}),u){const e=d.snapshot(R);j={runtimeConversationUid:t.channelId,runtimeSessionKey:t.channelId,historyGeneration:u.historyGeneration(t.channelId),targetMessageId:t.inboundMessageId,runtimeRunId:p,activeRunRegistrationId:`im:${e?.operationId??p}`},u.activeRuns.register(j,async()=>{const e=`chat_cancel:activeRun:${j.activeRunRegistrationId}`,n=await i.abort(_,e);return n.ok||"already_aborted"===n.reason?"accepted":"terminal_committing"===n.reason||"not_found"===n.reason?"already_terminal":"recovery_required"})}P(),a.info?.(`[im] runtime dispatching requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} origin=${g}`),e.onLifecycleEvent?.({type:"dispatch_start",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId}),L=Date.now(),V=e.enterTurn?.(R,p),logChatLatency(a,"claw.runtime.invoke",m,I);const l=setTimeout(()=>{const e=Date.now()-L-100;e>=500&&logChatLatency(a,"claw.runtime.event_loop_lag",m,L,{lag_ms:e,phase:"pre_run"})},100);l.unref?.();const f=()=>dispatchRuntimeReplyWithRoute({runtime:n,pluginConfig:e.pluginConfig,preparedPrompt:t.dispatchInput,replyRoute:h,replySinks:{im:F},onDispatchInvoked:()=>{U=!0,logChatLatency(a,"claw.runtime.dispatch_returned",m,L)}}),E=V?V.run(f):f(),w=await Promise.race([E,M]).finally(()=>{}),$=D.detectedFailure();if($)throw new Error($.diagnosticMessage);if(P(),await K("success"),P(),!i.claimTerminal(_,"completion")){if(P(),"cancelling"===i.phase(_))throw new Error("cancellation in progress");throw new Error("terminal outcome already claimed")}if(!await z("completed"))return await failMessageStream(q,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}).catch(()=>{}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:SESSION_RECOVERY_REQUIRED_CODE,cause:"runtime_error",...Q?{recovery:Q}:{}}),!1;const S=F.dispatcher.getTerminalErrorText?.();if(void 0!==S){let n=!1;const r=classifyAgentRuntimeFailure({raw:S,source:"im_runtime",runtimeKind:"openclaw"});try{await writeRuntimeFailure(q,{body:r.publicMessage,stopReason:messageStopReasonForRuntimeFailure(r),businessErrorCode:r.businessErrorCode})}catch(e){n=!0,a.warn?.(`[im] runtime reply stream error-terminal write outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}return u&&j&&u.activeRuns.markTerminal(j,"failed"),a.warn?.(`[im] runtime dispatch ended with an error terminal requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} counts=${JSON.stringify(w.counts)} terminalWriteFailed=${n} elapsed_ms=${Date.now()-I}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"runtime_error_terminal_reply",cause:"runtime_error"}),n||await y("",S),!1}let v=!1;try{await q.finalize({stopReason:"end_turn"})}catch(e){v=!0,a.warn?.(`[im] runtime reply stream finalize outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}u&&j&&u.activeRuns.markTerminal(j,"completed");const C={...w.counts,source:A.getSourceCount()};a.info?.(`[im] runtime dispatch completed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} counts=${JSON.stringify(C)} elapsed_ms=${Date.now()-I}`),logChatLatency(a,"claw.turn.terminal",m,I,{outcome:v?"outcome_unknown":"completed",tool_count:C.tool}),e.onLifecycleEvent?.({type:"dispatch_complete",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,counts:C,...v?{terminalWriteFailed:!0}:{}});const k=F.dispatcher.getAccumulatedBody?.()??"";return v||await y(k,""),e.titles&&maybeRenameImConversationTitle({titles:e.titles,conversationId:t.channelId,question:O,answer:k,logger:a}),!0}catch(n){await K("failure");const r=f.signal.aborted,s=r?f.signal.reason:void 0,o="cancelling"===i.phase(_),c=s??E,d="string"==typeof c&&c.startsWith("chat_cancel");let l="runtime_error";(r||o)&&(l="abort");const p=o||d||r?"cancellation":"completion";if(!i.claimTerminal(_,p)){if(a.warn?.(`[im] terminal claim raced requestId=${m} channel=${t.channelId}; best-effort envelope terminal if still streaming`),!q.isTerminated)try{d?await q.cancel({stopReason:"user_stop"}):await failMessageStream(q,{body:formatErrMessage(n),stopReason:"error"})}catch(e){a.warn?.(`[im] raced terminal write failed requestId=${m}: ${formatErrMessage(e)}`)}throw n}const g=d?"cancelled":"error";if(u&&j&&u.activeRuns.markTerminal(j,"cancelled"===g?"cancelled":"failed"),b&&!await z(g))throw d?await q.cancel({stopReason:"user_stop"}).catch(()=>{}):await failMessageStream(q,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}).catch(()=>{}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:SESSION_RECOVERY_REQUIRED_CODE,cause:l,...Q?{recovery:Q}:{}}),n;const h=classifyAgentRuntimeFailure({raw:n,source:"im_runtime",runtimeKind:"openclaw",requestAborted:d}),R=d?h:D.detectedFailure()??h;let w=!1;try{d?await q.cancel({stopReason:"user_stop"}):await writeRuntimeFailure(q,{body:R.publicMessage,stopReason:messageStopReasonForRuntimeFailure(R),businessErrorCode:R.businessErrorCode})}catch(e){w=!0,a.warn?.(`[im] runtime stream terminal write failed requestId=${m} channel=${t.channelId}: ${formatErrMessage(e)}`)}throw a.warn?.(`[im] runtime dispatch failed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} cause=${l} dispatchInvoked=${U} error_code=${R.errorCode}`),logChatLatency(a,"claw.turn.terminal",m,I,{outcome:l,error_code:R.errorCode}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:R.errorCode,cause:l}),d||w||await y("",R.publicMessage),n}finally{U&&!G||V?.close(),S(),B?.(),x?.(),Y?.(),i.unregister(_),U?G&&d.release(G):d.releaseBeforeRuntime(b)}};
1
+ import{createHash,randomUUID}from"node:crypto";import{dispatchRuntimeReplyWithRoute,hasRuntimeReplyApi}from"./runtime/reply-router.js";import{createImReplySink}from"./runtime/im-reply-sink.js";import{SerializedCallbackQueue}from"./runtime/callback-queue.js";import{promptRunKey}from"./runtime/prompt-run-store.js";import{classifyAgentRuntimeFailure}from"./runtime/agent-runtime-failure-classifier.js";import{SESSION_RECOVERY_REQUIRED_CODE,SESSION_RECOVERY_REQUIRED_MESSAGE}from"./runtime/session-mutation-coordinator.js";import{interceptAgentRuntimeFailures,messageStopReasonForRuntimeFailure,SESSION_BUSY_PUBLIC_MESSAGE}from"./runtime/im-runtime-failure-policy.js";import{writeRuntimeFailure}from"./runtime/write-runtime-failure.js";import{maybeRenameImConversationTitle}from"./runtime/conversation-title.js";import{logChatLatency}from"./chat-latency.js";const normalizePositiveTimeoutMs=e=>{if("number"!=typeof e||!Number.isFinite(e))return;const n=Math.max(0,Math.trunc(e));return n>0?n:void 0},ABORT_TERMINAL_GRACE_MS=1e4,PRE_RUN_LOOP_PROBE_MS=100,PRE_RUN_LOOP_LAG_LOG_MS=500,formatErrMessage=e=>{if(e instanceof Error)return e.message;if("string"==typeof e)return e;try{return JSON.stringify(e)}catch{return String(e)}},failMessageStream=async(e,n)=>{void 0!==n.body&&e.setBody(n.body),await e.fail(void 0===n.stopReason?void 0:{stopReason:n.stopReason})};export const deterministicReplyMessageId=(e,n,t,r)=>`${e}_${createHash("sha256").update(`${n}:${t}:${r}`).digest("hex").slice(0,32)}`;export const dispatchImInboundPrompt=async e=>{const{runtime:n,prompt:t,client:r,logger:a,promptRunStore:i,assistantMessageCommitRouter:s,toolResultObservationRouter:o,modelCallObservationRouter:c,sessionMutationCoordinator:d,abortTransport:l,activeRunAuthority:u}=e;if(!hasRuntimeReplyApi(n))return a.warn?.(`[im] runtime dispatch skipped reason=reply_api_unavailable channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"skipped",reason:"reply_api_unavailable",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),!1;const m=t.requestId??t.inboundMessageId,p=randomUUID(),I=Date.now();logChatLatency(a,"claw.inbound.received",m,I,{origin:e.origin??"im",request_id_source:t.requestId?"realtime_correlation":"message_id_fallback"});const g=e.origin??"im",h={protocol:"im",requestId:m,sessionId:t.sessionKey??"agent:main",sourceMessageId:t.inboundMessageId,chatId:t.channelId,origin:g},y=async(n,r)=>{if(e.onTerminalResult)try{await e.onTerminalResult(n,r)}catch(e){a.warn?.(`[im] terminal business callback failed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}},_=promptRunKey(t.channelId,t.inboundMessageId),f=t.sessionKey??"agent:main",R=new AbortController;let b,E,w;const M=new Promise((e,n)=>{w=n});let $;const v=()=>{void 0!==$&&(clearTimeout($),$=void 0)};if(e.waitIfExclusive)try{await e.waitIfExclusive("im_dispatch",R.signal)}catch(n){return a.warn?.(`[im] runtime dispatch aborted waiting for exclusive mutation requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:n instanceof Error?n.message:String(n),cause:"abort"}),!1}if(!i.register(_,{requestId:m,channelId:t.channelId,requestCancel:async e=>{if(E??=e,R.abort(e),!b)return{ok:!0};const n=await d.requestCancel(b,l);return n.ok?n.terminalObservation.then(e=>{e.ok||w(new Error(SESSION_RECOVERY_REQUIRED_CODE))}):w(new Error(SESSION_RECOVERY_REQUIRED_CODE)),{ok:n.ok}},startedAt:I,sessionKey:f}))return a.warn?.(`[im] runtime dispatch rejected duplicate inboundMessageId=${t.inboundMessageId} requestId=${m} channel=${t.channelId}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"duplicate_inbound_message_id",cause:"abort"}),!1;const S=deterministicReplyMessageId("asst",t.channelId,t.inboundMessageId,t.selfPrincipalId),C={conversationId:t.channelId,clientMessageId:S,idempotencyKey:S,replyTo:e.inReplyTo??t.inboundMessageId,requestId:m,type:"agent_reply",...e.questionInput?{deliveryContext:e.questionInput.deliveryContext}:{},content:{}},q=r.messages.startStream(C);let k=!1;try{const e=await q.opened();k="duplicate"===e.outcome,logChatLatency(a,"claw.reply_stream.opened",m,I,{outcome:e.outcome})}catch(n){let s="unconfirmed";try{s=(await r.messages.getEnvelope(t.channelId,S)).state}catch{}return a.warn?.(`[im] runtime reply stream open outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} reply_id=${S} reconciled_state=${s}: ${formatErrMessage(n)}; runtime_not_invoked`),e.onLifecycleEvent?.({type:"skipped",reason:"reply_open_outcome_unknown",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),i.unregister(_),!1}if(k)return a.info?.(`[im] runtime dispatch skipped existing deterministic reply requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}`),e.onLifecycleEvent?.({type:"skipped",reason:"idempotent_reply_exists",channelId:t.channelId,inboundMessageId:t.inboundMessageId}),i.unregister(_),!1;const O="string"==typeof t.dispatchInput.message?t.dispatchInput.message:"";let L;const A=new SerializedCallbackQueue;let T;const D=createImReplySink({stream:q,abortSignal:R.signal,callbackQueue:A,logger:a,requestId:m,startedAtMs:I,runId:p,channelId:t.channelId,inboundMessageId:t.inboundMessageId,origin:g,sessionKey:f,onQuestionToolStart:(e,n)=>T?.observeTool(e,n),onAgentRunStart:e=>{d.bindRun(b,e),T?.bindRun(e),void 0!==L&&logChatLatency(a,"claw.runtime.pre_run_completed",m,L)}}),U=interceptAgentRuntimeFailures(D,"im_runtime",e=>a.warn?.(`[im] runtime failure reply suppressed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} error_code=${e.errorCode}`)),F=U.sink;let K=!1;const N=async e=>{F.dispatcher.markComplete(e);try{await F.dispatcher.waitForIdle()}catch{}},G=()=>{v();const n=normalizePositiveTimeoutMs(e.abortTerminalGraceMs)??1e4;$=setTimeout(()=>{$=void 0,(async()=>{if(q.isTerminated)return;const e=R.signal.reason??E,n="string"==typeof e&&e.startsWith("chat_cancel");if(await N("failure"),!q.isTerminated)try{n?await q.cancel({stopReason:"user_stop"}):await failMessageStream(q,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}),a.warn?.(`[im] abort settlement watchdog terminalized reply requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} user_cancel=${n}`)}catch(e){a.warn?.(`[im] abort settlement watchdog terminal write failed requestId=${m} channel=${t.channelId}: ${formatErrMessage(e)}`)}})()},n)};R.signal.aborted?G():R.signal.addEventListener("abort",G,{once:!0});const P=()=>{if(!R.signal.aborted)return;const e=R.signal.reason;throw e instanceof Error?e:new Error(String(e??"runtime dispatch aborted"))};let x,Q,j,Y,B,V,z;const W=async e=>{if(!b)throw new Error("session_mutation_not_acquired");const n=await d.converge(b,e);return n.ok?(x=n.permit,!0):(a.error?.(`[im] session mutation recovery required requestId=${m} sessionKey=${f} reason=${n.recovery.reason}`),Q={code:n.recovery.code,reason:n.recovery.reason,sessionKey:n.recovery.sessionKey,operationId:n.recovery.operationId,surface:"message_service",...n.recovery.terminalKind?{terminalKind:n.recovery.terminalKind}:{}},!1)};try{P();const r=d.tryStartInteractive({sessionKey:f,label:"im_chat_message"},"message_service");if(!r.ok){try{await failMessageStream(q,{body:SESSION_BUSY_PUBLIC_MESSAGE,stopReason:"error"})}catch(e){a.warn?.(`[im] session busy terminal write failed channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}return a.warn?.(`[im] session write gate busy sessionKey=${f} channel=${t.channelId} in_reply_to=${t.inboundMessageId} active_operation=${r.activeOperationId??"unknown"}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"session_busy",cause:"runtime_error"}),!1}if(b=r.token,logChatLatency(a,"claw.session_gate.acquired",m,I),d.bindRun(b,p),Y=s.register(f,D),B=o.register(f,D),V=c.register(p,{requestId:m,startedAtMs:I,logger:a}),u){const e=d.snapshot(f);j={runtimeConversationUid:t.channelId,runtimeSessionKey:t.channelId,historyGeneration:u.historyGeneration(t.channelId),targetMessageId:t.inboundMessageId,runtimeRunId:p,activeRunRegistrationId:`im:${e?.operationId??p}`},u.activeRuns.register(j,async()=>{const e=`chat_cancel:activeRun:${j.activeRunRegistrationId}`,n=await i.abort(_,e);return n.ok||"already_aborted"===n.reason?"accepted":"terminal_committing"===n.reason||"not_found"===n.reason?"already_terminal":"recovery_required"})}if(P(),a.info?.(`[im] runtime dispatching requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} origin=${g}`),e.onLifecycleEvent?.({type:"dispatch_start",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId}),L=Date.now(),z=e.enterTurn?.(f,p),e.questionInput){const n=e.questionInput,r=b,a=u?.historyGeneration(t.channelId);T=n.adapter.beginTurn({deliveryContext:n.deliveryContext,historyGeneration:n.historyGeneration,instanceId:t.selfPrincipalId,targetAgentId:n.targetAgentId,localAgentId:n.localAgentId,runtimeEpoch:n.runtimeEpoch,senderId:n.senderId,conversationId:t.channelId,turnMessageId:t.inboundMessageId,sessionKey:f,runId:p,signal:R.signal,assertCurrent:()=>{n.assertCurrent();const e=d.snapshot(f);if(!e||e.operationId!==r.operationId||"active"!==e.phase||void 0!==a&&u?.historyGeneration(t.channelId)!==a)throw new Error("Question parent writer is no longer current")},cancelRun:e=>i.abort(_,e),publishStatus:e=>D.projectUserInputStatus(e)})}logChatLatency(a,"claw.runtime.invoke",m,I);const l=setTimeout(()=>{const e=Date.now()-L-100;e>=500&&logChatLatency(a,"claw.runtime.event_loop_lag",m,L,{lag_ms:e,phase:"pre_run"})},100);l.unref?.();const E=()=>dispatchRuntimeReplyWithRoute({runtime:n,pluginConfig:e.pluginConfig,preparedPrompt:t.dispatchInput,replyRoute:h,replySinks:{im:F},onDispatchInvoked:()=>{K=!0,logChatLatency(a,"claw.runtime.dispatch_returned",m,L)}}),w=z?z.run(E):E(),$=await Promise.race([w,M]).finally(()=>{}),v=U.detectedFailure();if(v)throw new Error(v.diagnosticMessage);if(P(),await N("success"),P(),!i.claimTerminal(_,"completion")){if(P(),"cancelling"===i.phase(_))throw new Error("cancellation in progress");throw new Error("terminal outcome already claimed")}if(!await W("completed"))return await failMessageStream(q,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}).catch(()=>{}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:SESSION_RECOVERY_REQUIRED_CODE,cause:"runtime_error",...Q?{recovery:Q}:{}}),!1;const S=F.dispatcher.getTerminalErrorText?.();if(void 0!==S){let n=!1;const r=classifyAgentRuntimeFailure({raw:S,source:"im_runtime",runtimeKind:"openclaw"});try{await writeRuntimeFailure(q,{body:r.publicMessage,stopReason:messageStopReasonForRuntimeFailure(r),businessErrorCode:r.businessErrorCode})}catch(e){n=!0,a.warn?.(`[im] runtime reply stream error-terminal write outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}return u&&j&&u.activeRuns.markTerminal(j,"failed"),a.warn?.(`[im] runtime dispatch ended with an error terminal requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} counts=${JSON.stringify($.counts)} terminalWriteFailed=${n} elapsed_ms=${Date.now()-I}`),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:"runtime_error_terminal_reply",cause:"runtime_error"}),n||await y("",S),!1}let C=!1;try{await q.finalize({stopReason:"end_turn"})}catch(e){C=!0,a.warn?.(`[im] runtime reply stream finalize outcome unknown requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId}: ${formatErrMessage(e)}`)}u&&j&&u.activeRuns.markTerminal(j,"completed");const k={...$.counts,source:D.getSourceCount()};a.info?.(`[im] runtime dispatch completed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} counts=${JSON.stringify(k)} elapsed_ms=${Date.now()-I}`),logChatLatency(a,"claw.turn.terminal",m,I,{outcome:C?"outcome_unknown":"completed",tool_count:k.tool}),e.onLifecycleEvent?.({type:"dispatch_complete",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,counts:k,...C?{terminalWriteFailed:!0}:{}});const A=F.dispatcher.getAccumulatedBody?.()??"";return C||await y(A,""),e.titles&&maybeRenameImConversationTitle({titles:e.titles,conversationId:t.channelId,question:O,answer:A,logger:a}),!0}catch(n){await N("failure");const r=R.signal.aborted,s=r?R.signal.reason:void 0,o="cancelling"===i.phase(_),c=s??E,d="string"==typeof c&&c.startsWith("chat_cancel");let l="runtime_error";(r||o)&&(l="abort");const p=o||d||r?"cancellation":"completion";if(!i.claimTerminal(_,p)){if(a.warn?.(`[im] terminal claim raced requestId=${m} channel=${t.channelId}; best-effort envelope terminal if still streaming`),!q.isTerminated)try{d?await q.cancel({stopReason:"user_stop"}):await failMessageStream(q,{body:formatErrMessage(n),stopReason:"error"})}catch(e){a.warn?.(`[im] raced terminal write failed requestId=${m}: ${formatErrMessage(e)}`)}throw n}const g=d?"cancelled":"error";if(u&&j&&u.activeRuns.markTerminal(j,"cancelled"===g?"cancelled":"failed"),b&&!await W(g))throw d?await q.cancel({stopReason:"user_stop"}).catch(()=>{}):await failMessageStream(q,{body:SESSION_RECOVERY_REQUIRED_MESSAGE,stopReason:"error"}).catch(()=>{}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:SESSION_RECOVERY_REQUIRED_CODE,cause:l,...Q?{recovery:Q}:{}}),n;const h=classifyAgentRuntimeFailure({raw:n,source:"im_runtime",runtimeKind:"openclaw",requestAborted:d}),f=d?h:U.detectedFailure()??h;let w=!1;try{d?await q.cancel({stopReason:"user_stop"}):await writeRuntimeFailure(q,{body:f.publicMessage,stopReason:messageStopReasonForRuntimeFailure(f),businessErrorCode:f.businessErrorCode})}catch(e){w=!0,a.warn?.(`[im] runtime stream terminal write failed requestId=${m} channel=${t.channelId}: ${formatErrMessage(e)}`)}throw a.warn?.(`[im] runtime dispatch failed requestId=${m} channel=${t.channelId} in_reply_to=${t.inboundMessageId} cause=${l} dispatchInvoked=${K} error_code=${f.errorCode}`),logChatLatency(a,"claw.turn.terminal",m,I,{outcome:l,error_code:f.errorCode}),e.onLifecycleEvent?.({type:"dispatch_error",requestId:m,channelId:t.channelId,inboundMessageId:t.inboundMessageId,error:f.errorCode,cause:l}),d||w||await y("",f.publicMessage),n}finally{T?.close(),K&&!x||z?.close(),v(),V?.(),B?.(),Y?.(),i.unregister(_),K?x&&d.release(x):d.releaseBeforeRuntime(b)}};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{resolveOpenClawTitleProvider}from"./runtime/openclaw-title-provider.js";import{persistRequestedModel}from"./runtime/requested-model.js";import{fileDeliveryFinalizeDecision}from"./upload/finalize-hook.js";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{getOpenClawNativeSessions}from"./runtime/openclaw-native-sessions.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{AutomationTurnPolicy}from"./automation/turn-policy.js";import{registerAutomationTools}from"./automation/tools.js";import{extractBeeosConversationIdFromSessionKey}from"./runtime/openclaw-session-key-resolution.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{MSConnectionStatusSnapshot}from"./runtime/ms-connection-status.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{deliverableFileExtractor}from"./upload/deliverable-extractor.js";import{observeToolCallForDelivery,rememberDeliveryWorkspace}from"./upload/delivery-observe.js";import{BEEOS_FILE_RESOURCE_ROUTING_RULE}from"./files/beeos-file-uri.js";import{marksFullRuntimeRegistered,planPluginRegistration,replayCachedPluginTools,resolveCapabilityToolReplay}from"./plugin-registration-plan.js";import{buildAppendSystemContext,buildToolRoutingHint,canvasToolBlockReason}from"./prompt-system-context.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{createCanvasRelayService}from"./canvas/relay-service.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_PRESENTATION_GATE,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,sharedAutomationTurnPolicy=new AutomationTurnPolicy;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 sharedMSConnectionStatus=new MSConnectionStatusSnapshot,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,_fullRuntimeRegistered=!1;const cachedCapabilityTools=[];let rawRegisterTool,wrappedRegisterTool;const ensureRegisterToolCache=e=>{rawRegisterTool||(rawRegisterTool=e.registerTool.bind(e),wrappedRegisterTool=e=>(cachedCapabilityTools.push(e),rawRegisterTool(e)),e.registerTool=wrappedRegisterTool)},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 sharedPluginContext=null,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){const t=e.runtime?.config?.current?.();let r;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),registerLocalFileExtractor(deliverableFileExtractor),e.logger.info?.("[beeos-claw] Upload deps + local file extractors 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(sharedAutomationTurnPolicy.disable(),"function"==typeof e.on)try{e.on("before_tool_call",(e,t)=>sharedAutomationTurnPolicy.check(e.toolName,{sessionKey:t?.sessionKey,runId:t?.runId??e.runId}),{priority:100}),sharedAutomationTurnPolicy.enable()}catch{e.logger.warn?.("[beeos-claw] Automation Wake disabled: tool guard unavailable")}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"}`);observeToolCallForDelivery(t,o);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,getMSConnectionStatus:e=>sharedMSConnectionStatus.get(e)})),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,...o.messageBus.enabled&&sharedAutomationTurnPolicy.isReady()&&i?{automationWake:!0}:{}},!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 u=sharedRuntimeOwner;sharedSessionMutationCoordinator??=new SessionMutationCoordinator(sharedSessionWriteGate,new OpenClawSessionWriteQuiescence(e.logger,{agentId:o.gateway.agentId||"main"}),e.logger,{onRecoveryRequired:t=>{scheduleProcessRuntimeRestart(t,e.logger)}});const d=sharedSessionMutationCoordinator,m={resolveProvider:async()=>{const t=e.runtime?.config?.current?.();if(!t)throw new Error("Conversation title OpenClaw config unavailable");return resolveOpenClawTitleProvider(t)},conversations:{update:async e=>{const t=sharedRuntimeAuthority?.authorityLease;if(!t||!o.messageBus.serviceUrl)throw new Error("Conversation title runtime lease unavailable");const r=await fetch(new URL(`/api/v1/runtime/conversations/${encodeURIComponent(e.conversationId)}`,o.messageBus.serviceUrl),{method:"PATCH",signal:AbortSignal.timeout(5e3),headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.runtimeLeaseCredential}`},body:JSON.stringify({title:e.title,titleSource:e.titleSource})});if(!r.ok)throw new Error(`Conversation title update HTTP ${r.status}`);return await r.json()}}};if(!sharedAcpServer){const t=sharedRuntimeAuthority&&o.agentGatewayUrl?new AgentGatewayTemplateOriginReporter({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:sharedRuntimeAuthority.boundAgentFetch}):void 0;sharedAcpServer=new ACPServer(o,e.logger,d,n,i,l,a,sharedTerminalManager,sharedRuntimeAuthority,void 0,sharedMcpProjection,sharedMcpCredentialVault,s,{gateway:u.client,titles:m,declareModel:async t=>{const r=e.runtime?.config;if(!r?.mutateConfigFile)throw new Error("OpenClaw model configuration runtime is unavailable");return await persistRequestedModel(r,t)},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})),runExclusive:(t,r,o)=>sharedImHotPathDeferral.runExclusive(t,r,e.logger,o)})}const p=sharedAcpServer,y=()=>sharedAgentMappings.snapshot()?.values().next().value;r.current=p,sharedMcpNativeProjectionNudge.request({registrationMode:e.registrationMode,fingerprint:fingerprintMcpServers(t.openclawConfig),logger:e.logger});const h=sharedPromptRunStore,{enabled:f,principalId:w,a2aEnabled:v}=o.messageBus;let C;o.agentGatewayUrl&&agentKeys&&(C=createTokenProvider({agentGatewayUrl:o.agentGatewayUrl,identity:agentKeyPairToIdentity(agentKeys)}));const b=process.env.MESSAGE_RUNTIME_DELIVERY_KEY?.trim(),R=w||process.env.BEEOS_PRINCIPAL_ID||"",S=C?sharedMessageClientOwner.configure({tokenProvider:C,identityId:R,...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,_=sharedRuntimeAuthority,E=o.messageBus.serviceUrl;if(S&&_&&E&&b){const e=S.nodeTransport;p.setCronConversationDelivery({deliver:async t=>{const r=_.authorityLease;if(!r)throw new Error("current runtime authority lease unavailable for cron delivery");const o=new MessageServiceCronDeliveryHttpAuthority({messageServiceUrl:E,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 O=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)}`)}),A=u.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})}:{},...S&&o.agentGatewayUrl&&s&&b?{transportAdapter:new RuntimeServiceTransportReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl,messageService:S.nodeTransport,messageClient:S.client,configuredServiceUrl:o.messageBus.serviceUrl,boundAgentFetch:s,scopedDeliveryKey:b})}:{},commandJournal:O,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 u.acquire(A);try{await p.start(o.bridge.url||void 0)}catch(e){throw await u.release(A),e}e.logger.info?.(`[beeos-claw] ACP Server started (total service start: ${Date.now()-t}ms)`)},async stop(){try{await u.release(A)}finally{await p.stop()}await O.checkpoint(),e.logger.info?.("[runtime-service] command journal checkpoint completed"),e.logger.info?.("[beeos-claw] ACP Server stopped")}});if(f&&void 0!==C&&C){const t=sharedMessageClientOwner.createRegistrationToken(),r=async(e,t)=>{const r=await C(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={leaseId:i.leaseId,runtimeEpoch:i.runtimeEpoch};sharedMSConnectionStatus.bind(c);const u=new Map,y=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 c=u.get(i.id);return c&&c.localAgentId===r||(c={localAgentId:r,inbound:createImInboundHandler({automationTurnPolicy:sharedAutomationTurnPolicy,titles:m,runtime:e.runtime,pluginConfig:e.pluginConfig,client:l,localServer:a(s,r,i.id),promptRunStore:h,waitIfExclusive:(t,r)=>sharedImHotPathDeferral.waitIfExclusive(t,e.logger,r).then(()=>{}),assistantMessageCommitRouter:sharedAssistantMessageCommitRouter,toolResultObservationRouter:sharedToolResultObservationRouter,modelCallObservationRouter:sharedModelCallObservationRouter,sessionMutationCoordinator:d,logger:e.logger,writeObs:n,principalIdOverride:s.principalId,activeRunAuthority:p.getActiveRunAuthority(),...o.agentGatewayUrl&&agentKeys?{completeA2ATask:(t,r,n)=>completeA2ATask({agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},t,r,n)}:{}})},u.set(i.id,c)),[{principalId:s.principalId,targetAgentId:i.id,runtimeEpoch:t.runtimeEpoch,inbound:c.inbound}]}):[]},executionJournal:g,logger:e.logger}),f=sharedAgentMappings.subscribe(()=>{sharedActiveMessageConsumer===y&&y.refresh()}),w=sharedRuntimeAuthority?.subscribeAuthority(()=>{sharedActiveMessageConsumer===y&&y.refresh()})??(()=>{}),v=new MessageClientTerminalStateRecovery({logger:e.logger,isCurrent:()=>{if(sharedActiveMessageConsumer!==y)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 C=l.getSnapshot().connection;sharedMSConnectionStatus.setIfCurrent(c,C);const b=l.subscribe(()=>{const t=l.getSnapshot();t.connection!==C&&(C=t.connection,sharedMSConnectionStatus.setIfCurrent(c,t.connection),"connected"===t.connection?(v.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"}`),v.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=y,y.start();try{await l.connect()}catch(e){throw sharedActiveMessageConsumer===y&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),v.dispose(),await y.stop(),f(),w(),b(),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===y&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),v.dispose(),await y.stop(),w(),b(),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 f?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=${f})`);if(!1!==v&&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,()=>y()?.instanceId),e.logger.info?.("[beeos-claw] A2A tools registered (beeos_discover_agents, beeos_call_agent) via REST")}if(o.agentGatewayUrl&&(registerAutomationTools(e,{agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys??void 0,logger:e.logger,getDefaultAgent:()=>{const e=y();return e?{id:e.id,instanceId:e.instanceId}:void 0},getCurrentConversationId:e=>{const t=p.getGatewaySessionKeyForToolCall(e);return t?extractBeeosConversationIdFromSessionKey(t):void 0}}),e.logger.info?.("[beeos-claw] Automation tools registered (create/create_webhook/capabilities/preflight/list/get/run/runs/get_run)")),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(createCanvasRelayService({getAgent:y,onceAgentReady:e=>sharedAgentMappings.onceReady(e),initRelay:n,stopRelay:()=>t.getRelayClient()?.disconnect(),logger:e.logger})),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),getActiveSessionId:e=>p.getSessionIdForToolCall(e),getPlatformAgentId:e=>p.getPlatformAgentIdForToolCall(e),getGatewaySessionKey:e=>p.getGatewaySessionKeyForToolCall(e),waitForAgentSync:()=>sharedAgentMappings.waitForSynchronization()},i=new FormSchemaRegistry,s=buildCronSchemas(a);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 u=p.getResourceRenderer();if(u){generateActionTools(l,e,async(e,t)=>p.gatewayRequest(e,t),u,e.logger)}const d=buildToolRoutingHint(BEEOS_FILE_RESOURCE_ROUTING_RULE,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME),m=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),rememberDeliveryWorkspace(t.sessionKey,t.workspaceDir)}const r=isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:t?.channelId});return t?.sessionKey||r?{appendSystemContext:buildAppendSystemContext({thirdParty:r,canvasSystemPrompt:`${CANVAS_PRESENTATION_GATE}\n\n${CANVAS_SYSTEM_PROMPT}`,toolRoutingHint:d})}:{appendSystemContext:d}}),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),!m.has(e.toolName))return;const r=t?.sessionKey?f.get(t.sessionKey):void 0,o=isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:r}),n=canvasToolBlockReason({thirdParty:o,hasSession:Boolean(t?.sessionKey)});return n?{block:!0,blockReason:n}:void 0}),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 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)return;const t=planPluginRegistration(e.registrationMode,_fullRuntimeRegistered);if("skip"!==t){_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");getOpenClawNativeSessions();const r=(t,r)=>{registerUploadTools(e,t,r),registerConnectorMcpProxy(e,t),registerArouterLatencyRouting(e),registerArouterMediaProviders(e)};if("capabilities"===t&&sharedPluginContext){if(cachedCapabilityTools.length>0){const t=resolveCapabilityToolReplay({currentRegisterTool:e.registerTool,wrappedRegisterTool:wrappedRegisterTool,originalRegisterTool:rawRegisterTool}),r=replayCachedPluginTools(r=>t.call(e,r),cachedCapabilityTools);return registerArouterLatencyRouting(e),registerArouterMediaProviders(e),void e.logger.info?.(`[beeos-claw] capability re-register mode=${e.registrationMode} tools_only count=${r}`)}const t={current:sharedAcpServer??null};return r(sharedPluginContext,t),registerServices(e,sharedPluginContext,t),void e.logger.info?.(`[beeos-claw] capability re-register mode=${e.registrationMode} capabilities_only`)}const o=initializePlugin(e);if(!o)return;const n={current:null};installUnhandledRejectionObserver(e,o.writeObs,n),ensureRegisterToolCache(e),cachedCapabilityTools.length=0,r(o,n),agentKeys&&o.cfg.agentGatewayUrl&&(e.on?e.on("before_agent_finalize",fileDeliveryFinalizeDecision):e.logger.warn?.("[file-delivery] OpenClaw plugin hooks unavailable; finalization check disabled")),registerServices(e,o,n),sharedPluginContext=o,marksFullRuntimeRegistered(e.registrationMode,t)&&(_fullRuntimeRegistered=!0),_pluginRegisteredAt=Date.now(),e.logger.info?.("[beeos-claw] Plugin registered successfully")}finally{_initializing=!1}}else e.logger.info?.(`[beeos-claw] skip re-register mode=${e.registrationMode??"full"} already_registered`)}};export default plugin;
1
+ import{resolveOpenClawTitleProvider}from"./runtime/openclaw-title-provider.js";import{persistRequestedModel}from"./runtime/requested-model.js";import{fileDeliveryFinalizeDecision}from"./upload/finalize-hook.js";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{getOpenClawNativeSessions}from"./runtime/openclaw-native-sessions.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{OpenClawQuestionAdapter,observeQuestionTool}from"./runtime/openclaw-question-adapter.js";import{AutomationTurnPolicy}from"./automation/turn-policy.js";import{registerAutomationTools}from"./automation/tools.js";import{extractBeeosConversationIdFromSessionKey}from"./runtime/openclaw-session-key-resolution.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{MSConnectionStatusSnapshot}from"./runtime/ms-connection-status.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{deliverableFileExtractor}from"./upload/deliverable-extractor.js";import{observeToolCallForDelivery,rememberDeliveryWorkspace}from"./upload/delivery-observe.js";import{BEEOS_FILE_RESOURCE_ROUTING_RULE}from"./files/beeos-file-uri.js";import{marksFullRuntimeRegistered,planPluginRegistration,replayCachedPluginTools,resolveCapabilityToolReplay}from"./plugin-registration-plan.js";import{buildAppendSystemContext,buildToolRoutingHint,canvasToolBlockReason}from"./prompt-system-context.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{createCanvasRelayService}from"./canvas/relay-service.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_PRESENTATION_GATE,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,sharedAutomationTurnPolicy=new AutomationTurnPolicy;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 sharedMSConnectionStatus=new MSConnectionStatusSnapshot,sharedAgentMappings=new AgentMappingRegistry;let sharedGatewayClient;const sharedQuestionAdapters=new Set;let sharedRuntimeOwner,sharedAgentProjection,sharedRuntimeAgentCapabilities,sharedMcpProjection,sharedMcpCredentialVault;const sharedMessageClientOwner=new BeeosClawMessageClientProcessOwner;let sharedActiveMessageConsumer,sharedInboundExecutionJournal;const sharedTerminalManager=new TerminalManager;let _initializing=!1,_pluginRegisteredAt=0,_fullRuntimeRegistered=!1;const cachedCapabilityTools=[];let rawRegisterTool,wrappedRegisterTool;const ensureRegisterToolCache=e=>{rawRegisterTool||(rawRegisterTool=e.registerTool.bind(e),wrappedRegisterTool=e=>(cachedCapabilityTools.push(e),rawRegisterTool(e)),e.registerTool=wrappedRegisterTool)},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 sharedPluginContext=null,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){const t=e.runtime?.config?.current?.();let r;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),registerLocalFileExtractor(deliverableFileExtractor),e.logger.info?.("[beeos-claw] Upload deps + local file extractors 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(sharedAutomationTurnPolicy.disable(),"function"==typeof e.on)try{e.on("before_tool_call",(e,t)=>sharedAutomationTurnPolicy.check(e.toolName,{sessionKey:t?.sessionKey,runId:t?.runId??e.runId}),{priority:100}),sharedAutomationTurnPolicy.enable()}catch{e.logger.warn?.("[beeos-claw] Automation Wake disabled: tool guard unavailable")}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"}`);observeToolCallForDelivery(t,o);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,getMSConnectionStatus:e=>sharedMSConnectionStatus.get(e)})),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,...o.messageBus.enabled&&sharedAutomationTurnPolicy.isReady()&&i?{automationWake:!0}:{}},!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;e.on?.("before_tool_call",(e,t)=>{for(const r of sharedQuestionAdapters)observeQuestionTool(r,e,t)}),sharedSessionMutationCoordinator??=new SessionMutationCoordinator(sharedSessionWriteGate,new OpenClawSessionWriteQuiescence(e.logger,{agentId:o.gateway.agentId||"main"}),e.logger,{onRecoveryRequired:t=>{scheduleProcessRuntimeRestart(t,e.logger)}});const u=sharedSessionMutationCoordinator,p={resolveProvider:async()=>{const t=e.runtime?.config?.current?.();if(!t)throw new Error("Conversation title OpenClaw config unavailable");return resolveOpenClawTitleProvider(t)},conversations:{update:async e=>{const t=sharedRuntimeAuthority?.authorityLease;if(!t||!o.messageBus.serviceUrl)throw new Error("Conversation title runtime lease unavailable");const r=await fetch(new URL(`/api/v1/runtime/conversations/${encodeURIComponent(e.conversationId)}`,o.messageBus.serviceUrl),{method:"PATCH",signal:AbortSignal.timeout(5e3),headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.runtimeLeaseCredential}`},body:JSON.stringify({title:e.title,titleSource:e.titleSource})});if(!r.ok)throw new Error(`Conversation title update HTTP ${r.status}`);return await r.json()}}};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,titles:p,declareModel:async t=>{const r=e.runtime?.config;if(!r?.mutateConfigFile)throw new Error("OpenClaw model configuration runtime is unavailable");return await persistRequestedModel(r,t)},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})),runExclusive:(t,r,o)=>sharedImHotPathDeferral.runExclusive(t,r,e.logger,o)})}const m=sharedAcpServer,y=()=>sharedAgentMappings.snapshot()?.values().next().value;r.current=m,sharedMcpNativeProjectionNudge.request({registrationMode:e.registrationMode,fingerprint:fingerprintMcpServers(t.openclawConfig),logger:e.logger});const h=sharedPromptRunStore,{enabled:f,principalId:w,a2aEnabled:v}=o.messageBus;let C;o.agentGatewayUrl&&agentKeys&&(C=createTokenProvider({agentGatewayUrl:o.agentGatewayUrl,identity:agentKeyPairToIdentity(agentKeys)}));const b=process.env.MESSAGE_RUNTIME_DELIVERY_KEY?.trim(),R=w||process.env.BEEOS_PRINCIPAL_ID||"",_=C?sharedMessageClientOwner.configure({tokenProvider:C,identityId:R,...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,S=sharedRuntimeAuthority,E=o.messageBus.serviceUrl;if(_&&S&&E&&b){const e=_.nodeTransport;m.setCronConversationDelivery({deliver:async t=>{const r=S.authorityLease;if(!r)throw new Error("current runtime authority lease unavailable for cron delivery");const o=new MessageServiceCronDeliveryHttpAuthority({messageServiceUrl:E,fetchAuthorized:async(t,o)=>await e.fetchRuntimeChannel(t,{runtimeLeaseCredential:r.runtimeLeaseCredential},o)});return await o.deliver({...t,runtimeLeaseCredential:r.runtimeLeaseCredential})}})}else m.setCronConversationDelivery(void 0);const O=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)}`)}),A=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:O,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),m.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(A);try{await m.start(o.bridge.url||void 0)}catch(e){throw await d.release(A),e}e.logger.info?.(`[beeos-claw] ACP Server started (total service start: ${Date.now()-t}ms)`)},async stop(){try{await d.release(A)}finally{await m.stop()}await O.checkpoint(),e.logger.info?.("[runtime-service] command journal checkpoint completed"),e.logger.info?.("[beeos-claw] ACP Server stopped")}});if(f&&void 0!==C&&C){const t=sharedMessageClientOwner.createRegistrationToken(),r=async(e,t)=>{const r=await C(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)=>{m.ensureImInboundSession(e,t,r,o)},chatAbort:(e,t)=>m.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 OpenClawQuestionAdapter({gateway:sharedGatewayClient,client:l,logger:e.logger,assertDeliveryCurrent:e=>{const t=sharedRuntimeAuthority?.authorityLease,r="object"!=typeof e.deliveryContext||null===e.deliveryContext||Array.isArray(e.deliveryContext)?void 0:e.deliveryContext,o=sharedAgentMappings.snapshot();if(!(t&&r&&t.instanceId===s.principalId&&t.runtimeEpoch===s.runtimeEpoch&&r.delivery_principal===s.principalId&&r.runtime_epoch===s.runtimeEpoch&&o&&[...o.values()].some(e=>e.id===r.target_agent_id&&e.instanceId===s.principalId)))throw new Error("Question receipt authority is no longer current")}});c.start(),sharedQuestionAdapters.add(c);const d={leaseId:i.leaseId,runtimeEpoch:i.runtimeEpoch};sharedMSConnectionStatus.bind(d);const y=new Map,f=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=y.get(i.id);return g&&g.localAgentId===r||(g={localAgentId:r,inbound:createImInboundHandler({questionAdapter:c,questionAuthority:{runtimeEpoch:s.runtimeEpoch,assertCurrent:()=>{const e=sharedRuntimeAuthority?.authorityLease,t=sharedAgentMappings.snapshot()?.get(r);if(!e||e.instanceId!==s.principalId||e.runtimeEpoch!==s.runtimeEpoch||t?.id!==i.id||t.instanceId!==s.principalId)throw new Error("Question source authority is no longer current")}},automationTurnPolicy:sharedAutomationTurnPolicy,titles:p,runtime:e.runtime,pluginConfig:e.pluginConfig,client:l,localServer:a(s,r,i.id),promptRunStore:h,waitIfExclusive:(t,r)=>sharedImHotPathDeferral.waitIfExclusive(t,e.logger,r).then(()=>{}),assistantMessageCommitRouter:sharedAssistantMessageCommitRouter,toolResultObservationRouter:sharedToolResultObservationRouter,modelCallObservationRouter:sharedModelCallObservationRouter,sessionMutationCoordinator:u,logger:e.logger,writeObs:n,principalIdOverride:s.principalId,activeRunAuthority:m.getActiveRunAuthority(),...o.agentGatewayUrl&&agentKeys?{completeA2ATask:(t,r,n)=>completeA2ATask({agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},t,r,n)}:{}})},y.set(i.id,g)),[{principalId:s.principalId,targetAgentId:i.id,runtimeEpoch:t.runtimeEpoch,inbound:g.inbound}]}):[]},executionJournal:g,logger:e.logger}),w=sharedAgentMappings.subscribe(()=>{sharedActiveMessageConsumer===f&&f.refresh()}),v=sharedRuntimeAuthority?.subscribeAuthority(()=>{sharedActiveMessageConsumer===f&&f.refresh()})??(()=>{}),C=new MessageClientTerminalStateRecovery({logger:e.logger,isCurrent:()=>{if(sharedActiveMessageConsumer!==f)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 m.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient recovery: ${t instanceof Error?t.message:String(t)}`)}}}});let b=l.getSnapshot().connection;sharedMSConnectionStatus.setIfCurrent(d,b);const R=l.subscribe(()=>{const t=l.getSnapshot();t.connection!==b&&(b=t.connection,sharedMSConnectionStatus.setIfCurrent(d,t.connection),"connected"===t.connection?(C.notifyConnected(),e.logger.info?.(`[beeos-claw] connection transport=message_service_realtime state=connected localAgentId=${m.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"}`),C.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=f,f.start();try{await l.connect()}catch(e){throw sharedActiveMessageConsumer===f&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(d),C.dispose(),sharedQuestionAdapters.delete(c),await c.stop(),await f.stop(),w(),v(),R(),await l.disconnect().catch(()=>{}),e}try{await m.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===f&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(d),C.dispose(),sharedQuestionAdapters.delete(c),await c.stop(),await f.stop(),v(),R(),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 f?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=${f})`);if(!1!==v&&o.agentGatewayUrl){const t=e=>m.getGatewaySessionKeyForToolCall(e),r=e=>m.getMentionsForToolCall(e),n=(e,t)=>{m.broadcastNotification(e,t)},a=new TaskHandleRegistry;registerA2ATools(e,{agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},a,t,r,n,()=>y()?.instanceId),e.logger.info?.("[beeos-claw] A2A tools registered (beeos_discover_agents, beeos_call_agent) via REST")}if(o.agentGatewayUrl&&(registerAutomationTools(e,{agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys??void 0,logger:e.logger,getDefaultAgent:()=>{const e=y();return e?{id:e.id,instanceId:e.instanceId}:void 0},getCurrentConversationId:e=>{const t=m.getGatewaySessionKeyForToolCall(e);return t?extractBeeosConversationIdFromSessionKey(t):void 0}}),e.logger.info?.("[beeos-claw] Automation tools registered (create/create_webhook/capabilities/preflight/list/get/run/runs/get_run)")),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:()=>m.flushPendingCanvasLinks(),onReconnect:()=>m.onRelayReconnected(),logger:e.logger});t.setRelayClient(a),t.setAgentId(n.id),m.wireRelayActionHandler(),a.connect(),e.logger.info?.(`[beeos-claw] Canvas Relay multiplexed client connected (agentId=${n.id}, name=${n.name??"none"})`)};e.registerService(createCanvasRelayService({getAgent:y,onceAgentReady:e=>sharedAgentMappings.onceReady(e),initRelay:n,stopRelay:()=>t.getRelayClient()?.disconnect(),logger:e.logger})),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),getActiveSessionId:e=>m.getSessionIdForToolCall(e),getPlatformAgentId:e=>m.getPlatformAgentIdForToolCall(e),getGatewaySessionKey:e=>m.getGatewaySessionKeyForToolCall(e),waitForAgentSync:()=>sharedAgentMappings.waitForSynchronization()},i=new FormSchemaRegistry,s=buildCronSchemas(a);i.register(s.create),i.register(s.update);const l=new ResourceDescriptorRegistry;l.register(CRON_RESOURCE);const c=e=>async(t,r,o)=>(m.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))}),m.setCanvasState(t,{schemaRegistry:i,descriptorRegistry:l});const g=m.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=m.getResourceRenderer();if(d){generateActionTools(l,e,async(e,t)=>m.gatewayRequest(e,t),d,e.logger)}const u=buildToolRoutingHint(BEEOS_FILE_RESOURCE_ROUTING_RULE,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME),p=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),rememberDeliveryWorkspace(t.sessionKey,t.workspaceDir)}const r=isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:t?.channelId});return t?.sessionKey||r?{appendSystemContext:buildAppendSystemContext({thirdParty:r,canvasSystemPrompt:`${CANVAS_PRESENTATION_GATE}\n\n${CANVAS_SYSTEM_PROMPT}`,toolRoutingHint:u})}:{appendSystemContext: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&&m.setPendingToolCallSessionKey(t.sessionKey),!p.has(e.toolName))return;const r=t?.sessionKey?f.get(t.sessionKey):void 0,o=isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:r}),n=canvasToolBlockReason({thirdParty:o,hasSession:Boolean(t?.sessionKey)});return n?{block:!0,blockReason:n}:void 0}),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 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)return;const t=planPluginRegistration(e.registrationMode,_fullRuntimeRegistered);if("skip"!==t){_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");getOpenClawNativeSessions();const r=(t,r)=>{registerUploadTools(e,t,r),registerConnectorMcpProxy(e,t),registerArouterLatencyRouting(e),registerArouterMediaProviders(e)};if("capabilities"===t&&sharedPluginContext){if(cachedCapabilityTools.length>0){const t=resolveCapabilityToolReplay({currentRegisterTool:e.registerTool,wrappedRegisterTool:wrappedRegisterTool,originalRegisterTool:rawRegisterTool}),r=replayCachedPluginTools(r=>t.call(e,r),cachedCapabilityTools);return registerArouterLatencyRouting(e),registerArouterMediaProviders(e),void e.logger.info?.(`[beeos-claw] capability re-register mode=${e.registrationMode} tools_only count=${r}`)}const t={current:sharedAcpServer??null};return r(sharedPluginContext,t),registerServices(e,sharedPluginContext,t),void e.logger.info?.(`[beeos-claw] capability re-register mode=${e.registrationMode} capabilities_only`)}const o=initializePlugin(e);if(!o)return;const n={current:null};installUnhandledRejectionObserver(e,o.writeObs,n),ensureRegisterToolCache(e),cachedCapabilityTools.length=0,r(o,n),agentKeys&&o.cfg.agentGatewayUrl&&(e.on?e.on("before_agent_finalize",fileDeliveryFinalizeDecision):e.logger.warn?.("[file-delivery] OpenClaw plugin hooks unavailable; finalization check disabled")),registerServices(e,o,n),sharedPluginContext=o,marksFullRuntimeRegistered(e.registrationMode,t)&&(_fullRuntimeRegistered=!0),_pluginRegisteredAt=Date.now(),e.logger.info?.("[beeos-claw] Plugin registered successfully")}finally{_initializing=!1}}else e.logger.info?.(`[beeos-claw] skip re-register mode=${e.registrationMode??"full"} already_registered`)}};export default plugin;
@@ -1 +1 @@
1
- import{deterministicReplyMessageId}from"./im-runtime-dispatch.js";import{logChatLatency}from"./chat-latency.js";export class MessageProjectionInboundConsumer{delivered=new Set;cancelledTargets=new Set;inFlightMessages=new Set;inFlightCancels=new Set;eligibleMessages=new Set;transportContext=new Map;client;bindings;logger;executionJournal;maxConcurrentConversations;conversationQueue=[];queuedConversations=new Set;activeConversations=new Set;rerunConversations=new Set;laneTasks=new Set;cancelTasks=new Set;unsubscribeProjection;unsubscribeRealtime;previousConnection;activeConversationCount=0;started=!1;constructor(e){this.client=e.client,this.bindings=e.bindings,this.logger=e.logger,this.executionJournal=e.executionJournal,this.previousConnection=e.client.getSnapshot().connection,this.maxConcurrentConversations=Math.max(1,Math.floor(e.maxConcurrentConversations??16))}start(){this.started||(this.started=!0,this.unsubscribeRealtime=this.client.listen({eventTypes:["message.created"]},e=>this.admitRealtimeMessage(e)),this.previousConnection=this.client.getSnapshot().connection,this.unsubscribeProjection=this.client.subscribe(()=>{const e=this.client.getSnapshot().connection,t="connected"===e&&"connected"!==this.previousConnection;this.previousConnection=e,t&&this.recoverDurableSnapshot()}),"connected"===this.previousConnection&&this.recoverDurableSnapshot())}async stop(){this.started=!1,this.unsubscribeProjection?.(),this.unsubscribeProjection=void 0,this.unsubscribeRealtime?.(),this.unsubscribeRealtime=void 0,this.conversationQueue.length=0,this.queuedConversations.clear(),this.rerunConversations.clear(),this.eligibleMessages.clear(),this.transportContext.clear(),await Promise.allSettled([...this.laneTasks,...this.cancelTasks])}refresh(){this.recoverDurableSnapshot()}admitRealtimeMessage(e){if(!this.started||"message.created"!==e.type)return;const{instanceId:t,agentId:n,runtimeEpoch:i,conversationId:s,messageId:o}=e.scope;if(!(t&&n&&i&&s&&o))return;if(!this.bindings().find(e=>e.principalId===t&&e.targetAgentId===n&&e.runtimeEpoch===i))return;const r=e.data.message;if(r.id!==o||r.conversationId!==s)return;const a=this.client.getSnapshot().messages[o];if(!a||!isInbound(a)||a.conversationId!==s||a.senderId!==r.senderId||!isCanonicalInboundPublisher(a,e))return;const c=`${s}:${o}`,d=validRequestId(e.correlation.requestId),l=Date.now();d&&this.transportContext.set(c,{requestId:d,admittedAtMs:l,requestIdSource:"realtime_correlation"}),logChatLatency(this.logger,"claw.inbound.realtime_admitted",d??o,l,{request_id_source:d?"realtime_correlation":"message_id_fallback"}),this.eligibleMessages.add(c),this.schedule()}recoverDurableSnapshot(){if(this.started){for(const e of Object.values(this.client.getSnapshot().messages))isInbound(e)&&isRecoverableInboundPublisher(e)&&this.eligibleMessages.add(`${e.conversationId}:${e.id}`);this.schedule()}}schedule(){if(!this.started)return;const e=Object.values(this.client.getSnapshot().messages).filter(isInbound).sort(compareProjectionOrder),t=e.filter(e=>this.eligibleMessages.has(`${e.conversationId}:${e.id}`));for(const n of t)"chat_cancel"===n.type&&this.dispatchCancel(n,e);for(const e of new Set(t.filter(e=>"chat_message"===e.type).map(e=>e.conversationId)))this.enqueueConversation(e);this.pumpConversationQueue()}enqueueConversation(e){this.activeConversations.has(e)?this.rerunConversations.add(e):this.queuedConversations.has(e)||(this.queuedConversations.add(e),this.conversationQueue.push(e))}pumpConversationQueue(){for(;this.started&&this.activeConversationCount<this.maxConcurrentConversations&&this.conversationQueue.length>0;){const e=this.conversationQueue.shift();this.queuedConversations.delete(e),this.activeConversations.add(e),this.activeConversationCount+=1;const t=this.drainConversation(e).catch(t=>{this.logger.warn?.(`[beeos-claw] projected inbound lane failed conversation=${e}: ${t instanceof Error?t.message:String(t)}`)}).finally(()=>{this.laneTasks.delete(t),this.activeConversations.delete(e),this.activeConversationCount-=1,this.rerunConversations.delete(e)&&this.started&&this.enqueueConversation(e),this.pumpConversationQueue()});this.laneTasks.add(t)}}async drainConversation(e){const t=Object.values(this.client.getSnapshot().messages).filter(t=>"chat_message"===t.type&&t.conversationId===e&&this.eligibleMessages.has(`${t.conversationId}:${t.id}`)).sort(compareProjectionOrder);for(const e of t){if(!this.started)break;const t=resolveExecutionBinding(e,this.bindings());if(!t)continue;const n=`${e.conversationId}:${e.id}`;if(this.cancelledTargets.has(n)){this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n);continue}if(this.delivered.has(n)||this.inFlightMessages.has(n))continue;const i=await this.reconcileRequest(e,t);if(!this.started)break;if("handled"===i){this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n);continue}if("pending"===i)break;if(!sameBinding(t,resolveExecutionBinding(e,this.bindings())))break;let s;try{s=await this.executionJournal.claim(n)}catch(t){this.logger.warn?.(`[beeos-claw] inbound execution claim failed conversation=${e.conversationId} message_id=${e.id}: ${t instanceof Error?t.message:String(t)}`);break}if(!this.started)break;if("existing"===s){this.logger.warn?.(`[beeos-claw] projected inbound skip claimed conversation=${e.conversationId} message_id=${e.id}`),this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n);continue}if(!sameBinding(t,resolveExecutionBinding(e,this.bindings())))break;this.inFlightMessages.add(n);try{const i=this.transportContext.get(n);i&&logChatLatency(this.logger,"claw.inbound.execution_claimed",i.requestId,i.admittedAtMs,{request_id_source:i.requestIdSource}),await t.inbound.onChatMessage(i?{...e,requestId:i.requestId}:e),this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n)}catch(t){this.logger.warn?.(`[beeos-claw] projected inbound handler failed conversation=${e.conversationId} message_id=${e.id}: ${t instanceof Error?t.message:String(t)}`);break}finally{this.inFlightMessages.delete(n)}}}dispatchCancel(e,t){const n=`${e.conversationId}:${e.id}`;if(this.delivered.has(n)||this.inFlightCancels.has(n))return;const i=resolveCancelTarget(e,t,this.bindings());if(!i)return;this.cancelledTargets.add(`${i.message.conversationId}:${i.message.id}`),this.inFlightCancels.add(n);const s=i.binding.inbound.onChatCancel(e).then(()=>{this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n)}).catch(t=>{this.logger.warn?.(`[beeos-claw] projected inbound cancel failed conversation=${e.conversationId} message_id=${e.id}: ${t instanceof Error?t.message:String(t)}`)}).finally(()=>{this.cancelTasks.delete(s),this.inFlightCancels.delete(n)});this.cancelTasks.add(s)}async reconcileRequest(e,t){const n=Object.values(this.client.getSnapshot().messages),i=n.filter(n=>isReplyFor(n,e,t.targetAgentId));if(i.some(isTerminalReply))return"handled";if(i.length>0)return"pending";if(n.some(n=>isReasoningFor(n,e,t.targetAgentId)))return"pending";const s=deterministicReplyMessageId("asst",e.conversationId,e.id,t.principalId);try{const t=await this.client.messages.getEnvelope(e.conversationId,s);return"agent_reply"!==t.type||t.conversationId!==e.conversationId||void 0!==t.replyTo&&t.replyTo.length>0&&t.replyTo!==e.id?(this.logger.warn?.(`[beeos-claw] deterministic reply reconciliation mismatch conversation=${e.conversationId} message_id=${e.id} reply_id=${s}`),"pending"):isTerminalReply(t)?"handled":"pending"}catch(t){return 404===httpStatus(t)?"execute":(this.logger.warn?.(`[beeos-claw] deterministic reply reconciliation failed conversation=${e.conversationId} message_id=${e.id}: ${t instanceof Error?t.message:String(t)}`),"pending")}}}function isInbound(e){return"chat_message"===e.type||"chat_cancel"===e.type}function isCanonicalUserPublisher(e){return"string"==typeof e.senderId&&e.senderId.startsWith("user:")&&e.senderId.length>5}function isCanonicalA2ACommand(e){const t=asRecord(asRecord(e.content)?.metadata);return"chat_message"===e.type&&"a2a"===t?.protocol&&"string"==typeof e.senderId&&e.senderId.length>0}function isCanonicalInboundPublisher(e,t){return"user"===t.actor.kind?t.actor.id===e.senderId&&isCanonicalUserPublisher(e):"service"===t.actor.kind&&"message-service"===t.actor.id&&isCanonicalA2ACommand(e)}function isRecoverableInboundPublisher(e){return isCanonicalUserPublisher(e)||isCanonicalA2ACommand(e)}function resolveExecutionBinding(e,t){if(!e.id)return;const n=asRecord(e.content),i=asRecord(n?.metadata),s=nonEmptyString(i?.delivery_principal),o=nonEmptyString(i?.target_agent_id),r=epochValue(i?.runtime_epoch);if(s&&o&&void 0!==r)for(const e of t){if(e.principalId!==s)continue;if(e.targetAgentId!==o)continue;if(epochValue(e.runtimeEpoch)===r)return e}}function epochValue(e){const t=nonEmptyString(e);if(void 0!==t&&/^\d+$/.test(t))return BigInt(t)}function sameBinding(e,t){return void 0!==t&&t.principalId===e.principalId&&t.targetAgentId===e.targetAgentId&&t.runtimeEpoch===e.runtimeEpoch&&t.inbound===e.inbound}function resolveCancelTarget(e,t,n){const i=nonEmptyString(asRecord(e.content)?.target_message_id);if(!i)return;const s=t.find(t=>"chat_message"===t.type&&t.id===i&&t.conversationId===e.conversationId);if(!s)return;const o=resolveExecutionBinding(s,n),r=resolveExecutionBinding(e,n);return o&&r&&o.principalId===r.principalId&&o.targetAgentId===r.targetAgentId&&o.runtimeEpoch===r.runtimeEpoch?{message:s,binding:o}:void 0}function isReplyFor(e,t,n){if("agent_reply"!==e.type||e.conversationId!==t.conversationId||e.replyTo!==t.id)return!1;if(e.senderId===n)return!0;if(!isTerminalReply(e))return!1;const i=nonEmptyString(asRecord(asRecord(t.content)?.metadata)?.delivery_principal);return void 0!==i&&e.senderId===i}function isReasoningFor(e,t,n){return"agent_reasoning"===e.type&&e.conversationId===t.conversationId&&e.replyTo===t.id&&e.senderId===n}function isTerminalReply(e){return"agent_reply"===e.type&&("completed"===e.state||"failed"===e.state||"refused"===e.state||"cancelled"===e.state)}function httpStatus(e){return null!==e&&"object"==typeof e&&"status"in e&&"number"==typeof e.status?e.status:void 0}function asRecord(e){return null===e||"object"!=typeof e||Array.isArray(e)?void 0:e}function nonEmptyString(e){return"string"==typeof e&&e.length>0?e:void 0}function validRequestId(e){if(!("string"!=typeof e||0===e.length||e.length>128))return/^[A-Za-z0-9._:-]+$/.test(e)?e:void 0}function compareProjectionOrder(e,t){try{const n=BigInt(e.offset)-BigInt(t.offset);if(0n!==n)return n<0n?-1:1}catch{const n=e.offset.localeCompare(t.offset);if(0!==n)return n}return e.id.localeCompare(t.id)}
1
+ import{deterministicReplyMessageId}from"./im-runtime-dispatch.js";import{logChatLatency}from"./chat-latency.js";export class MessageProjectionInboundConsumer{delivered=new Set;cancelledTargets=new Set;inFlightMessages=new Set;inFlightCancels=new Set;eligibleMessages=new Set;transportContext=new Map;client;bindings;logger;executionJournal;maxConcurrentConversations;conversationQueue=[];queuedConversations=new Set;activeConversations=new Set;rerunConversations=new Set;laneTasks=new Set;controlTails=new Map;recoveringControls=new Map;cancelTasks=new Set;unsubscribeProjection;unsubscribeRealtime;previousConnection;activeConversationCount=0;started=!1;constructor(e){this.client=e.client,this.bindings=e.bindings,this.logger=e.logger,this.executionJournal=e.executionJournal,this.previousConnection=e.client.getSnapshot().connection,this.maxConcurrentConversations=Math.max(1,Math.floor(e.maxConcurrentConversations??16))}start(){this.started||(this.started=!0,this.unsubscribeRealtime=this.client.listen({eventTypes:["message.created","message.terminal"]},e=>this.admitRealtimeMessage(e)),this.previousConnection=this.client.getSnapshot().connection,this.unsubscribeProjection=this.client.subscribe(()=>{const e=this.client.getSnapshot().connection,t="connected"===e&&"connected"!==this.previousConnection;this.previousConnection=e,t?this.recoverDurableSnapshot():this.schedule()}),"connected"===this.previousConnection&&this.recoverDurableSnapshot())}async stop(){this.started=!1,this.unsubscribeProjection?.(),this.unsubscribeProjection=void 0,this.unsubscribeRealtime?.(),this.unsubscribeRealtime=void 0,this.conversationQueue.length=0,this.queuedConversations.clear(),this.rerunConversations.clear(),this.eligibleMessages.clear(),this.transportContext.clear(),await Promise.allSettled([...this.laneTasks,...this.cancelTasks]),this.controlTails.clear(),this.recoveringControls.clear()}refresh(){this.recoverDurableSnapshot()}admitRealtimeMessage(e){if(!this.started||"message.created"!==e.type&&"message.terminal"!==e.type)return;const{instanceId:t,agentId:n,runtimeEpoch:i,conversationId:s,messageId:o}=e.scope;if(!(t&&n&&i&&s&&o))return;if(!this.bindings().find(e=>e.principalId===t&&e.targetAgentId===n&&e.runtimeEpoch===i))return;const r=e.data.message;if(r.id!==o||r.conversationId!==s)return;const a=this.client.getSnapshot().messages[o];if(!a||!isInbound(a)||a.conversationId!==s||a.senderId!==r.senderId||!isCanonicalInboundPublisher(a,e))return;const c=`${s}:${o}`,d=validRequestId(e.correlation.requestId),l=Date.now();d&&this.transportContext.set(c,{requestId:d,admittedAtMs:l,requestIdSource:"realtime_correlation"}),logChatLatency(this.logger,"claw.inbound.realtime_admitted",d??o,l,{request_id_source:d?"realtime_correlation":"message_id_fallback"}),this.eligibleMessages.add(c),this.schedule()}recoverDurableSnapshot(){if(this.started){for(const e of Object.values(this.client.getSnapshot().messages))isInbound(e)&&isRecoverableInboundPublisher(e)&&this.eligibleMessages.add(`${e.conversationId}:${e.id}`);this.schedule()}}schedule(){if(!this.started)return;const e=Object.values(this.client.getSnapshot().messages).filter(isInbound).sort(compareProjectionOrder),t=e.filter(e=>this.eligibleMessages.has(`${e.conversationId}:${e.id}`));for(const n of t)"chat_cancel"===n.type&&this.dispatchCancel(n,e),"user.continue"===n.type&&this.dispatchContinue(n);for(const e of new Set(t.filter(e=>"chat_message"===e.type).map(e=>e.conversationId)))this.enqueueConversation(e);this.pumpConversationQueue()}enqueueConversation(e){this.activeConversations.has(e)?this.rerunConversations.add(e):this.queuedConversations.has(e)||(this.queuedConversations.add(e),this.conversationQueue.push(e))}pumpConversationQueue(){for(;this.started&&this.activeConversationCount<this.maxConcurrentConversations&&this.conversationQueue.length>0;){const e=this.conversationQueue.shift();this.queuedConversations.delete(e),this.activeConversations.add(e),this.activeConversationCount+=1;const t=this.drainConversation(e).catch(t=>{this.logger.warn?.(`[beeos-claw] projected inbound lane failed conversation=${e}: ${t instanceof Error?t.message:String(t)}`)}).finally(()=>{this.laneTasks.delete(t),this.activeConversations.delete(e),this.activeConversationCount-=1,this.rerunConversations.delete(e)&&this.started&&this.enqueueConversation(e),this.pumpConversationQueue()});this.laneTasks.add(t)}}async drainConversation(e){const t=Object.values(this.client.getSnapshot().messages).filter(t=>"chat_message"===t.type&&t.conversationId===e&&this.eligibleMessages.has(`${t.conversationId}:${t.id}`)).sort(compareProjectionOrder);for(const e of t){if(!this.started)break;const t=resolveExecutionBinding(e,this.bindings());if(!t)continue;const n=`${e.conversationId}:${e.id}`;if(this.cancelledTargets.has(n)){this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n);continue}if(this.delivered.has(n)||this.inFlightMessages.has(n))continue;const i=await this.reconcileRequest(e,t);if(!this.started)break;if("handled"===i){this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n);continue}if("pending"===i)break;if(!sameBinding(t,resolveExecutionBinding(e,this.bindings())))break;let s;try{s=await this.executionJournal.claim(n)}catch(t){this.logger.warn?.(`[beeos-claw] inbound execution claim failed conversation=${e.conversationId} message_id=${e.id}: ${t instanceof Error?t.message:String(t)}`);break}if(!this.started)break;if("existing"===s){this.logger.warn?.(`[beeos-claw] projected inbound skip claimed conversation=${e.conversationId} message_id=${e.id}`),this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n);continue}if(!sameBinding(t,resolveExecutionBinding(e,this.bindings())))break;this.inFlightMessages.add(n);try{const i=this.transportContext.get(n);i&&logChatLatency(this.logger,"claw.inbound.execution_claimed",i.requestId,i.admittedAtMs,{request_id_source:i.requestIdSource}),await t.inbound.onChatMessage(i?{...e,requestId:i.requestId}:e),this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n)}catch(t){this.logger.warn?.(`[beeos-claw] projected inbound handler failed conversation=${e.conversationId} message_id=${e.id}: ${t instanceof Error?t.message:String(t)}`);break}finally{this.inFlightMessages.delete(n)}}}dispatchCancel(e,t){const n=`${e.conversationId}:${e.id}`;if(this.delivered.has(n)||this.inFlightCancels.has(n))return;const i=resolveCancelTarget(e,t,this.bindings());if(!i)return;this.cancelledTargets.add(`${i.message.conversationId}:${i.message.id}`),this.inFlightCancels.add(n);const s=i.binding.inbound.onChatCancel(e).then(()=>{this.delivered.add(n),this.eligibleMessages.delete(n),this.transportContext.delete(n)}).catch(t=>{this.logger.warn?.(`[beeos-claw] projected inbound cancel failed conversation=${e.conversationId} message_id=${e.id}: ${t instanceof Error?t.message:String(t)}`)}).finally(()=>{this.cancelTasks.delete(s),this.inFlightCancels.delete(n)});this.cancelTasks.add(s)}dispatchContinue(e){const t=`${e.conversationId}:${e.id}`;if(this.delivered.has(t)||this.inFlightCancels.has(t))return;const n=resolveExecutionBinding(e,this.bindings());if(!n)return;const i=this.client.getSnapshot().hydrationByConversation[e.conversationId];if(!i||i.historyGeneration!==e.historyGeneration||!i.complete||BigInt(i.latestOffset)<BigInt(e.offset))return void this.recoverControls(e.conversationId);this.inFlightCancels.add(t);const s=(this.controlTails.get(e.conversationId)??Promise.resolve()).then(async()=>{if(!this.started||!sameBinding(n,resolveExecutionBinding(e,this.bindings())))return;"deferred"!==await(n.inbound.onUserContinue?.(e))&&(this.delivered.add(t),this.eligibleMessages.delete(t),this.transportContext.delete(t))}).catch(()=>{this.logger.warn?.("[beeos-claw] projected user.continue unavailable; native adapter owns reconciliation")}).finally(()=>{this.cancelTasks.delete(s),this.inFlightCancels.delete(t),this.controlTails.get(e.conversationId)===s&&this.controlTails.delete(e.conversationId)});this.controlTails.set(e.conversationId,s),this.cancelTasks.add(s)}recoverControls(e){if(this.recoveringControls.has(e))return;const t=this.client.conversations;if(!t?.refresh)return;const n=t.refresh(e).then(()=>{this.started&&this.recoverDurableSnapshot()}).catch(()=>{this.logger.warn?.("[question] complete durable control projection unavailable")}).finally(()=>{this.recoveringControls.delete(e),this.cancelTasks.delete(n)});this.recoveringControls.set(e,n),this.cancelTasks.add(n)}async reconcileRequest(e,t){const n=Object.values(this.client.getSnapshot().messages),i=n.filter(n=>isReplyFor(n,e,t.targetAgentId));if(i.some(isTerminalReply))return"handled";if(i.length>0)return"pending";if(n.some(n=>isReasoningFor(n,e,t.targetAgentId)))return"pending";const s=deterministicReplyMessageId("asst",e.conversationId,e.id,t.principalId);try{const t=await this.client.messages.getEnvelope(e.conversationId,s);return"agent_reply"!==t.type||t.conversationId!==e.conversationId||void 0!==t.replyTo&&t.replyTo.length>0&&t.replyTo!==e.id?(this.logger.warn?.(`[beeos-claw] deterministic reply reconciliation mismatch conversation=${e.conversationId} message_id=${e.id} reply_id=${s}`),"pending"):isTerminalReply(t)?"handled":"pending"}catch(t){return 404===httpStatus(t)?"execute":(this.logger.warn?.(`[beeos-claw] deterministic reply reconciliation failed conversation=${e.conversationId} message_id=${e.id}: ${t instanceof Error?t.message:String(t)}`),"pending")}}}function isInbound(e){return"chat_message"===e.type||"chat_cancel"===e.type||"user.continue"===e.type}function isCanonicalUserPublisher(e){return"string"==typeof e.senderId&&e.senderId.startsWith("user:")&&e.senderId.length>5}function isCanonicalA2ACommand(e){const t=asRecord(asRecord(e.content)?.metadata);return"chat_message"===e.type&&"a2a"===t?.protocol&&"string"==typeof e.senderId&&e.senderId.length>0}function isCanonicalInboundPublisher(e,t){return"user"===t.actor.kind?t.actor.id===e.senderId&&isCanonicalUserPublisher(e):"service"===t.actor.kind&&"message-service"===t.actor.id&&isCanonicalA2ACommand(e)}function isRecoverableInboundPublisher(e){return isCanonicalUserPublisher(e)||isCanonicalA2ACommand(e)}function resolveExecutionBinding(e,t){if(!e.id)return;const n=asRecord(e.content),i=asRecord(e.deliveryContext??("user.continue"===e.type?void 0:n?.metadata)),s=nonEmptyString(i?.delivery_principal),o=nonEmptyString(i?.target_agent_id),r=epochValue(i?.runtime_epoch);if(s&&o&&void 0!==r)for(const e of t){if(e.principalId!==s)continue;if(e.targetAgentId!==o)continue;if(epochValue(e.runtimeEpoch)===r)return e}}function epochValue(e){const t=nonEmptyString(e);if(void 0!==t&&/^\d+$/.test(t))return BigInt(t)}function sameBinding(e,t){return void 0!==t&&t.principalId===e.principalId&&t.targetAgentId===e.targetAgentId&&t.runtimeEpoch===e.runtimeEpoch&&t.inbound===e.inbound}function resolveCancelTarget(e,t,n){const i=nonEmptyString(asRecord(e.content)?.target_message_id);if(!i)return;const s=t.find(t=>"chat_message"===t.type&&t.id===i&&t.conversationId===e.conversationId);if(!s)return;const o=resolveExecutionBinding(s,n),r=resolveExecutionBinding(e,n);return o&&r&&o.principalId===r.principalId&&o.targetAgentId===r.targetAgentId&&o.runtimeEpoch===r.runtimeEpoch?{message:s,binding:o}:void 0}function isReplyFor(e,t,n){if("agent_reply"!==e.type||e.conversationId!==t.conversationId||e.replyTo!==t.id)return!1;if(e.senderId===n)return!0;if(!isTerminalReply(e))return!1;const i=nonEmptyString(asRecord(asRecord(t.content)?.metadata)?.delivery_principal);return void 0!==i&&e.senderId===i}function isReasoningFor(e,t,n){return"agent_reasoning"===e.type&&e.conversationId===t.conversationId&&e.replyTo===t.id&&e.senderId===n}function isTerminalReply(e){return"agent_reply"===e.type&&("completed"===e.state||"failed"===e.state||"refused"===e.state||"cancelled"===e.state)}function httpStatus(e){return null!==e&&"object"==typeof e&&"status"in e&&"number"==typeof e.status?e.status:void 0}function asRecord(e){return null===e||"object"!=typeof e||Array.isArray(e)?void 0:e}function nonEmptyString(e){return"string"==typeof e&&e.length>0?e:void 0}function validRequestId(e){if(!("string"!=typeof e||0===e.length||e.length>128))return/^[A-Za-z0-9._:-]+$/.test(e)?e:void 0}function compareProjectionOrder(e,t){try{const n=BigInt(e.offset)-BigInt(t.offset);if(0n!==n)return n<0n?-1:1}catch{const n=e.offset.localeCompare(t.offset);if(0!==n)return n}return e.id.localeCompare(t.id)}
@@ -1 +1 @@
1
- import{resolveSyncAgentName}from"../agent-sync.js";import{AgentProjectionWorker,RuntimeProjectionTransportError}from"./agent-projection-worker.js";export class OpenClawAgentProjectionSource{getGateway;runtimeIds=new Map;constructor(t){this.getGateway=t}async listAgents(){const t=this.getGateway();if(!t?.isConnected)return{complete:!1,agents:[]};const e=await t.request("agents.list",{});return Array.isArray(e?.agents)?(this.runtimeIds.clear(),{complete:!0,agents:e.agents.map(t=>{const e=resolveSyncAgentName(t);return this.runtimeIds.set(e,t.id),{localKey:e,displayName:t.identity?.name||t.name||e,...t.description?{description:t.description}:{},...t.version?{version:t.version}:{},...t.identity?.avatarUrl?{avatarUrl:t.identity.avatarUrl}:{},...t.homepage?{homepage:t.homepage}:{},...t.defaultModel?{defaultModel:t.defaultModel}:{}}}).filter(t=>t.localKey)}):{complete:!1,agents:[]}}async listSkills(t){const e=this.getGateway();if(!e?.isConnected)return{complete:!1,skills:[]};try{const i=await e.request("skills.status",{agentId:this.runtimeIds.get(t)??t});return Array.isArray(i?.skills)?{complete:!0,skills:i.skills.filter(t=>!1!==t.eligible).map(t=>({skillKey:t.skillKey||t.name,name:t.name,...t.description?{description:t.description}:{},enabled:!t.disabled,...t.version?{version:t.version}:{}}))}:{complete:!1,skills:[]}}catch{return{complete:!1,skills:[]}}}}export class AgentGatewayHttpProjectionTransport{options;constructor(t){this.options=t}async apply(t,e){const i={runtimeEpoch:t.runtimeEpoch,revision:t.revision,snapshotId:t.snapshotId,canonicalHash:t.canonicalHash,agentsComplete:t.agentsComplete,skillsComplete:t.agents.every(t=>t.skillsComplete),agents:t.agents.map(t=>({runtimeAgentUid:t.runtimeAgentUid,localKey:t.localKey,displayName:t.displayName,...t.description?{description:t.description}:{},...t.version?{version:t.version}:{},...t.avatarUrl?{avatarUrl:t.avatarUrl}:{},...t.homepage?{homepage:t.homepage}:{},...t.defaultModel?{defaultModel:t.defaultModel}:{},capabilities:t.capabilities,skills:t.skills.map(t=>({skillKey:t.skillKey,name:t.name,...t.description?{description:t.description}:{},enabled:t.enabled,...t.version?{version:t.version}:{}})),skillsComplete:t.skillsComplete})),runtimeLeaseCredential:this.options.getRuntimeLeaseCredential(t)},n=await this.options.boundAgentFetch(new URL("/api/v1/agents/sync",this.options.agentGatewayUrl),{method:"POST",signal:e,headers:{"content-type":"application/json"},body:JSON.stringify(i)});if(!n.ok){const t=408===n.status||425===n.status||429===n.status||n.status>=500,e=(await n.text().catch(()=>"")).replace(/"runtimeLeaseCredential"\s*:\s*"[^"]*"/g,'"runtimeLeaseCredential":"<redacted>"').slice(0,512);throw new RuntimeProjectionTransportError(`Agent Gateway projection HTTP ${n.status}${e?`: ${e}`:""}`,t)}const o=await n.json(),r=o.acceptanceStatus,a=stringInteger(o.runtimeEpoch),s=stringInteger(o.revision);if(!a||!s||!["accepted","idempotent","stale_epoch","stale_revision","revision_hash_conflict"].includes(String(r)))throw new RuntimeProjectionTransportError("Agent Gateway returned malformed projection authority",!1);if("accepted"===r||"idempotent"===r){if(o.snapshotId!==t.snapshotId||o.canonicalHash!==t.canonicalHash||a!==t.runtimeEpoch||s!==t.revision)throw new RuntimeProjectionTransportError("Agent Gateway projection response correlation mismatch",!1);const e=validateAcceptedRows(o.data,t),i=e.map(t=>({runtimeAgentUid:t.runtimeAgentUid,platformAgentId:t.platformAgentId})),n=new Map;for(const t of e)n.set(t.runtimeLocalKey,{id:t.platformAgentId,instanceId:t.instanceId,name:t.runtimeLocalKey,displayName:String(t.raw.displayName??t.runtimeLocalKey),description:String(t.raw.description??""),version:String(t.raw.version??""),avatarUrl:String(t.raw.avatarUrl??""),status:String(t.raw.status??"active")});return this.options.onAccepted?.(n),{status:r,acceptedRuntimeEpoch:a,acceptedRevision:s,canonicalHash:String(o.canonicalHash),bindings:i}}return"revision_hash_conflict"===r?{status:r,acceptedRuntimeEpoch:a,acceptedRevision:s,acceptedCanonicalHash:String(o.canonicalHash??"")}:{status:r,acceptedRuntimeEpoch:a,acceptedRevision:s}}}const TEMPLATE_ORIGIN_REPORT_RETRY_LIMIT=3,TEMPLATE_ORIGIN_REPORT_RETRY_DELAY_MS=500;export class AgentGatewayTemplateOriginReporter{options;constructor(t){this.options=t}async report({platformAgentId:t,templateId:e}){let i;for(let n=1;n<=3;n++)try{return void await this.reportOnce(t,e)}catch(t){i=t,n<3&&await delay(this.options.retryDelayMs??500)}throw i instanceof Error?i:new Error(String(i))}async reportOnce(t,e){const i=await this.options.boundAgentFetch(new URL(`/api/v1/agents/${encodeURIComponent(t)}/template-origin`,this.options.agentGatewayUrl),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({templateId:e})});if(!i.ok){const t=await i.text().catch(()=>"");throw new Error(`Agent Gateway template-origin HTTP ${i.status}${t?`: ${t.slice(0,512)}`:""}`)}}}function delay(t){return new Promise(e=>setTimeout(e,t))}const TERMINAL_COMPACTION_RETRY_LIMIT=3,TERMINAL_COMPACTION_RETRY_DELAY_MS=250,TERMINAL_COMPACTION_BATCH_SIZE=32;export class AgentProjectionProduction{worker;unsubscribe;binding;transition=Promise.resolve();started=!1;terminalCompactionTimer;terminalCompactionRecovery;terminalCompactionAttempts=new Map;authority;journal;logger;constructor(t){this.authority=t.authority,this.journal=t.journal,this.logger=t.logger,this.worker=new AgentProjectionWorker({stateFile:t.stateFile,getLease:()=>t.authority.authorityLease,identities:t.identities,source:t.source,transport:t.transport,capabilities:t.capabilities,reconcileIntervalMs:t.reconcileIntervalMs,random:t.random,onBlocked:e=>t.logger?.warn?.(`[agent-projection] blocked: ${e}`),onReconciled:()=>this.onWorkerReconciled()})}async prepareCommittedMutation(t){return await this.worker.prepareCommittedMutation(t)}async applyCommittedMutation(t){return await this.worker.applyCommittedMutation(t)}async compactCommittedMutation(t){try{await this.worker.compactCommittedMutation(t),this.terminalCompactionAttempts.delete(t.operationId)}catch(t){throw this.scheduleTerminalCompactionRetry(),t}}start(){this.started||(this.started=!0,this.unsubscribe=this.authority.subscribeAuthority(t=>this.onAuthority(t)),this.onAuthority(this.authority.authorityLease),this.retryTerminalCompactions())}markDirty(){this.worker.markDirty()}onGatewayReconnect(){this.started&&this.authority.authorityLease&&(this.worker.reconcile("reconnect"),this.retryTerminalCompactions())}async stop(){this.started=!1,this.unsubscribe?.(),this.unsubscribe=void 0,this.terminalCompactionTimer&&clearTimeout(this.terminalCompactionTimer),this.terminalCompactionTimer=void 0,await this.queue(async()=>{this.binding=void 0,await this.worker.shutdown()})}onAuthority(t){const e=t?`${t.instanceId}\0${t.handlerIdentity}\0${t.runtimeEpoch}\0${t.leaseId}`:void 0;e!==this.binding&&(this.binding=e,this.queue(async()=>{await this.worker.shutdown(),this.started&&e&&e===this.binding&&this.worker.start()}))}queue(t){const e=this.transition.then(t,t);return this.transition=e.catch(()=>{}),e}onWorkerReconciled(){this.terminalCompactionAttempts.clear(),this.retryTerminalCompactions()}scheduleTerminalCompactionRetry(){this.started&&this.journal&&!this.terminalCompactionTimer&&(this.terminalCompactionTimer=setTimeout(()=>{this.terminalCompactionTimer=void 0,this.retryTerminalCompactions()},250))}async retryTerminalCompactions(){if(!this.started||!this.journal)return;if(this.terminalCompactionRecovery)return await this.terminalCompactionRecovery;const t=(async()=>{let t=!1;for(const e of(await this.worker.operationReferences()).slice(0,32)){const i=terminalIntentForReference(await this.journal.get(e.operationId),e);if(!i)continue;const n=this.terminalCompactionAttempts.get(e.operationId)??0;if(!(n>=3))try{await this.worker.compactCommittedMutation(i),this.terminalCompactionAttempts.delete(e.operationId)}catch(i){const o=n+1;this.terminalCompactionAttempts.set(e.operationId,o),this.logger?.warn?.(`[agent-projection] terminal reference compaction deferred (${o}/3): ${i instanceof Error?i.message:String(i)}`),t||=o<3}}t&&this.scheduleTerminalCompactionRetry()})();this.terminalCompactionRecovery=t;try{await t}finally{this.terminalCompactionRecovery===t&&(this.terminalCompactionRecovery=void 0)}}}function terminalIntentForReference(t,e){const i=t?.agentProjectionIntent;if(!i||i.operationId!==e.operationId||i.runtimeEpoch!==e.runtimeEpoch||i.projectionRevision!==e.projectionRevision||i.canonicalHash!==e.canonicalHash||i.snapshotRef!==e.snapshotRef)return;const n="projection_committed"===t.phase&&t.projectionReceipt?.revision===i.projectionRevision&&t.projectionReceipt.snapshot.revision===i.projectionRevision,o="blocked"===t.agentProjectionTerminal?.state&&t.agentProjectionTerminal.revision===i.projectionRevision;return n||o?i:void 0}function stringInteger(t){return"string"==typeof t&&/^(0|[1-9][0-9]*)$/.test(t)?t:Number.isSafeInteger(t)&&t>=0?String(t):null}const nonEmpty=t=>"string"==typeof t&&t.length>0?t:null;function validateAcceptedRows(t,e){if(!Array.isArray(t)||t.length!==e.agents.length)throw new RuntimeProjectionTransportError("Agent Gateway projection row count mismatch",!1);const i=new Map(e.agents.map(t=>[t.runtimeAgentUid,t.localKey])),n=new Set,o=new Set,r=new Set,a=t.map(t=>{if(!t||"object"!=typeof t||Array.isArray(t))throw new RuntimeProjectionTransportError("Agent Gateway projection row is malformed",!1);const a=t,s=nonEmpty(a.runtimeAgentUid),c=nonEmpty(a.runtimeLocalKey),l=nonEmpty(a.id),p=nonEmpty(a.instanceId);if(!s||!c||!l||!p||p!==e.instanceId||i.get(s)!==c||n.has(s)||o.has(c)||r.has(l))throw new RuntimeProjectionTransportError("Agent Gateway projection identity binding mismatch",!1);return n.add(s),o.add(c),r.add(l),{raw:a,runtimeAgentUid:s,runtimeLocalKey:c,platformAgentId:l,instanceId:p}});if(n.size!==i.size)throw new RuntimeProjectionTransportError("Agent Gateway projection omitted identity binding",!1);return a}
1
+ import{resolveSyncAgentName}from"../agent-sync.js";import{AgentProjectionWorker,RuntimeProjectionTransportError}from"./agent-projection-worker.js";export class OpenClawAgentProjectionSource{getGateway;runtimeIds=new Map;constructor(t){this.getGateway=t}async listAgents(){const t=this.getGateway();if(!t?.isConnected)return{complete:!1,agents:[]};const e=await t.request("agents.list",{});return Array.isArray(e?.agents)?(this.runtimeIds.clear(),{complete:!0,agents:e.agents.map(t=>{const e=resolveSyncAgentName(t);return this.runtimeIds.set(e,t.id),{localKey:e,displayName:t.identity?.name||t.name||e,...t.description?{description:t.description}:{},...t.version?{version:t.version}:{},...t.identity?.avatarUrl?{avatarUrl:t.identity.avatarUrl}:{},...t.homepage?{homepage:t.homepage}:{},...t.defaultModel?{defaultModel:t.defaultModel}:{}}}).filter(t=>t.localKey)}):{complete:!1,agents:[]}}async listSkills(t){const e=this.getGateway();if(!e?.isConnected)return{complete:!1,skills:[]};try{const i=await e.request("skills.status",{agentId:this.runtimeIds.get(t)??t});return Array.isArray(i?.skills)?{complete:!0,skills:i.skills.filter(t=>!1!==t.eligible).map(t=>({skillKey:t.skillKey||t.name,name:t.name,...t.description?{description:t.description}:{},enabled:!t.disabled,...t.version?{version:t.version}:{}}))}:{complete:!1,skills:[]}}catch{return{complete:!1,skills:[]}}}}export class AgentGatewayHttpProjectionTransport{options;constructor(t){this.options=t}async apply(t,e){const i={runtimeEpoch:t.runtimeEpoch,revision:t.revision,snapshotId:t.snapshotId,canonicalHash:t.canonicalHash,agentsComplete:t.agentsComplete,skillsComplete:t.agents.every(t=>t.skillsComplete),agents:t.agents.map(t=>({runtimeAgentUid:t.runtimeAgentUid,localKey:t.localKey,displayName:t.displayName,...t.description?{description:t.description}:{},...t.version?{version:t.version}:{},...t.avatarUrl?{avatarUrl:t.avatarUrl}:{},...t.homepage?{homepage:t.homepage}:{},...t.defaultModel?{defaultModel:t.defaultModel}:{},capabilities:t.capabilities,skills:t.skills.map(t=>({skillKey:t.skillKey,name:t.name,...t.description?{description:t.description}:{},enabled:t.enabled,...t.version?{version:t.version}:{}})),skillsComplete:t.skillsComplete})),runtimeLeaseCredential:this.options.getRuntimeLeaseCredential(t)},n=await this.options.boundAgentFetch(new URL("/api/v1/agents/sync",this.options.agentGatewayUrl),{method:"POST",signal:e,headers:{"content-type":"application/json"},body:JSON.stringify(i)});if(!n.ok){const t=408===n.status||425===n.status||429===n.status||n.status>=500,e=(await n.text().catch(()=>"")).replace(/"runtimeLeaseCredential"\s*:\s*"[^"]*"/g,'"runtimeLeaseCredential":"<redacted>"').slice(0,512);throw new RuntimeProjectionTransportError(`Agent Gateway projection HTTP ${n.status}${e?`: ${e}`:""}`,t)}const o=await n.json(),r=o.acceptanceStatus,a=stringInteger(o.runtimeEpoch),s=stringInteger(o.revision);if(!a||!s||!["accepted","idempotent","stale_epoch","stale_revision","revision_hash_conflict"].includes(String(r)))throw new RuntimeProjectionTransportError("Agent Gateway returned malformed projection authority",!1);if("accepted"===r||"idempotent"===r){if(o.snapshotId!==t.snapshotId||o.canonicalHash!==t.canonicalHash||a!==t.runtimeEpoch||s!==t.revision)throw new RuntimeProjectionTransportError("Agent Gateway projection response correlation mismatch",!1);const e=validateAcceptedRows(o.data,t),i=e.map(t=>({runtimeAgentUid:t.runtimeAgentUid,platformAgentId:t.platformAgentId})),n=new Map;for(const t of e)n.set(t.runtimeLocalKey,{id:t.platformAgentId,instanceId:t.instanceId,name:t.runtimeLocalKey,displayName:String(t.raw.displayName??t.runtimeLocalKey),description:String(t.raw.description??""),version:String(t.raw.version??""),avatarUrl:String(t.raw.avatarUrl??""),status:String(t.raw.status??"active")});return this.options.onAccepted?.(n),{status:r,acceptedRuntimeEpoch:a,acceptedRevision:s,canonicalHash:String(o.canonicalHash),bindings:i}}return"revision_hash_conflict"===r?{status:r,acceptedRuntimeEpoch:a,acceptedRevision:s,acceptedCanonicalHash:String(o.canonicalHash??"")}:{status:r,acceptedRuntimeEpoch:a,acceptedRevision:s}}}const TEMPLATE_ORIGIN_REPORT_RETRY_LIMIT=3,TEMPLATE_ORIGIN_REPORT_RETRY_DELAY_MS=500;export class AgentGatewayTemplateOriginReporter{options;constructor(t){this.options=t}async report({platformAgentId:t,templateId:e}){let i;for(let n=1;n<=3;n++)try{return void await this.reportOnce(t,e)}catch(t){i=t,n<3&&await delay(this.options.retryDelayMs??500)}throw i instanceof Error?i:new Error(String(i))}async reportOnce(t,e){const i=await this.options.boundAgentFetch(new URL(`/api/v1/agents/${encodeURIComponent(t)}/template-origin`,this.options.agentGatewayUrl),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({templateId:e})});if(!i.ok){const t=await i.text().catch(()=>"");throw new Error(`Agent Gateway template-origin HTTP ${i.status}${t?`: ${t.slice(0,512)}`:""}`)}}}function delay(t){return new Promise(e=>setTimeout(e,t))}const TERMINAL_COMPACTION_RETRY_LIMIT=3,TERMINAL_COMPACTION_RETRY_DELAY_MS=250,TERMINAL_COMPACTION_BATCH_SIZE=32;export class AgentProjectionProduction{worker;unsubscribe;binding;transition=Promise.resolve();started=!1;terminalCompactionTimer;terminalCompactionRecovery;terminalCompactionAttempts=new Map;authority;journal;logger;constructor(t){this.authority=t.authority,this.journal=t.journal,this.logger=t.logger,this.worker=new AgentProjectionWorker({stateFile:t.stateFile,getLease:()=>t.authority.authorityLease,identities:t.identities,source:t.source,transport:t.transport,capabilities:t.capabilities,reconcileIntervalMs:t.reconcileIntervalMs,random:t.random,onBlocked:e=>t.logger?.warn?.(`[agent-projection] blocked: ${e}`),onReconciled:()=>this.onWorkerReconciled()})}async prepareCommittedMutation(t){return await this.worker.prepareCommittedMutation(t)}async applyCommittedMutation(t){return await this.worker.applyCommittedMutation(t)}async compactCommittedMutation(t){try{await this.worker.compactCommittedMutation(t),this.terminalCompactionAttempts.delete(t.operationId)}catch(t){throw this.scheduleTerminalCompactionRetry(),t}}start(){this.started||(this.started=!0,this.unsubscribe=this.authority.subscribeAuthority(t=>this.onAuthority(t)),this.onAuthority(this.authority.authorityLease),this.retryTerminalCompactions())}markDirty(){this.worker.markDirty()}onGatewayReconnect(){this.started&&this.authority.authorityLease&&(this.worker.reconcile("reconnect").catch(t=>{this.logger?.warn?.(`[agent-projection] reconnect reconciliation failed: ${t instanceof Error?t.message:String(t)}`)}),this.retryTerminalCompactions())}async stop(){this.started=!1,this.unsubscribe?.(),this.unsubscribe=void 0,this.terminalCompactionTimer&&clearTimeout(this.terminalCompactionTimer),this.terminalCompactionTimer=void 0,await this.queue(async()=>{this.binding=void 0,await this.worker.shutdown()})}onAuthority(t){const e=t?`${t.instanceId}\0${t.handlerIdentity}\0${t.runtimeEpoch}\0${t.leaseId}`:void 0;e!==this.binding&&(this.binding=e,this.queue(async()=>{await this.worker.shutdown(),this.started&&e&&e===this.binding&&this.worker.start()}))}queue(t){const e=this.transition.then(t,t);return this.transition=e.catch(()=>{}),e}onWorkerReconciled(){this.terminalCompactionAttempts.clear(),this.retryTerminalCompactions()}scheduleTerminalCompactionRetry(){this.started&&this.journal&&!this.terminalCompactionTimer&&(this.terminalCompactionTimer=setTimeout(()=>{this.terminalCompactionTimer=void 0,this.retryTerminalCompactions()},250))}async retryTerminalCompactions(){if(!this.started||!this.journal)return;if(this.terminalCompactionRecovery)return await this.terminalCompactionRecovery;const t=(async()=>{let t=!1;for(const e of(await this.worker.operationReferences()).slice(0,32)){const i=terminalIntentForReference(await this.journal.get(e.operationId),e);if(!i)continue;const n=this.terminalCompactionAttempts.get(e.operationId)??0;if(!(n>=3))try{await this.worker.compactCommittedMutation(i),this.terminalCompactionAttempts.delete(e.operationId)}catch(i){const o=n+1;this.terminalCompactionAttempts.set(e.operationId,o),this.logger?.warn?.(`[agent-projection] terminal reference compaction deferred (${o}/3): ${i instanceof Error?i.message:String(i)}`),t||=o<3}}t&&this.scheduleTerminalCompactionRetry()})();this.terminalCompactionRecovery=t;try{await t}finally{this.terminalCompactionRecovery===t&&(this.terminalCompactionRecovery=void 0)}}}function terminalIntentForReference(t,e){const i=t?.agentProjectionIntent;if(!i||i.operationId!==e.operationId||i.runtimeEpoch!==e.runtimeEpoch||i.projectionRevision!==e.projectionRevision||i.canonicalHash!==e.canonicalHash||i.snapshotRef!==e.snapshotRef)return;const n="projection_committed"===t.phase&&t.projectionReceipt?.revision===i.projectionRevision&&t.projectionReceipt.snapshot.revision===i.projectionRevision,o="blocked"===t.agentProjectionTerminal?.state&&t.agentProjectionTerminal.revision===i.projectionRevision;return n||o?i:void 0}function stringInteger(t){return"string"==typeof t&&/^(0|[1-9][0-9]*)$/.test(t)?t:Number.isSafeInteger(t)&&t>=0?String(t):null}const nonEmpty=t=>"string"==typeof t&&t.length>0?t:null;function validateAcceptedRows(t,e){if(!Array.isArray(t)||t.length!==e.agents.length)throw new RuntimeProjectionTransportError("Agent Gateway projection row count mismatch",!1);const i=new Map(e.agents.map(t=>[t.runtimeAgentUid,t.localKey])),n=new Set,o=new Set,r=new Set,a=t.map(t=>{if(!t||"object"!=typeof t||Array.isArray(t))throw new RuntimeProjectionTransportError("Agent Gateway projection row is malformed",!1);const a=t,s=nonEmpty(a.runtimeAgentUid),c=nonEmpty(a.runtimeLocalKey),l=nonEmpty(a.id),p=nonEmpty(a.instanceId);if(!s||!c||!l||!p||p!==e.instanceId||i.get(s)!==c||n.has(s)||o.has(c)||r.has(l))throw new RuntimeProjectionTransportError("Agent Gateway projection identity binding mismatch",!1);return n.add(s),o.add(c),r.add(l),{raw:a,runtimeAgentUid:s,runtimeLocalKey:c,platformAgentId:l,instanceId:p}});if(n.size!==i.size)throw new RuntimeProjectionTransportError("Agent Gateway projection omitted identity binding",!1);return a}
@@ -1 +1 @@
1
- import{canonicalizeJcs,verifyAndDeriveCoreExecutionAttempt}from"@beeos-ai/beeos-types/runtime";const TRUSTED_CORE_ATTEMPT=Symbol("beeos.trusted-core-attempt"),VERIFIED_MESSAGE_GRANT=Symbol("beeos.verified-message-grant");export function createAcpCoreExecutionAttempt(e){if(e.historyBoundary&&"local"!==e.historyBoundary.kind)throw new Error("ACP attempt cannot use a Message Service history boundary");return Object.freeze({...e,origin:"acp",[TRUSTED_CORE_ATTEMPT]:!0})}export async function verifyAndCreateServiceCoreExecutionAttempt(e){let t;try{t=verifyAndDeriveCoreExecutionAttempt(e.authorization,e.derivationContext,e.verifier)}catch{return{ok:!1,violations:["MALFORMED_EXECUTION_AUTHORIZATION"]}}if(!t.ok)return t;await e.recordVerifiedAttempt(t.attempt);const o=await e.resolvePorts();if(o.historyBoundary&&"message-service"!==o.historyBoundary.kind)throw new Error("Service attempt cannot fall back to local conversation history");if(o.sessionModelProjection&&o.historyBoundary)throw new Error("Service attempt cannot bind clear history and session model projection together");return{ok:!0,context:Object.freeze({operationId:e.operationId,runtimeEpoch:e.runtimeEpoch,fence:e.fence,resources:o.resources,...o.historyBoundary?{historyBoundary:o.historyBoundary}:{},...o.sessionModelProjection?{sessionModelProjection:o.sessionModelProjection}:{},...o.canvasControl?{canvasControl:o.canvasControl}:{},...e.abortSignal?{abortSignal:e.abortSignal}:{},verifiedAttempt:t.attempt,origin:"service",[TRUSTED_CORE_ATTEMPT]:!0})}}export async function verifyAndCreateMessageServiceCoreExecutionAttempt(e){const t=e.verifier.verifyAndDecodeGrant(e.executionGrant);if(!t)return{ok:!1,violations:["GRANT_SIGNATURE_INVALID"]};if(t.compactHash!==e.request.executionGrantHash)return{ok:!1,violations:["GRANT_HASH_MISMATCH"]};const o=t.claims,n=validateMessageGrant(o,e.request,e.lease,e.nowEpochSeconds??Math.floor(Date.now()/1e3),!0===e.allowExpiredGrant);if(n.length)return{ok:!1,violations:n};const r=await e.resolvePorts();if(r.historyBoundary&&"message-service"!==r.historyBoundary.kind)throw new Error("Service attempt cannot fall back to local conversation history");if(r.sessionModelProjection&&r.historyBoundary)throw new Error("Service attempt cannot bind clear history and session model projection together");const i=Object.freeze({[VERIFIED_MESSAGE_GRANT]:!0,claims:Object.freeze({...o}),compactHash:t.compactHash});return{ok:!0,grant:i,context:Object.freeze({operationId:e.request.operationId,runtimeEpoch:e.runtimeEpoch,resources:r.resources,...r.historyBoundary?{historyBoundary:r.historyBoundary}:{},...r.sessionModelProjection?{sessionModelProjection:r.sessionModelProjection}:{},...r.canvasControl?{canvasControl:r.canvasControl}:{},...e.abortSignal?{abortSignal:e.abortSignal}:{},verifiedMessageGrant:i,origin:"service",[TRUSTED_CORE_ATTEMPT]:!0})}}export function assertTrustedCoreExecutionAttempt(e,t){if(!e||!0!==e[TRUSTED_CORE_ATTEMPT]||e.operationId!==t.operationId||e.runtimeEpoch!==t.runtimeEpoch)throw new Error("untrusted or mismatched Core execution attempt context");if(!("service"!==e.origin||e.verifiedMessageGrant||e.fence&&e.verifiedAttempt))throw new Error("Service Core execution attempt lacks verified authorization")}function validateMessageGrant(e,t,o,n,r){if(!e||"object"!=typeof e)return["MALFORMED_EXECUTION_GRANT"];const i="historyGeneration"in e?e.historyGeneration:void 0,s="instance"===e.scope&&"runtimeAgentUid"in e?e.runtimeAgentUid:void 0;if(!exactKeys(e,["iss","aud","purpose","sub","jti","iat","exp","operationId","requestMessageId","instanceId","handlerIdentity","dispatchEpoch","method","requestHash","paramsHash","commandHash","idempotencyKeyHash","callerUserId","callerIdentityId","scope","command","executionMode",..."instance"===e.scope?void 0!==s?["runtimeAgentUid"]:[]:"agent"===e.scope?["platformAgentId","runtimeAgentUid"]:"conversation"===e.scope?["platformConversationId","runtimeConversationUid","runtimeSessionKey","platformAgentId","runtimeAgentUid",...void 0!==i?["historyGeneration"]:[]]:[]]))return["GRANT_FIELDS_INVALID"];const a=[];"beeos-gateway"===e.iss&&"beeos-runtime-command"===e.aud&&"runtime-execution-grant"===e.purpose&&e.sub===t.runtimeHandlerIdentity&&e.handlerIdentity===t.runtimeHandlerIdentity&&"start"===e.executionMode||a.push("GRANT_IDENTITY_MISMATCH"),(!Number.isSafeInteger(e.iat)||!Number.isSafeInteger(e.exp)||"string"!=typeof e.jti||0===e.jti.length||e.iat>n||e.exp<=e.iat||!r&&e.exp<=n||e.exp-e.iat>60)&&a.push("GRANT_TIME_INVALID");if([[e.operationId,t.operationId],[e.requestMessageId,t.requestMessageId],[e.instanceId,t.instanceId],[e.dispatchEpoch,t.dispatchEpoch],[e.method,t.method],[e.requestHash,t.requestHash],[e.paramsHash,t.paramsHash],[e.commandHash,t.commandHash],[e.idempotencyKeyHash,t.idempotencyKeyHash],[e.callerUserId,t.callerUserId],[e.callerIdentityId,t.callerIdentityId],[o.instanceId,t.instanceId],[o.handlerIdentity,t.runtimeHandlerIdentity],[o.runtimeEpoch,t.dispatchEpoch]].some(([e,t])=>e!==t)&&a.push("GRANT_REQUEST_MISMATCH"),semanticEqual(e.command,t.command)||a.push("GRANT_COMMAND_MISMATCH"),targetMatches(e,t)||a.push("GRANT_TARGET_MISMATCH"),"conversation"===e.scope){const o=t.command.context,n=o?.agent;o&&"conversation"===o.scope&&n?.platformAgentId===e.platformAgentId&&n?.runtimeAgentUid===e.runtimeAgentUid||a.push("GRANT_TARGET_MISMATCH");const r=o?.historyGeneration;"session/cancel"===e.method?"string"==typeof i&&0!==i.length&&i===r||a.push("GRANT_HISTORY_GENERATION_MISMATCH"):void 0!==i&&a.push("GRANT_HISTORY_GENERATION_MISMATCH")}return a}function semanticEqual(e,t){try{return canonicalizeJcs(e)===canonicalizeJcs(t)}catch{return!1}}function targetMatches(e,t){const o=t.command.context,n=t.request.params;if(!o||o.scope!==e.scope)return!1;if("instance"===o.scope){if(!("runtimeAgentUid"in e))return!0;const o=e.runtimeAgentUid;if("string"!=typeof o||0===o.length)return!1;const n=t.command.params;return n?.runtimeAgentUid===o}if("agent"===o.scope)return o.agent.runtimeAgentUid===e.runtimeAgentUid&&null!==n&&"object"==typeof n&&!Array.isArray(n)&&n.platformAgentId===e.platformAgentId;const r=null===n||"object"!=typeof n||Array.isArray(n)?void 0:"string"==typeof n.conversationId?n.conversationId:"string"==typeof n.sessionId?n.sessionId:void 0;return o.conversation.runtimeConversationUid===e.runtimeConversationUid&&o.conversation?.runtimeSessionKey===e.runtimeSessionKey&&r===e.platformConversationId}function exactKeys(e,t){const o=Object.keys(e).sort(),n=[...t].sort();return o.length===n.length&&o.every((e,t)=>e===n[t])}
1
+ import{canonicalizeJcs,verifyAndDeriveCoreExecutionAttempt}from"@beeos-ai/beeos-types/runtime";const TRUSTED_CORE_ATTEMPT=Symbol("beeos.trusted-core-attempt"),VERIFIED_MESSAGE_GRANT=Symbol("beeos.verified-message-grant");export function createAcpCoreExecutionAttempt(e){if(e.historyBoundary&&"local"!==e.historyBoundary.kind)throw new Error("ACP attempt cannot use a Message Service history boundary");return Object.freeze({...e,origin:"acp",[TRUSTED_CORE_ATTEMPT]:!0})}export async function verifyAndCreateServiceCoreExecutionAttempt(e){let t;try{t=verifyAndDeriveCoreExecutionAttempt(e.authorization,e.derivationContext,e.verifier)}catch{return{ok:!1,violations:["MALFORMED_EXECUTION_AUTHORIZATION"]}}if(!t.ok)return t;await e.recordVerifiedAttempt(t.attempt);const o=await e.resolvePorts();if(o.historyBoundary&&"message-service"!==o.historyBoundary.kind)throw new Error("Service attempt cannot fall back to local conversation history");if(o.sessionModelProjection&&o.historyBoundary)throw new Error("Service attempt cannot bind clear history and session model projection together");return{ok:!0,context:Object.freeze({operationId:e.operationId,runtimeEpoch:e.runtimeEpoch,fence:e.fence,resources:o.resources,...o.historyBoundary?{historyBoundary:o.historyBoundary}:{},...o.sessionModelProjection?{sessionModelProjection:o.sessionModelProjection}:{},...o.sessionReasoningProjection?{sessionReasoningProjection:o.sessionReasoningProjection}:{},...o.canvasControl?{canvasControl:o.canvasControl}:{},...e.abortSignal?{abortSignal:e.abortSignal}:{},verifiedAttempt:t.attempt,origin:"service",[TRUSTED_CORE_ATTEMPT]:!0})}}export async function verifyAndCreateMessageServiceCoreExecutionAttempt(e){const t=e.verifier.verifyAndDecodeGrant(e.executionGrant);if(!t)return{ok:!1,violations:["GRANT_SIGNATURE_INVALID"]};if(t.compactHash!==e.request.executionGrantHash)return{ok:!1,violations:["GRANT_HASH_MISMATCH"]};const o=t.claims,n=validateMessageGrant(o,e.request,e.lease,e.nowEpochSeconds??Math.floor(Date.now()/1e3),!0===e.allowExpiredGrant);if(n.length)return{ok:!1,violations:n};const r=await e.resolvePorts();if(r.historyBoundary&&"message-service"!==r.historyBoundary.kind)throw new Error("Service attempt cannot fall back to local conversation history");if(r.sessionModelProjection&&r.historyBoundary)throw new Error("Service attempt cannot bind clear history and session model projection together");const i=Object.freeze({[VERIFIED_MESSAGE_GRANT]:!0,claims:Object.freeze({...o}),compactHash:t.compactHash});return{ok:!0,grant:i,context:Object.freeze({operationId:e.request.operationId,runtimeEpoch:e.runtimeEpoch,resources:r.resources,...r.historyBoundary?{historyBoundary:r.historyBoundary}:{},...r.sessionModelProjection?{sessionModelProjection:r.sessionModelProjection}:{},...r.sessionReasoningProjection?{sessionReasoningProjection:r.sessionReasoningProjection}:{},...r.canvasControl?{canvasControl:r.canvasControl}:{},...e.abortSignal?{abortSignal:e.abortSignal}:{},verifiedMessageGrant:i,origin:"service",[TRUSTED_CORE_ATTEMPT]:!0})}}export function assertTrustedCoreExecutionAttempt(e,t){if(!e||!0!==e[TRUSTED_CORE_ATTEMPT]||e.operationId!==t.operationId||e.runtimeEpoch!==t.runtimeEpoch)throw new Error("untrusted or mismatched Core execution attempt context");if(!("service"!==e.origin||e.verifiedMessageGrant||e.fence&&e.verifiedAttempt))throw new Error("Service Core execution attempt lacks verified authorization")}function validateMessageGrant(e,t,o,n,r){if(!e||"object"!=typeof e)return["MALFORMED_EXECUTION_GRANT"];const i="historyGeneration"in e?e.historyGeneration:void 0,s="instance"===e.scope&&"runtimeAgentUid"in e?e.runtimeAgentUid:void 0;if(!exactKeys(e,["iss","aud","purpose","sub","jti","iat","exp","operationId","requestMessageId","instanceId","handlerIdentity","dispatchEpoch","method","requestHash","paramsHash","commandHash","idempotencyKeyHash","callerUserId","callerIdentityId","scope","command","executionMode",..."instance"===e.scope?void 0!==s?["runtimeAgentUid"]:[]:"agent"===e.scope?["platformAgentId","runtimeAgentUid"]:"conversation"===e.scope?["platformConversationId","runtimeConversationUid","runtimeSessionKey","platformAgentId","runtimeAgentUid",...void 0!==i?["historyGeneration"]:[]]:[]]))return["GRANT_FIELDS_INVALID"];const a=[];"beeos-gateway"===e.iss&&"beeos-runtime-command"===e.aud&&"runtime-execution-grant"===e.purpose&&e.sub===t.runtimeHandlerIdentity&&e.handlerIdentity===t.runtimeHandlerIdentity&&"start"===e.executionMode||a.push("GRANT_IDENTITY_MISMATCH"),(!Number.isSafeInteger(e.iat)||!Number.isSafeInteger(e.exp)||"string"!=typeof e.jti||0===e.jti.length||e.iat>n||e.exp<=e.iat||!r&&e.exp<=n||e.exp-e.iat>60)&&a.push("GRANT_TIME_INVALID");if([[e.operationId,t.operationId],[e.requestMessageId,t.requestMessageId],[e.instanceId,t.instanceId],[e.dispatchEpoch,t.dispatchEpoch],[e.method,t.method],[e.requestHash,t.requestHash],[e.paramsHash,t.paramsHash],[e.commandHash,t.commandHash],[e.idempotencyKeyHash,t.idempotencyKeyHash],[e.callerUserId,t.callerUserId],[e.callerIdentityId,t.callerIdentityId],[o.instanceId,t.instanceId],[o.handlerIdentity,t.runtimeHandlerIdentity],[o.runtimeEpoch,t.dispatchEpoch]].some(([e,t])=>e!==t)&&a.push("GRANT_REQUEST_MISMATCH"),semanticEqual(e.command,t.command)||a.push("GRANT_COMMAND_MISMATCH"),targetMatches(e,t)||a.push("GRANT_TARGET_MISMATCH"),"conversation"===e.scope){const o=t.command.context,n=o?.agent;o&&"conversation"===o.scope&&n?.platformAgentId===e.platformAgentId&&n?.runtimeAgentUid===e.runtimeAgentUid||a.push("GRANT_TARGET_MISMATCH");const r=o?.historyGeneration;"session/cancel"===e.method?"string"==typeof i&&0!==i.length&&i===r||a.push("GRANT_HISTORY_GENERATION_MISMATCH"):void 0!==i&&a.push("GRANT_HISTORY_GENERATION_MISMATCH")}return a}function semanticEqual(e,t){try{return canonicalizeJcs(e)===canonicalizeJcs(t)}catch{return!1}}function targetMatches(e,t){const o=t.command.context,n=t.request.params;if(!o||o.scope!==e.scope)return!1;if("instance"===o.scope){if(!("runtimeAgentUid"in e))return!0;const o=e.runtimeAgentUid;if("string"!=typeof o||0===o.length)return!1;const n=t.command.params;return n?.runtimeAgentUid===o}if("agent"===o.scope)return o.agent.runtimeAgentUid===e.runtimeAgentUid&&null!==n&&"object"==typeof n&&!Array.isArray(n)&&n.platformAgentId===e.platformAgentId;const r=null===n||"object"!=typeof n||Array.isArray(n)?void 0:"string"==typeof n.conversationId?n.conversationId:"string"==typeof n.sessionId?n.sessionId:void 0;return o.conversation.runtimeConversationUid===e.runtimeConversationUid&&o.conversation?.runtimeSessionKey===e.runtimeSessionKey&&r===e.platformConversationId}function exactKeys(e,t){const o=Object.keys(e).sort(),n=[...t].sort();return o.length===n.length&&o.every((e,t)=>e===n[t])}