beeos-claw 0.6.65 → 0.6.66-staging.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{closeSync,existsSync,openSync,readFileSync,readSync,statSync}from"node:fs";import path from"node:path";import{resolveOpenClawStateDir}from"../runtime/openclaw-state-dir.js";export const LOCAL_HISTORY_LIMIT=200;export const LOCAL_HISTORY_TAIL_WINDOW_LINES=6400;export const LOCAL_HISTORY_TAIL_CHUNK_BYTES=65536;export const LOCAL_HISTORY_TAIL_MAX_BYTES=1048576;export function findLocalHistoryEntryByMessageId(t,e){for(let s=t.length-1;s>=0;s--){const i=t[s];if(!i)continue;const o=i.__openclaw;if(isRecord(o)&&o.id===e)return i;if(i.id===e)return i}}export class LocalSessionHistory{logger;stateDirOverride;agentsDirOverride;constructor(t){this.logger=t.logger,this.stateDirOverride=t.stateDir,this.agentsDirOverride=t.agentsDir}readEntries(t){const{agentId:e,sessionKey:s,sessionId:i}=t;if(!e||!s)return[];const o=this.resolveSessionsDir(e),r=this.resolveSessionFile(o,s,i);if(!r)return[];const n=this.readTailLines(r);if(0===n.length)return[];const c=[];for(const t of n){let e;try{e=JSON.parse(t)}catch{continue}if(!e||"object"!=typeof e)continue;const s=e;if("message"!==s.type)continue;const i=s.message;if(!i||"object"!=typeof i)continue;const o=i,r="string"==typeof o.role?o.role:void 0;if(!r)continue;const n="string"==typeof s.id?s.id:void 0,a=n?{id:n}:void 0,l=o.timestamp,f=s.timestamp,p="number"==typeof l||"string"==typeof l?l:"string"==typeof f?f:void 0,y={...o,role:r,...void 0!==p?{timestamp:p}:{},...a?{__openclaw:a}:{}};if(c.push(y),c.length>=200)break}return c.reverse()}resolveStateDir(){return this.stateDirOverride??resolveOpenClawStateDir()}resolveSessionsDir(t){return this.agentsDirOverride?path.join(this.agentsDirOverride,t,"sessions"):path.join(this.resolveStateDir(),"agents",t,"sessions")}resolveSessionFile(t,e,s){const i=path.join(t,"sessions.json");let o;if(existsSync(i))try{const t=readFileSync(i,"utf-8"),e=JSON.parse(t);e&&"object"==typeof e&&(o=e)}catch(t){this.logger.warn?.(`[local-session-history] failed to read sessions.json at ${i}: ${String(t)}`)}const r=s?[e,s]:[e];let n,c=s;for(const t of r){const e=o?.[t];if(!e||"object"!=typeof e)continue;const s=e,i="string"==typeof s.sessionFile?s.sessionFile.trim():"",r="string"==typeof s.sessionId?s.sessionId.trim():"";if(i&&(n=i),r&&!c&&(c=r),n)break}if(n){const e=path.isAbsolute(n)?n:path.join(t,n);return existsSync(e)?e:void 0}if(!c)return;const a=path.join(t,`${c}.jsonl`);return existsSync(a)?a:void 0}readTailLines(t){let e,s;try{e=statSync(t).size}catch(e){return this.logger.warn?.(`[local-session-history] failed to stat ${t}: ${String(e)}`),[]}if(e<=0)return[];try{s=openSync(t,"r");let i=e,o=1048576,r="";const n=[];for(;i>0&&o>0&&n.length<6400;){const t=Math.min(65536,i,o),e=i-t,c=Buffer.allocUnsafe(t),a=readSync(s,c,0,t,e);if(a<=0)break;i=e,o-=a;const l=(c.toString("utf-8",0,a)+r).split("\n");r=l.shift()??"";for(let t=l.length-1;t>=0;t--){const e=l[t]?.trim();if(e&&(n.push(e),n.length>=6400))break}}const c=r.trim();return c&&n.length<6400&&n.push(c),n}catch(e){return this.logger.warn?.(`[local-session-history] failed to tail ${t}: ${String(e)}`),[]}finally{if(void 0!==s)try{closeSync(s)}catch{}}}}function isRecord(t){return!!t&&"object"==typeof t&&!Array.isArray(t)}
1
+ import{closeSync,existsSync,openSync,readFileSync,readSync,statSync}from"node:fs";import path from"node:path";import{resolveOpenClawStateDir}from"../runtime/openclaw-state-dir.js";import{getOpenClawNativeSessions}from"../runtime/openclaw-native-sessions.js";export const LOCAL_HISTORY_LIMIT=200;export const LOCAL_HISTORY_TAIL_WINDOW_LINES=6400;export const LOCAL_HISTORY_TAIL_CHUNK_BYTES=65536;export const LOCAL_HISTORY_TAIL_MAX_BYTES=1048576;export function findLocalHistoryEntryByMessageId(e,t){for(let s=e.length-1;s>=0;s--){const i=e[s];if(!i)continue;const n=i.__openclaw;if(isRecord(n)&&n.id===t)return i;if(i.id===t)return i}}export class LocalSessionHistory{logger;stateDirOverride;agentsDirOverride;nativeSessions;constructor(e){this.logger=e.logger,this.stateDirOverride=e.stateDir,this.agentsDirOverride=e.agentsDir,this.nativeSessions=e.nativeSessions}readEntries(e){const{agentId:t,sessionKey:s,sessionId:i}=e;if(!t||!s)return[];const n=this.nativeSessions??getOpenClawNativeSessions();let r;if(n)try{const e=n.getEntry(t,s);if(!e?.sessionId)return[];r=n.readEvents(t,s,e.sessionId).slice(-6400).reverse().map(e=>JSON.stringify(e))}catch(e){return this.logger.warn?.(`[local-session-history] native transcript read unavailable: ${String(e)}`),[]}else{const e=this.resolveSessionsDir(t),n=this.resolveSessionFile(e,s,i);if(!n)return[];r=this.readTailLines(n)}if(0===r.length)return[];const o=[];for(const e of r){let t;try{t=JSON.parse(e)}catch{continue}if(!t||"object"!=typeof t)continue;const s=t;if("message"!==s.type)continue;const i=s.message;if(!i||"object"!=typeof i)continue;const n=i,r="string"==typeof n.role?n.role:void 0;if(!r)continue;const a="string"==typeof s.id?s.id:void 0,c=a?{id:a}:void 0,l=n.timestamp,f=s.timestamp,p="number"==typeof l||"string"==typeof l?l:"string"==typeof f?f:void 0,y={...n,role:r,...void 0!==p?{timestamp:p}:{},...c?{__openclaw:c}:{}};if(o.push(y),o.length>=200)break}return o.reverse()}resolveStateDir(){return this.stateDirOverride??resolveOpenClawStateDir()}resolveSessionsDir(e){return this.agentsDirOverride?path.join(this.agentsDirOverride,e,"sessions"):path.join(this.resolveStateDir(),"agents",e,"sessions")}resolveSessionFile(e,t,s){const i=path.join(e,"sessions.json");let n;if(existsSync(i))try{const e=readFileSync(i,"utf-8"),t=JSON.parse(e);t&&"object"==typeof t&&(n=t)}catch(e){this.logger.warn?.(`[local-session-history] failed to read sessions.json at ${i}: ${String(e)}`)}const r=s?[t,s]:[t];let o,a=s;for(const e of r){const t=n?.[e];if(!t||"object"!=typeof t)continue;const s=t,i="string"==typeof s.sessionFile?s.sessionFile.trim():"",r="string"==typeof s.sessionId?s.sessionId.trim():"";if(i&&(o=i),r&&!a&&(a=r),o)break}if(o){const t=path.isAbsolute(o)?o:path.join(e,o);return existsSync(t)?t:void 0}if(!a)return;const c=path.join(e,`${a}.jsonl`);return existsSync(c)?c:void 0}readTailLines(e){let t,s;try{t=statSync(e).size}catch(t){return this.logger.warn?.(`[local-session-history] failed to stat ${e}: ${String(t)}`),[]}if(t<=0)return[];try{s=openSync(e,"r");let i=t,n=1048576,r="";const o=[];for(;i>0&&n>0&&o.length<6400;){const e=Math.min(65536,i,n),t=i-e,a=Buffer.allocUnsafe(e),c=readSync(s,a,0,e,t);if(c<=0)break;i=t,n-=c;const l=(a.toString("utf-8",0,c)+r).split("\n");r=l.shift()??"";for(let e=l.length-1;e>=0;e--){const t=l[e]?.trim();if(t&&(o.push(t),o.length>=6400))break}}const a=r.trim();return a&&o.length<6400&&o.push(a),o}catch(t){return this.logger.warn?.(`[local-session-history] failed to tail ${e}: ${String(t)}`),[]}finally{if(void 0!==s)try{closeSync(s)}catch{}}}}function isRecord(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}
@@ -1 +1 @@
1
- export const HEARTBEAT_TOKEN="HEARTBEAT_OK";export const SILENT_REPLY_TOKEN="NO_REPLY";function escapeRegExp(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}const silentExactRegexByToken=new Map,silentTrailingRegexByToken=new Map,silentLeadingAttachedRegexByToken=new Map,silentLeadingRegexByToken=new Map;function getSilentExactRegex(e){const t=silentExactRegexByToken.get(e);if(t)return t;const i=new RegExp(`^\\s*${escapeRegExp(e)}\\s*$`,"i");return silentExactRegexByToken.set(e,i),i}function getSilentTrailingRegex(e){const t=silentTrailingRegexByToken.get(e);if(t)return t;const i=new RegExp(`(?:^|\\s+|\\*+)${escapeRegExp(e)}\\s*$`,"i");return silentTrailingRegexByToken.set(e,i),i}function getSilentLeadingAttachedRegex(e){const t=silentLeadingAttachedRegexByToken.get(e);if(t)return t;const i=escapeRegExp(e),n=new RegExp(`^\\s*(?:${i}\\s+)*${i}(?=[\\p{L}\\p{N}])`,"iu");return silentLeadingAttachedRegexByToken.set(e,n),n}function getSilentLeadingRegex(e){const t=silentLeadingRegexByToken.get(e);if(t)return t;const i=new RegExp(`^(?:\\s*${escapeRegExp(e)})+\\s*`,"i");return silentLeadingRegexByToken.set(e,i),i}export function isSilentReplyText(e,t="NO_REPLY"){return!!e&&getSilentExactRegex(t).test(e)}export function isSilentReplyEnvelopeText(e,t="NO_REPLY"){if(!e)return!1;const i=e.trim();if(!(i&&i.startsWith("{")&&i.endsWith("}")&&i.includes(t)))return!1;try{const e=JSON.parse(i);if(!e||"object"!=typeof e||Array.isArray(e))return!1;const n=e,r=Object.keys(n);return 1===r.length&&"action"===r[0]&&"string"==typeof n.action&&n.action.trim()===t}catch{return!1}}export function isSilentReplyPayloadText(e,t="NO_REPLY"){return isSilentReplyText(e,t)||isSilentReplyEnvelopeText(e,t)}export function stripSilentToken(e,t="NO_REPLY"){return e.replace(getSilentTrailingRegex(t),"").trim()}export function stripLeadingSilentToken(e,t="NO_REPLY"){return e.replace(getSilentLeadingRegex(t),"").trim()}export function startsWithSilentToken(e,t="NO_REPLY"){return!!e&&getSilentLeadingAttachedRegex(t).test(e)}export function isSilentReplyPrefixText(e,t="NO_REPLY"){if(!e)return!1;const i=e.trimStart();if(!i)return!1;if(i!==i.toUpperCase())return!1;const n=i.toUpperCase();if(!n)return!1;if(n.length<2)return!1;if(/[^A-Z_]/.test(n))return!1;const r=t.toUpperCase();return!!r.startsWith(n)&&(!!n.includes("_")||"NO_REPLY"===r&&"NO"===n)}const HEARTBEAT_TOKEN_AT_END_RE=new RegExp(`${escapeRegExp("HEARTBEAT_OK")}[^\\w]{0,4}$`);function stripTokenAtEdges(e){let t=e.trim();if(!t)return{text:"",didStrip:!1};if(!t.includes("HEARTBEAT_OK"))return{text:t,didStrip:!1};let i=!1,n=!0;for(;n;){n=!1;const e=t.trim();if(e.startsWith("HEARTBEAT_OK"))t=e.slice(12).trimStart(),i=!0,n=!0;else if(HEARTBEAT_TOKEN_AT_END_RE.test(e)){const r=e.lastIndexOf("HEARTBEAT_OK"),s=e.slice(0,r).trimEnd();t=s?`${s}${e.slice(r+12).trimStart()}`.trimEnd():"",i=!0,n=!0}}return{text:t.replace(/\s+/g," ").trim(),didStrip:i}}function stripHeartbeatMarkup(e){return e.replace(/<[^>]*>/g," ").replace(/&nbsp;/gi," ").replace(/^[*`~_]+/,"").replace(/[*`~_]+$/,"")}export function stripHeartbeatToken(e,t={}){if(!e)return{shouldSkip:!0,text:"",didStrip:!1};const i=e.trim();if(!i)return{shouldSkip:!0,text:"",didStrip:!1};const n=t.mode??"message",r=t.maxAckChars,s="string"==typeof r?Number(r):r,p=Math.max(0,"number"==typeof s&&Number.isFinite(s)?s:300),o=stripHeartbeatMarkup(i);if(!i.includes("HEARTBEAT_OK")&&!o.includes("HEARTBEAT_OK"))return{shouldSkip:!1,text:i,didStrip:!1};const a=stripTokenAtEdges(i),l=stripTokenAtEdges(o),c=a.didStrip&&a.text?a:l;if(!c.didStrip)return{shouldSkip:!1,text:i,didStrip:!1};if(!c.text)return{shouldSkip:!0,text:"",didStrip:!0};const x=c.text.trim();return"heartbeat"===n&&x.length<=p?{shouldSkip:!0,text:"",didStrip:!0}:{shouldSkip:!1,text:x,didStrip:!0}}export function normalizeStreamingText(e,t={}){if(!e)return{skip:!0};let i=e;if(!t.isHeartbeat&&i.includes("HEARTBEAT_OK")){const e=stripHeartbeatToken(i,{mode:"message"});if(e.didStrip&&t.onHeartbeatStrip?.(),e.shouldSkip)return{skip:!0};i=e.text}return isSilentReplyText(i,"NO_REPLY")||isSilentReplyPrefixText(i,"NO_REPLY")||isSilentReplyPrefixText(i,"HEARTBEAT_OK")?{skip:!0}:(i&&startsWithSilentToken(i,"NO_REPLY")&&(i=stripLeadingSilentToken(i,"NO_REPLY")),i?{skip:!1,text:i}:{skip:!0})}
1
+ function escapeRegExp(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}export const HEARTBEAT_TOKEN="HEARTBEAT_OK";export const SILENT_REPLY_TOKEN="NO_REPLY";const HARMONY_CHANNEL_MARKER_RE=/^\s*(?:set-thought\s+)?<[\w]*\|[^>]*>\s*$/,BOX_DRAWING_HR_ONLY_RE=/^\s*─{3,}\s*$/;export function isInternalFormattingArtifact(e){return!!e&&(HARMONY_CHANNEL_MARKER_RE.test(e)||BOX_DRAWING_HR_ONLY_RE.test(e))}function createTokenRegex(e){const t=new Map;return n=>{const i=t.get(n);if(i)return i;const s=e(escapeRegExp(n));return t.set(n,s),s}}const getSilentExactRegex=createTokenRegex(e=>new RegExp(`^\\s*${e}(?:\\s+${e})*\\s*$`,"i")),getSilentTrailingRegex=createTokenRegex(e=>new RegExp(`$(?<=((?:^|\\s+|\\*+)${e}(?:\\s+${e})*\\s*))`,"i"));function stripEdgePunctuation(e){const t=e.match(/^\p{P}+/u)?.[0].length??0,n=e.match(/$(?<=(\p{P}+))/u)?.[1]?.length??0;return e.slice(t,e.length-n)}export function isSilentReplyText(e,t="NO_REPLY"){return!!e&&(getSilentExactRegex(t).test(e)||getSilentExactRegex(t).test(stripEdgePunctuation(e.trim())))}function isSilentReplyJsonStringText(e,t="NO_REPLY"){if(!e)return!1;const n=e.trim();if(!n.startsWith('"')||!n.endsWith('"')||!n.includes(t))return!1;try{const e=JSON.parse(n);return"string"==typeof e&&e.trim()===t}catch{return!1}}export function isSilentReplyEnvelopeText(e,t="NO_REPLY"){if(!e)return!1;const n=e.trim();if(!(n&&n.startsWith("{")&&n.endsWith("}")&&n.includes(t)))return!1;try{const e=JSON.parse(n);if(!e||"object"!=typeof e||Array.isArray(e))return!1;const i=Object.keys(e);return 1===i.length&&"action"===i[0]&&"string"==typeof e.action&&e.action.trim()===t}catch{return!1}}const taggedReasoningPrefixRe=/^\s*<\s*(?:(?:antml:|mm:)?(?:think(?:ing)?|thought)|antthinking)\b[^<>]*>[\s\S]*?<\s*\/\s*(?:(?:antml:|mm:)?(?:think(?:ing)?|thought)|antthinking)\s*>\s*/i,openReasoningPrefixRe=/^\s*<\s*(?:(?:antml:|mm:)?(?:think(?:ing)?|thought)|antthinking)\b[^<>]*>/i,plainReasoningPrefixRe=/^\s*(?:think(?:ing)?|thought|analysis|reasoning)\s*:?\s*\r?\n/i;function stripLeadingReasoningBlocks(e){let t=e;for(;;){const e=t.replace(taggedReasoningPrefixRe,"");if(e===t)return t;t=e}}function stripFinalSilentToken(e,t){const n=escapeRegExp(t),i=e.replace(new RegExp(`(?:^|[\\s*.])${n}\\s*$`,"i"),"").trim();return i===e.trim()?null:i}const silentIntentTextRe=/^\s*(?:i|i'll|i\s+will|i'm|i\s+am|we|we'll|we\s+will|the\s+assistant|assistant|the\s+bot|bot|openclaw)\s+(?:(?:will\s+)?(?:stay|remain|keep|be)\s+(?:quiet|silent)(?:\s+(?:here|for\s+now|on\s+this|in\s+this\s+(?:chat|thread|channel|conversation)))?|(?:do\s+not|don't|dont|will\s+not|won't|would\s+not|should\s+not)\s+(?:reply|respond)(?:\s+(?:here|for\s+now|on\s+this|in\s+this\s+(?:chat|thread|channel|conversation)))?|(?:have|has)\s+nothing\s+(?:to|for)\s+(?:say|add|reply|respond))(?:[.!?]+)?\s*$/i;function hasSilentIntentFinalSilentToken(e,t){const n=stripFinalSilentToken(e,t);return null!==n&&(!n||silentIntentTextRe.test(n))}const substantiveAnswerCueRe=/\b(?:answer|here(?:'s|\s+is)|tell\s+them|you\s+(?:should|can|could|need|must)|please|try|use|send|service\s+is|resolved|retry|yes|no,|sure)\b/i,bareReasoningPlaceholderRe=/^\s*(?:(?:internal|private)\s+)?(?:reasoning|thinking|thoughts?|analysis)(?:\s+notes?)?\s*$/i;function hasPlainReasoningFinalSilentToken(e,t){const n=stripFinalSilentToken(e,t);if(null===n)return!1;if(!n||silentIntentTextRe.test(n))return!0;const i=n.split(/\r?\n/).map(e=>e.trim()).filter(Boolean),s=i.at(-1),r=i.slice(0,-1).join("\n");return Boolean(s&&silentIntentTextRe.test(s)&&r&&!substantiveAnswerCueRe.test(r))||bareReasoningPlaceholderRe.test(n)}function isReasoningPrefixedSilentReplyText(e,t="NO_REPLY"){if(!e)return!1;const n=e.trim();if(!n)return!1;const i=stripLeadingReasoningBlocks(n);if(i!==n)return isSilentReplyText(i,t)||hasSilentIntentFinalSilentToken(i,t);if(openReasoningPrefixRe.test(n)){const e=n.replace(openReasoningPrefixRe,"");return isSilentReplyText(e,t)||hasPlainReasoningFinalSilentToken(e,t)}if(!plainReasoningPrefixRe.test(n))return!1;const s=n.replace(plainReasoningPrefixRe,"");return isSilentReplyText(s,t)||hasPlainReasoningFinalSilentToken(s,t)}export function isSilentReplyPayloadText(e,t="NO_REPLY"){return isSilentReplyText(e,t)||isSilentReplyJsonStringText(e,t)||isSilentReplyEnvelopeText(e,t)||isReasoningPrefixedSilentReplyText(e,t)}export function stripSilentToken(e,t="NO_REPLY"){const n=getSilentTrailingRegex(t).exec(e)?.[1]?.length??0;return e.slice(0,e.length-n).trim()}const getSilentLeadingAttachedRegex=createTokenRegex(e=>new RegExp(`^\\s*(?:${e}\\s+)*${e}(?=[\\p{L}\\p{N}])`,"iu")),getSilentLeadingRegex=createTokenRegex(e=>new RegExp(`^\\s*${e}((?:\\s*${e})*)(\\s*)`,"i"));export function stripLeadingSilentToken(e,t="NO_REPLY"){return e.replace(getSilentLeadingRegex(t),"").trim()}export function startsWithSilentToken(e,t="NO_REPLY"){if(!e)return!1;if(getSilentLeadingAttachedRegex(t).test(e))return!0;const n=getSilentLeadingRegex(t).exec(e);return!(!n||!/[\r\n]/.test(n[2]??""))&&e.slice(n[0].length).trimStart().length>0}export function isSilentReplyPrefixText(e,t="NO_REPLY"){if(!e)return!1;const n=t.toUpperCase(),i=e.trimStart();if(!i||i.length>n.length)return!1;const s=i.toUpperCase();return i===s&&(!(s.length<2)&&(!!n.startsWith(s)&&(!!s.includes("_")||(s===n||(/[^A-Z_]/.test(n)?/[^A-Z_]/.test(s):"NO_REPLY"===n&&"NO"===s)))))}const HEARTBEAT_TOKEN_AT_END_RE=new RegExp(`${escapeRegExp("HEARTBEAT_OK")}[^\\w]{0,4}$`);function stripTokenAtEdges(e){let t=e.trim();if(!t)return{text:"",didStrip:!1};if(!t.includes("HEARTBEAT_OK"))return{text:t,didStrip:!1};let n=!1,i=!0;for(;i;){i=!1;const e=t.trim();if(e.startsWith("HEARTBEAT_OK"))t=e.slice(12).trimStart(),n=!0,i=!0;else if(HEARTBEAT_TOKEN_AT_END_RE.test(e)){const s=e.lastIndexOf("HEARTBEAT_OK"),r=e.slice(0,s).trimEnd();t=r?`${r}${e.slice(s+12).trimStart()}`.trimEnd():"",n=!0,i=!0}}return{text:t.replace(/\s+/g," ").trim(),didStrip:n}}function stripHeartbeatMarkup(e){return e.replace(/<[^>]*>/g," ").replace(/&nbsp;/gi," ").replace(/^[*`~_]+/,"").replace(/[*`~_]+$/,"")}export function stripHeartbeatToken(e,t={}){if(!e)return{shouldSkip:!0,text:"",didStrip:!1};const n=e.trim();if(!n)return{shouldSkip:!0,text:"",didStrip:!1};const i=t.mode??"message",s=t.maxAckChars,r="string"==typeof s?Number(s):s,o=Math.max(0,"number"==typeof r&&Number.isFinite(r)?r:300),l=stripHeartbeatMarkup(n);if(!n.includes("HEARTBEAT_OK")&&!l.includes("HEARTBEAT_OK"))return{shouldSkip:!1,text:n,didStrip:!1};const a=stripTokenAtEdges(n),p=stripTokenAtEdges(l),c=a.didStrip&&a.text?a:p;if(!c.didStrip)return{shouldSkip:!1,text:n,didStrip:!1};if(!c.text)return{shouldSkip:!0,text:"",didStrip:!0};const u=c.text.trim();return"heartbeat"===i&&u.length<=o?{shouldSkip:!0,text:"",didStrip:!0}:{shouldSkip:!1,text:u,didStrip:!0}}export function normalizeStreamingText(e,t={}){if(!e)return{skip:!0};let n=e;if(!t.isHeartbeat&&n.includes("HEARTBEAT_OK")){const e=stripHeartbeatToken(n,{mode:"message"});if(e.didStrip&&t.onHeartbeatStrip?.(),e.shouldSkip)return{skip:!0};n=e.text}return isSilentReplyText(n,"NO_REPLY")||isSilentReplyPrefixText(n,"NO_REPLY")||isSilentReplyPrefixText(n,"HEARTBEAT_OK")?{skip:!0}:(n&&startsWithSilentToken(n,"NO_REPLY")&&(n=stripLeadingSilentToken(n,"NO_REPLY")),n?{skip:!1,text:n}:{skip:!0})}
@@ -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",{sessionKey: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 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}}
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- 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{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){let t,r;try{t=e.runtime?.config?.loadConfig?.()}catch{}try{r=resolveConfigWithMeta({pluginConfig:e.pluginConfig??{},openclawConfig:t})}catch(t){return e.logger.error?.(`[beeos-claw] Failed to resolve config: ${t instanceof Error?t.message:String(t)}`),null}const o=r.config,n=buildThirdPartyChannelSet(t);n.size>0&&e.logger.info?.(`[beeos-claw] Third-party channels detected: ${[...n].join(", ")}`);const a=o.log.enabled?createObsEventWriter({dir:o.log.dir,redaction:{gatewayToken:o.gateway.token},logger:e.logger}):()=>{};a({component:"plugin",domain:"lifecycle",name:"lifecycle.startup",severity:"info",payload:{pluginVersion:PLUGIN_VERSION,pid:process.pid,nodeVersion:process.version,cwd:process.cwd()}}),a({component:"plugin",domain:"config",name:"config.resolved",severity:"info",payload:{config:o,sources:r.sources,openclawConfigPath:r.openclawConfigPath}});for(const t of r.validation)a({component:"plugin",domain:"config",name:"config.validation",severity:t.severity,summary:t.message,error:{code:t.code,message:t.message,nextSteps:t.nextSteps}}),"error"===t.severity?e.logger.error?.(`[beeos-claw] ${t.message}`):e.logger.warn?.(`[beeos-claw] ${t.message}`);if(r.validation.some(e=>"error"===e.severity)&&e.logger.error?.("[beeos-claw] Plugin has configuration errors — some features may not work."),o.keyFile)try{agentKeys=loadAgentKeyPair(o.keyFile);const t=Buffer.from(agentKeys.publicKey).toString("base64");e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (publicKey: ${t})`)}catch(t){e.logger.warn?.(`[beeos-claw] Failed to load agent key pair: ${t}`),e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no key pair — signed requests disabled)`)}else e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no keyFile configured)`);return agentKeys&&o.agentGatewayUrl&&(setUploadDeps({gatewayUrl:o.agentGatewayUrl,keys:agentKeys,logger:e.logger}),registerLocalFileExtractor(browserScreenshotExtractor),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;sharedSessionMutationCoordinator??=new SessionMutationCoordinator(sharedSessionWriteGate,new OpenClawSessionWriteQuiescence(e.logger,{agentId:o.gateway.agentId||"main"}),e.logger,{onRecoveryRequired:t=>{scheduleProcessRuntimeRestart(t,e.logger)}});const u=sharedSessionMutationCoordinator;if(!sharedAcpServer){const t=sharedRuntimeAuthority&&o.agentGatewayUrl?new AgentGatewayTemplateOriginReporter({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:sharedRuntimeAuthority.boundAgentFetch}):void 0;sharedAcpServer=new ACPServer(o,e.logger,u,n,i,l,a,sharedTerminalManager,sharedRuntimeAuthority,void 0,sharedMcpProjection,sharedMcpCredentialVault,s,{gateway:d.client,agentProjection:sharedAgentProjection,markAgentProjectionDirty:()=>sharedAgentProjection?.markDirty(),templateOriginReporter:t,agentMappings:sharedAgentMappings,agentCapabilities:sharedRuntimeAgentCapabilities,createRunOutputRouter:createPeerRunOutputRouterFactory(new MessageServiceRunOutputChannel({getClient:()=>sharedMessageClientOwner.current?.client??null,logger:e.logger,writeObs:n})),runExclusive:(t,r,o)=>sharedImHotPathDeferral.runExclusive(t,r,e.logger,o)})}const m=sharedAcpServer,p=()=>sharedAgentMappings.snapshot()?.values().next().value;r.current=m,sharedMcpNativeProjectionNudge.request({registrationMode:e.registrationMode,fingerprint:fingerprintMcpServers(t.openclawConfig),logger:e.logger});const y=sharedPromptRunStore,{enabled:h,principalId:f,a2aEnabled:w}=o.messageBus;let v;o.agentGatewayUrl&&agentKeys&&(v=createTokenProvider({agentGatewayUrl:o.agentGatewayUrl,identity:agentKeyPairToIdentity(agentKeys)}));const C=process.env.MESSAGE_RUNTIME_DELIVERY_KEY?.trim(),b=f||process.env.BEEOS_PRINCIPAL_ID||"",R=v?sharedMessageClientOwner.configure({tokenProvider:v,identityId:b,...o.messageBus.serviceUrl?{serviceUrl:o.messageBus.serviceUrl}:{},logger:e.logger,...sharedRuntimeAuthority&&C?{runtimeDelivery:{scopedDeliveryKey:C,authority:{currentLease:()=>{const e=sharedRuntimeAuthority?.authorityLease,t=sharedRuntimeAuthority?.currentConsumerBinding();return e&&t?{...e,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration}:null}}}}:{}}):void 0,_=sharedRuntimeAuthority,S=o.messageBus.serviceUrl;if(R&&_&&S&&C){const e=R.nodeTransport;m.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:S,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 E=sharedRuntimeAuthority?.journal??new FileRuntimeCommandJournal(join(c,"runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:t=>e.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=threshold error=${t instanceof Error?t.message:String(t)}`)}),O=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})}:{},...R&&o.agentGatewayUrl&&s&&C?{transportAdapter:new RuntimeServiceTransportReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl,messageService:R.nodeTransport,messageClient:R.client,configuredServiceUrl:o.messageBus.serviceUrl,boundAgentFetch:s,scopedDeliveryKey:C})}:{},commandJournal:E,poisonJournal:new FileRuntimePoisonDeliveryJournal(join(c,"runtime-poison-deliveries.json")),onConsumerDeliveryError:t=>{const r=t instanceof Error?`${t.name}: ${t.message}${"status"in t?` status=${t.status}`:""}${"code"in t?` code=${t.code}`:""}\n${t.stack??""}`:String(t);e.logger.error?.(`[runtime-service] delivery worker failed before acknowledgement; delivery retained for retry: ${r}`)},onConsumerLeaseStarvation:({starvedForMs:t,recovered:r})=>{const o=Math.round(t/1e3);r?e.logger.info?.(`[runtime-service] runtime authority lease restored after ${o}s without delivery reads`):e.logger.warn?.(`[runtime-service] no runtime authority lease for ${o}s; delivery reads suspended, queued runtime operations (model switch, session control) will not be consumed`)},onConsumerRequestFailure:t=>e.logger.error?.(`[runtime-service] request failed operationId=${t.operationId} method=${t.method}: ${t.error instanceof Error?t.error.stack??t.error.message:String(t.error)}`)});if(sharedRuntimeAuthority?.setRuntimeServiceMethods("ready"===n.status?n.serviceMethods:[]),sharedRuntimeAuthority?.setRuntimeResourceBindingContractRevision("ready"===n.status?n.resourceBindingContractRevision:void 0),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(O);try{await m.start(o.bridge.url||void 0)}catch(e){throw await d.release(O),e}e.logger.info?.(`[beeos-claw] ACP Server started (total service start: ${Date.now()-t}ms)`)},async stop(){try{await d.release(O)}finally{await m.stop()}await E.checkpoint(),e.logger.info?.("[runtime-service] command journal checkpoint completed"),e.logger.info?.("[beeos-claw] ACP Server stopped")}});if(h&&void 0!==v&&v){const t=sharedMessageClientOwner.createRegistrationToken(),r=async(e,t)=>{const r=await v(e);if(!r.identityId||r.identityId!==e)throw new Error("Message SDK token identity did not match the current runtime authority");return{principalId:e,runtimeEpoch:t}},a=(e,t,r)=>({getLocalAgentId:()=>t,resolveLocalAgentId:e=>e===r?t:void 0,getDefaultPlatformAgent:()=>({instanceId:e.principalId}),ensureImInboundSession:(e,t,r,o)=>{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={leaseId:i.leaseId,runtimeEpoch:i.runtimeEpoch};sharedMSConnectionStatus.bind(c);const d=new Map,p=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=d.get(i.id);return c&&c.localAgentId===r||(c={localAgentId:r,inbound:createImInboundHandler({automationTurnPolicy:sharedAutomationTurnPolicy,runtime:e.runtime,pluginConfig:e.pluginConfig,client:l,localServer:a(s,r,i.id),promptRunStore:y,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)}:{}})},d.set(i.id,c)),[{principalId:s.principalId,targetAgentId:i.id,runtimeEpoch:t.runtimeEpoch,inbound:c.inbound}]}):[]},executionJournal:g,logger:e.logger}),h=sharedAgentMappings.subscribe(()=>{sharedActiveMessageConsumer===p&&p.refresh()}),f=sharedRuntimeAuthority?.subscribeAuthority(()=>{sharedActiveMessageConsumer===p&&p.refresh()})??(()=>{}),w=new MessageClientTerminalStateRecovery({logger:e.logger,isCurrent:()=>{if(sharedActiveMessageConsumer!==p)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 v=l.getSnapshot().connection;sharedMSConnectionStatus.setIfCurrent(c,v);const C=l.subscribe(()=>{const t=l.getSnapshot();t.connection!==v&&(v=t.connection,sharedMSConnectionStatus.setIfCurrent(c,t.connection),"connected"===t.connection?(w.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"}`),w.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=p,p.start();try{await l.connect()}catch(e){throw sharedActiveMessageConsumer===p&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),w.dispose(),await p.stop(),h(),f(),C(),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===p&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),w.dispose(),await p.stop(),f(),C(),await l.disconnect(),e.logger.info?.("[beeos-claw] MessageClient disconnected")}})},disconnect:async()=>{await sharedMessageClientOwner.release(t)}}}):void 0;e.registerService({id:"beeos-message-bus",async start(){i?i.start():e.logger.warn?.("[beeos-claw] Message bus requires runtime authority; waiting is unavailable without Agent Gateway runtime identity")},async stop(){await(i?.stop())}})}else h?e.logger.warn?.("[beeos-claw] Message bus enabled but Agent Gateway token provider is unavailable — disabled"):e.logger.info?.(`[beeos-claw] Message bus disabled (enabled=${h})`);if(!1!==w&&o.agentGatewayUrl){const t=e=>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,()=>p()?.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=p();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:p,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),y=new Set([RENDER_UI_TOOL_NAME,CLOSE_UI_TOOL_NAME,RENDER_FORM_TOOL_NAME]),h=256,f=new Map;if("function"==typeof e.on){try{e.on("before_prompt_build",(e,t)=>{if(t?.sessionKey){if(f.size>=h&&!f.has(t.sessionKey)){const e=f.keys().next().value;void 0!==e&&f.delete(e)}f.set(t.sessionKey,t.channelId),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),!y.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");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{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){let t,r;try{t=e.runtime?.config?.loadConfig?.()}catch{}try{r=resolveConfigWithMeta({pluginConfig:e.pluginConfig??{},openclawConfig:t})}catch(t){return e.logger.error?.(`[beeos-claw] Failed to resolve config: ${t instanceof Error?t.message:String(t)}`),null}const o=r.config,n=buildThirdPartyChannelSet(t);n.size>0&&e.logger.info?.(`[beeos-claw] Third-party channels detected: ${[...n].join(", ")}`);const a=o.log.enabled?createObsEventWriter({dir:o.log.dir,redaction:{gatewayToken:o.gateway.token},logger:e.logger}):()=>{};a({component:"plugin",domain:"lifecycle",name:"lifecycle.startup",severity:"info",payload:{pluginVersion:PLUGIN_VERSION,pid:process.pid,nodeVersion:process.version,cwd:process.cwd()}}),a({component:"plugin",domain:"config",name:"config.resolved",severity:"info",payload:{config:o,sources:r.sources,openclawConfigPath:r.openclawConfigPath}});for(const t of r.validation)a({component:"plugin",domain:"config",name:"config.validation",severity:t.severity,summary:t.message,error:{code:t.code,message:t.message,nextSteps:t.nextSteps}}),"error"===t.severity?e.logger.error?.(`[beeos-claw] ${t.message}`):e.logger.warn?.(`[beeos-claw] ${t.message}`);if(r.validation.some(e=>"error"===e.severity)&&e.logger.error?.("[beeos-claw] Plugin has configuration errors — some features may not work."),o.keyFile)try{agentKeys=loadAgentKeyPair(o.keyFile);const t=Buffer.from(agentKeys.publicKey).toString("base64");e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (publicKey: ${t})`)}catch(t){e.logger.warn?.(`[beeos-claw] Failed to load agent key pair: ${t}`),e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no key pair — signed requests disabled)`)}else e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no keyFile configured)`);return agentKeys&&o.agentGatewayUrl&&(setUploadDeps({gatewayUrl:o.agentGatewayUrl,keys:agentKeys,logger:e.logger}),registerLocalFileExtractor(browserScreenshotExtractor),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;sharedSessionMutationCoordinator??=new SessionMutationCoordinator(sharedSessionWriteGate,new OpenClawSessionWriteQuiescence(e.logger,{agentId:o.gateway.agentId||"main"}),e.logger,{onRecoveryRequired:t=>{scheduleProcessRuntimeRestart(t,e.logger)}});const u=sharedSessionMutationCoordinator;if(!sharedAcpServer){const t=sharedRuntimeAuthority&&o.agentGatewayUrl?new AgentGatewayTemplateOriginReporter({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:sharedRuntimeAuthority.boundAgentFetch}):void 0;sharedAcpServer=new ACPServer(o,e.logger,u,n,i,l,a,sharedTerminalManager,sharedRuntimeAuthority,void 0,sharedMcpProjection,sharedMcpCredentialVault,s,{gateway:d.client,agentProjection:sharedAgentProjection,markAgentProjectionDirty:()=>sharedAgentProjection?.markDirty(),templateOriginReporter:t,agentMappings:sharedAgentMappings,agentCapabilities:sharedRuntimeAgentCapabilities,createRunOutputRouter:createPeerRunOutputRouterFactory(new MessageServiceRunOutputChannel({getClient:()=>sharedMessageClientOwner.current?.client??null,logger:e.logger,writeObs:n})),runExclusive:(t,r,o)=>sharedImHotPathDeferral.runExclusive(t,r,e.logger,o)})}const m=sharedAcpServer,p=()=>sharedAgentMappings.snapshot()?.values().next().value;r.current=m,sharedMcpNativeProjectionNudge.request({registrationMode:e.registrationMode,fingerprint:fingerprintMcpServers(t.openclawConfig),logger:e.logger});const y=sharedPromptRunStore,{enabled:h,principalId:f,a2aEnabled:w}=o.messageBus;let v;o.agentGatewayUrl&&agentKeys&&(v=createTokenProvider({agentGatewayUrl:o.agentGatewayUrl,identity:agentKeyPairToIdentity(agentKeys)}));const C=process.env.MESSAGE_RUNTIME_DELIVERY_KEY?.trim(),b=f||process.env.BEEOS_PRINCIPAL_ID||"",R=v?sharedMessageClientOwner.configure({tokenProvider:v,identityId:b,...o.messageBus.serviceUrl?{serviceUrl:o.messageBus.serviceUrl}:{},logger:e.logger,...sharedRuntimeAuthority&&C?{runtimeDelivery:{scopedDeliveryKey:C,authority:{currentLease:()=>{const e=sharedRuntimeAuthority?.authorityLease,t=sharedRuntimeAuthority?.currentConsumerBinding();return e&&t?{...e,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration}:null}}}}:{}}):void 0,_=sharedRuntimeAuthority,S=o.messageBus.serviceUrl;if(R&&_&&S&&C){const e=R.nodeTransport;m.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:S,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 E=sharedRuntimeAuthority?.journal??new FileRuntimeCommandJournal(join(c,"runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:t=>e.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=threshold error=${t instanceof Error?t.message:String(t)}`)}),O=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})}:{},...R&&o.agentGatewayUrl&&s&&C?{transportAdapter:new RuntimeServiceTransportReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl,messageService:R.nodeTransport,messageClient:R.client,configuredServiceUrl:o.messageBus.serviceUrl,boundAgentFetch:s,scopedDeliveryKey:C})}:{},commandJournal:E,poisonJournal:new FileRuntimePoisonDeliveryJournal(join(c,"runtime-poison-deliveries.json")),onConsumerDeliveryError:t=>{const r=t instanceof Error?`${t.name}: ${t.message}${"status"in t?` status=${t.status}`:""}${"code"in t?` code=${t.code}`:""}\n${t.stack??""}`:String(t);e.logger.error?.(`[runtime-service] delivery worker failed before acknowledgement; delivery retained for retry: ${r}`)},onConsumerLeaseStarvation:({starvedForMs:t,recovered:r})=>{const o=Math.round(t/1e3);r?e.logger.info?.(`[runtime-service] runtime authority lease restored after ${o}s without delivery reads`):e.logger.warn?.(`[runtime-service] no runtime authority lease for ${o}s; delivery reads suspended, queued runtime operations (model switch, session control) will not be consumed`)},onConsumerRequestFailure:t=>e.logger.error?.(`[runtime-service] request failed operationId=${t.operationId} method=${t.method}: ${t.error instanceof Error?t.error.stack??t.error.message:String(t.error)}`)});if(sharedRuntimeAuthority?.setRuntimeServiceMethods("ready"===n.status?n.serviceMethods:[]),sharedRuntimeAuthority?.setRuntimeResourceBindingContractRevision("ready"===n.status?n.resourceBindingContractRevision:void 0),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(O);try{await m.start(o.bridge.url||void 0)}catch(e){throw await d.release(O),e}e.logger.info?.(`[beeos-claw] ACP Server started (total service start: ${Date.now()-t}ms)`)},async stop(){try{await d.release(O)}finally{await m.stop()}await E.checkpoint(),e.logger.info?.("[runtime-service] command journal checkpoint completed"),e.logger.info?.("[beeos-claw] ACP Server stopped")}});if(h&&void 0!==v&&v){const t=sharedMessageClientOwner.createRegistrationToken(),r=async(e,t)=>{const r=await v(e);if(!r.identityId||r.identityId!==e)throw new Error("Message SDK token identity did not match the current runtime authority");return{principalId:e,runtimeEpoch:t}},a=(e,t,r)=>({getLocalAgentId:()=>t,resolveLocalAgentId:e=>e===r?t:void 0,getDefaultPlatformAgent:()=>({instanceId:e.principalId}),ensureImInboundSession:(e,t,r,o)=>{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={leaseId:i.leaseId,runtimeEpoch:i.runtimeEpoch};sharedMSConnectionStatus.bind(c);const d=new Map,p=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=d.get(i.id);return c&&c.localAgentId===r||(c={localAgentId:r,inbound:createImInboundHandler({automationTurnPolicy:sharedAutomationTurnPolicy,runtime:e.runtime,pluginConfig:e.pluginConfig,client:l,localServer:a(s,r,i.id),promptRunStore:y,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)}:{}})},d.set(i.id,c)),[{principalId:s.principalId,targetAgentId:i.id,runtimeEpoch:t.runtimeEpoch,inbound:c.inbound}]}):[]},executionJournal:g,logger:e.logger}),h=sharedAgentMappings.subscribe(()=>{sharedActiveMessageConsumer===p&&p.refresh()}),f=sharedRuntimeAuthority?.subscribeAuthority(()=>{sharedActiveMessageConsumer===p&&p.refresh()})??(()=>{}),w=new MessageClientTerminalStateRecovery({logger:e.logger,isCurrent:()=>{if(sharedActiveMessageConsumer!==p)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 v=l.getSnapshot().connection;sharedMSConnectionStatus.setIfCurrent(c,v);const C=l.subscribe(()=>{const t=l.getSnapshot();t.connection!==v&&(v=t.connection,sharedMSConnectionStatus.setIfCurrent(c,t.connection),"connected"===t.connection?(w.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"}`),w.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=p,p.start();try{await l.connect()}catch(e){throw sharedActiveMessageConsumer===p&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),w.dispose(),await p.stop(),h(),f(),C(),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===p&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),w.dispose(),await p.stop(),f(),C(),await l.disconnect(),e.logger.info?.("[beeos-claw] MessageClient disconnected")}})},disconnect:async()=>{await sharedMessageClientOwner.release(t)}}}):void 0;e.registerService({id:"beeos-message-bus",async start(){i?i.start():e.logger.warn?.("[beeos-claw] Message bus requires runtime authority; waiting is unavailable without Agent Gateway runtime identity")},async stop(){await(i?.stop())}})}else h?e.logger.warn?.("[beeos-claw] Message bus enabled but Agent Gateway token provider is unavailable — disabled"):e.logger.info?.(`[beeos-claw] Message bus disabled (enabled=${h})`);if(!1!==w&&o.agentGatewayUrl){const t=e=>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,()=>p()?.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=p();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:p,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),y=new Set([RENDER_UI_TOOL_NAME,CLOSE_UI_TOOL_NAME,RENDER_FORM_TOOL_NAME]),h=256,f=new Map;if("function"==typeof e.on){try{e.on("before_prompt_build",(e,t)=>{if(t?.sessionKey){if(f.size>=h&&!f.has(t.sessionKey)){const e=f.keys().next().value;void 0!==e&&f.delete(e)}f.set(t.sessionKey,t.channelId),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),!y.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;
@@ -0,0 +1 @@
1
+ import{existsSync,readFileSync,readdirSync,realpathSync}from"node:fs";import{createRequire}from"node:module";import{dirname,join,resolve}from"node:path";const adapters=new Map;export function getOpenClawNativeSessions(){const e=process.env.OPENCLAW_ROOT?.trim()||hostRoot();if(!e)return;const n=JSON.parse(readFileSync(join(e,"package.json"),"utf8"));if("openclaw"!==n.name)return;if("2026.9.8"!==n.version)throw new Error(`unsupported OpenClaw native session contract: ${n.version}`);const t=adapters.get(e);if(t)return t;const r=createRequire(join(e,"package.json")),s=r("openclaw/plugin-sdk/session-store-runtime"),o=join(e,"dist"),i=readdirSync(o).filter(e=>/^runs-.*\.mjs$/u.test(e)&&/export \{[^}]*\bwaitForEmbeddedAgentRunEnd\s*[,}]/u.test(readFileSync(join(o,e),"utf8")));if(1!==i.length)throw new Error("OpenClaw native run-end facade is missing or ambiguous");const a=r(join(o,i[0]));if("function"!=typeof s.getSessionEntry||"function"!=typeof s.loadTranscriptEventsSync||"function"!=typeof s.readTranscriptStatsSync||"function"!=typeof a.waitForEmbeddedAgentRunEnd)throw new Error("OpenClaw native session contract is unavailable");const c={getEntry:(e,n)=>s.getSessionEntry({agentId:e,sessionKey:n,readConsistency:"latest"}),readEvents:(e,n,t)=>{const r={agentId:e,sessionKey:n,sessionId:t},o=s.readTranscriptStatsSync(r);if(!Number.isSafeInteger(o.sizeBytes)||o.sizeBytes<0||o.sizeBytes>1048576||!Number.isSafeInteger(o.eventCount)||o.eventCount<0||o.eventCount>6400)throw new Error("native transcript exceeds the local recovery budget; use Gateway history pagination");return s.loadTranscriptEventsSync(r)},waitForRunEnd:(e,n)=>a.waitForEmbeddedAgentRunEnd(e,n)};return adapters.set(e,c),c}function hostRoot(){if(!process.argv[1]||!existsSync(process.argv[1]))return;let e=dirname(realpathSync(resolve(process.argv[1])));for(let n=0;n<4;n++){const n=join(e,"package.json");if(existsSync(n)&&"openclaw"===JSON.parse(readFileSync(n,"utf8")).name)return e;const t=dirname(e);if(t===e)break;e=t}}
@@ -1 +1 @@
1
- import{existsSync,readFileSync}from"node:fs";import{isAbsolute,join}from"node:path";import{resolveOpenClawStateDir}from"./openclaw-state-dir.js";const DEFAULT_POLL_INTERVAL_MS=250,DEFAULT_QUIET_CONFIRMATION_MS=750;export class OpenClawSessionWriteQuiescence{logger;stateDir;defaultAgentId;pollIntervalMs;quietConfirmationMs;now;sleep;constructor(e,s={}){this.logger=e,this.stateDir=s.stateDir??resolveOpenClawStateDir(),this.defaultAgentId=s.agentId??"main",this.pollIntervalMs=s.pollIntervalMs??250,this.quietConfirmationMs=s.quietConfirmationMs??750,this.now=s.now??(()=>Date.now()),this.sleep=s.sleep??(e=>new Promise(s=>{const t=setTimeout(s,e);t.unref?.()}))}async waitForQuiescence(e,s){const t=this.now();let i,o,n;for(;this.now()-t<s;){const s=n??this.resolveLockPath(e);if(s){if(o=s,n=s,existsSync(s))i=void 0;else if(i??=this.now(),this.now()-i>=this.quietConfirmationMs)return{quiescent:!0,reason:"unlocked",lockPath:s,elapsedMs:this.now()-t};await this.sleep(this.pollIntervalMs)}else await this.sleep(this.pollIntervalMs)}return this.logger.warn?.(`[im] session writer did not become quiescent sessionKey=${e} timeout_ms=${s}`),{quiescent:!1,reason:o?"timeout":"session_unresolved",...o?{lockPath:o}:{},elapsedMs:this.now()-t}}resolveLockPath(e){const s=/^agent:([^:]+):/.exec(e)?.[1]??this.defaultAgentId,t=join(this.stateDir,"agents",s,"sessions"),i=join(t,"sessions.json");if(!existsSync(i))return;let o;try{const s=JSON.parse(readFileSync(i,"utf8"));if(!s||"object"!=typeof s)return;const t=s[e];t&&"object"==typeof t&&(o=t)}catch(s){return void this.logger.debug?.(`[im] session index read deferred sessionKey=${e} error=${s instanceof Error?s.message:String(s)}`)}if(!o)return;const n="string"==typeof o.sessionFile?o.sessionFile.trim():"",r="string"==typeof o.sessionId?o.sessionId.trim():"",l=n?isAbsolute(n)?n:join(t,n):r?join(t,`${r}.jsonl`):"";return l&&existsSync(l)?`${l}.lock`:void 0}}
1
+ import{existsSync,readFileSync}from"node:fs";import{isAbsolute,join}from"node:path";import{resolveOpenClawStateDir}from"./openclaw-state-dir.js";import{getOpenClawNativeSessions}from"./openclaw-native-sessions.js";const DEFAULT_POLL_INTERVAL_MS=250,DEFAULT_QUIET_CONFIRMATION_MS=750;export class OpenClawSessionWriteQuiescence{logger;stateDir;defaultAgentId;pollIntervalMs;quietConfirmationMs;now;sleep;nativeSessions;constructor(e,s={}){this.logger=e,this.stateDir=s.stateDir??resolveOpenClawStateDir(),this.nativeSessions=s.nativeSessions,this.defaultAgentId=s.agentId??"main",this.pollIntervalMs=s.pollIntervalMs??250,this.quietConfirmationMs=s.quietConfirmationMs??750,this.now=s.now??(()=>Date.now()),this.sleep=s.sleep??(e=>new Promise(s=>{const t=setTimeout(s,e);t.unref?.()}))}async waitForQuiescence(e,s){const t=this.now(),i=this.nativeSessions??getOpenClawNativeSessions();if(i){const n="agent"===e.split(":")[0]?e.split(":")[1]:this.defaultAgentId,o=i.getEntry(n,e);if(!o?.sessionId)return{quiescent:!1,reason:"session_unresolved",elapsedMs:this.now()-t};const r=await i.waitForRunEnd(o.sessionId,s),a=i.getEntry(n,e),l=a?.sessionId===o.sessionId&&a.lifecycleRevision===o.lifecycleRevision;return{quiescent:r&&l,reason:l?r?"unlocked":"timeout":"session_unresolved",elapsedMs:this.now()-t}}let n,o,r;for(;this.now()-t<s;){const s=r??this.resolveLockPath(e);if(s){if(o=s,r=s,existsSync(s))n=void 0;else if(n??=this.now(),this.now()-n>=this.quietConfirmationMs)return{quiescent:!0,reason:"unlocked",lockPath:s,elapsedMs:this.now()-t};await this.sleep(this.pollIntervalMs)}else await this.sleep(this.pollIntervalMs)}return this.logger.warn?.(`[im] session writer did not become quiescent sessionKey=${e} timeout_ms=${s}`),{quiescent:!1,reason:o?"timeout":"session_unresolved",...o?{lockPath:o}:{},elapsedMs:this.now()-t}}resolveLockPath(e){const s=/^agent:([^:]+):/.exec(e)?.[1]??this.defaultAgentId,t=join(this.stateDir,"agents",s,"sessions"),i=join(t,"sessions.json");if(!existsSync(i))return;let n;try{const s=JSON.parse(readFileSync(i,"utf8"));if(!s||"object"!=typeof s)return;const t=s[e];t&&"object"==typeof t&&(n=t)}catch(s){return void this.logger.debug?.(`[im] session index read deferred sessionKey=${e} error=${s instanceof Error?s.message:String(s)}`)}if(!n)return;const o="string"==typeof n.sessionFile?n.sessionFile.trim():"",r="string"==typeof n.sessionId?n.sessionId.trim():"",a=o?isAbsolute(o)?o:join(t,o):r?join(t,`${r}.jsonl`):"";return a&&existsSync(a)?`${a}.lock`:void 0}}
@@ -1 +1 @@
1
- import{randomUUID}from"node:crypto";import{performance}from"node:perf_hooks";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";import{ClearConversationUseCase}from"./conversation-clear-use-case.js";import{AROUTER_PROVIDER_ID}from"../media/arouter-config.js";import{openClawSessionKeysFromList}from"./openclaw-session-key-resolution.js";async function modelSwitchTimingSpan(e,t,i){const s=performance.now();let n="succeeded";try{return await i()}catch(e){throw n="failed",e}finally{e&&process.stderr.write(`[MODEL_SWITCH_TIMING] component=runtime operationId=${e} stage=${t} outcome=${n} durationMs=${(performance.now()-s).toFixed(1)}\n`)}}export class RuntimeSessionControlService{clearRuntime;fallback;activeRuns;constructor(e,t,i){this.clearRuntime=e,this.fallback=t,this.activeRuns=i}async execute(e,t,i){const s=e;if("session.clear"===s.name){if(!i.historyBoundary)throw new RuntimeCommandError("INVALID_PARAMS",`${i.origin} clear attempt lacks its history boundary`);const e=s.context.conversation;return await new ClearConversationUseCase(i.historyBoundary,this.clearRuntime).execute({operationId:s.context.operationId,conversationId:e.runtimeConversationUid,runtimeSessionKey:e.runtimeSessionKey,runtimeEpoch:s.context.dispatchEpoch})}if("session.cancel"===s.name){if(!this.activeRuns)throw new RuntimeCommandError("INVALID_PARAMS","session.cancel active-run authority is not configured");const e=s;return await this.activeRuns.cancelExact({runtimeConversationUid:e.context.conversation.runtimeConversationUid,runtimeSessionKey:e.context.conversation.runtimeSessionKey,historyGeneration:e.context.historyGeneration,targetMessageId:e.params.targetMessageId}),{}}if(!this.fallback)throw new Error(`session command delegate is not configured: ${s.name}`);return await this.fallback.execute(s,t,i)}async projectCommitted(e,t,i){return await(this.fallback?.projectCommitted?.(e,t,i))}}export class OpenClawSessionMetadataService{metadata;constructor(e){this.metadata="function"==typeof e?new OpenClawGatewaySessionMetadataPort(e):e}async execute(e,t,i){const s=e,n=s.context.conversation.runtimeSessionKey,o={lease:t,attempt:i};switch(s.name){case"session.load":return await this.metadata.load(n,o);case"session.rename":{const e=s.params.title.slice(0,64);return await this.metadata.rename(n,e,o)}case"session.delete":return await this.metadata.delete(n,o);case"session.setMode":{const e=s.params.mode;return await this.metadata.setMode(n,e,o)}case"session.setModel":{const e=s.params.modelOverrideId;if(null!==e&&("string"!=typeof e||0===e.length||e.trim()!==e))throw new RuntimeCommandError("INVALID_PARAMS","modelOverrideId must be a non-empty string or null");const t=null===e?await modelSwitchTimingSpan(i.operationId,"resolve_agent_default_model",async()=>await this.metadata.resolveAgentDefaultModel(n,o)):void 0;if(null===e&&!t)throw new RuntimeCommandError("AGENT_DEFAULT_MODEL_NOT_CONFIGURED","agent default model is not configured");return await modelSwitchTimingSpan(i.operationId,"metadata_set_model",async()=>await this.metadata.setModel(n,e,t??void 0,o))}}throw new RuntimeCommandError("INVALID_PARAMS","unsupported session metadata command")}async projectCommitted(e,t,i){if("session.setModel"!==e.name)return;const s=e,n=t;if(this.metadata.projectCommittedSetModel&&await this.metadata.projectCommittedSetModel(s.context.conversation.runtimeSessionKey,n),"service"===i?.origin){if(!i.sessionModelProjection)throw new Error("Message Service session model projection is unavailable");return Object.freeze({state:"pending",revision:"0"})}if(!this.metadata.projectCommittedSetModel)throw new Error("session metadata projection is unavailable")}}export class OpenClawGatewaySessionMetadataPort{getGateway;projectModel;liveSessionKeys;constructor(e,t,i){this.getGateway=e,this.projectModel=t,this.liveSessionKeys=i}async load(e,t){const i=await this.requireExistingOpenClawKey(e,t);return await this.gateway().request("sessions.resolve",{sessionKey:i}),{loaded:!0}}async rename(e,t,i){const s=await this.requireExistingOpenClawStoreKey(e,i);return await this.gateway().request("sessions.patch",{key:s,label:t}),{title:t}}async delete(e,t){const i=await this.requireExistingOpenClawStoreKey(e,t);return await this.gateway().request("sessions.delete",{key:i}),{deleted:!0}}async setMode(e,t,i){const s="verbose"===t||"detailed"===t?{verboseLevel:"full"}:"reasoning"===t||"thinking"===t?{reasoningLevel:"stream"}:"concise"===t||"default"===t?{verboseLevel:"default"}:null;if(!s)throw new RuntimeCommandError("INVALID_PARAMS",`unsupported session mode: ${t}`);const n=await this.requireExistingOpenClawStoreKey(e,i);if(!isAuthoritativeSetModeAck(await this.gateway().request("sessions.patch",{key:n,...s}),n,s))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch did not acknowledge the exact target and effective mode");return{mode:t}}async resolveAgentDefaultModel(e,t){const i=runtimeAgentId(this.liveSessionKeys?(await this.resolveSetModelKey(e,t)).key:await this.requireExistingOpenClawKey(e,t)),s=await this.gateway().request("agents.list",{}),n=s?.agents?.find(e=>e.id===i),o=n?.defaultModel??n?.model;if("string"!=typeof o||!o.trim()||o.trim()!==o)return null;const r=toPlatformModelId(o),a=r.indexOf("/");return{modelId:r,...a>0?{modelProvider:r.slice(0,a)}:{}}}async setModel(e,t,i,s){const n=null===t?null:toOpenClawSessionModel(t),o=s?.attempt.operationId;let r=await modelSwitchTimingSpan(o,"resolve_set_model_key",async()=>await this.resolveSetModelKey(e,s));const a=async()=>await modelSwitchTimingSpan(o,"openclaw_sessions_patch",async()=>await this.gateway().request("sessions.patch",{key:r.key,model:n,mustExist:!r.materialized}));let l;try{l=await a()}catch(t){if(r.materialized||!isMissingOpenClawStoreSession(t,r.key))throw t;r=await modelSwitchTimingSpan(o,"materialize_set_model_key",async()=>await this.materializeSetModelKey(e,r.key,s)),l=await a()}const{key:c,materialized:d}=r;if(!isAuthoritativeSetModelAck(l,c,n,t))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch did not acknowledge the exact existing session and effective model");const m=l.resolved.model,u=toPlatformModelId(m);if(null===t&&(!i||u!==i.modelId))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch effective model does not match the prevalidated agent default");if(null!==t&&u!==t&&m!==n)throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch effective model does not match the requested override");if(d){const e=await modelSwitchTimingSpan(o,"openclaw_sessions_list_post_materialize",async()=>await this.gateway().request("sessions.list",{includeDerivedTitles:!0}));if(!openClawSessionKeysFromList(e).has(c))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch acknowledged a model-intent session that is absent from the authoritative store");if(!this.liveSessionKeys?.registerMaterializedModelKey)throw new RuntimeCommandError("OUTCOME_UNKNOWN","model-intent session registration authority is unavailable");this.liveSessionKeys.registerMaterializedModelKey(c)}return{...null===t?{}:{modelOverrideId:t},effectiveModelId:u,...l.resolved?.modelProvider?{modelProvider:l.resolved.modelProvider}:{}}}async projectCommittedSetModel(e,t){if(!this.projectModel)throw new Error("session metadata projection is unavailable");const i=await this.requireExistingOpenClawKey(e);await this.projectModel(i,t)}async requireExistingOpenClawKey(e,t){if(await(this.liveSessionKeys?.waitForHydration?.()),!this.liveSessionKeys)return e;try{return this.liveSessionKeys.resolveLiveKey(e)}catch(i){if(!t||!isMissingLiveSession(i)||!this.liveSessionKeys.reconcileMissingLiveKey)throw i;return await this.liveSessionKeys.reconcileMissingLiveKey(e,t),this.liveSessionKeys.resolveLiveKey(e)}}async requireExistingOpenClawStoreKey(e,t){const i=await this.requireExistingOpenClawKey(e,t),s=await this.gateway().request("sessions.list",{includeDerivedTitles:!0});if(!openClawSessionKeysFromList(s).has(i))throw new RuntimeCommandError("INVALID_PARAMS",`OpenClaw session key does not exist (refusing sessions.patch upsert): ${i}`);return i}async resolveSetModelKey(e,t){let i;const s="service"===t?.attempt.origin&&void 0!==t.attempt.verifiedMessageGrant;try{i=await this.requireExistingOpenClawKey(e,t)}catch(e){if(!(s&&t&&isMissingLiveSession(e)&&this.liveSessionKeys?.materializeMissingModelKey&&this.liveSessionKeys.registerMaterializedModelKey))throw e}return i?{key:i,materialized:!1}:await this.materializeSetModelKey(e,void 0,t)}async materializeSetModelKey(e,t,i){if(!("service"===i?.attempt.origin&&void 0!==i.attempt.verifiedMessageGrant&&i&&this.liveSessionKeys?.materializeMissingModelKey&&this.liveSessionKeys.registerMaterializedModelKey)){throw new RuntimeCommandError("INVALID_PARAMS",`OpenClaw session key does not exist (refusing sessions.patch upsert): ${t??e}`)}const s=await this.liveSessionKeys.materializeMissingModelKey(e,i);if(t&&s!==t)throw new RuntimeCommandError("INVALID_PARAMS","verified model-intent key conflicts with the live local session");return{key:s,materialized:!0}}gateway(){const e=this.getGateway();if(!e?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");return e}}function isMissingLiveSession(e){return e instanceof RuntimeCommandError&&"INVALID_PARAMS"===e.code&&e.message.startsWith("no live local session")}function isMissingOpenClawStoreSession(e,t){return e instanceof Error&&e.message===`No session found: ${t}`}function runtimeAgentId(e){const t=e.split(":");return"agent"===t[0]&&t[1]?.trim()?t[1]:"main"}function toOpenClawSessionModel(e){return e.startsWith(`${AROUTER_PROVIDER_ID}/`)?e:`${AROUTER_PROVIDER_ID}/${e}`}function toPlatformModelId(e){const t=`${AROUTER_PROVIDER_ID}/`;return e.startsWith(t)?e.slice(t.length):e}function isAuthoritativeSetModeAck(e,t,i){if(!e||"object"!=typeof e||Array.isArray(e))return!1;const s=e;if(!0!==s.ok||s.key!==t||!s.entry||"object"!=typeof s.entry||Array.isArray(s.entry))return!1;const n=s.entry;return"verboseLevel"in i?n.verboseLevel===i.verboseLevel:n.reasoningLevel===i.reasoningLevel}function isAuthoritativeSetModelAck(e,t,i,s){if(!e||"object"!=typeof e||Array.isArray(e))return!1;const n=e;if("ok"in n&&!0!==n.ok)return!1;if("key"in n&&n.key!==t)return!1;const o=n.resolved;if(!o||"object"!=typeof o||Array.isArray(o))return!1;const r=o.model;return"string"==typeof r&&0!==r.length&&r.trim()===r&&(null!==s||null===i)}export class LocalConversationHistoryBoundary{onCommit;kind="local";intents=new Map;convergence=new Map;generations=new Map;constructor(e){this.onCommit=e}currentGeneration(e){return String(this.generations.get(e)??0n)}async prepare(e){const t=this.intents.get(e.operationId);if(t)return t;const i=(new Date).toISOString(),s=this.generations.get(e.conversationId)??0n,n={intentId:randomUUID(),operationId:e.operationId,conversationId:e.conversationId,state:"prepared",runtimeEpoch:e.runtimeEpoch,fromGeneration:String(s),toGeneration:String(s+1n),boundaryOffset:"0",version:"1",createdAt:i,updatedAt:i};return this.intents.set(e.operationId,n),n}async markResetting(e,t){return this.assertState(e,"prepared"),this.convergence.set(e.operationId,t),this.transition(e,"resetting")}async loadRunConvergence(e){const t=this.convergence.get(e.operationId);if(!t)throw new Error("local clear convergence receipt missing");return t}async markResetDone(e){return this.assertState(e,"resetting"),this.transition(e,"reset_done")}async commit(e){return this.assertState(e,"reset_done"),await(this.onCommit?.(e.conversationId)),this.generations.set(e.conversationId,BigInt(e.toGeneration)),this.transition(e,"committed")}async abortPrepared(e){this.assertState(e,"prepared"),this.transition(e,"aborted")}transition(e,t){const i={...e,state:t,version:String(BigInt(e.version)+1n),updatedAt:(new Date).toISOString()};return this.intents.set(e.operationId,i),i}assertState(e,t){const i=this.intents.get(e.operationId);if(!i||i.version!==e.version||i.state!==t)throw new Error(`local clear transition fenced: expected ${t}`)}}
1
+ import{randomUUID}from"node:crypto";import{performance}from"node:perf_hooks";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";import{ClearConversationUseCase}from"./conversation-clear-use-case.js";import{AROUTER_PROVIDER_ID}from"../media/arouter-config.js";import{getOpenClawNativeSessions}from"./openclaw-native-sessions.js";import{openClawSessionKeysFromList}from"./openclaw-session-key-resolution.js";async function modelSwitchTimingSpan(e,t,i){const s=performance.now();let n="succeeded";try{return await i()}catch(e){throw n="failed",e}finally{e&&process.stderr.write(`[MODEL_SWITCH_TIMING] component=runtime operationId=${e} stage=${t} outcome=${n} durationMs=${(performance.now()-s).toFixed(1)}\n`)}}export class RuntimeSessionControlService{clearRuntime;fallback;activeRuns;constructor(e,t,i){this.clearRuntime=e,this.fallback=t,this.activeRuns=i}async execute(e,t,i){const s=e;if("session.clear"===s.name){if(!i.historyBoundary)throw new RuntimeCommandError("INVALID_PARAMS",`${i.origin} clear attempt lacks its history boundary`);const e=s.context.conversation;return await new ClearConversationUseCase(i.historyBoundary,this.clearRuntime).execute({operationId:s.context.operationId,conversationId:e.runtimeConversationUid,runtimeSessionKey:e.runtimeSessionKey,runtimeEpoch:s.context.dispatchEpoch})}if("session.cancel"===s.name){if(!this.activeRuns)throw new RuntimeCommandError("INVALID_PARAMS","session.cancel active-run authority is not configured");const e=s;return await this.activeRuns.cancelExact({runtimeConversationUid:e.context.conversation.runtimeConversationUid,runtimeSessionKey:e.context.conversation.runtimeSessionKey,historyGeneration:e.context.historyGeneration,targetMessageId:e.params.targetMessageId}),{}}if(!this.fallback)throw new Error(`session command delegate is not configured: ${s.name}`);return await this.fallback.execute(s,t,i)}async projectCommitted(e,t,i){return await(this.fallback?.projectCommitted?.(e,t,i))}}export class OpenClawSessionMetadataService{metadata;constructor(e){this.metadata="function"==typeof e?new OpenClawGatewaySessionMetadataPort(e):e}async execute(e,t,i){const s=e,n=s.context.conversation.runtimeSessionKey,o={lease:t,attempt:i};switch(s.name){case"session.load":return await this.metadata.load(n,o);case"session.rename":{const e=s.params.title.slice(0,64);return await this.metadata.rename(n,e,o)}case"session.delete":return await this.metadata.delete(n,o);case"session.setMode":{const e=s.params.mode;return await this.metadata.setMode(n,e,o)}case"session.setModel":{const e=s.params.modelOverrideId;if(null!==e&&("string"!=typeof e||0===e.length||e.trim()!==e))throw new RuntimeCommandError("INVALID_PARAMS","modelOverrideId must be a non-empty string or null");const t=null===e?await modelSwitchTimingSpan(i.operationId,"resolve_agent_default_model",async()=>await this.metadata.resolveAgentDefaultModel(n,o)):void 0;if(null===e&&!t)throw new RuntimeCommandError("AGENT_DEFAULT_MODEL_NOT_CONFIGURED","agent default model is not configured");return await modelSwitchTimingSpan(i.operationId,"metadata_set_model",async()=>await this.metadata.setModel(n,e,t??void 0,o))}}throw new RuntimeCommandError("INVALID_PARAMS","unsupported session metadata command")}async projectCommitted(e,t,i){if("session.setModel"!==e.name)return;const s=e,n=t;if(this.metadata.projectCommittedSetModel&&await this.metadata.projectCommittedSetModel(s.context.conversation.runtimeSessionKey,n),"service"===i?.origin){if(!i.sessionModelProjection)throw new Error("Message Service session model projection is unavailable");return Object.freeze({state:"pending",revision:"0"})}if(!this.metadata.projectCommittedSetModel)throw new Error("session metadata projection is unavailable")}}export class OpenClawGatewaySessionMetadataPort{getGateway;projectModel;liveSessionKeys;constructor(e,t,i){this.getGateway=e,this.projectModel=t,this.liveSessionKeys=i}async load(e,t){const i=await this.requireExistingOpenClawKey(e,t);return await this.gateway().request("sessions.resolve",{key:i}),{loaded:!0}}async rename(e,t,i){const s=await this.requireExistingOpenClawStoreKey(e,i);return await this.gateway().request("sessions.patch",{key:s,label:t,...await this.requireSessionIdentity(s)}),{title:t}}async delete(e,t){const i=await this.requireExistingOpenClawStoreKey(e,t);return await this.gateway().request("sessions.delete",{key:i}),{deleted:!0}}async setMode(e,t,i){const s="verbose"===t||"detailed"===t?{verboseLevel:"full"}:"reasoning"===t||"thinking"===t?{reasoningLevel:"stream"}:"concise"===t||"default"===t?{verboseLevel:"default"}:null;if(!s)throw new RuntimeCommandError("INVALID_PARAMS",`unsupported session mode: ${t}`);const n=await this.requireExistingOpenClawStoreKey(e,i);if(!isAuthoritativeSetModeAck(await this.gateway().request("sessions.patch",{key:n,...s,...await this.requireSessionIdentity(n)}),n,s))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch did not acknowledge the exact target and effective mode");return{mode:t}}async resolveAgentDefaultModel(e,t){const i=runtimeAgentId(this.liveSessionKeys?(await this.resolveSetModelKey(e,t)).key:await this.requireExistingOpenClawKey(e,t)),s=await this.gateway().request("agents.list",{}),n=s?.agents?.find(e=>e.id===i),o=n?.defaultModel??n?.model;if("string"!=typeof o||!o.trim()||o.trim()!==o)return null;const r=toPlatformModelId(o),a=r.indexOf("/");return{modelId:r,...a>0?{modelProvider:r.slice(0,a)}:{}}}async setModel(e,t,i,s){const n=null===t?null:toOpenClawSessionModel(t),o=s?.attempt.operationId;let r=await modelSwitchTimingSpan(o,"resolve_set_model_key",async()=>await this.resolveSetModelKey(e,s));const a=await this.readSessionIdentity(r.key);a||r.materialized||(r=await this.materializeSetModelKey(e,r.key,s));const l=await modelSwitchTimingSpan(o,"openclaw_sessions_patch",async()=>await this.gateway().request("sessions.patch",{key:r.key,model:n,...a?{expectedSessionId:a.sessionId,...a.lifecycleRevision?{expectedLifecycleRevision:a.lifecycleRevision}:{}}:{}})),{key:c,materialized:d}=r;if(!isAuthoritativeSetModelAck(l,c,n,t))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch did not acknowledge the exact existing session and effective model");const m=l.resolved.model,u=toPlatformModelId(m);if(null===t&&(!i||u!==i.modelId))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch effective model does not match the prevalidated agent default");if(null!==t&&u!==t&&m!==n)throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch effective model does not match the requested override");if(d){const e=await modelSwitchTimingSpan(o,"openclaw_sessions_list_post_materialize",async()=>await this.gateway().request("sessions.list",{includeDerivedTitles:!0}));if(!openClawSessionKeysFromList(e).has(c))throw new RuntimeCommandError("OUTCOME_UNKNOWN","sessions.patch acknowledged a model-intent session that is absent from the authoritative store");if(!this.liveSessionKeys?.registerMaterializedModelKey)throw new RuntimeCommandError("OUTCOME_UNKNOWN","model-intent session registration authority is unavailable");this.liveSessionKeys.registerMaterializedModelKey(c)}return{...null===t?{}:{modelOverrideId:t},effectiveModelId:u,...l.resolved?.modelProvider?{modelProvider:l.resolved.modelProvider}:{}}}async projectCommittedSetModel(e,t){if(!this.projectModel)throw new Error("session metadata projection is unavailable");const i=await this.requireExistingOpenClawKey(e);await this.projectModel(i,t)}async readSessionIdentity(e){const t=getOpenClawNativeSessions();if(t)return t.getEntry(runtimeAgentId(e),e);const i=await this.gateway().request("sessions.list",{includeDerivedTitles:!0}),s=i?.sessions?.filter(t=>(t.key??t.sessionKey)===e)??[];if(0===s.length)return;const n=s[0];if(1!==s.length||"string"!=typeof n.sessionId||!n.sessionId.trim())throw new RuntimeCommandError("INVALID_PARAMS","OpenClaw session identity is missing or ambiguous");return{sessionId:n.sessionId,..."string"==typeof n.lifecycleRevision&&n.lifecycleRevision?{lifecycleRevision:n.lifecycleRevision}:{}}}async requireSessionIdentity(e){const t=await this.readSessionIdentity(e);if(!t)throw new RuntimeCommandError("INVALID_PARAMS",`OpenClaw session key does not exist (refusing sessions.patch upsert): ${e}`);return{expectedSessionId:t.sessionId,...t.lifecycleRevision?{expectedLifecycleRevision:t.lifecycleRevision}:{}}}async requireExistingOpenClawKey(e,t){if(await(this.liveSessionKeys?.waitForHydration?.()),!this.liveSessionKeys)return e;try{return this.liveSessionKeys.resolveLiveKey(e)}catch(i){if(!t||!isMissingLiveSession(i)||!this.liveSessionKeys.reconcileMissingLiveKey)throw i;return await this.liveSessionKeys.reconcileMissingLiveKey(e,t),this.liveSessionKeys.resolveLiveKey(e)}}async requireExistingOpenClawStoreKey(e,t){const i=await this.requireExistingOpenClawKey(e,t),s=await this.gateway().request("sessions.list",{includeDerivedTitles:!0});if(!openClawSessionKeysFromList(s).has(i))throw new RuntimeCommandError("INVALID_PARAMS",`OpenClaw session key does not exist (refusing sessions.patch upsert): ${i}`);return i}async resolveSetModelKey(e,t){let i;const s="service"===t?.attempt.origin&&void 0!==t.attempt.verifiedMessageGrant;try{i=await this.requireExistingOpenClawKey(e,t)}catch(e){if(!(s&&t&&isMissingLiveSession(e)&&this.liveSessionKeys?.materializeMissingModelKey&&this.liveSessionKeys.registerMaterializedModelKey))throw e}return i?{key:i,materialized:!1}:await this.materializeSetModelKey(e,void 0,t)}async materializeSetModelKey(e,t,i){if(!("service"===i?.attempt.origin&&void 0!==i.attempt.verifiedMessageGrant&&i&&this.liveSessionKeys?.materializeMissingModelKey&&this.liveSessionKeys.registerMaterializedModelKey)){throw new RuntimeCommandError("INVALID_PARAMS",`OpenClaw session key does not exist (refusing sessions.patch upsert): ${t??e}`)}const s=await this.liveSessionKeys.materializeMissingModelKey(e,i);if(t&&s!==t)throw new RuntimeCommandError("INVALID_PARAMS","verified model-intent key conflicts with the live local session");return{key:s,materialized:!0}}gateway(){const e=this.getGateway();if(!e?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");return e}}function isMissingLiveSession(e){return e instanceof RuntimeCommandError&&"INVALID_PARAMS"===e.code&&e.message.startsWith("no live local session")}function runtimeAgentId(e){const t=e.split(":");return"agent"===t[0]&&t[1]?.trim()?t[1]:"main"}function toOpenClawSessionModel(e){return e.startsWith(`${AROUTER_PROVIDER_ID}/`)?e:`${AROUTER_PROVIDER_ID}/${e}`}function toPlatformModelId(e){const t=`${AROUTER_PROVIDER_ID}/`;return e.startsWith(t)?e.slice(t.length):e}function isAuthoritativeSetModeAck(e,t,i){if(!e||"object"!=typeof e||Array.isArray(e))return!1;const s=e;if(!0!==s.ok||s.key!==t||!s.entry||"object"!=typeof s.entry||Array.isArray(s.entry))return!1;const n=s.entry;return"verboseLevel"in i?n.verboseLevel===i.verboseLevel:n.reasoningLevel===i.reasoningLevel}function isAuthoritativeSetModelAck(e,t,i,s){if(!e||"object"!=typeof e||Array.isArray(e))return!1;const n=e;if("ok"in n&&!0!==n.ok)return!1;if("key"in n&&n.key!==t)return!1;const o=n.resolved;if(!o||"object"!=typeof o||Array.isArray(o))return!1;const r=o.model;return"string"==typeof r&&0!==r.length&&r.trim()===r&&(null!==s||null===i)}export class LocalConversationHistoryBoundary{onCommit;kind="local";intents=new Map;convergence=new Map;generations=new Map;constructor(e){this.onCommit=e}currentGeneration(e){return String(this.generations.get(e)??0n)}async prepare(e){const t=this.intents.get(e.operationId);if(t)return t;const i=(new Date).toISOString(),s=this.generations.get(e.conversationId)??0n,n={intentId:randomUUID(),operationId:e.operationId,conversationId:e.conversationId,state:"prepared",runtimeEpoch:e.runtimeEpoch,fromGeneration:String(s),toGeneration:String(s+1n),boundaryOffset:"0",version:"1",createdAt:i,updatedAt:i};return this.intents.set(e.operationId,n),n}async markResetting(e,t){return this.assertState(e,"prepared"),this.convergence.set(e.operationId,t),this.transition(e,"resetting")}async loadRunConvergence(e){const t=this.convergence.get(e.operationId);if(!t)throw new Error("local clear convergence receipt missing");return t}async markResetDone(e){return this.assertState(e,"resetting"),this.transition(e,"reset_done")}async commit(e){return this.assertState(e,"reset_done"),await(this.onCommit?.(e.conversationId)),this.generations.set(e.conversationId,BigInt(e.toGeneration)),this.transition(e,"committed")}async abortPrepared(e){this.assertState(e,"prepared"),this.transition(e,"aborted")}transition(e,t){const i={...e,state:t,version:String(BigInt(e.version)+1n),updatedAt:(new Date).toISOString()};return this.intents.set(e.operationId,i),i}assertState(e,t){const i=this.intents.get(e.operationId);if(!i||i.version!==e.version||i.state!==t)throw new Error(`local clear transition fenced: expected ${t}`)}}
@@ -2,7 +2,7 @@
2
2
  "id": "beeos-claw",
3
3
  "name": "beeos-claw",
4
4
  "description": "BeeOS platform bridge plugin for OpenClaw. Provides file upload/download, ACP bridge, and observability.",
5
- "version": "0.6.48-staging.1",
5
+ "version": "0.6.66-staging.1",
6
6
  "activation": {
7
7
  "onStartup": true
8
8
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beeos-claw",
3
- "version": "0.6.65",
3
+ "version": "0.6.66-staging.1",
4
4
  "description": "OpenClaw plugin that bridges the BeeOS platform with the local OpenClaw Gateway, providing file transfer, ACP bridge, and observability.",
5
5
  "license": "MIT",
6
6
  "author": "BeeOS <dev@beeos.ai>",
@@ -68,7 +68,7 @@
68
68
  "@beeos-ai/beeos-types": "4.4.1",
69
69
  "@beeos-ai/bridge-client": "^0.1.0",
70
70
  "@beeos-ai/message-sdk": "2.0.16",
71
- "@modelcontextprotocol/sdk": "^1.29.0",
71
+ "@modelcontextprotocol/sdk": "^1.32.1",
72
72
  "adm-zip": "0.6.1",
73
73
  "ws": "^8.18.0"
74
74
  },
@@ -81,11 +81,11 @@
81
81
  "eslint": "^9.39.4",
82
82
  "terser": "^5.39.0",
83
83
  "typescript": "^5.6.3",
84
- "vitest": "^4.1.0"
84
+ "vitest": "^4.1.11"
85
85
  },
86
86
  "overrides": {
87
- "fast-uri": ">=3.1.4",
88
- "brace-expansion": ">=5.0.9"
87
+ "fast-uri": ">=4.2.1",
88
+ "brace-expansion": ">=5.0.12"
89
89
  },
90
- "gitHead": "f841b1237980ec7cf1d00a506f64337e0f4e7343"
90
+ "gitHead": "228d57b6cfc296b693b95812a0b84b0b2aefc6bf"
91
91
  }